当使用Cognito组角色时,AWS IAM SDK会获取特定角色的所有策略文档

问题描述

我正在使用AWS Cognito用户池组来管理API Gateway API的权限。我相信这是网上论坛的有效用法,如文档所示:https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-user-groups.html#using-groups-to-control-permission-with-amazon-api-gateway

不幸的是,据我所知,该用例的文档基本上是不存在的(除了那段小段落)。我试图弄清楚它如何与自定义API网关授权者Lambda函数一起使用。我已经创建了一个测试角色,并将其分配给了Cognito中的一个测试组。该角色具有单个策略,但将来该角色将具有多个策略。

现在,在我的自定义授权者中,我已经在验证访问令牌等,并且一切正常。我现在正在尝试在使用组/角色/策略中添加此细粒度的访问控制。我已经安装了IAM SDK,并一直在挖掘以查看需要进行哪些呼叫。似乎没有简单的方法来获得角色及其所有策略。我想出的最好的方法如下:

public async Task<IEnumerable<string>> GetGroupPermissionsForUserAsync(Models.User user)
{
    if (!user.UserAttributes.TryGetValue(UserAttributeName.UserPoolId,out var userPoolId))
    {
        return null;
    }

    var groups = await GetUserGroups(user.Username,userPoolId);
    var groupRoleArn = groups.FirstOrDefault()?.RoleArn;
    var policies = new List<string>();
    if (!string.IsNullOrWhiteSpace(groupRoleArn))
    {
        var roleName = groupRoleArn.Substring(groupRoleArn.IndexOf('/') + 1);
        var rolePoliciesResponse = await _iamClient.ListAttachedRolePoliciesAsync(new ListAttachedRolePoliciesRequest { RoleName = roleName });
        
        foreach (var rolePolicy in rolePoliciesResponse.AttachedPolicies)
        {
            var policyVersionsResponse = await _iamClient.ListPolicyVersionsAsync(new ListPolicyVersionsRequest
            {
                PolicyArn = rolePolicy.PolicyArn
            });

            var latestPolicyVerson = policyVersionsResponse.Versions.OrderByDescending(x => x.CreateDate).LastOrDefault();
            var policyVersionResponse = await _iamClient.GetPolicyVersionAsync(new GetPolicyVersionRequest
            {
                PolicyArn = rolePolicy.PolicyArn,VersionId = latestPolicyVerson.VersionId
            });
            
            if (!string.IsNullOrWhiteSpace(policyVersionResponse?.PolicyVersion.Document))
            {
                policies.Add(HttpUtility.UrlDecode(policyVersionResponse.PolicyVersion.Document));
            }
        }
    }

    return policies;
}

private async Task<IEnumerable<GroupType>> GetUserGroups(string username,string userPoolId)
{
    string nextToken = null;
    var groups = new List<GroupType>();
    do
    {
        var response = await _cognitoClient.AdminListGroupsForUserAsync(new AdminListGroupsForUserRequest
        {
            Username = username,UserPoolId = userPoolId
        });

        groups.AddRange(response.Groups);

        nextToken = response.NextToken;
    } while (!string.IsNullOrWhiteSpace(nextToken));

    return groups.OrderBy(x => x.Precedence);
}

如您所见,我必须打很多电话才能获得角色策略。

  1. _cognitoClient.AdminListGroupsForUserAsync从Cognito获取用户组。
  2. _iamClient.ListAttachedRolePoliciesAsync获取与角色关联的策略。
  3. _iamClient.ListPolicyVersionsAsync获取每个策略的版本。
  4. _iamClient.GetPolicyVersionAsync获取最终包含策略文档的单个策略版本。

ListPolicyVersionsAsync返回带有文档属性的响应,但由于某种原因它始终为null,因此需要额外的GetPolicyVersionAsync调用

这不仅增加了延迟(这是授权函数一个问题,在该函数中,每次对API的调用都将通过此代码运行),但它给我留下了一堆我需要某种方式的单独策略删除重复数据,然后再返回API网关。

真的没有更容易/更快的方法吗?是否可以通过较少的呼叫获得所有这些信息,并且是否可以在规则重叠的情况下展平策略?

解决方法

使用Cognito和API Gateway设置授权的方法有很多,很难在不知道您试图通过API Gateway方法访问哪些AWS资源的情况下说出最适合您的方法。例如,您的API Gateway方法是与访问数据库信息的lambda方法集成在一起,还是直接调用服务?

如果我理解正确,那么您希望合并用户可能所属的(可能)多个组的权限,以便提出用于该特定请求的一组权限。因此,user角色可以允许访问用户数据,而admin角色可以进一步扩展对其他管理端点的访问?

假设您有一个非常标准的方案,其中API网关方法与lambda方法集成在一起,然后该lambda方法访问诸如数据库之类的基础AWS资源,那么您可以按以下方式使用自定义授权程序:

如果自定义授权者仅返回执行请求的API方法所需的权限,则可以大大简化操作:

{
  "principalId": "sub-from-ID-token","policyDocument": {
    "Version": "2012-10-17","Statement": [
      {
        "Action": "execute-api:Invoke","Effect": "Allow","Resource": [
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:GET/user/address"
        ]
      }
    ]
  }
}

在标准情况下,成功授权的请求将执行包含api逻辑的lambda。此后备Lambda将具有 execution角色,该角色定义了访问受保护资源(例如DynamoDB表中的数据等)的所有权限。因此,每当后备Lambda访问其他AWS资源时,您都不会不必更新多个组角色的权限,而是在系统中的单个位置管理这些权限。

使用此设置,您可以为需要保护的api的每个区域提供一个相当简单的自定义授权器。

例如,为了保护您的/user/*端点,您可以有一个自定义授权者,该授权者仅检查传递给授权者的 ID令牌是否包含user组在其cognito:groups声明中。如果是这样,则您返回执行请求的api方法(execute-api:Invoke)所需的权限。然后,您可能会有另一个自定义授权者,通过检查/admin/*组是否在ID令牌的admin声明中,来保护cognito:groups路由,等等。

自定义授权者响应的缓存

每个自定义授权者都有一个可选的TTL设置,该设置确定其响应(对于特定的JWT令牌)将被缓存多长时间。这有助于减少任何lambda预热时间,或减少授权者中其他标注所花费的时间。

如果授权者采用多种方法,例如:

  • 获取/用户/地址
  • POST /用户/地址
  • 获取/ user / mobile

然后必须注意,将为所有这些方法缓存成功授权的响应,直到缓存超时为止。因此,自定义授权者返回的策略必须返回定义用户可以执行的所有方法的资源列表,或者使用通配符。

涵盖所有/user/*条路线的授权者的示例响应

{
  "principalId": "sub-from-ID-token","Resource": [
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:GET/user/address","arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:POST/user/address","arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:GET/user/mobile"
        ]
      }
    ]
  }
}

涵盖所有/user/*路由的授权者的示例响应(使用通配符)

{
  "principalId": "sub-from-ID-token","Resource": [
          "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:*/user/*"
        ]
      }
    ]
  }
}