Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type ProviderFormValues = {
smallFastModel: string;
accessKey: string;
accessKeySecret: string;
roleArn: string;
apiKey: string;
enabled: boolean;
};
Expand Down Expand Up @@ -66,6 +67,7 @@ const defaultInitialValues: ProviderFormValues = {
smallFastModel: "",
accessKey: "",
accessKeySecret: "",
roleArn: "",
apiKey: "",
enabled: true,
};
Expand Down Expand Up @@ -526,6 +528,16 @@ export const ProviderForm: FC<ProviderFormProps> = ({
View docs
</DocsLink>
</p>
<FormField
field={getFieldHelpers("roleArn")}
label="Role ARN"
className="w-full"
placeholder="arn:aws:iam::123456789012:role/BedrockRole"
/>
<p className="text-xs text-content-secondary m-0">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 [CRF-1] The Bedrock provider docs (docs/ai-coder/ai-gateway/providers.md, linked via "View docs" in the credentials section above) list SDK default credentials and static access keys but never mention role assumption, STS, or cross-account access. The backend already validates role ARNs (validateAIProviderRoleARN in codersdk/aiproviders.go:326), but none of the mechanism or required permissions reach the admin.

The inline help text here is functional but minimal. An admin configuring cross-account Bedrock access for the first time will click "View docs," find nothing about roles, and have to guess what permissions the assumed role needs (bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream).

Consider adding a section under the Bedrock provider docs covering: what the Role ARN field does, when to use it (cross-account access), and what permissions the assumed role requires. This can be a separate docs PR.

(Leorio)

🤖

Optional. When a role ARN is set, the gateway assumes that role
(using the base identity) before calling Bedrock.
</p>
</>
)}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const baseOpenAIFormValues: ProviderFormValues = {
smallFastModel: "",
accessKey: "",
accessKeySecret: "",
roleArn: "",
apiKey: "sk-test",
enabled: true,
};
Expand All @@ -42,6 +43,7 @@ const baseBedrockFormValues: ProviderFormValues = {
smallFastModel: "anthropic.claude-haiku-4-5",
accessKey: "AKIA-test",
accessKeySecret: "secret",
roleArn: "",
apiKey: "",
enabled: true,
};
Expand All @@ -55,6 +57,7 @@ const baseCopilotFormValues: ProviderFormValues = {
smallFastModel: "",
accessKey: "",
accessKeySecret: "",
roleArn: "",
apiKey: "",
enabled: true,
};
Expand Down Expand Up @@ -407,6 +410,30 @@ describe("providerFormValuesToCreate", () => {
});
expect(req.api_keys).toBeUndefined();
});

it("includes role_arn when a role ARN is provided", () => {
const req = providerFormValuesToCreate({
...baseBedrockFormValues,
roleArn: "arn:aws:iam::123456789012:role/BedrockRole",
});
const s = req.settings as unknown as Record<string, unknown>;
expect(s.role_arn).toBe("arn:aws:iam::123456789012:role/BedrockRole");
});

it("omits role_arn when the form value is blank", () => {
const req = providerFormValuesToCreate(baseBedrockFormValues);
const s = req.settings as unknown as Record<string, unknown>;
expect(s.role_arn).toBeUndefined();
});

it("trims whitespace around the role ARN", () => {
const req = providerFormValuesToCreate({
...baseBedrockFormValues,
roleArn: " arn:aws:iam::123456789012:role/BedrockRole ",
});
const s = req.settings as unknown as Record<string, unknown>;
expect(s.role_arn).toBe("arn:aws:iam::123456789012:role/BedrockRole");
});
});

describe("Copilot", () => {
Expand Down Expand Up @@ -559,6 +586,32 @@ describe("providerFormValuesToUpdate", () => {
expect(s.access_key).toBeUndefined();
expect(s.access_key_secret).toBeUndefined();
});

it("sends role_arn even when the access keys are kept", () => {
const req = providerFormValuesToUpdate(
{
...baseBedrockFormValues,
accessKey: SAVED_CREDENTIAL_MASK,
accessKeySecret: SAVED_CREDENTIAL_MASK,
roleArn: "arn:aws:iam::123456789012:role/BedrockRole",
},
MockAIProviderBedrock,
);
const s = req.settings as unknown as Record<string, unknown>;
expect(s.role_arn).toBe("arn:aws:iam::123456789012:role/BedrockRole");
});

it("omits role_arn when the field is cleared", () => {
const req = providerFormValuesToUpdate(
{
...baseBedrockFormValues,
roleArn: "",
},
MockAIProviderBedrock,
);
const s = req.settings as unknown as Record<string, unknown>;
expect(s.role_arn).toBeUndefined();
});
});

describe("Copilot", () => {
Expand Down Expand Up @@ -635,6 +688,23 @@ describe("aiProviderToFormValues", () => {
expect(values.accessKeySecret).toBe("");
});

it("round-trips role_arn back into the form", () => {
const provider: AIProvider = {
...MockAIProviderBedrock,
settings: settings({
_type: "bedrock",
role_arn: "arn:aws:iam::123456789012:role/BedrockRole",
}),
};
const values = aiProviderToFormValues(provider);
expect(values.roleArn).toBe("arn:aws:iam::123456789012:role/BedrockRole");
});

it("seeds an empty role ARN when the provider has none", () => {
const values = aiProviderToFormValues(MockAIProviderBedrock);
expect(values.roleArn).toBe("");
});

it("seeds Copilot form values without a credential field", () => {
const values = aiProviderToFormValues(MockAIProviderCopilot);
expect(values.type).toBe("copilot");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ const buildBedrockSettings = (
smallFastModel: string,
accessKey: string,
accessKeySecret: string,
roleArn: string,
): BedrockSettingsWire => ({
_type: BEDROCK_SETTINGS_TYPE,
_version: BEDROCK_SETTINGS_VERSION,
Expand All @@ -117,6 +118,7 @@ const buildBedrockSettings = (
small_fast_model: smallFastModel,
...(accessKey ? { access_key: accessKey } : {}),
...(accessKeySecret ? { access_key_secret: accessKeySecret } : {}),
...(roleArn ? { role_arn: roleArn } : {}),
});

// Bedrock credentials live in `settings`; openai/anthropic keys go in
Expand All @@ -141,6 +143,7 @@ export const providerFormValuesToCreate = (
values.smallFastModel.trim(),
sanitizeCredential(values.accessKey),
sanitizeCredential(values.accessKeySecret),
values.roleArn.trim(),
);
return {
type: "anthropic",
Expand Down Expand Up @@ -215,6 +218,7 @@ export const providerFormValuesToUpdate = (
values.smallFastModel.trim(),
credentialsChanged ? newAccessKey : "",
credentialsChanged ? newAccessKeySecret : "",
values.roleArn.trim(),
);

return { ...base, settings: settings as AIProviderSettings };
Expand All @@ -238,6 +242,7 @@ export const aiProviderToFormValues = (
smallFastModel: s.small_fast_model ?? "",
accessKey: "",
accessKeySecret: "",
roleArn: s.role_arn ?? "",
enabled: provider.enabled,
};
}
Expand Down
Loading