diff --git a/src/Microsoft.AspNet.Identity/UserManager.cs b/src/Microsoft.AspNet.Identity/UserManager.cs index c5cf9da653..4e1e7f3af5 100644 --- a/src/Microsoft.AspNet.Identity/UserManager.cs +++ b/src/Microsoft.AspNet.Identity/UserManager.cs @@ -958,6 +958,37 @@ namespace Microsoft.AspNet.Identity return await UpdateAsync(user, cancellationToken); } + /// + /// Add a user to roles + /// + /// + /// + /// + /// + public virtual async Task AddToRolesAsync(TUser user, IEnumerable roles, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (roles == null) + { + throw new ArgumentNullException("roles"); + } + var userRoles = await userRoleStore.GetRolesAsync(user, cancellationToken); + foreach (var role in roles) + { + if (userRoles.Contains(role)) + { + return new IdentityResult(Resources.UserAlreadyInRole); + } + await userRoleStore.AddToRoleAsync(user, role, cancellationToken); + } + return await UpdateAsync(user, cancellationToken); + } + /// /// Remove a user from a role. /// @@ -981,6 +1012,36 @@ namespace Microsoft.AspNet.Identity return await UpdateAsync(user, cancellationToken); } + /// + /// Remove a user from a specified roles. + /// + /// + /// + /// + /// + public virtual async Task RemoveFromRolesAsync(TUser user, IEnumerable roles, CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + var userRoleStore = GetUserRoleStore(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + if (roles == null) + { + throw new ArgumentNullException("roles"); + } + foreach (var role in roles) + { + if (!await userRoleStore.IsInRoleAsync(user, role, cancellationToken)) + { + return new IdentityResult(Resources.UserNotInRole); + } + await userRoleStore.RemoveFromRoleAsync(user, role, cancellationToken); + } + return await UpdateAsync(user, cancellationToken); + } + /// /// Returns the roles for the user /// diff --git a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs index 383b19883e..1690ee5b1f 100644 --- a/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs +++ b/test/Microsoft.AspNet.Identity.Test/UserManagerTest.cs @@ -142,6 +142,117 @@ namespace Microsoft.AspNet.Identity.Test store.VerifyAll(); } + [Fact] + public async Task AddToRolesCallsStore() + { + // Setup + var store = new Mock>(); + var user = new TestUser { UserName = "Foo" }; + var roles = new string[] {"A", "B", "C"}; + store.Setup(s => s.AddToRoleAsync(user, "A", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.AddToRoleAsync(user, "B", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.AddToRoleAsync(user, "C", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); + store.Setup(s => s.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(new List()).Verifiable(); + var userManager = new UserManager(store.Object) {UserValidator = null}; + + // Act + var result = await userManager.AddToRolesAsync(user, roles); + + // Assert + Assert.True(result.Succeeded); + store.VerifyAll(); + } + + [Fact] + public async Task AddToRolesFailsIfUserInRole() + { + // Setup + var store = new Mock>(); + var user = new TestUser { UserName = "Foo" }; + var roles = new string[] { "A", "B", "C" }; + store.Setup(s => s.AddToRoleAsync(user, "A", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.GetRolesAsync(user, CancellationToken.None)).ReturnsAsync(new List { "B" }).Verifiable(); + var userManager = new UserManager(store.Object) { UserValidator = null }; + + // Act + var result = await userManager.AddToRolesAsync(user, roles); + + // Assert + IdentityResultAssert.IsFailure(result, "User already in role."); + store.VerifyAll(); + } + + [Fact] + public async Task RemoveFromRolesCallsStore() + { + // Setup + var store = new Mock>(); + var user = new TestUser { UserName = "Foo" }; + var roles = new string[] { "A", "B", "C" }; + store.Setup(s => s.RemoveFromRoleAsync(user, "A", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.RemoveFromRoleAsync(user, "B", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.RemoveFromRoleAsync(user, "C", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.UpdateAsync(user, CancellationToken.None)).Returns(Task.FromResult(0)).Verifiable(); + store.Setup(s => s.IsInRoleAsync(user, "A", CancellationToken.None)) + .Returns(Task.FromResult(true)) + .Verifiable(); + store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None)) + .Returns(Task.FromResult(true)) + .Verifiable(); + store.Setup(s => s.IsInRoleAsync(user, "C", CancellationToken.None)) + .Returns(Task.FromResult(true)) + .Verifiable(); + var userManager = new UserManager(store.Object) { UserValidator = null }; + + // Act + var result = await userManager.RemoveFromRolesAsync(user, roles); + + // Assert + Assert.True(result.Succeeded); + store.VerifyAll(); + } + + [Fact] + public async Task RemoveFromRolesFailsIfNotInRole() + { + // Setup + var store = new Mock>(); + var user = new TestUser { UserName = "Foo" }; + var roles = new string[] { "A", "B", "C" }; + store.Setup(s => s.RemoveFromRoleAsync(user, "A", CancellationToken.None)) + .Returns(Task.FromResult(0)) + .Verifiable(); + store.Setup(s => s.IsInRoleAsync(user, "A", CancellationToken.None)) + .Returns(Task.FromResult(true)) + .Verifiable(); + store.Setup(s => s.IsInRoleAsync(user, "B", CancellationToken.None)) + .Returns(Task.FromResult(false)) + .Verifiable(); + var userManager = new UserManager(store.Object) { UserValidator = null }; + + // Act + var result = await userManager.RemoveFromRolesAsync(user, roles); + + // Assert + IdentityResultAssert.IsFailure(result, "User is not in role."); + store.VerifyAll(); + } + #endif [Fact] @@ -278,8 +389,10 @@ namespace Microsoft.AspNet.Identity.Test var manager = new UserManager(new NoopUserStore()); Assert.False(manager.SupportsUserRole); await Assert.ThrowsAsync(async () => await manager.AddToRoleAsync(null, "bogus")); + await Assert.ThrowsAsync(async () => await manager.AddToRolesAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.GetRolesAsync(null)); await Assert.ThrowsAsync(async () => await manager.RemoveFromRoleAsync(null, "bogus")); + await Assert.ThrowsAsync(async () => await manager.RemoveFromRolesAsync(null, null)); await Assert.ThrowsAsync(async () => await manager.IsInRoleAsync(null, "bogus")); } @@ -333,6 +446,8 @@ namespace Microsoft.AspNet.Identity.Test Assert.Throws("twoFactorProvider", () => manager.RegisterTwoFactorProvider(null, null)); Assert.Throws("provider", () => manager.RegisterTwoFactorProvider("bogus", null)); + await Assert.ThrowsAsync("roles", async () => await manager.AddToRolesAsync(new TestUser(), null)); + await Assert.ThrowsAsync("roles", async () => await manager.RemoveFromRolesAsync(new TestUser(), null)); } [Fact] @@ -354,6 +469,8 @@ namespace Microsoft.AspNet.Identity.Test async () => await manager.AddPasswordAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.AddToRoleAsync(null, null)); + await Assert.ThrowsAsync("user", + async () => await manager.AddToRolesAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.ChangePasswordAsync(null, null, null)); await Assert.ThrowsAsync("user", @@ -372,6 +489,8 @@ namespace Microsoft.AspNet.Identity.Test async () => await manager.RemovePasswordAsync(null)); await Assert.ThrowsAsync("user", async () => await manager.RemoveFromRoleAsync(null, null)); + await Assert.ThrowsAsync("user", + async () => await manager.RemoveFromRolesAsync(null, null)); await Assert.ThrowsAsync("user", async () => await manager.UpdateSecurityStampAsync(null)); await Assert.ThrowsAsync("user", @@ -448,6 +567,7 @@ namespace Microsoft.AspNet.Identity.Test await Assert.ThrowsAsync(() => manager.AddLoginAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddPasswordAsync(null, null)); await Assert.ThrowsAsync(() => manager.AddToRoleAsync(null, null)); + await Assert.ThrowsAsync(() => manager.AddToRolesAsync(null, null)); await Assert.ThrowsAsync(() => manager.ChangePasswordAsync(null, null, null)); await Assert.ThrowsAsync(() => manager.GetClaimsAsync(null)); await Assert.ThrowsAsync(() => manager.GetLoginsAsync(null)); @@ -457,6 +577,7 @@ namespace Microsoft.AspNet.Identity.Test await Assert.ThrowsAsync(() => manager.RemoveLoginAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemovePasswordAsync(null)); await Assert.ThrowsAsync(() => manager.RemoveFromRoleAsync(null, null)); + await Assert.ThrowsAsync(() => manager.RemoveFromRolesAsync(null, null)); await Assert.ThrowsAsync(() => manager.RemoveClaimAsync(null, null)); await Assert.ThrowsAsync(() => manager.FindByUserNamePasswordAsync(null, null)); await Assert.ThrowsAsync(() => manager.FindByLoginAsync(null)); @@ -740,6 +861,7 @@ namespace Microsoft.AspNet.Identity.Test IUserPasswordStore, IUserClaimStore, IUserLoginStore, + IUserRoleStore, IUserEmailStore, IUserPhoneNumberStore, IUserLockoutStore, @@ -929,6 +1051,26 @@ namespace Microsoft.AspNet.Identity.Test { throw new NotImplementedException(); } + + public Task AddToRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = new CancellationToken()) + { + throw new NotImplementedException(); + } + + public Task RemoveFromRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = new CancellationToken()) + { + throw new NotImplementedException(); + } + + public Task> GetRolesAsync(TestUser user, CancellationToken cancellationToken = new CancellationToken()) + { + throw new NotImplementedException(); + } + + public Task IsInRoleAsync(TestUser user, string roleName, CancellationToken cancellationToken = new CancellationToken()) + { + throw new NotImplementedException(); + } } } } \ No newline at end of file