【问题标题】:AWS IAM SDK getting all policy documents for a specific role when using Cognito group rolesAWS IAM SDK 在使用 Cognito 组角色时获取特定角色的所有策略文档
【发布时间】:2020-12-05 23:19:16
【问题描述】:

我正在使用 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 网关之前进行重复数据删除。

真的没有更简单/更快的方法可以做到这一点吗?有没有办法用更少的调用来获取所有这些信息?有没有办法在它们的规则重叠的情况下扁平化策略?

【问题讨论】:

    标签: c# amazon-web-services aws-sdk amazon-cognito amazon-iam


    【解决方案1】:

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

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

    假设您有一个非常标准的场景,其中您的 API Gateway 方法与 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 将具有一个 执行角色,它定义了访问受保护资源(例如 DynamoDB 表中的数据等)的所有权限。因此,只要您的支持 lambda 访问不同的 AWS 资源,您就不会不必更新多个组角色的权限,而是在系统中的一个位置管理这些权限。

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

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

    自定义授权方响应的缓存

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

    如果授权人前面有多个方法,例如:

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

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

    涵盖所有/user/* 路由的授权方的示例响应

    {
      "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",
              "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",
      "policyDocument": {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Action": "execute-api:Invoke",
            "Effect": "Allow",
            "Resource": [
              "arn:aws:execute-api:eu-west-1:1234567890:qwerty:prod:*/user/*"
            ]
          }
        ]
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-08
      • 2018-11-02
      • 2020-04-25
      • 2018-09-29
      • 2020-11-26
      • 2019-07-08
      相关资源
      最近更新 更多