UNPKG

@cloud-copilot/iam-collect

Version:

Collect IAM information from AWS Accounts

486 lines 19.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AuthorizationDetailsSync = void 0; exports.getAccessKeysForUser = getAccessKeysForUser; exports.getLoginProfileForUser = getLoginProfileForUser; exports.getMfaDevicesForUser = getMfaDevicesForUser; exports.parseUsernameFromArn = parseUsernameFromArn; exports.getAllUsers = getAllUsers; exports.getAllUsersWithMetadata = getAllUsersWithMetadata; exports.getAuthorizationDetails = getAuthorizationDetails; exports.getPoliciesAttachedDirectlyToRole = getPoliciesAttachedDirectlyToRole; exports.getManagedPoliciesAttachedToRole = getManagedPoliciesAttachedToRole; const client_iam_1 = require("@aws-sdk/client-iam"); const ClientPool_js_1 = require("../../aws/ClientPool.js"); const client_tools_js_1 = require("../../utils/client-tools.js"); const tags_js_1 = require("../../utils/tags.js"); const sync_js_1 = require("../sync.js"); exports.AuthorizationDetailsSync = { awsService: 'iam', name: 'authorizationDetails', global: true, execute: async (accountId, region, credentials, storage, endpoint, syncOptions) => { const client = ClientPool_js_1.AwsClientPool.defaultInstance.client(client_iam_1.IAMClient, credentials, region, endpoint); const authDetails = await getAuthorizationDetails(client); const roles = authDetails.roles || []; const roleData = roles.map((role) => { return { arn: role.Arn, 'managed-policies': role.AttachedManagedPolicies?.map((p) => p.PolicyArn), 'trust-policy': role.AssumeRolePolicyDocument, 'instance-profiles': role.InstanceProfileList?.map((i) => i.Arn), 'inline-policies': role.RolePolicyList, tags: (0, tags_js_1.convertTagsToRecord)(role.Tags), metadata: { arn: role.Arn, name: role.RoleName, id: role.RoleId, path: role.Path, created: role.CreateDate, permissionBoundary: role.PermissionsBoundary?.PermissionsBoundaryArn } }; }); await (0, sync_js_1.syncData)(roleData, storage, accountId, { service: 'iam', resourceType: 'role', account: accountId }); const groupArns = {}; const groupData = authDetails.groups.map((group) => { groupArns[group.GroupName] = group.Arn; return { arn: group.Arn, 'inline-policies': group.GroupPolicyList, 'managed-policies': group.AttachedManagedPolicies?.map((p) => p.PolicyArn), metadata: { arn: group.Arn, path: group.Path, name: group.GroupName, id: group.GroupId, created: group.CreateDate } }; }); await (0, sync_js_1.syncData)(groupData, storage, accountId, { service: 'iam', resourceType: 'group', account: accountId }); const customerPolicyData = authDetails.policies.map((policy) => { return { arn: policy.Arn, metadata: { arn: policy.Arn, name: policy.PolicyName, id: policy.PolicyId, description: policy.Description, defaultVersionId: policy.DefaultVersionId, path: policy.Path, permissionsBoundaryUsageCount: policy.PermissionsBoundaryUsageCount, isAttachable: policy.IsAttachable, createDate: policy.CreateDate, updateDate: policy.UpdateDate }, 'current-policy': policy.PolicyVersionList?.filter((version) => version.IsDefaultVersion).at(0)?.Document, policy: undefined, tags: (0, tags_js_1.convertTagsToRecord)(policy.Tags) }; }); await (0, sync_js_1.syncData)(customerPolicyData, storage, accountId, { service: 'iam', resourceType: 'policy', account: accountId }); const awsManagedPolicyData = authDetails.awsManagedPolicies.map((policy) => { return { arn: policy.Arn, metadata: { arn: policy.Arn, name: policy.PolicyName, id: policy.PolicyId, description: policy.Description, defaultVersionId: policy.DefaultVersionId, path: policy.Path, permissionsBoundaryUsageCount: policy.PermissionsBoundaryUsageCount, isAttachable: policy.IsAttachable, createDate: policy.CreateDate, updateDate: policy.UpdateDate }, 'current-policy': policy.PolicyVersionList?.filter((version) => version.IsDefaultVersion).at(0)?.Document, policy: undefined }; }); await (0, sync_js_1.syncData)(awsManagedPolicyData, storage, accountId, { service: 'iam', resourceType: 'policy', account: 'aws' }); const userData = authDetails.users.map((user) => { return { arn: user.Arn, 'managed-policies': user.AttachedManagedPolicies?.map((p) => p.PolicyArn), 'inline-policies': user.UserPolicyList, groups: user.GroupList?.map((g) => groupArns[g]), tags: (0, tags_js_1.convertTagsToRecord)(user.Tags), metadata: { arn: user.Arn, path: user.Path, permissionBoundary: user.PermissionsBoundary?.PermissionsBoundaryArn, id: user.UserId, name: user.UserName, created: user.CreateDate } }; }); (0, sync_js_1.syncData)(userData, storage, accountId, { service: 'iam', resourceType: 'user', account: accountId }); } }; /** * Get the access keys for an IAM user. * * @param region The region to use for the API call * @param credentials The credentials to use for the API call * @param userName The name of the user to lookup the access keys for * @returns Returns a list of access keys for the user. Will return an empty array if there are no access keys */ async function getAccessKeysForUser(client, userName) { const listAccessKeysCommand = new client_iam_1.ListAccessKeysCommand({ UserName: userName }); const result = await (0, client_tools_js_1.runAndCatch404)(() => { return client.send(listAccessKeysCommand); }); const accessKeys = result?.AccessKeyMetadata || []; const lastUsed = await Promise.all(accessKeys.map(async (key) => { const command = new client_iam_1.GetAccessKeyLastUsedCommand({ AccessKeyId: key.AccessKeyId }); const result = await client.send(command); return { keyId: key.AccessKeyId, results: result.AccessKeyLastUsed }; })); return accessKeys.map((key) => { return { ...key, lastUsed: lastUsed.find((lu) => lu.keyId == key.AccessKeyId)?.results || undefined }; }); } /** * Get the login profile for an IAM user if it exists. * * @param region The region to use for the API call * @param credentials The credentials to use for the API call * @param userName The name of the user to lookup the login profile for * @returns Returns the login profile for the user if it exists. Otherwise returns undefined */ async function getLoginProfileForUser(client, userName) { const loginProfileCommand = new client_iam_1.GetLoginProfileCommand({ UserName: userName }); return (0, client_tools_js_1.runAndCatch404)(async () => { const loginProfile = await client.send(loginProfileCommand); return loginProfile.LoginProfile; }); } /** * Get the MFA devices for an IAM user. * * @param region The region to use for the API call * @param credentials The credentials to use for the API call * @param userName The name of the user to lookup the MFA devices for * @returns Returns a list of MFA devices for the user. Will return an empty array if there are no MFA devices. */ async function getMfaDevicesForUser(client, userName) { const listMfaDevicesCommand = new client_iam_1.ListMFADevicesCommand({ UserName: userName }); const result = await (0, client_tools_js_1.runAndCatch404)(async () => { const result = await client.send(listMfaDevicesCommand); return result.MFADevices || []; }); return result || []; } /** * Parses a username out of an ARN. Does not validate the ARN is a valid IAM user ARN. * * @param arn The arn to parse the username out of * @returns Returns the username from the ARN */ function parseUsernameFromArn(arn) { return arn.split('/').at(-1); } /** * Get all IAM users in an account. * * @param region The region to use for the API call * @param credentials The credentials to use for the API call * @returns Returns a list of all IAM users in the account */ async function getAllUsers(client) { const userList = []; let isTruncated = true; let marker = undefined; let listUsersCommand; while (isTruncated) { listUsersCommand = new client_iam_1.ListUsersCommand({ Marker: marker, MaxItems: 1000 }); const usersResult = await client.send(listUsersCommand); userList.push(...(usersResult.Users || [])); isTruncated = usersResult.IsTruncated || false; marker = usersResult.Marker; } return userList; } /** * Get all IAM users in an account with metadata about each user. * * @param region The region to use for the API call * @param credentials The credentials to use for the API call * @returns Returns all users for the account with metadata about each user */ async function getAllUsersWithMetadata(client) { const users = await getAllUsers(client); await Promise.all(users.map(async (user) => addMetaDataToUser(client, user))); return users; } async function addMetaDataToUser(client, user) { user.metadata = { hasConsoleAccess: false, createdAt: user.CreateDate, passwordChanged: undefined, passwordLastUsed: user.PasswordLastUsed, mfaEnabled: false, numberAccessKeys: 0, accessKeys: [], managedPolicies: [], inlinePolicies: [], groups: [] }; const [loginProfile, accessKeys, mfaDevices, userManagedPolicies, userInlinePolicies, userGroups] = await Promise.all([ getLoginProfileForUser(client, user.UserName), getAccessKeysForUser(client, user.UserName), getMfaDevicesForUser(client, user.UserName), getManagedPoliciesAttachedToUser(client, user.UserName), getInlinePolicesAttachedToUser(client, user.UserName), getGroupsForUser(client, user.UserName) ]); if (loginProfile) { user.metadata.hasConsoleAccess = true; user.metadata.passwordChanged = loginProfile.CreateDate; } const activeKeys = accessKeys.filter((k) => k.Status == 'Active'); user.metadata.numberAccessKeys = activeKeys.length; user.metadata.oldestAccessKey = activeKeys .map((k) => k.CreateDate) .sort() .at(0); user.metadata.accessKeyLastUsed = activeKeys .map((k) => k.lastUsed?.LastUsedDate) .sort() .at(0); user.metadata.accessKeys = activeKeys; user.metadata.mfaEnabled = mfaDevices.length > 0; user.metadata.managedPolicies = userManagedPolicies; user.metadata.inlinePolicies = userInlinePolicies; user.metadata.groups = userGroups; } /** * Return the results of the Authorization Details call for this account. * Excludes users and AWS managed policies. * * @param credentials The credentials to use for the API call * @returns Returns the results of the Authorization Details call for this account */ async function getAuthorizationDetails(client) { let isTruncated = false; let getDetailsCommand; let response; let marker; const groupDetails = []; const roleDetails = []; const policyDetails = []; const awsManagedPolicies = []; const userDetails = []; do { getDetailsCommand = new client_iam_1.GetAccountAuthorizationDetailsCommand({ Marker: marker, Filter: ['Role', 'Group', 'LocalManagedPolicy', 'AWSManagedPolicy', 'User'] }); response = await client.send(getDetailsCommand); groupDetails.push(...(response.GroupDetailList?.map(parseGroupDocs) || [])); roleDetails.push(...(response.RoleDetailList?.map(parseRoleDocs) || [])); userDetails.push(...(response.UserDetailList?.map(parseUserDocs) || [])); for (const policy of response.Policies || []) { const policyDetail = parsePolicyDocs(policy); if (policyDetail.Arn?.startsWith('arn:aws:iam::aws:policy/')) { awsManagedPolicies.push(policyDetail); } else { policyDetails.push(policyDetail); } // policyDetails.push(...(response.Policies?.map(parsePolicyDocs) || [])) } isTruncated = !!response.IsTruncated; marker = response.Marker; } while (isTruncated); const policiesWithTags = await getTagsForMangedPolicies(client, policyDetails); return { groups: groupDetails, roles: roleDetails, policies: policiesWithTags, awsManagedPolicies, users: userDetails }; } async function getTagsForMangedPolicies(client, policies) { const promises = policies.map(async (policy) => { const command = new client_iam_1.ListPolicyTagsCommand({ PolicyArn: policy.Arn }); const tags = await (0, client_tools_js_1.runAndCatch404)(async () => { const result = await client.send(command); return result.Tags; }); return { ...policy, Tags: tags }; }); return Promise.all(promises); } /** * Gets the policies that are attached directly to a role. Does not include managed policies. * * @param credentials The credentials to use for the API call * @param roleName The name of the role to get the policies for * @returns Returns the policies that are attached directly to the role */ async function getPoliciesAttachedDirectlyToRole(client, roleName) { const command = new client_iam_1.ListRolePoliciesCommand({ RoleName: roleName }); const results = await client.send(command); const roleNames = results.PolicyNames; const policyDetails = await Promise.all(roleNames.map(async (policyName) => { const policyCommand = new client_iam_1.GetRolePolicyCommand({ RoleName: roleName, PolicyName: policyName }); const policyResult = await client.send(policyCommand); return policyResult; })); return policyDetails.map((policyDetail) => ({ name: policyDetail.PolicyName, document: JSON.parse(decodeURIComponent(policyDetail.PolicyDocument)) })); } async function getManagedPoliciesAttachedToRole(client, roleName) { const command = new client_iam_1.ListAttachedRolePoliciesCommand({ RoleName: roleName }); const results = await client.send(command); const policies = results.AttachedPolicies; return policies; } /** * Decodes and parses the policy documents in the group. * * @param group The GroupDetail object to parse the policy documents for * @returns Returns the GroupDetail object with the policy documents decoded and parsed */ function parseGroupDocs(group) { if (group.GroupPolicyList) { group.GroupPolicyList.forEach((policy) => { if (policy.PolicyDocument) { policy.PolicyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument)); } }); } return group; } /** * Decodes and parses the policy documents in the role. * * @param role the RoleDetail object to parse the policy documents for * @returns Returns the RoleDetail object with the policy documents decoded and parsed */ function parseRoleDocs(role) { if (role.AssumeRolePolicyDocument) { role.AssumeRolePolicyDocument = JSON.parse(decodeURIComponent(role.AssumeRolePolicyDocument)); } if (role.RolePolicyList) { role.RolePolicyList.forEach((policy) => { if (policy.PolicyDocument) { policy.PolicyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument)); } }); } return role; } /** * Decodes and parses the policy documents in the managed policy. * * @param policy The ManagedPolicyDetail object to parse the policy documents for * @returns Returns the ManagedPolicyDetail object with the policy documents decoded and parsed */ function parsePolicyDocs(policy) { if (policy.PolicyVersionList) { policy.PolicyVersionList.forEach((version) => { if (version.Document) { version.Document = JSON.parse(decodeURIComponent(version.Document)); } }); } return policy; } /** * Decodes and parses the policy documents attached directly to a user. * * @param user The userDetail object to parse the policy documents for * @returns Returns the UserDetail object with the policy documents decoded and parsed */ function parseUserDocs(user) { if (user.UserPolicyList) { user.UserPolicyList.forEach((policy) => { if (policy.PolicyDocument) { policy.PolicyDocument = JSON.parse(decodeURIComponent(policy.PolicyDocument)); } }); } return user; } /** * Get the managed policies attached to a user. * * @param client The IAM client to use * @param username The user's username * @returns Returns the ARNs of the managed policies attached to the user */ async function getManagedPoliciesAttachedToUser(client, username) { const command = new client_iam_1.ListAttachedUserPoliciesCommand({ UserName: username }); const results = await client.send(command); const policyArns = results.AttachedPolicies.map((policy) => policy.PolicyArn); return policyArns; } /** * Get the inline policies attached to a user. * * @param client The IAM client to use * @param username The username of the user * @returns Returns the inline policies attached to the user */ async function getInlinePolicesAttachedToUser(client, username) { const command = new client_iam_1.ListUserPoliciesCommand({ UserName: username }); const results = await client.send(command); const policyNames = results.PolicyNames; const policyDetails = await Promise.all(policyNames.map(async (policyName) => { const policyCommand = new client_iam_1.GetUserPolicyCommand({ UserName: username, PolicyName: policyName }); const policyResult = await client.send(policyCommand); return { name: policyName, document: JSON.parse(decodeURIComponent(policyResult.PolicyDocument)) }; })); return policyDetails; } /** * Get the groups a user is a member of. * * @param client The IAM client to use * @param username The username of the user * @returns Returns the ARNs of the groups the user is a member of */ async function getGroupsForUser(client, username) { const command = new client_iam_1.ListGroupsForUserCommand({ UserName: username }); const results = await client.send(command); const groupArns = results.Groups.map((group) => group.Arn); return groupArns; } //# sourceMappingURL=authorizationDetails.js.map