// Copyright (c) .NET Foundation. 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.ComponentModel;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore
{
///
/// Creates a new instance of a persistence store for roles.
///
/// The type of the class representing a role
public class RoleStore : RoleStore
where TRole : IdentityRole
{
///
/// Constructs a new instance of .
///
/// The .
/// The .
public RoleStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
///
/// Creates a new instance of a persistence store for roles.
///
/// The type of the class representing a role.
/// The type of the data context class used to access the store.
public class RoleStore : RoleStore
where TRole : IdentityRole
where TContext : DbContext
{
///
/// Constructs a new instance of .
///
/// The .
/// The .
public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
///
/// Creates a new instance of a persistence store for roles.
///
/// The type of the class representing a role.
/// The type of the data context class used to access the store.
/// The type of the primary key for a role.
public class RoleStore : RoleStore, IdentityRoleClaim>,
IQueryableRoleStore,
IRoleClaimStore
where TRole : IdentityRole
where TKey : IEquatable
where TContext : DbContext
{
///
/// Constructs a new instance of .
///
/// The .
/// The .
public RoleStore(TContext context, IdentityErrorDescriber describer = null) : base(context, describer) { }
}
///
/// Creates a new instance of a persistence store for roles.
///
/// The type of the class representing a role.
/// The type of the data context class used to access the store.
/// The type of the primary key for a role.
/// The type of the class representing a user role.
/// The type of the class representing a role claim.
public class RoleStore :
IQueryableRoleStore,
IRoleClaimStore
where TRole : IdentityRole
where TKey : IEquatable
where TContext : DbContext
where TUserRole : IdentityUserRole, new()
where TRoleClaim : IdentityRoleClaim, new()
{
///
/// Constructs a new instance of .
///
/// The .
/// The .
public RoleStore(TContext context, IdentityErrorDescriber describer = null)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}
Context = context;
ErrorDescriber = describer ?? new IdentityErrorDescriber();
}
private bool _disposed;
///
/// Gets the database context for this store.
///
public TContext Context { get; private set; }
///
/// Gets or sets the for any error that occurred with the current operation.
///
public IdentityErrorDescriber ErrorDescriber { get; set; }
///
/// Gets or sets a flag indicating if changes should be persisted after CreateAsync, UpdateAsync and DeleteAsync are called.
///
///
/// True if changes should be automatically persisted, otherwise false.
///
public bool AutoSaveChanges { get; set; } = true;
/// Saves the current store.
/// The used to propagate notifications that the operation should be canceled.
/// The that represents the asynchronous operation.
private async Task SaveChanges(CancellationToken cancellationToken)
{
if (AutoSaveChanges)
{
await Context.SaveChangesAsync(cancellationToken);
}
}
///
/// Creates a new role in a store as an asynchronous operation.
///
/// The role to create in the store.
/// The used to propagate notifications that the operation should be canceled.
/// A that represents the of the asynchronous query.
public async virtual Task CreateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
Context.Add(role);
await SaveChanges(cancellationToken);
return IdentityResult.Success;
}
///
/// Updates a role in a store as an asynchronous operation.
///
/// The role to update in the store.
/// The used to propagate notifications that the operation should be canceled.
/// A that represents the of the asynchronous query.
public async virtual Task UpdateAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
Context.Attach(role);
role.ConcurrencyStamp = Guid.NewGuid().ToString();
Context.Update(role);
try
{
await SaveChanges(cancellationToken);
}
catch (DbUpdateConcurrencyException)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
///
/// Deletes a role from the store as an asynchronous operation.
///
/// The role to delete from the store.
/// The used to propagate notifications that the operation should be canceled.
/// A that represents the of the asynchronous query.
public async virtual Task DeleteAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
Context.Remove(role);
try
{
await SaveChanges(cancellationToken);
}
catch (DbUpdateConcurrencyException)
{
return IdentityResult.Failed(ErrorDescriber.ConcurrencyFailure());
}
return IdentityResult.Success;
}
///
/// Gets the ID for a role from the store as an asynchronous operation.
///
/// The role whose ID should be returned.
/// The used to propagate notifications that the operation should be canceled.
/// A that contains the ID of the role.
public virtual Task GetRoleIdAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
return Task.FromResult(ConvertIdToString(role.Id));
}
///
/// Gets the name of a role from the store as an asynchronous operation.
///
/// The role whose name should be returned.
/// The used to propagate notifications that the operation should be canceled.
/// A that contains the name of the role.
public virtual Task GetRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
return Task.FromResult(role.Name);
}
///
/// Sets the name of a role in the store as an asynchronous operation.
///
/// The role whose name should be set.
/// The name of the role.
/// The used to propagate notifications that the operation should be canceled.
/// The that represents the asynchronous operation.
public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
role.Name = roleName;
return Task.CompletedTask;
}
///
/// Converts the provided to a strongly typed key object.
///
/// The id to convert.
/// An instance of representing the provided .
public virtual TKey ConvertIdFromString(string id)
{
if (id == null)
{
return default(TKey);
}
return (TKey)TypeDescriptor.GetConverter(typeof(TKey)).ConvertFromInvariantString(id);
}
///
/// Converts the provided to its string representation.
///
/// The id to convert.
/// An representation of the provided .
public virtual string ConvertIdToString(TKey id)
{
if (id.Equals(default(TKey)))
{
return null;
}
return id.ToString();
}
///
/// Finds the role who has the specified ID as an asynchronous operation.
///
/// The role ID to look for.
/// The used to propagate notifications that the operation should be canceled.
/// A that result of the look up.
public virtual Task FindByIdAsync(string id, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
var roleId = ConvertIdFromString(id);
return Roles.FirstOrDefaultAsync(u => u.Id.Equals(roleId), cancellationToken);
}
///
/// Finds the role who has the specified normalized name as an asynchronous operation.
///
/// The normalized role name to look for.
/// The used to propagate notifications that the operation should be canceled.
/// A that result of the look up.
public virtual Task FindByNameAsync(string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
return Roles.FirstOrDefaultAsync(r => r.NormalizedName == normalizedName, cancellationToken);
}
///
/// Get a role's normalized name as an asynchronous operation.
///
/// The role whose normalized name should be retrieved.
/// The used to propagate notifications that the operation should be canceled.
/// A that contains the name of the role.
public virtual Task GetNormalizedRoleNameAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
return Task.FromResult(role.NormalizedName);
}
///
/// Set a role's normalized name as an asynchronous operation.
///
/// The role whose normalized name should be set.
/// The normalized name to set
/// The used to propagate notifications that the operation should be canceled.
/// The that represents the asynchronous operation.
public virtual Task SetNormalizedRoleNameAsync(TRole role, string normalizedName, CancellationToken cancellationToken = default(CancellationToken))
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
role.NormalizedName = normalizedName;
return Task.CompletedTask;
}
///
/// Throws if this class has been disposed.
///
protected void ThrowIfDisposed()
{
if (_disposed)
{
throw new ObjectDisposedException(GetType().Name);
}
}
///
/// Dispose the stores
///
public void Dispose() => _disposed = true;
///
/// Get the claims associated with the specified as an asynchronous operation.
///
/// The role whose claims should be retrieved.
/// The used to propagate notifications that the operation should be canceled.
/// A that contains the claims granted to a role.
public async virtual Task> GetClaimsAsync(TRole role, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
return await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id)).Select(c => new Claim(c.ClaimType, c.ClaimValue)).ToListAsync(cancellationToken);
}
///
/// Adds the given to the specified .
///
/// The role to add the claim to.
/// The claim to add to the role.
/// The used to propagate notifications that the operation should be canceled.
/// The that represents the asynchronous operation.
public virtual Task AddClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
if (claim == null)
{
throw new ArgumentNullException(nameof(claim));
}
RoleClaims.Add(CreateRoleClaim(role, claim));
return Task.FromResult(false);
}
///
/// Removes the given from the specified .
///
/// The role to remove the claim from.
/// The claim to remove from the role.
/// The used to propagate notifications that the operation should be canceled.
/// The that represents the asynchronous operation.
public async virtual Task RemoveClaimAsync(TRole role, Claim claim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (role == null)
{
throw new ArgumentNullException(nameof(role));
}
if (claim == null)
{
throw new ArgumentNullException(nameof(claim));
}
var claims = await RoleClaims.Where(rc => rc.RoleId.Equals(role.Id) && rc.ClaimValue == claim.Value && rc.ClaimType == claim.Type).ToListAsync(cancellationToken);
foreach (var c in claims)
{
RoleClaims.Remove(c);
}
}
///
/// A navigation property for the roles the store contains.
///
public virtual IQueryable Roles => Context.Set();
private DbSet RoleClaims { get { return Context.Set(); } }
///
/// Creates an entity representing a role claim.
///
/// The associated role.
/// The associated claim.
/// The role claim entity.
protected virtual TRoleClaim CreateRoleClaim(TRole role, Claim claim)
=> new TRoleClaim { RoleId = role.Id, ClaimType = claim.Type, ClaimValue = claim.Value };
}
}