src/Amazon.AspNetCore.Identity.Cognito/CognitoUserStore/CognitoUserStore.IUserClaimStore.cs (141 lines of code) (raw):
/*
* Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.AspNetCore.Identity.Cognito.Exceptions;
using Amazon.CognitoIdentityProvider;
using Amazon.CognitoIdentityProvider.Model;
using Amazon.Extensions.CognitoAuthentication;
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
namespace Amazon.AspNetCore.Identity.Cognito
{
public partial class CognitoUserStore<TUser> : IUserClaimStore<TUser> where TUser : CognitoUser
{
/// <summary>
/// Gets a list of <see cref="Claim"/>s to be belonging to the specified <paramref name="user"/> as an asynchronous operation.
/// </summary>
/// <param name="user">The role whose claims to retrieve.</param>
/// <returns>
/// A <see cref="Task{TResult}"/> that represents the result of the asynchronous query, a list of <see cref="Claim"/>s.
/// </returns>
public virtual async Task<IList<Claim>> GetClaimsAsync(TUser user, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
try
{
var details = await _cognitoClient.AdminGetUserAsync(new AdminGetUserRequest
{
Username = user.Username,
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);
return details.UserAttributes?.Select(att => new Claim(att.Name, att.Value)).ToList() ?? new List<Claim>();
}
catch (AmazonCognitoIdentityProviderException e)
{
throw new CognitoServiceException("Failed to retrieve Cognito User claims", e);
}
}
/// <summary>
/// Add claims to a user as an asynchronous operation.
/// </summary>
/// <param name="user">The user to add the claim to.</param>
/// <param name="claims">The collection of <see cref="Claim"/>s to add.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual async Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
if (claims == null)
{
throw new ArgumentNullException(nameof(claims));
}
if (claims.Any())
{
try
{
await _cognitoClient.AdminUpdateUserAttributesAsync(new AdminUpdateUserAttributesRequest
{
UserAttributes = CreateAttributeList(claims.ToDictionary(claim => claim.Type, claim => claim.Value)),
Username = user.Username,
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);
}
catch (AmazonCognitoIdentityProviderException e)
{
throw new CognitoServiceException("Failed to add a claim to the Cognito User", e);
}
}
}
/// <summary>
/// Replaces the given <paramref name="claim"/> on the specified <paramref name="user"/> with the <paramref name="newClaim"/>
/// </summary>
/// <param name="user">The user to replace the claim on.</param>
/// <param name="claim">The claim to replace.</param>
/// <param name="newClaim">The new claim to replace the existing <paramref name="claim"/> with.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
throw new NotSupportedException("Cognito does not support replacing claims. Call RemoveClaimsAsync() and AddClaimsAsync() instead.");
}
/// <summary>
/// Removes the specified <paramref name="claims"/> from the given <paramref name="user"/>.
/// </summary>
/// <param name="user">The user to remove the specified <paramref name="claims"/> from.</param>
/// <param name="claims">A collection of <see cref="Claim"/>s to remove.</param>
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual async Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
if (claims == null)
{
throw new ArgumentNullException(nameof(claims));
}
var userClaims = await GetClaimsAsync(user, cancellationToken).ConfigureAwait(false);
// Only removes the claims that the user actually have.
var matchedClaims = userClaims?.Select(claim => new { claim.Type, claim.Value })
.Intersect(claims.Select(claim => new { claim.Type, claim.Value }));
if (matchedClaims != null && matchedClaims.Any())
{
try
{
await _cognitoClient.AdminDeleteUserAttributesAsync(new AdminDeleteUserAttributesRequest
{
UserAttributeNames = matchedClaims.Select(claim => claim.Type).ToList(),
Username = user.Username,
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);
}
catch (AmazonCognitoIdentityProviderException e)
{
throw new CognitoServiceException("Failed to remove a claim from the Cognito User", e);
}
}
}
/// <summary>
/// Returns a list of users who contain the specified <see cref="Claim"/>.
/// </summary>
/// <param name="claim">The claim to look for.</param>
/// <returns>
/// A <see cref="Task{TResult}"/> that represents the result of the asynchronous query, a list of <typeparamref name="TUser"/> who
/// contain the specified claim.
/// </returns>
public async Task<IList<TUser>> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
if (claim == null)
{
throw new ArgumentNullException(nameof(claim));
}
if (CognitoAttribute.FilterableAttributes.Contains(claim.Type))
{
try
{
var response = await _cognitoClient.ListUsersAsync(new ListUsersRequest
{
Filter = claim.Type + "=\"" + claim.Value + "\"",
UserPoolId = _pool.PoolID
}, cancellationToken).ConfigureAwait(false);
return response.Users?
.Select(user =>
_pool.GetUser(
user.Username,
user.UserStatus,
user.Attributes?
.ToDictionary(att => att.Name, att => att.Value)
?? new Dictionary<string, string>()
)
)
.ToList() as IList<TUser> ?? new List<TUser>();
}
catch (AmazonCognitoIdentityProviderException e)
{
throw new CognitoServiceException("Failed to get the list of users for a specific claim", e);
}
}
else
{
throw new NotSupportedException(String.Format("Retrieving the list of users with the claim type {0} is not supported", claim.Type));
}
}
}
}