Skip to content

Conversation

@mateovespConductor
Copy link
Contributor

@mateovespConductor mateovespConductor commented Nov 19, 2025

Description

  • Bug fix
  • New feature

Useful links:

Summary by CodeRabbit

  • New Features

    • Resolve AWS IAM role grants by parsing URL‑encoded trust policies to derive principal relationships (users, roles, groups, accounts, and SSO users).
  • Improvements

    • Robust handling of varied Principal formats and malformed policy input.
    • Expanded grant targets (IAM users, roles, groups, SSO users) with per‑principal error handling and clearer logging during role resolution.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

Parses URL-encoded IAM AssumeRole trust policies to extract AWS principal ARNs, classifies principal types (IAM Role/User), and implements roleResourceType.Grants to fetch an IAM role, parse its trust policy, create principal resources, and emit grants per detected principal.

Changes

Cohort / File(s) Summary
Principal extraction helpers
pkg/connector/helpers.go
Added extractTrustPrincipals(policyDocument string) ([]string, error), containsAssumeRole(action any) bool, extractAWSPrincipals(principalField any) []string, and detectPrincipalResource(principalARN string) (*v2.ResourceType, string, bool). Introduced encoding/json and net/url imports. Handles URL-decoding, JSON parsing, filtering for sts:AssumeRole (and wildcards), extracting AWS principal ARNs from multiple Principal shapes, and classifying ARN types (IAM user/role).
Role grants implementation
pkg/connector/role.go
Implemented roleResourceType.Grants to validate resource IDs, resolve the IAM client (using parent when present), call IAM GetRole, handle missing or empty AssumeRolePolicyDocument, extract trust principals via helpers, detect principal resource types, create principal resources, and build grants with roleAssignmentEntitlement. Added imports (path, arn, grant, ctxzap, zap) and enhanced logging and per-principal error handling.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Caller
    participant Grants as roleResourceType.Grants
    participant IAM as IAM Client
    participant Helpers as helpers.go
    participant Creator as Principal Resource Creator
    participant Result

    Caller->>Grants: Grants(ctx, resource, token)
    Grants->>Grants: validate resource ID, resolve IAM client (use parent if present)
    alt no IAM client
        Grants->>Result: return error
    else IAM client available
        Grants->>IAM: GetRole(roleName)
        alt role missing or no AssumeRolePolicyDocument
            Grants->>Result: return no grants
        else role with policy
            Grants->>Helpers: extractTrustPrincipals(policyDocument)
            Helpers->>Helpers: URL-decode → JSON-unmarshal → filter Allow + sts:AssumeRole / sts:* / *
            Helpers-->>Grants: list of principal ARNs
            loop for each principal ARN
                Grants->>Helpers: detectPrincipalResource(principalARN)
                Helpers-->>Grants: (resourceType, id, ok)
                alt ok
                    Grants->>Creator: create principal resource (type, id)
                    Creator-->>Grants: principal resource or error
                    Grants->>Grants: build grant via roleAssignmentEntitlement
                else
                    Grants->>Grants: skip principal (log)
                end
            end
            Grants->>Result: return grants
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • URL-decoding and JSON unmarshalling error handling in extractTrustPrincipals.
    • Correct recognition of sts:AssumeRole, sts:*, and * in containsAssumeRole.
    • Handling varied Principal shapes (string, list, map) in extractAWSPrincipals.
    • ARN parsing and detection logic in detectPrincipalResource.
    • IAM client resolution (parent vs direct), logging, and per-principal grant creation in roleResourceType.Grants.

Poem

🐇
I nibble bytes where policies hide,
URL-decoded dreams open wide,
ARNs hop out in moonlit queues,
Roles and users find their dues,
I log a grant and leap anew.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'BB-1481 Add grants to iam_roles' clearly describes the main feature being added: grants functionality for IAM roles.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mateovesp/add-iam-role-grants

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 70314e2 and 4bdfe78.

📒 Files selected for processing (1)
  • pkg/connector/helpers.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/connector/helpers.go

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
pkg/connector/helpers.go (1)

242-274: Consider using the arn.Parse utility for more robust ARN parsing.

The principal detection logic is correct, but for consistency with other helper functions in this file (e.g., iamUserNameFromARN, iamGroupNameFromARN), consider using the arn.Parse utility from the AWS SDK. This would provide more robust validation and cleaner resource type detection.

Example refactor:

 func detectPrincipalResource(principalARN string) (*v2.ResourceType, string, bool) {
+	// Skip wildcards
+	if principalARN == "*" {
+		return nil, "", false
+	}
+
+	// Skip service principals
+	if strings.Contains(principalARN, ":service/") {
+		return nil, "", false
+	}
+
+	parsedARN, err := arn.Parse(principalARN)
+	if err != nil {
+		return nil, "", false
+	}
+
 	switch {
-	// Skip wildcards and service principals — not actual IAM identities
-	case principalARN == "*",
-		strings.Contains(principalARN, ":service/"):
-		return nil, "", false
-
 	// IAM User ARN (e.g. arn:aws:iam::123456789012:user/Alice)
-	case strings.Contains(principalARN, ":user/"):
+	case strings.HasPrefix(parsedARN.Resource, "user/"):
 		return resourceTypeIAMUser, principalARN, true
 
 	// IAM Role ARN (e.g. arn:aws:iam::123456789012:role/MyRole)
-	case strings.Contains(principalARN, ":role/"):
+	case strings.HasPrefix(parsedARN.Resource, "role/"):
 		return resourceTypeRole, principalARN, true
 
 	// Account root (e.g. arn:aws:iam::123456789012:root)
-	case strings.HasSuffix(principalARN, ":root"):
-		parts := strings.Split(principalARN, ":")
-		if len(parts) >= 5 {
-			accountID := parts[4]
-			return resourceTypeAccountIam, accountID, true
-		}
-		return nil, "", false
+	case parsedARN.Resource == "root":
+		return resourceTypeAccountIam, parsedARN.AccountID, true
 
 	// Everything else → unsupported or not relevant
 	default:
 		return nil, "", false
 	}
 }
pkg/connector/role.go (2)

135-139: Consider using arn.Parse for more robust role name extraction.

The current string splitting approach is correct for IAM role ARNs, but for consistency with other helper functions in this codebase (e.g., iamUserNameFromARN), consider using the arn.Parse utility from the AWS SDK.

Apply this diff:

-	parts := strings.Split(resource.Id.Resource, "/")
-	if len(parts) < 2 {
-		return nil, "", nil, fmt.Errorf("invalid role ARN: %s", resource.Id.Resource)
+	parsedARN, err := arn.Parse(resource.Id.Resource)
+	if err != nil {
+		return nil, "", nil, fmt.Errorf("invalid role ARN: %w", err)
 	}
-	roleName := parts[len(parts)-1]
+	roleName := path.Base(parsedARN.Resource)
+	if roleName == "/" || roleName == "." {
+		return nil, "", nil, fmt.Errorf("invalid role resource in ARN: %s", resource.Id.Resource)
+	}

Note: You'll need to import the arn package if not already imported.


150-152: Consider logging when AssumeRolePolicyDocument is nil.

While returning nil grants is correct when there's no trust policy, adding a debug or info log would improve observability, helping operators understand why a role has no grants.

Apply this diff:

 	if roleResp.Role.AssumeRolePolicyDocument == nil {
+		l.Debug("role has no AssumeRolePolicyDocument, returning no grants",
+			zap.String("role_name", roleName),
+		)
 		return nil, "", nil, nil
 	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37651b4 and 68c68da.

📒 Files selected for processing (2)
  • pkg/connector/helpers.go (2 hunks)
  • pkg/connector/role.go (3 hunks)
🔇 Additional comments (5)
pkg/connector/helpers.go (3)

4-6: LGTM!

The added imports appropriately support trust policy decoding and JSON parsing.


140-189: LGTM!

The trust policy parsing logic is well-structured with appropriate error handling for URL decoding and JSON parsing. The function correctly filters for "Allow" statements with "sts:AssumeRole" actions.


191-240: LGTM!

The helper functions correctly handle both single and list forms of Action and Principal fields, which aligns with AWS IAM policy flexibility.

pkg/connector/role.go (2)

6-6: LGTM!

The added imports appropriately support the Grants implementation with string manipulation, grant creation, and structured logging.

Also applies to: 15-15, 17-18


161-184: LGTM!

The grant creation logic is well-structured with appropriate error handling. The implementation correctly skips invalid principals while logging errors, which ensures resilient operation without failing the entire grant extraction process.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
pkg/connector/role.go (1)

169-181: Consider making trust‑policy parse failures a soft error instead of failing Grants

Right now any error from extractTrustPrincipals causes Grants to return an error for this role. Given that GetRole failures and empty roles are treated as “log and skip,” it may be more robust to do the same here: log the parse error (with role name and maybe ARN) and return no grants, rather than failing the sync for this resource because of an unexpected or slightly malformed trust policy.

For example:

-	principals, err := extractTrustPrincipals(
-		awsSdk.ToString(roleResp.Role.AssumeRolePolicyDocument),
-	)
-	if err != nil {
-		return nil, "", nil, fmt.Errorf("failed to extract principals for role %s: %w", roleName, err)
-	}
+	principals, err := extractTrustPrincipals(
+		awsSdk.ToString(roleResp.Role.AssumeRolePolicyDocument),
+	)
+	if err != nil {
+		l.Error("baton-aws: failed to extract trust principals from AssumeRolePolicyDocument, returning no grants",
+			zap.String("role_name", roleName),
+			zap.Error(err),
+		)
+		return nil, "", nil, nil
+	}

Optionally, you could also add a debug/info log in the !ok branch after detectPrincipalResource so that unsupported principal strings don’t silently disappear.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68c68da and d0c6452.

📒 Files selected for processing (2)
  • pkg/connector/helpers.go (2 hunks)
  • pkg/connector/role.go (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/connector/helpers.go
🔇 Additional comments (1)
pkg/connector/role.go (1)

114-199: Grants implementation correctly derives role name, fetches trust policy, and maps principals to grants

The end-to-end flow (resource validation, IAM client resolution, ARN parsing with path.Base, GetRole, trust principal extraction, and grant.NewGrant construction) is coherent and matches how List+Get need to be combined for IAM. From a correctness standpoint this looks good and I don’t see blocking issues here.

Please ensure existing connector tests (or new ones) cover:

  • A role with multiple trusted IAM users/roles.
  • A role with no AssumeRolePolicyDocument.
  • A role whose AssumeRolePolicyDocument includes principals that should be ignored (e.g., services), to confirm grants are emitted only for intended principals.

@mateovespConductor mateovespConductor changed the title [BB-1481] Add grants to roles for iam_users [BB-1481] Add grants to iam_roles Nov 20, 2025
iamClient := o.iamClient
if resource.ParentResourceId != nil {
var err error
iamClient, err = o.awsClientFactory.GetIAMClient(ctx, resource.ParentResourceId.Resource)
Copy link
Contributor

@btipling btipling Nov 20, 2025

Choose a reason for hiding this comment

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

Just a comment or request for any changes and not any issue. I think this is fine, I don't think these make any API calls to create the clients and they are cached. I don't think this type of cache would be a problem on a lambda, but not 100% sure

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f01cf8a and 4324052.

📒 Files selected for processing (2)
  • pkg/connector/helpers.go (2 hunks)
  • pkg/connector/role.go (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (9)
pkg/connector/helpers.go (3)

140-189: LGTM! Well-structured trust policy parsing.

The function correctly handles URL decoding, JSON parsing, and statement filtering. The logic for extracting AWS principals from statements with Effect "Allow" and action "sts:AssumeRole" is sound.


191-208: LGTM! Clean type switch implementation.

The function correctly handles both single string and slice formats for IAM policy actions.


210-240: LGTM! Handles both string and array principal formats.

The function correctly extracts AWS principals from the Principal field, handling both single ARN strings and arrays of ARNs.

pkg/connector/role.go (6)

107-112: LGTM! Proper expansion of grantable resource types.

The entitlement now correctly supports grants to IAM users, roles, groups, and SSO users, which aligns with AWS IAM trust policy capabilities.


124-139: LGTM! Proper input validation and client resolution.

The function correctly validates the resource and resolves the IAM client based on parent resource context, following the same pattern as the List method.


141-149: LGTM! Correct ARN parsing and role name extraction.

Using path.Base correctly extracts the role name from the ARN resource field, even when the role has a path prefix.


154-160: Verify that silently ignoring GetRole errors is intentional.

When GetRole fails, the function logs the error but returns nil, "", nil, nil (success with no grants) rather than propagating the error. This means:

  • Permission errors or transient failures won't fail the sync
  • Operators may not notice that grants are missing for this role
  • The connector continues processing other resources

Is this error-swallowing behavior intentional to ensure partial sync success, or should GetRole failures propagate as errors?

Similar error handling patterns also appear at:

  • Lines 162-165 (nil response handling)
  • Lines 167-172 (missing AssumeRolePolicyDocument)

174-179: LGTM! Correct trust policy principal extraction.

The function properly extracts principals from the trust policy and propagates errors.


181-216: LGTM! Well-structured grant creation with proper error handling.

The loop correctly:

  • Filters principals using detectPrincipalResource
  • Creates principal resources with appropriate error logging
  • Adds GrantExpandable annotations for role principals to support nested role assumptions
  • Constructs grants for each valid principal

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
pkg/connector/helpers.go (1)

242-248: Simplify the duplicate function comment.

The function comment contains redundant text across lines 242-247, making it harder to read. The purpose is stated twice in slightly different ways.

Apply this diff to consolidate the comment:

-// detectPrincipalResource analyzes a principal ARN and determines:
-// which Baton resource type it corresponds to (IAM user, IAM role, or account root)
-// the resource identifier to use in the Grant
-// It returns ok=false when the principal should be ignored.
-// detectPrincipalResource determines what type of IAM principal an ARN belongs to.
-// Supports IAM users and IAM roles.
+// detectPrincipalResource analyzes a principal ARN and determines which Baton resource type
+// it corresponds to (IAM user or IAM role) and the resource identifier to use in the Grant.
+// Returns ok=false when the principal should be ignored (e.g., account root, service principals).
 func detectPrincipalResource(principalARN string) (*v2.ResourceType, string, bool) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4324052 and a975e9e.

📒 Files selected for processing (1)
  • pkg/connector/helpers.go (2 hunks)
🔇 Additional comments (3)
pkg/connector/helpers.go (3)

4-6: LGTM!

The new imports are appropriate for parsing URL-encoded JSON trust policies.


140-189: LGTM!

The trust policy parsing logic correctly URL-decodes, filters for Allow+AssumeRole statements, and gracefully handles malformed data. The defensive approach of silently skipping invalid statements is appropriate for resilient parsing of AWS policy documents.


210-240: LGTM!

The principal extraction logic correctly handles both single string and list formats for AWS principals, with appropriate nil returns for invalid cases.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
pkg/connector/helpers.go (1)

247-253: Comment still mentions account root even though it’s ignored.

The function only returns matches for IAM users (user/) and IAM roles (role/), and returns ok=false for account root and other principals, but the second comment line still says “(IAM user, IAM role, or account root)”. This conflicts with the implementation and the later “Supports IAM users and IAM roles” line.

Recommend updating the block comment to remove the “account root” mention and clarify what’s ignored, e.g.:

-// detectPrincipalResource analyzes a principal ARN and determines:
-// which Baton resource type it corresponds to (IAM user, IAM role, or account root)
-// the resource identifier to use in the Grant
-// It returns ok=false when the principal should be ignored.
-// detectPrincipalResource determines what type of IAM principal an ARN belongs to.
-// Supports IAM users and IAM roles.
+// detectPrincipalResource analyzes a principal ARN and determines:
+// which Baton resource type it corresponds to (IAM user or IAM role)
+// the resource identifier to use in the Grant.
+// It returns ok=false when the principal should be ignored (e.g., account root, service principals).
+// detectPrincipalResource determines what type of IAM principal an ARN belongs to.
+// Supports IAM users and IAM roles.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a975e9e and 70314e2.

📒 Files selected for processing (1)
  • pkg/connector/helpers.go (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test
🔇 Additional comments (2)
pkg/connector/helpers.go (2)

191-213: Wildcard sts:AssumeRole detection logic looks good.

The updated containsAssumeRole correctly handles both scalar and list Action fields and treats "sts:AssumeRole", "sts:*", and "*" as granting AssumeRole. This should cover the common wildcard policy patterns without introducing false positives in typical IAM trust policies.


215-245: AWS principal extraction is safe and focused.

extractAWSPrincipals cleanly restricts itself to Principal["AWS"], supports both single-string and list forms, and safely ignores non-map or non-string cases. That matches the expected trust policy shapes and avoids accidentally treating non-AWS principals (e.g., Service) as grantees.

Add defense in case a statement is created as a single object
@mateovespConductor mateovespConductor merged commit d7a934a into main Dec 10, 2025
4 checks passed
@mateovespConductor mateovespConductor deleted the mateovesp/add-iam-role-grants branch January 2, 2026 12:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants