-
Notifications
You must be signed in to change notification settings - Fork 303
feat(internal): support optional username/password in basic auth when configured in IR #14378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
ddfd1f1
7ace343
072175f
bcaa25f
b15016a
899333a
69540a3
176ccd4
f212400
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -523,36 +523,63 @@ def _write_get_headers_body(writer: AST.NodeWriter) -> None: | |
| writer.write_newline_if_last_line_not() | ||
| basic_auth_scheme = self._get_basic_auth_scheme() | ||
| if basic_auth_scheme is not None: | ||
| either_omitted = ( | ||
| getattr(basic_auth_scheme, "username_omit", None) is True | ||
| or getattr(basic_auth_scheme, "password_omit", None) is True | ||
| ) | ||
| if not self._context.ir.sdk_config.is_auth_mandatory: | ||
| username_var = names.get_username_constructor_parameter_name(basic_auth_scheme) | ||
| password_var = names.get_password_constructor_parameter_name(basic_auth_scheme) | ||
| writer.write_line(f"{username_var} = self.{names.get_username_getter_name(basic_auth_scheme)}()") | ||
| writer.write_line(f"{password_var} = self.{names.get_password_getter_name(basic_auth_scheme)}()") | ||
| writer.write_line(f"if {username_var} is not None and {password_var} is not None:") | ||
| condition_op = "or" if either_omitted else "and" | ||
| writer.write_line(f"if {username_var} is not None {condition_op} {password_var} is not None:") | ||
| with writer.indent(): | ||
| writer.write(f'headers["{ClientWrapperGenerator.AUTHORIZATION_HEADER}"] = ') | ||
| if either_omitted: | ||
| writer.write_node( | ||
| AST.ClassInstantiation( | ||
| class_=httpx.HttpX.BASIC_AUTH, | ||
| args=[ | ||
| AST.Expression(f'{username_var} or ""'), | ||
| AST.Expression(f'{password_var} or ""'), | ||
| ], | ||
| ) | ||
| ) | ||
| else: | ||
| writer.write_node( | ||
| AST.ClassInstantiation( | ||
| class_=httpx.HttpX.BASIC_AUTH, | ||
| args=[ | ||
| AST.Expression(f"{username_var}"), | ||
| AST.Expression(f"{password_var}"), | ||
| ], | ||
| ) | ||
| ) | ||
| writer.write("._auth_header") | ||
| writer.write_newline_if_last_line_not() | ||
| else: | ||
| writer.write(f'headers["{ClientWrapperGenerator.AUTHORIZATION_HEADER}"] = ') | ||
| if either_omitted: | ||
| writer.write_node( | ||
| AST.ClassInstantiation( | ||
| class_=httpx.HttpX.BASIC_AUTH, | ||
| args=[ | ||
| AST.Expression(f"{username_var}"), | ||
| AST.Expression(f"{password_var}"), | ||
| AST.Expression(f'self.{names.get_username_getter_name(basic_auth_scheme)}() or ""'), | ||
| AST.Expression(f'self.{names.get_password_getter_name(basic_auth_scheme)}() or ""'), | ||
| ], | ||
| ) | ||
| ) | ||
|
Comment on lines
+563
to
572
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing validation when auth is mandatory and If both Fix: if either_omitted:
username_expr = f"self.{names.get_username_getter_name(basic_auth_scheme)}()"
password_expr = f"self.{names.get_password_getter_name(basic_auth_scheme)}()"
writer.write_line(f"_username = {username_expr}")
writer.write_line(f"_password = {password_expr}")
writer.write_line("if _username is None and _password is None:")
with writer.indent():
writer.write_line('raise ValueError("At least one of username or password must be provided")')
writer.write(f'headers["{ClientWrapperGenerator.AUTHORIZATION_HEADER}"] = ')
writer.write_node(
AST.ClassInstantiation(
class_=httpx.HttpX.BASIC_AUTH,
args=[
AST.Expression('_username or ""'),
AST.Expression('_password or ""'),
],
)
)Spotted by Graphite |
||
| writer.write("._auth_header") | ||
| writer.write_newline_if_last_line_not() | ||
| else: | ||
| writer.write(f'headers["{ClientWrapperGenerator.AUTHORIZATION_HEADER}"] = ') | ||
| writer.write_node( | ||
| AST.ClassInstantiation( | ||
| class_=httpx.HttpX.BASIC_AUTH, | ||
| args=[ | ||
| AST.Expression(f"self.{names.get_username_getter_name(basic_auth_scheme)}()"), | ||
| AST.Expression(f"self.{names.get_password_getter_name(basic_auth_scheme)}()"), | ||
| ], | ||
| else: | ||
| writer.write_node( | ||
| AST.ClassInstantiation( | ||
| class_=httpx.HttpX.BASIC_AUTH, | ||
| args=[ | ||
| AST.Expression(f"self.{names.get_username_getter_name(basic_auth_scheme)}()"), | ||
| AST.Expression(f"self.{names.get_password_getter_name(basic_auth_scheme)}()"), | ||
| ], | ||
| ) | ||
| ) | ||
| ) | ||
| writer.write("._auth_header") | ||
| writer.write_newline_if_last_line_not() | ||
| for param in constructor_parameters: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -122,21 +122,27 @@ export class RootClientGenerator extends FileGenerator<RubyFile, SdkCustomConfig | |
| } | ||
| const usernameName = basicAuthScheme.username.snakeCase.safeName; | ||
| const passwordName = basicAuthScheme.password.snakeCase.safeName; | ||
| // usernameOmit/passwordOmit may exist in newer IR versions | ||
| const scheme = basicAuthScheme as unknown as Record<string, unknown>; | ||
| const eitherOmitted = scheme.usernameOmit === true || scheme.passwordOmit === true; | ||
|
Comment on lines
+126
to
+127
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟡 Ruby generator uses CLAUDE.md explicitly prohibits Prompt for agentsWas this helpful? React with 👍 or 👎 to provide feedback.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Bumping the Ruby generator's IR SDK dependency to v65 would be a larger change with potential side effects and is beyond the scope of this PR. The cast safely handles the version mismatch — if the fields don't exist at runtime (older IR), the |
||
| const conditionOp = eitherOmitted ? "||" : "&&"; | ||
| const usernameExpr = eitherOmitted ? `${usernameName} || ""` : usernameName; | ||
| const passwordExpr = eitherOmitted ? `${passwordName} || ""` : passwordName; | ||
| if (isAuthOptional || basicAuthSchemes.length > 1) { | ||
| if (i === 0) { | ||
| writer.writeLine(`if !${usernameName}.nil? && !${passwordName}.nil?`); | ||
| writer.writeLine(`if !${usernameName}.nil? ${conditionOp} !${passwordName}.nil?`); | ||
| } else { | ||
| writer.writeLine(`elsif !${usernameName}.nil? && !${passwordName}.nil?`); | ||
| writer.writeLine(`elsif !${usernameName}.nil? ${conditionOp} !${passwordName}.nil?`); | ||
| } | ||
| writer.writeLine( | ||
| ` headers["Authorization"] = "Basic #{Base64.strict_encode64("#{${usernameName}}:#{${passwordName}}")}"` | ||
| ` headers["Authorization"] = "Basic #{Base64.strict_encode64("#{${usernameExpr}}:#{${passwordExpr}}")}"` | ||
| ); | ||
| if (i === basicAuthSchemes.length - 1) { | ||
| writer.writeLine(`end`); | ||
| } | ||
| } else { | ||
| writer.writeLine( | ||
| `headers["Authorization"] = "Basic #{Base64.strict_encode64("#{${usernameName}}:#{${passwordName}}")}"` | ||
| `headers["Authorization"] = "Basic #{Base64.strict_encode64("#{${usernameExpr}}:#{${passwordExpr}}")}"` | ||
| ); | ||
| } | ||
| } | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.