AWS IAM SDK รับเอกสารนโยบายทั้งหมดสำหรับบทบาทเฉพาะเมื่อใช้บทบาทกลุ่ม Cognito

Aug 17 2020

ฉันใช้กลุ่ม AWS Cognito User Pool เพื่อจัดการสิทธิ์สำหรับ API เกตเวย์ API ฉันเชื่อว่านี่เป็นการใช้งานที่ถูกต้องสำหรับ Groups ตามที่เอกสารระบุไว้ที่นี่:https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-user-groups.html#using-groups-to-control-permission-with-amazon-api-gateway.

น่าเสียดายที่โดยพื้นฐานแล้วเอกสารนั้นไม่มีอยู่สำหรับกรณีการใช้งานนี้เท่าที่ฉันสามารถบอกได้ (นอกเหนือจากย่อหน้าเล็ก ๆ นั้น) ฉันกำลังพยายามหาวิธีการทำงานกับฟังก์ชัน Lambda ผู้เขียนเกตเวย์ API ที่กำหนดเอง ฉันได้สร้างบทบาทการทดสอบและกำหนดสิ่งนี้ให้กับกลุ่มทดสอบใน 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ส่งคืนการตอบกลับพร้อมคุณสมบัติเอกสาร แต่มักจะเป็นโมฆะด้วยเหตุผลบางประการดังนั้นจึงจำเป็นต้องมีการGetPolicyVersionAsyncโทรพิเศษ

สิ่งนี้ไม่เพียง แต่เพิ่มเวลาในการตอบสนอง (ซึ่งเป็นปัญหาในฟังก์ชั่นผู้เขียนที่ทุกการเรียก API จะทำงานผ่านรหัสนี้) แต่มันทำให้ฉันมีเพียงนโยบายส่วนบุคคลจำนวนมากซึ่งฉันจำเป็นต้องหลอกลวง ก่อนที่จะกลับไปที่ API Gateway

ไม่มีวิธีที่ง่ายกว่า / เร็วกว่านี้จริง ๆ หรือ มีวิธีรับข้อมูลทั้งหมดนี้ด้วยการโทรน้อยลงหรือไม่และมีวิธีทำให้นโยบายแบนราบในกรณีที่กฎทับซ้อนกันหรือไม่

คำตอบ

lemming Aug 24 2020 at 17:00

มีหลายวิธีในการตั้งค่าการอนุญาตด้วย Cognito และ API Gateway ซึ่งเป็นการยากที่จะบอกว่าอะไรจะดีที่สุดสำหรับคุณโดยไม่ทราบว่าทรัพยากร AWS ใดที่คุณพยายามเข้าถึงผ่านเมธอด API Gateway ของคุณ ตัวอย่างเช่นเมธอด API Gateway ของคุณผสานรวมกับเมธอดแลมบ์ดาที่เข้าถึงข้อมูลฐานข้อมูลหรือเรียกใช้บริการโดยตรงหรือไม่

ถ้าฉันเข้าใจถูกต้องคุณต้องการรวมสิทธิ์สำหรับ (ที่อาจเป็นไปได้) หลายกลุ่มที่ผู้ใช้อาจเป็นสมาชิกเพื่อที่จะได้รับสิทธิ์ชุดเดียวที่จะใช้สำหรับคำขอนั้น ๆ ดังนั้นuserบทบาทอาจอนุญาตให้เข้าถึงข้อมูลผู้ใช้และadminบทบาทนี้อาจขยายการเข้าถึงไปยังปลายทางของผู้ดูแลระบบอื่น ๆ ต่อไป?

สมมติว่าคุณมีสถานการณ์ที่ค่อนข้างเป็นมาตรฐานที่เมธอด API Gateway ของคุณผสานรวมกับเมธอดแลมบ์ดาซึ่งกำลังเข้าถึงทรัพยากร AWS พื้นฐานเช่นฐานข้อมูลจากนั้นคุณสามารถใช้ Custom Authorizers ได้ดังนี้:

ช่วยลดความซับซ้อนของสิ่งต่างๆได้มากหาก Custom Authorizers เพียงแค่คืนสิทธิ์ที่จำเป็นในการเรียกใช้เมธอด 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 แลมด้าสำรองนี้จะมีบทบาทในการดำเนินการซึ่งกำหนดสิทธิ์ทั้งหมดในการเข้าถึงทรัพยากรที่มีการป้องกันเช่นข้อมูลในตาราง DynamoDB เป็นต้นดังนั้นเมื่อใดก็ตามที่แลมบ์ดาสำรองของคุณเข้าถึงทรัพยากร AWS อื่นคุณไม่จำเป็นต้องอัปเดตสิทธิ์สำหรับ บทบาทของกลุ่มจำนวนหนึ่ง แต่คุณจะจัดการสิทธิ์เหล่านั้นที่ตำแหน่งเดียวในระบบของคุณแทน

ด้วยการตั้งค่านี้คุณสามารถมี Custom Authorizer ที่ค่อนข้างง่ายสำหรับแต่ละพื้นที่ของ API ของคุณที่ต้องการการปกป้อง

ตัวอย่างเช่นเพื่อป้องกัน/user/*ปลายทางของคุณคุณสามารถมีผู้ให้สิทธิ์แบบกำหนดเองหนึ่งรายการที่ตรวจสอบเพื่อดูว่าโทเค็น ID ที่ส่งไปยัง Authorizer มีuserกลุ่มในการcognito:groupsอ้างสิทธิ์หรือไม่ หากเป็นเช่นนั้นคุณจะคืนสิทธิ์ที่จำเป็นในการเรียกใช้เมธอด api ที่ร้องขอ ( execute-api:Invoke) จากนั้นคุณอาจมีผู้กำหนดเองอื่นที่ปกป้อง/admin/*เส้นทางของคุณโดยตรวจสอบว่าadminกลุ่มนั้นอยู่ในการcognito:groupsอ้างสิทธิ์ของโทเค็น ID หรือไม่เป็นต้น

การแคชการตอบสนองของ Authorizer ที่กำหนดเอง

ผู้ให้สิทธิ์ที่กำหนดเองแต่ละตัวมีการตั้งค่า TTL ที่เป็นทางเลือกซึ่งกำหนดระยะเวลาที่จะแคชการตอบกลับ (สำหรับโทเค็น JWT เฉพาะ) ซึ่งจะช่วยลดเวลาในการอุ่นเครื่องแลมบ์ดาหรือเวลาที่ใช้โดยข้อความเสริมเพิ่มเติมในเครื่องมือสร้าง

หาก Authorizer นำเสนอวิธีการต่างๆเช่น:

  • รับ / ผู้ใช้ / ที่อยู่
  • POST / ผู้ใช้ / ที่อยู่
  • รับ / ผู้ใช้ / มือถือ

สิ่งสำคัญคือต้องทราบว่าการตอบกลับที่ได้รับอนุญาตที่ประสบความสำเร็จจะถูกแคชสำหรับวิธีการเหล่านี้ทั้งหมดจนกว่าการแคชจะหมดเวลา ดังนั้นนโยบายที่ส่งคืนโดยผู้ให้สิทธิ์ที่กำหนดเองจะต้องส่งคืนรายการทรัพยากรที่กำหนดวิธีการทั้งหมดที่ผู้ใช้สามารถดำเนินการได้หรือใช้สัญลักษณ์แทน

ตัวอย่างการตอบกลับสำหรับ Authorizer ที่ครอบคลุม/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"
        ]
      }
    ]
  }
}

ตัวอย่างการตอบกลับ (โดยใช้สัญลักษณ์แทน) สำหรับ Authorizer ที่ครอบคลุม/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/*"
        ]
      }
    ]
  }
}