// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Http;
using Microsoft.Framework.Logging;
namespace Microsoft.AspNet.Identity
{
///
/// Exposes role related api which will automatically save changes to the RoleStore
///
///
public class RoleManager : IDisposable where TRole : class
{
private bool _disposed;
private HttpContext _context;
///
/// Constructor
///
/// The IRoleStore commits changes via the UpdateAsync/CreateAsync methods
///
public RoleManager(IRoleStore store,
IEnumerable> roleValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
ILogger> logger,
IHttpContextAccessor contextAccessor)
{
if (store == null)
{
throw new ArgumentNullException("store");
}
Store = store;
KeyNormalizer = keyNormalizer ?? new UpperInvariantLookupNormalizer();
ErrorDescriber = errors ?? new IdentityErrorDescriber();
_context = contextAccessor?.Value;
if (roleValidators != null)
{
foreach (var v in roleValidators)
{
RoleValidators.Add(v);
}
}
Logger = logger ?? new Logger>(new LoggerFactory());
}
///
/// Persistence abstraction that the Manager operates against
///
protected IRoleStore Store { get; private set; }
///
/// Used to validate roles before persisting changes
///
internal IList> RoleValidators { get; } = new List>();
///
/// Used to generate public API error messages
///
internal IdentityErrorDescriber ErrorDescriber { get; set; }
///
/// Used to log results
///
internal ILogger> Logger { get; set; }
///
/// Used to normalize user names, role names, emails for uniqueness
///
internal ILookupNormalizer KeyNormalizer { get; set; }
///
/// Returns an IQueryable of roles if the store is an IQueryableRoleStore
///
public virtual IQueryable Roles
{
get
{
var queryableStore = Store as IQueryableRoleStore;
if (queryableStore == null)
{
throw new NotSupportedException(Resources.StoreNotIQueryableRoleStore);
}
return queryableStore.Roles;
}
}
///
/// Returns true if the store is an IQueryableRoleStore
///
public virtual bool SupportsQueryableRoles
{
get
{
ThrowIfDisposed();
return Store is IQueryableRoleStore;
}
}
///
/// Returns true if the store is an IUserClaimStore
///
public virtual bool SupportsRoleClaims
{
get
{
ThrowIfDisposed();
return Store is IRoleClaimStore;
}
}
private CancellationToken CancellationToken
{
get
{
return _context?.RequestAborted ?? CancellationToken.None;
}
}
///
/// Dispose this object
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private async Task ValidateRoleInternal(TRole role)
{
var errors = new List();
foreach (var v in RoleValidators)
{
var result = await v.ValidateAsync(this, role);
if (!result.Succeeded)
{
errors.AddRange(result.Errors);
}
}
return errors.Count > 0 ? IdentityResult.Failed(errors.ToArray()) : IdentityResult.Success;
}
///
/// Create a role
///
///
///
public virtual async Task CreateAsync(TRole role)
{
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException("role");
}
var result = await ValidateRoleInternal(role);
if (!result.Succeeded)
{
return result;
}
await UpdateNormalizedRoleNameAsync(role);
return await LogResultAsync(await Store.CreateAsync(role, CancellationToken), role);
}
///
/// Update the user's normalized user name
///
///
///
public virtual async Task UpdateNormalizedRoleNameAsync(TRole role)
{
var name = await GetRoleNameAsync(role);
await Store.SetNormalizedRoleNameAsync(role, NormalizeKey(name), CancellationToken);
}
///
/// Update an existing role
///
///
///
public virtual async Task UpdateAsync(TRole role)
{
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException("role");
}
return await LogResultAsync(await UpdateRoleAsync(role), role);
}
private async Task UpdateRoleAsync(TRole role)
{
var result = await ValidateRoleInternal(role);
if (!result.Succeeded)
{
return result;
}
await UpdateNormalizedRoleNameAsync(role);
return await Store.UpdateAsync(role, CancellationToken);
}
///
/// Delete a role
///
///
///
public virtual async Task DeleteAsync(TRole role)
{
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException("role");
}
return await LogResultAsync(await Store.DeleteAsync(role, CancellationToken), role);
}
///
/// Returns true if the role exists
///
///
///
public virtual async Task RoleExistsAsync(string roleName)
{
ThrowIfDisposed();
if (roleName == null)
{
throw new ArgumentNullException("roleName");
}
return await FindByNameAsync(NormalizeKey(roleName)) != null;
}
///
/// Normalize a key (role name) for uniqueness comparisons
///
///
///
public virtual string NormalizeKey(string key)
{
return (KeyNormalizer == null) ? key : KeyNormalizer.Normalize(key);
}
///
/// Find a role by id
///
///
///
public virtual async Task FindByIdAsync(string roleId)
{
ThrowIfDisposed();
return await Store.FindByIdAsync(roleId, CancellationToken);
}
///
/// Return the name of the role
///
///
///
public virtual async Task GetRoleNameAsync(TRole role)
{
ThrowIfDisposed();
return await Store.GetRoleNameAsync(role, CancellationToken);
}
///
/// Set the name of the role
///
///
///
///
public virtual async Task SetRoleNameAsync(TRole role, string name)
{
ThrowIfDisposed();
await Store.SetRoleNameAsync(role, name, CancellationToken);
await UpdateNormalizedRoleNameAsync(role);
return await LogResultAsync(IdentityResult.Success, role);
}
///
/// Return the role id for a role
///
///
///
public virtual async Task GetRoleIdAsync(TRole role)
{
ThrowIfDisposed();
return await Store.GetRoleIdAsync(role, CancellationToken);
}
///
/// FindByLoginAsync a role by name
///
///
///
public virtual async Task FindByNameAsync(string roleName)
{
ThrowIfDisposed();
if (roleName == null)
{
throw new ArgumentNullException("roleName");
}
return await Store.FindByNameAsync(NormalizeKey(roleName), CancellationToken);
}
// IRoleClaimStore methods
private IRoleClaimStore GetClaimStore()
{
var cast = Store as IRoleClaimStore;
if (cast == null)
{
throw new NotSupportedException(Resources.StoreNotIRoleClaimStore);
}
return cast;
}
///
/// Add a user claim
///
///
///
///
public virtual async Task AddClaimAsync(TRole role, Claim claim)
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (role == null)
{
throw new ArgumentNullException("role");
}
await claimStore.AddClaimAsync(role, claim, CancellationToken);
return await LogResultAsync(await UpdateRoleAsync(role), role);
}
///
/// Remove a user claim
///
///
///
///
public virtual async Task RemoveClaimAsync(TRole role, Claim claim)
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (role == null)
{
throw new ArgumentNullException("role");
}
await claimStore.RemoveClaimAsync(role, claim, CancellationToken);
return await LogResultAsync(await UpdateRoleAsync(role), role);
}
///
/// Get a role's claims
///
///
///
public virtual async Task> GetClaimsAsync(TRole role)
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (role == null)
{
throw new ArgumentNullException("role");
}
return await claimStore.GetClaimsAsync(role, CancellationToken);
}
///
/// Logs the current Identity Result and returns result object
///
///
///
///
///
protected async Task LogResultAsync(IdentityResult result,
TRole role, [System.Runtime.CompilerServices.CallerMemberName] string methodName = "")
{
result.Log(Logger, Resources.FormatLoggingResultMessageForRole(methodName, await GetRoleIdAsync(role)));
return result;
}
private void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
///
/// When disposing, actually dipose the store
///
///
protected virtual void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
Store.Dispose();
}
_disposed = true;
}
}
}