AWS IAM SDK mendapatkan semua dokumen kebijakan untuk peran tertentu saat menggunakan peran grup Cognito

Aug 17 2020

Saya menggunakan grup AWS Cognito User Pool untuk mengelola izin API Gateway API. Saya yakin ini adalah penggunaan yang valid untuk Grup seperti yang disebutkan dalam dokumentasi di sini:https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-user-groups.html#using-groups-to-control-permission-with-amazon-api-gateway.

Sayangnya, dokumentasi pada dasarnya tidak ada untuk kasus penggunaan ini sejauh yang saya tahu (terlepas dari paragraf kecil itu). Saya mencoba untuk mencari tahu bagaimana ini bekerja dengan fungsi Lambda pengotorisasi API Gateway kustom. Saya telah membuat peran pengujian dan menetapkannya ke grup pengujian di Cognito. Peran tersebut memiliki satu kebijakan yang menyertainya namun kedepannya peran tersebut akan memiliki beberapa kebijakan.

Sekarang, di authoriser kustom saya, saya sudah memvalidasi token akses dll dan semuanya berfungsi dengan baik. Sekarang saya mencoba menambahkan kontrol akses mendetail ini dalam menggunakan grup / peran / kebijakan. Saya telah menginstal IAM SDK dan telah menggali lebih dalam untuk melihat panggilan mana yang perlu saya lakukan. Sepertinya tidak ada cara mudah untuk mendapatkan peran dan semua kebijakannya. Yang terbaik yang saya dapatkan adalah sebagai berikut:

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);
}

Seperti yang Anda lihat, saya harus melakukan banyak panggilan hanya untuk mendapatkan kebijakan untuk suatu peran.

  1. _cognitoClient.AdminListGroupsForUserAsync untuk mendapatkan grup pengguna dari Cognito.
  2. _iamClient.ListAttachedRolePoliciesAsync untuk mendapatkan kebijakan yang terkait dengan peran tersebut.
  3. _iamClient.ListPolicyVersionsAsync untuk mendapatkan versi setiap kebijakan.
  4. _iamClient.GetPolicyVersionAsync untuk mendapatkan versi kebijakan individu yang akhirnya memiliki dokumen kebijakan.

ListPolicyVersionsAsyncmengembalikan respons dengan properti document tetapi selalu null karena alasan tertentu, oleh karena itu diperlukan GetPolicyVersionAsyncpanggilan tambahan .

Semua ini tidak hanya menambah latensi (yang merupakan masalah pada fungsi authoriser di mana setiap panggilan ke API akan dijalankan melalui kode ini) tetapi meninggalkan saya hanya dengan sekumpulan kebijakan individual yang saya perlu entah bagaimana menghapus penipuan sebelum kembali ke API Gateway.

Benarkah tidak ada cara yang lebih mudah / cepat untuk melakukan ini? Adakah cara untuk mendapatkan semua informasi ini dengan lebih sedikit panggilan dan adakah cara untuk meratakan kebijakan jika aturannya tumpang tindih?

Jawaban

lemming Aug 24 2020 at 17:00

Ada begitu banyak cara berbeda untuk mengatur Otorisasi dengan Cognito dan API Gateway sehingga sulit untuk mengatakan apa yang terbaik untuk Anda tanpa mengetahui sumber daya AWS mana yang Anda coba akses melalui metode API Gateway Anda. Misalnya, apakah metode API Gateway Anda terintegrasi dengan metode lambda yang mengakses info database, atau apakah metode tersebut memanggil layanan secara langsung?

Jika saya mengerti dengan benar, Anda ingin menggabungkan izin untuk (berpotensi) beberapa grup yang mungkin dimiliki pengguna untuk menghasilkan satu set izin yang akan digunakan untuk permintaan tertentu itu. Jadi, userperan tersebut memungkinkan akses ke data pengguna, dan adminperan tersebut selanjutnya dapat memperluas akses ke titik akhir admin lainnya?

Dengan asumsi bahwa Anda memiliki skenario yang cukup standar di mana metode API Gateway Anda berintegrasi dengan metode lambda yang kemudian mengakses sumber daya AWS yang mendasarinya seperti database, Anda dapat menggunakan Otorisasi Khusus sebagai berikut:

Ini sangat menyederhanakan banyak hal jika Otorisasi Khusus hanya mengembalikan izin yang diperlukan untuk menjalankan metode API yang diminta:

{
  "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"
        ]
      }
    ]
  }
}

Dalam skenario standar, permintaan yang berhasil diotorisasi kemudian akan mengeksekusi lambda yang berisi logika api. Lambda pendukung ini akan memiliki peran eksekusi yang menentukan semua izin untuk mengakses sumber daya yang dilindungi seperti data dalam tabel DynamoDB, dll. Jadi, kapan pun sumber daya AWS yang berbeda diakses oleh lambda pendukung Anda, Anda tidak perlu memperbarui izin untuk sejumlah peran grup, tetapi Anda mengelola izin tersebut di satu lokasi di sistem Anda.

Dengan pengaturan ini, Anda dapat memiliki satu Custom Authorizer yang cukup sederhana untuk setiap area api Anda yang perlu dilindungi.

Misalnya, untuk melindungi /user/*titik akhir Anda, Anda dapat memiliki satu pemberi otorisasi khusus yang hanya memeriksa untuk melihat apakah token ID yang diteruskan ke Pengotor berisi usergrup dalam cognito:groupsklaimnya. Jika ya, maka Anda mengembalikan izin yang diperlukan untuk menjalankan metode api yang diminta ( execute-api:Invoke). Anda kemudian dapat memiliki otorisasi khusus lain yang melindungi /admin/*rute Anda dengan memeriksa untuk melihat apakah admingrup tersebut ada dalam cognito:groupsklaim token ID , dll.

Caching tanggapan pengotorisasi kustom

Setiap pengotorisasi khusus memiliki setelan TTL opsional, yang menentukan berapa lama responsnya (untuk token JWT tertentu) akan disimpan dalam cache. Ini membantu mengurangi waktu pemanasan lambda, atau waktu yang dibutuhkan oleh pemanggilan tambahan di pengotorisasi.

Jika pemberi otorisasi mengedepankan sejumlah metode, misalnya:

  • DAPATKAN / pengguna / alamat
  • POST / pengguna / alamat
  • DAPATKAN / pengguna / seluler

maka penting untuk dicatat bahwa respons yang berhasil diotorisasi akan disimpan dalam cache untuk semua metode ini hingga waktu cache habis. Oleh karena itu, kebijakan yang dikembalikan oleh pemberi otorisasi ubahsuaian harus mengembalikan daftar sumber daya yang menentukan semua metode yang dapat dijalankan pengguna, atau menggunakan karakter pengganti.

Contoh respons untuk pemberi otorisasi yang mencakup semua /user/*rute

{
  "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"
        ]
      }
    ]
  }
}

Contoh tanggapan (menggunakan karakter pengganti) untuk pemberi otorisasi yang mencakup semua /user/*rute

{
  "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/*"
        ]
      }
    ]
  }
}