Skip to content

Commit

Permalink
Merge pull request #24 from p0-security/nathan/role-assume-error
Browse files Browse the repository at this point in the history
assume: Print a friendly error message when no access
  • Loading branch information
nbrahms authored Feb 22, 2024
2 parents 02c4c35 + ea30a36 commit 77e650d
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 19 deletions.
42 changes: 24 additions & 18 deletions src/commands/aws/role.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,29 @@ export const initOktaSaml = async (
};
};

/** Extracts all roles from a SAML assertion */
export const rolesFromSaml = (account: string, saml: string) => {
const samlText = Buffer.from(saml, "base64").toString("ascii");
const samlObject = parseXml(samlText);
const samlAttributes =
samlObject["saml2p:Response"]["saml2:Assertion"][
"saml2:AttributeStatement"
]["saml2:Attribute"];
const roleAttribute = samlAttributes.find(
(a: any) =>
a._attributes.Name === "https://aws.amazon.com/SAML/Attributes/Role"
);
// Format:
// 'arn:aws:iam::391052057035:saml-provider/p0dev-ext_okta_sso,arn:aws:iam::391052057035:role/path/to/role/SSOAmazonS3FullAccess'
const arns = (
flatten([roleAttribute?.["saml2:AttributeValue"]]) as string[]
)?.map((r) => r.split(",")[1]!);
const roles = arns
.filter((r) => r.startsWith(`arn:aws:iam::${account}:role/`))
.map((r) => r.split("/").slice(1).join("/"));
return { arns, roles };
};

/** Assumes a role in AWS via Okta SAML federation.
*
* Prerequisites:
Expand Down Expand Up @@ -97,24 +120,7 @@ Or, populate these environment variables using BASH command substitution:
const oktaAwsListRoles = async (args: { account?: string }) => {
const authn = await authenticate();
const { account, samlResponse } = await initOktaSaml(authn, args.account);
const samlText = Buffer.from(samlResponse, "base64").toString("ascii");
const samlObject = parseXml(samlText);
const samlAttributes =
samlObject["saml2p:Response"]["saml2:Assertion"][
"saml2:AttributeStatement"
]["saml2:Attribute"];
const roleAttribute = samlAttributes.find(
(a: any) =>
a._attributes.Name === "https://aws.amazon.com/SAML/Attributes/Role"
);
// Format:
// 'arn:aws:iam::391052057035:saml-provider/p0dev-ext_okta_sso,arn:aws:iam::391052057035:role/path/to/role/SSOAmazonS3FullAccess'
const arns = (
flatten([roleAttribute?.["saml2:AttributeValue"]]) as string[]
)?.map((r) => r.split(",")[1]!);
const roles = arns
.filter((r) => r.startsWith(`arn:aws:iam::${account}:role/`))
.map((r) => r.split("/").slice(1).join("/"));
const { arns, roles } = rolesFromSaml(account, samlResponse);
const isTty = sys.writeOutputIsTTY?.();
if (isTty) print2(`Your available roles for account ${account}:`);
if (!roles?.length) {
Expand Down
5 changes: 4 additions & 1 deletion src/plugins/okta/aws.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { initOktaSaml } from "../../commands/aws/role";
import { initOktaSaml, rolesFromSaml } from "../../commands/aws/role";
import { cached } from "../../drivers/auth";
import { Authn } from "../../types/identity";
import { assumeRoleWithSaml } from "../aws/assumeRole";
Expand All @@ -14,6 +14,9 @@ export const assumeRoleWithOktaSaml = async (
authn,
args.account
);
const { roles } = rolesFromSaml(account, samlResponse);
if (!roles.includes(args.role))
throw `Role not available. Available roles:\n${roles.map((r) => ` ${r}`).join("\n")}`;
return await assumeRoleWithSaml({
account,
role: args.role,
Expand Down

0 comments on commit 77e650d

Please sign in to comment.