Add IAM console access entitlement and grants#118
Conversation
…ibute Add console_access entitlement on AWS account resources (both account and account_iam) so that IAM users with AWS Management Console access (LoginProfile) are surfaced as grants in ConductorOne. Implementation: - Fetch IAM credential report (GenerateCredentialReport + GetCredentialReport) once per account per sync to determine console access status - Emit console_access assignment entitlement on account resources, grantable to iam_user principals - Emit grants for each IAM user with password_enabled=true in the report - Enrich IAM user profiles with console_access_enabled, console_password_last_used, and console_password_last_changed attributes - Add iam:GenerateCredentialReport and iam:GetCredentialReport to required permissions for account, account_iam, and iam_user resource types - Remove SkipEntitlementsAndGrants from account_iam resource type - Credential report fetch degrades gracefully (warns and skips) if permissions are missing Fixes: CXH-1523
| PasswordLastUsed string | ||
| PasswordLastChanged string | ||
| } | ||
|
|
||
| func (e *credentialReportEntry) IsPasswordEnabled() bool { |
There was a problem hiding this comment.
🟠 Bug: The credentialReportEntry struct does not capture the arn column from the credential report CSV. Both account.go:442 and account_iam.go:138 construct the user ARN as fmt.Sprintf("arn:aws:iam::%s:user/%s", accountID, username), but this is incorrect for IAM users with non-default paths (e.g., a user john under path /admins/ has ARN arn:aws:iam::ACCOUNT:user/admins/john, not arn:aws:iam::ACCOUNT:user/john).
The credential report CSV has an arn column with the full ARN. Add an ARN field here, parse it from the CSV, and use it in grant construction instead of building the ARN from the username. Without this fix, console access grants for IAM users with paths won't match the resources created in iam_user.go:List (which uses user.Arn from the API).
| userARN := fmt.Sprintf("arn:aws:iam::%s:user/%s", accountID, username) | ||
| uID, err := resourceSdk.NewResourceID(resourceTypeIAMUser, userARN) |
There was a problem hiding this comment.
🟠 Bug: This constructs the ARN using only the username from the credential report, omitting the IAM user path. IAM users created under non-default paths (e.g., /admins/) will produce an incorrect ARN that doesn't match the resource ID set in iam_user.go:List. Use the arn column from the credential report CSV instead of constructing it manually. See the comment on credential_report.go for the full fix.
| userARN := fmt.Sprintf("arn:aws:iam::%s:user/%s", accountID, username) | ||
| uID, err := resourceSdk.NewResourceID(resourceTypeIAMUser, userARN) |
There was a problem hiding this comment.
🟠 Bug: Same ARN construction issue as account.go — IAM users with non-default paths will produce incorrect ARNs. Use the arn column from the credential report entry instead.
| cacheKey = parentId.Resource | ||
| } | ||
|
|
||
| o.credReportMu.Lock() | ||
| defer o.credReportMu.Unlock() | ||
|
|
||
| if o.credReportCache == nil { | ||
| o.credReportCache = make(map[string]map[string]*credentialReportEntry) | ||
| } | ||
| if report, ok := o.credReportCache[cacheKey]; ok { | ||
| return report | ||
| } | ||
|
|
||
| report := fetchCredentialReportBestEffort(ctx, iamClient) | ||
| o.credReportCache[cacheKey] = report | ||
| return report | ||
| } | ||
|
|
||
| func userTagsToMap(u iamTypes.User) map[string]interface{} { |
There was a problem hiding this comment.
🟡 Suggestion: The mutex is held for the entire duration of fetchCredentialReportBestEffort, which polls AWS for up to 2 minutes. Consider releasing the lock after the cache check, performing the API call without the lock, then re-acquiring to store the result. This avoids blocking concurrent callers unnecessarily. A double-check pattern (check cache → unlock → fetch → lock → check again → store) would prevent redundant fetches while keeping the critical section short.
Connector PR Review: Add IAM console access entitlement and grantsBlocking Issues: 1 | Suggestions: 1 | Threads Resolved: 0 Review SummaryThe new commits simplify Security IssuesNone found. Correctness Issues
Suggestions
Prompt for AI agents |
Summary
console_accessentitlement on AWS account resources (accountandaccount_iam) to represent IAM console login capabilitypassword_enabled=trueconsole_access_enabled,console_password_last_used, andconsole_password_last_changedattributesGenerateCredentialReport/GetCredentialReportAPIs with graceful degradation if permissions are missingDetails
New file:
credential_report.goCore module for fetching and parsing the AWS IAM credential report CSV. Polls
GenerateCredentialReportwith 2-second intervals (up to 2 minutes), then parses the CSV into per-user entries.fetchCredentialReportBestEffort()wraps the fetch with graceful degradation — logs a warning and returns nil if the report cannot be generated.Modified:
iam_user.gosync.Mutex+ map keyed by parent account ID) to avoid redundant API calls during multi-account Orgs syncsiamUserProfile()now enriches the user profile with console access attributes from the credential reportModified:
account_iam.goandaccount.goconsole_accessassignment entitlement (grantable to IAM users)Grants()fetches the credential report and emits grants for users with passwords enabledAWSClientFactory.GetIAMClient()with fallback to the default IAM clientModified:
resource_types.goSkipEntitlementsAndGrantsfromresourceTypeAccountIamiam:GenerateCredentialReportandiam:GetCredentialReportpermissions to relevant resource typesModified:
connector.goTest plan
console_accessgrantsconsole_accessentitlement and grantsiam:GenerateCredentialReportpermissionconsole_access_enabled,console_password_last_used,console_password_last_changedFixes: CXH-1523
Note
Automated PR - This PR was created by an automated process. Please review the changes carefully before merging.
If you have any questions or concerns, please reach out to the team for assistance.