Skip to content

Commit 4fa9787

Browse files
committed
Add RoleStore with IQueryableRoleStore
- Add IdentityRole - Note on implementation decision of RoleStore, make methods virtual to allow custom tweaks in consumers resolves g0t4#3
1 parent daa60d8 commit 4fa9787

15 files changed

+305
-11
lines changed

src/AspNet.Identity.MongoDB/AspNet.Identity.MongoDB.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@
5353
</ItemGroup>
5454
<ItemGroup>
5555
<Compile Include="IdentityContext.cs" />
56+
<Compile Include="IdentityRole.cs" />
5657
<Compile Include="IdentityUser.cs" />
5758
<Compile Include="IdentityUserClaim.cs" />
5859
<Compile Include="Properties\AssemblyInfo.cs" />
60+
<Compile Include="RoleStore.cs" />
5961
<Compile Include="UserStore.cs" />
6062
</ItemGroup>
6163
<ItemGroup>

src/AspNet.Identity.MongoDB/IdentityContext.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@
66
public class IdentityContext
77
{
88
public MongoCollection Users { get; private set; }
9+
public MongoCollection Roles { get; private set; }
910

1011
public IdentityContext(MongoCollection users)
1112
{
1213
Users = users;
1314
EnsureUniqueIndexOnUserName(users);
1415
}
1516

17+
public IdentityContext(MongoCollection users, MongoCollection roles) : this(users)
18+
{
19+
Roles = roles;
20+
}
21+
1622
private void EnsureUniqueIndexOnUserName(MongoCollection users)
1723
{
1824
var userName = new IndexKeysBuilder().Ascending("UserName");
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace AspNet.Identity.MongoDB
2+
{
3+
using global::MongoDB.Bson;
4+
using global::MongoDB.Bson.Serialization.Attributes;
5+
using Microsoft.AspNet.Identity;
6+
7+
public class IdentityRole : IRole<string>
8+
{
9+
public IdentityRole()
10+
{
11+
Id = ObjectId.GenerateNewId().ToString();
12+
}
13+
14+
public IdentityRole(string roleName) : this()
15+
{
16+
Name = roleName;
17+
}
18+
19+
[BsonRepresentation(BsonType.ObjectId)]
20+
public string Id { get; private set; }
21+
22+
public string Name { get; set; }
23+
}
24+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
namespace AspNet.Identity.MongoDB
2+
{
3+
using System.Linq;
4+
using System.Threading.Tasks;
5+
using global::MongoDB.Bson;
6+
using global::MongoDB.Driver.Builders;
7+
using global::MongoDB.Driver.Linq;
8+
using Microsoft.AspNet.Identity;
9+
10+
/// <summary>
11+
/// Note: Deleting and updating do not modify the roles stored on a user document. If you desire this dynamic
12+
/// capability, override the appropriate operations on RoleStore as desired for your application. For example you could
13+
/// perform a document modification on the users collection before a delete or a rename.
14+
/// </summary>
15+
/// <typeparam name="TRole"></typeparam>
16+
public class RoleStore<TRole> : IRoleStore<TRole>, IQueryableRoleStore<TRole>
17+
where TRole : IdentityRole
18+
{
19+
private readonly IdentityContext _Context;
20+
21+
public RoleStore(IdentityContext context)
22+
{
23+
_Context = context;
24+
}
25+
26+
public virtual IQueryable<TRole> Roles
27+
{
28+
get { return _Context.Roles.AsQueryable<TRole>(); }
29+
}
30+
31+
public virtual void Dispose()
32+
{
33+
// no need to dispose of anything, mongodb handles connection pooling automatically
34+
}
35+
36+
public virtual Task CreateAsync(TRole role)
37+
{
38+
return Task.Run(() => _Context.Roles.Insert(role));
39+
}
40+
41+
public virtual Task UpdateAsync(TRole role)
42+
{
43+
return Task.Run(() => _Context.Roles.Save(role));
44+
}
45+
46+
public virtual Task DeleteAsync(TRole role)
47+
{
48+
var queryById = Query<TRole>.EQ(r => r.Id, role.Id);
49+
return Task.Run(() => _Context.Roles.Remove(queryById));
50+
}
51+
52+
public virtual Task<TRole> FindByIdAsync(string roleId)
53+
{
54+
return Task.Run(() => _Context.Roles.FindOneByIdAs<TRole>(ObjectId.Parse(roleId)));
55+
}
56+
57+
public virtual Task<TRole> FindByNameAsync(string roleName)
58+
{
59+
var queryByName = Query<TRole>.EQ(r => r.Name, roleName);
60+
return Task.Run(() => _Context.Roles.FindOneAs<TRole>(queryByName));
61+
}
62+
}
63+
}

src/AspNet.Identity.MongoDB/UserStore.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ public Task UpdateAsync(TUser user)
4848

4949
public Task DeleteAsync(TUser user)
5050
{
51-
var remove = Query<TUser>.EQ(u => u.Id, user.Id);
52-
return Task.Run(() => _Context.Users.Remove(remove));
51+
var queryById = Query<TUser>.EQ(u => u.Id, user.Id);
52+
return Task.Run(() => _Context.Users.Remove(queryById));
5353
}
5454

5555
public Task<TUser> FindByIdAsync(string userId)
@@ -60,8 +60,8 @@ public Task<TUser> FindByIdAsync(string userId)
6060
public Task<TUser> FindByNameAsync(string userName)
6161
{
6262
// todo exception on duplicates? or better to enforce unique index to ensure this
63-
var byName = Query<TUser>.EQ(u => u.UserName, userName);
64-
return Task.Run(() => _Context.Users.FindOneAs<TUser>(byName));
63+
var queryByName = Query<TUser>.EQ(u => u.UserName, userName);
64+
return Task.Run(() => _Context.Users.FindOneAs<TUser>(queryByName));
6565
}
6666

6767
public Task SetPasswordHashAsync(TUser user, string passwordHash)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
namespace IntegrationTests
2+
{
3+
using System.Linq;
4+
using AspNet.Identity.MongoDB;
5+
using Microsoft.AspNet.Identity;
6+
using NUnit.Framework;
7+
8+
[TestFixture]
9+
public class EnsureWeCanExtendIdentityRoleTests : UserIntegrationTestsBase
10+
{
11+
private RoleManager<ExtendedIdentityRole> _Manager;
12+
private ExtendedIdentityRole _Role;
13+
14+
public class ExtendedIdentityRole : IdentityRole
15+
{
16+
public string ExtendedField { get; set; }
17+
}
18+
19+
[SetUp]
20+
public void BeforeEachTestAfterBase()
21+
{
22+
var context = new IdentityContext(Users, Roles);
23+
var roleStore = new RoleStore<ExtendedIdentityRole>(context);
24+
_Manager = new RoleManager<ExtendedIdentityRole>(roleStore);
25+
_Role = new ExtendedIdentityRole
26+
{
27+
Name = "admin"
28+
};
29+
}
30+
31+
[Test]
32+
public void Create_ExtendedRoleType_SavesExtraFields()
33+
{
34+
_Role.ExtendedField = "extendedField";
35+
36+
_Manager.Create(_Role);
37+
38+
var savedRole = Roles.FindAllAs<ExtendedIdentityRole>().Single();
39+
Expect(savedRole.ExtendedField, Is.EqualTo("extendedField"));
40+
}
41+
42+
[Test]
43+
public void Create_ExtendedRoleType_ReadsExtraFields()
44+
{
45+
_Role.ExtendedField = "extendedField";
46+
47+
_Manager.Create(_Role);
48+
49+
var savedRole = _Manager.FindById(_Role.Id);
50+
Expect(savedRole.ExtendedField, Is.EqualTo("extendedField"));
51+
}
52+
}
53+
}

src/IntegrationTests/IdentityUserTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class IdentityUserTests : UserIntegrationTestsBase
1212
public void Insert_NoId_SetsId()
1313
{
1414
var user = new IdentityUser();
15-
user.SetUserId(null);
15+
user.SetId(null);
1616

1717
Users.Insert(user);
1818

src/IntegrationTests/IntegrationTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,11 @@
5858
</ItemGroup>
5959
<ItemGroup>
6060
<Compile Include="EnsureWeCanExtendIdentityUserTests.cs" />
61+
<Compile Include="EnsureWeCanExtendIdentityRoleTests.cs" />
6162
<Compile Include="IdentityContextTests.cs" />
6263
<Compile Include="IdentityUserTests.cs" />
6364
<Compile Include="Properties\AssemblyInfo.cs" />
65+
<Compile Include="RoleStoreTests.cs" />
6466
<Compile Include="UserClaimStoreTests.cs" />
6567
<Compile Include="UserEmailStoreTests.cs" />
6668
<Compile Include="UserIntegrationTestsBase.cs" />
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
namespace IntegrationTests
2+
{
3+
using System.Linq;
4+
using AspNet.Identity.MongoDB;
5+
using Microsoft.AspNet.Identity;
6+
using MongoDB.Bson;
7+
using NUnit.Framework;
8+
using Tests;
9+
10+
[TestFixture]
11+
public class RoleStoreTests : UserIntegrationTestsBase
12+
{
13+
[Test]
14+
public void Create_NewRole_Saves()
15+
{
16+
var roleName = "admin";
17+
var role = new IdentityRole(roleName);
18+
var manager = GetRoleManager();
19+
20+
manager.Create(role);
21+
22+
var savedRole = Roles.FindAll().Single();
23+
Expect(savedRole.Name, Is.EqualTo(roleName));
24+
}
25+
26+
[Test]
27+
public void FindByName_SavedRole_ReturnsRole()
28+
{
29+
var roleName = "name";
30+
var role = new IdentityRole {Name = roleName};
31+
var manager = GetRoleManager();
32+
manager.Create(role);
33+
34+
var foundRole = manager.FindByName(roleName);
35+
36+
Expect(foundRole, Is.Not.Null);
37+
Expect(foundRole.Name, Is.EqualTo(roleName));
38+
}
39+
40+
[Test]
41+
public void FindById_SavedRole_ReturnsRole()
42+
{
43+
var roleId = ObjectId.GenerateNewId().ToString();
44+
var role = new IdentityRole {Name = "name"};
45+
role.SetId(roleId);
46+
var manager = GetRoleManager();
47+
manager.Create(role);
48+
49+
var foundRole = manager.FindById(roleId);
50+
51+
Expect(foundRole, Is.Not.Null);
52+
Expect(foundRole.Id, Is.EqualTo(roleId));
53+
}
54+
55+
[Test]
56+
public void Delete_ExistingRole_Removes()
57+
{
58+
var role = new IdentityRole {Name = "name"};
59+
var manager = GetRoleManager();
60+
manager.Create(role);
61+
Expect(Roles.FindAll(), Is.Not.Empty);
62+
63+
manager.Delete(role);
64+
65+
Expect(Roles.FindAll(), Is.Empty);
66+
}
67+
68+
[Test]
69+
public void Update_ExistingRole_Updates()
70+
{
71+
var role = new IdentityRole {Name = "name"};
72+
var manager = GetRoleManager();
73+
manager.Create(role);
74+
var savedRole = manager.FindById(role.Id);
75+
savedRole.Name = "newname";
76+
77+
manager.Update(savedRole);
78+
79+
var changedRole = Roles.FindAll().Single();
80+
Expect(changedRole, Is.Not.Null);
81+
Expect(changedRole.Name, Is.EqualTo("newname"));
82+
}
83+
}
84+
}

src/IntegrationTests/UserIntegrationTestsBase.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class UserIntegrationTestsBase : AssertionHelper
99
{
1010
protected MongoDatabase Database;
1111
protected MongoCollection<IdentityUser> Users;
12+
protected MongoCollection<IdentityRole> Roles;
1213
protected IdentityContext IdentityContext;
1314

1415
[SetUp]
@@ -17,15 +18,23 @@ public void BeforeEachTest()
1718
var client = new MongoClient("mongodb://localhost:27017");
1819
Database = client.GetServer().GetDatabase("identity-testing");
1920
Users = Database.GetCollection<IdentityUser>("users");
20-
IdentityContext = new IdentityContext(Users);
21+
Roles = Database.GetCollection<IdentityRole>("roles");
22+
IdentityContext = new IdentityContext(Users, Roles);
2123

2224
Database.DropCollection("users");
25+
Database.DropCollection("roles");
2326
}
2427

2528
protected UserManager<IdentityUser> GetUserManager()
2629
{
2730
var store = new UserStore<IdentityUser>(IdentityContext);
2831
return new UserManager<IdentityUser>(store);
2932
}
33+
34+
protected RoleManager<IdentityRole> GetRoleManager()
35+
{
36+
var store = new RoleStore<IdentityRole>(IdentityContext);
37+
return new RoleManager<IdentityRole>(store);
38+
}
3039
}
3140
}

src/IntegrationTests/UserStoreTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public void FindById_SavedUser_ReturnsUser()
5252
{
5353
var userId = ObjectId.GenerateNewId().ToString();
5454
var user = new IdentityUser {UserName = "name"};
55-
user.SetUserId(userId);
55+
user.SetId(userId);
5656
var manager = GetUserManager();
5757
manager.Create(user);
5858

0 commit comments

Comments
 (0)