added replace claim functionality. fixes #232

- added ReplaceClaimAsync method to IUserClaimStore interface
 - implemented ReplaceClaimAsync method from IUserClaimStore inside the
   Microsoft.AspNet.Identity.EntityFramework.UserStore<TUser, TRole,
   TContext, TKey>
 - added ReplaceClaimAsync method to UserManager
 - added UserManager tests for ReplaceClaimAsync method.
 - added some UserStore tests for ReplaceClaimAsync implementation of
   IUserClaimStore
This commit is contained in:
tugberkugurlu 2014-10-23 15:02:24 +03:00 committed by Hao Kung
parent 0bbd7c8b48
commit d7f711bca1
8 changed files with 175 additions and 9 deletions

View File

@ -441,6 +441,32 @@ namespace Microsoft.AspNet.Identity.EntityFramework
return Task.FromResult(0);
}
public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (newClaim == null)
{
throw new ArgumentNullException("newClaim");
}
var matchedClaims = UserClaims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList();
foreach(var matchedClaim in matchedClaims)
{
matchedClaim.ClaimValue = newClaim.Value;
matchedClaim.ClaimType = newClaim.Type;
}
return Task.FromResult(0);
}
public Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -530,7 +556,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();
// todo: ensure logins loaded
var userLogin =
var userLogin =
UserLogins.FirstOrDefault(l => l.LoginProvider == loginProvider && l.ProviderKey == providerKey);
if (userLogin != null)
{

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<IList<Claim>> GetClaimsAsync(TUser user,
Task<IList<Claim>> GetClaimsAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
@ -32,6 +32,16 @@ namespace Microsoft.AspNet.Identity
/// <returns></returns>
Task AddClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Updates the give claim information with the given new claim information
/// </summary>
/// <param name="user"></param>
/// <param name="claim"></param>
/// <param name="newClaim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Remove a user claim
/// </summary>
@ -39,7 +49,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="claims"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims,
Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims,
CancellationToken cancellationToken = default(CancellationToken));
}
}

View File

@ -708,7 +708,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> UpdateSecurityStampAsync(TUser user,
public virtual async Task<IdentityResult> UpdateSecurityStampAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -727,7 +727,7 @@ namespace Microsoft.AspNet.Identity
/// <param name="user"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<string> GeneratePasswordResetTokenAsync(TUser user,
public virtual async Task<string> GeneratePasswordResetTokenAsync(TUser user,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
@ -946,6 +946,35 @@ namespace Microsoft.AspNet.Identity
return await UpdateAsync(user, cancellationToken);
}
/// <summary>
/// Updates the give claim information with the given new claim information
/// </summary>
/// <param name="user"></param>
/// <param name="claim"></param>
/// <param name="newClaim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public virtual async Task<IdentityResult> ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim,
CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
var claimStore = GetClaimStore();
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (newClaim == null)
{
throw new ArgumentNullException("newClaim");
}
if (user == null)
{
throw new ArgumentNullException("user");
}
await claimStore.ReplaceClaimAsync(user, claim, newClaim, cancellationToken);
return await UpdateAsync(user, cancellationToken);
}
/// <summary>
/// Remove a user claim
/// </summary>
@ -1598,7 +1627,7 @@ namespace Microsoft.AspNet.Identity
}
if (!_tokenProviders.ContainsKey(tokenProvider))
{
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
throw new NotSupportedException(String.Format(CultureInfo.CurrentCulture,
Resources.NoTokenProvider, tokenProvider));
}
await _tokenProviders[tokenProvider].NotifyAsync(token, this, user, cancellationToken);
@ -1827,7 +1856,7 @@ namespace Microsoft.AspNet.Identity
/// <summary>
/// Increments the access failed count for the user and if the failed access account is greater than or equal
/// to the MaxFailedAccessAttempsBeforeLockout, the user will be locked out for the next
/// to the MaxFailedAccessAttempsBeforeLockout, the user will be locked out for the next
/// DefaultAccountLockoutTimeSpan and the AccessFailedCount will be reset to 0.
/// </summary>
/// <param name="user"></param>

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
public InMemoryUserStore(InMemoryContext context) : base(context) { }
}
public class InMemoryUserStore<TUser, TContext> : InMemoryUserStore<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim, TContext>
public class InMemoryUserStore<TUser, TContext> : InMemoryUserStore<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim, TContext>
where TUser:IdentityUser
where TContext : DbContext
{
@ -370,6 +370,39 @@ namespace Microsoft.AspNet.Identity.EntityFramework.InMemory.Test
return Task.FromResult(0);
}
/// <summary>
/// Updates the give claim with the new one.
/// </summary>
/// <param name="user"></param>
/// <param name="claim"></param>
/// <param name="newClaim"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
ThrowIfDisposed();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (claim == null)
{
throw new ArgumentNullException("claim");
}
if (newClaim == null)
{
throw new ArgumentNullException("newClaim");
}
var matchedClaim = user.Claims.FirstOrDefault(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type);
if(matchedClaim != null)
{
matchedClaim.ClaimValue = newClaim.Value;
matchedClaim.ClaimType = newClaim.Type;
}
return Task.FromResult(0);
}
/// <summary>
/// Remove claims from a user
/// </summary>

View File

@ -172,6 +172,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test
await Assert.ThrowsAsync<ObjectDisposedException>(
async () => await store.RemoveFromRoleAsync(null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.RemoveClaimsAsync(null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.ReplaceClaimAsync(null, null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByLoginAsync(null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByIdAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(async () => await store.FindByNameAsync(null));
@ -199,6 +200,7 @@ namespace Microsoft.AspNet.Identity.EntityFramework.Test
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.UpdateAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.DeleteAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.AddClaimsAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.ReplaceClaimAsync(null, null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.RemoveClaimsAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetClaimsAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await store.GetLoginsAsync(null));

View File

@ -47,6 +47,17 @@ namespace Microsoft.AspNet.Identity.InMemory
return Task.FromResult(0);
}
public Task ReplaceClaimAsync(TUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
var matchedClaims = user.Claims.Where(uc => uc.ClaimValue == claim.Value && uc.ClaimType == claim.Type).ToList();
foreach(var matchedClaim in matchedClaims)
{
matchedClaim.ClaimValue = newClaim.Value;
matchedClaim.ClaimType = newClaim.Type;
}
return Task.FromResult(0);
}
public Task RemoveClaimsAsync(TUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
foreach (var claim in claims)

View File

@ -315,6 +315,27 @@ namespace Microsoft.AspNet.Identity.Test
store.VerifyAll();
}
[Fact]
public async Task UpdateClaimCallsStore()
{
// Setup
var store = new Mock<IUserClaimStore<TestUser>>();
var user = new TestUser { UserName = "Foo" };
var claim = new Claim("1", "1");
var newClaim = new Claim("1", "2");
store.Setup(s => s.ReplaceClaimAsync(user, It.IsAny<Claim>(), It.IsAny<Claim>(), CancellationToken.None))
.Returns(Task.FromResult(0))
.Verifiable();
var userManager = MockHelpers.TestUserManager<TestUser>(store.Object);
// Act
var result = await userManager.ReplaceClaimAsync(user, claim, newClaim);
// Assert
Assert.True(result.Succeeded);
store.VerifyAll();
}
[Fact]
public async Task RemoveClaimsCallsStore()
{
@ -455,6 +476,7 @@ namespace Microsoft.AspNet.Identity.Test
var manager = MockHelpers.TestUserManager(new NoopUserStore());
Assert.False(manager.SupportsUserClaim);
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.AddClaimAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.ReplaceClaimAsync(null, null, null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.RemoveClaimAsync(null, null));
await Assert.ThrowsAsync<NotSupportedException>(async () => await manager.GetClaimsAsync(null));
}
@ -541,11 +563,12 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await manager.UpdateAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user", async () => await manager.DeleteAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("claim", async () => await manager.AddClaimAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("claim", async () => await manager.ReplaceClaimAsync(null, null, null));
await Assert.ThrowsAsync<ArgumentNullException>("claims", async () => await manager.AddClaimsAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("userName", async () => await manager.FindByNameAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("userName", async () => await manager.FindByUserNamePasswordAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("login", async () => await manager.AddLoginAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("loginProvider",
await Assert.ThrowsAsync<ArgumentNullException>("loginProvider",
async () => await manager.RemoveLoginAsync(null, null, null));
await Assert.ThrowsAsync<ArgumentNullException>("providerKey",
async () => await manager.RemoveLoginAsync(null, "", null));
@ -594,6 +617,8 @@ namespace Microsoft.AspNet.Identity.Test
async () => await manager.RemoveFromRoleAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.RemoveFromRolesAsync(null, null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.ReplaceClaimAsync(null, new Claim("a", "b"), new Claim("a", "c")));
await Assert.ThrowsAsync<ArgumentNullException>("user",
async () => await manager.UpdateSecurityStampAsync(null));
await Assert.ThrowsAsync<ArgumentNullException>("user",
@ -690,6 +715,7 @@ namespace Microsoft.AspNet.Identity.Test
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.CreateAsync(null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.UpdateAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.DeleteAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.ReplaceClaimAsync(null, null, null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.UpdateSecurityStampAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GetSecurityStampAsync(null));
await Assert.ThrowsAsync<ObjectDisposedException>(() => manager.GeneratePasswordResetTokenAsync(null));
@ -730,6 +756,11 @@ namespace Microsoft.AspNet.Identity.Test
return Task.FromResult(0);
}
public Task ReplaceClaimAsync(TestUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
}
public Task RemoveClaimsAsync(TestUser user, IEnumerable<Claim> claim, CancellationToken cancellationToken = default(CancellationToken))
{
return Task.FromResult(0);
@ -991,6 +1022,11 @@ namespace Microsoft.AspNet.Identity.Test
throw new NotImplementedException();
}
public Task ReplaceClaimAsync(TestUser user, Claim claim, Claim newClaim, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();
}
public Task RemoveClaimsAsync(TestUser user, IEnumerable<Claim> claims, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotImplementedException();

View File

@ -353,6 +353,25 @@ namespace Microsoft.AspNet.Identity.Test
Assert.Equal(0, userClaims.Count);
}
[Fact]
public async Task CanReplaceUserClaim()
{
var manager = CreateManager();
var user = CreateTestUser();
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
IdentityResultAssert.IsSuccess(await manager.AddClaimAsync(user, new Claim("c", "a")));
var userClaims = await manager.GetClaimsAsync(user);
Assert.Equal(1, userClaims.Count);
Claim claim = new Claim("c", "b");
Claim oldClaim = userClaims.FirstOrDefault();
IdentityResultAssert.IsSuccess(await manager.ReplaceClaimAsync(user, oldClaim, claim));
var newUserClaims = await manager.GetClaimsAsync(user);
Assert.Equal(1, newUserClaims.Count);
Claim newClaim = newUserClaims.FirstOrDefault();
Assert.Equal(claim.Type, newClaim.Type);
Assert.Equal(claim.Value, newClaim.Value);
}
[Fact]
public async Task ChangePasswordFallsIfPasswordWrong()
{