AWS IAM SDK получает все документы политик для конкретной роли при использовании групповых ролей Cognito
Я использую группы пула пользователей 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. Я создал тестовую роль и назначил ее тестовой группе в 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);
}
Как видите, мне нужно сделать несколько звонков, чтобы получить правила для роли.
_cognitoClient.AdminListGroupsForUserAsync
чтобы получить группы пользователей из Cognito._iamClient.ListAttachedRolePoliciesAsync
чтобы получить политики, связанные с ролями._iamClient.ListPolicyVersionsAsync
чтобы получить версии для каждой политики._iamClient.GetPolicyVersionAsync
чтобы получить индивидуальную версию политики, которая, наконец, имеет документ политики.
ListPolicyVersionsAsync
возвращает ответ со свойством документа, но по какой-то причине он всегда равен нулю, отсюда и необходимость в дополнительном GetPolicyVersionAsync
вызове.
Все это не только увеличивает задержку (что является проблемой для функции авторизации, когда каждый вызов API будет выполняться через этот код), но и оставляет мне только кучу отдельных политик, которые мне нужно как-то де-дублировать перед возвращением в API Gateway.
Неужели нет более простого / быстрого способа сделать это? Есть ли способ получить всю эту информацию с меньшим количеством вызовов, и есть ли способ сгладить политики в случае, если их правила перекрываются?
Ответы
Существует так много разных способов настройки авторизации с помощью Cognito и API Gateway, что трудно сказать, что будет лучше для вас, не зная, к каким ресурсам AWS вы пытаетесь получить доступ с помощью методов API Gateway. Например, интегрируются ли ваши методы шлюза API с лямбда-методами, которые обращаются к информации базы данных, или они вызывают службы напрямую?
Если я правильно понимаю, вы хотите объединить разрешения для (потенциально) нескольких групп, к которым может принадлежать пользователь, чтобы создать единый набор разрешений для использования для этого конкретного запроса. Итак, user
роль может разрешать доступ к пользовательским данным, а admin
роль может дополнительно расширять доступ к другим конечным точкам администратора?
Предполагая, что у вас есть довольно стандартный сценарий, в котором методы шлюза API интегрируются с лямбда-методом, который затем обращается к базовым ресурсам 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. Эта поддерживающая лямбда будет иметь роль выполнения, которая определяет все разрешения для доступа к защищенным ресурсам, таким как данные в таблицах DynamoDB и т. Д. Таким образом, в любое время, когда ваша поддерживающая лямбда обращается к другому ресурсу AWS, вам не нужно обновлять разрешения для несколько групповых ролей, но вместо этого вы управляете этими разрешениями в одном месте в своей системе.
При такой настройке у вас может быть один довольно простой пользовательский авторизатор для каждой области вашего API, которую необходимо защитить.
Например, для защиты ваших /user/*
конечных точек у вас может быть один настраиваемый авторизатор, который просто проверяет, содержит ли маркер идентификатора, переданный в Authorizer, user
группу в своем cognito:groups
заявлении. Если это так, вы возвращаете разрешения, необходимые для выполнения запрошенного метода api ( execute-api:Invoke
). Затем у вас может быть другой настраиваемый авторизатор, который защищает ваши /admin/*
маршруты, проверяя, находится ли admin
группа в утверждении токена идентификатора и cognito:groups
т. Д.
Кеширование ответов настраиваемого авторизатора
У каждого настраиваемого авторизатора есть необязательный параметр TTL, который определяет, как долго его ответ (для определенного токена JWT) будет кэшироваться. Это помогает сократить время прогрева лямбда или время, затрачиваемое на дополнительные вызовы в авторизаторе.
Если авторизатор использует несколько методов, например:
- ПОЛУЧИТЬ / пользователь / адрес
- POST / пользователь / адрес
- GET / пользователь / мобильный
то важно отметить, что успешно авторизованный ответ будет кэшироваться для всех этих методов до тех пор, пока не истечет время кэширования. Поэтому политика, возвращаемая настраиваемым авторизатором, должна возвращать либо список ресурсов, определяющих все методы, которые может выполнять пользователь, либо использовать подстановочные знаки.
Пример ответа для авторизатора, охватывающий все /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/*"
]
}
]
}
}