Skip to content

Commit 7e6f888

Browse files
Copilotascott18
andcommitted
Implement admin safety checks for Role and UserRole behaviors
Co-authored-by: ascott18 <5017521+ascott18@users.noreply.github.com>
1 parent 9e13e74 commit 7e6f888

3 files changed

Lines changed: 463 additions & 1 deletion

File tree

Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
using Coalesce.Starter.Vue.Data.Models;
2+
using IntelliTect.Coalesce;
3+
using Microsoft.AspNetCore.Identity;
4+
5+
namespace Coalesce.Starter.Vue.Data.Test;
6+
7+
public class AdminSafetyTests : TestBase
8+
{
9+
[Fact]
10+
public async Task Role_CannotRemoveUserAdminPermission_WhenItWouldLeaveNoAdmins()
11+
{
12+
// Arrange
13+
var adminRole = new Role
14+
{
15+
Id = "admin-role",
16+
Name = "Admin",
17+
NormalizedName = "ADMIN",
18+
Permissions = new List<Permission> { Permission.UserAdmin, Permission.Admin }
19+
};
20+
21+
var user = new User
22+
{
23+
Id = "user1",
24+
UserName = "admin@test.com",
25+
NormalizedUserName = "ADMIN@TEST.COM",
26+
Email = "admin@test.com"
27+
};
28+
29+
var userRole = new UserRole { UserId = user.Id, RoleId = adminRole.Id };
30+
31+
Db.Roles.Add(adminRole);
32+
Db.Users.Add(user);
33+
Db.UserRoles.Add(userRole);
34+
await Db.SaveChangesAsync();
35+
36+
RefreshServices();
37+
38+
// Get fresh instances after saving
39+
var roleToUpdate = await Db.Roles.FindAsync(adminRole.Id);
40+
var behaviors = new Role.Behaviors(Mocker.Get<RoleManager<Role>>(), Mocker.Get<CrudContext<AppDbContext>>());
41+
42+
// Act & Assert
43+
roleToUpdate!.Permissions = new List<Permission> { Permission.Admin }; // Remove UserAdmin permission
44+
var result = behaviors.BeforeSave(SaveKind.Update, adminRole, roleToUpdate);
45+
46+
Assert.False(result.WasSuccessful);
47+
Assert.Contains("no user administrators", result.Message);
48+
}
49+
50+
[Fact]
51+
public async Task Role_CanRemoveUserAdminPermission_WhenOtherAdminsExist()
52+
{
53+
// Arrange
54+
var adminRole1 = new Role
55+
{
56+
Id = "admin-role-1",
57+
Name = "Admin1",
58+
NormalizedName = "ADMIN1",
59+
Permissions = new List<Permission> { Permission.UserAdmin }
60+
};
61+
62+
var adminRole2 = new Role
63+
{
64+
Id = "admin-role-2",
65+
Name = "Admin2",
66+
NormalizedName = "ADMIN2",
67+
Permissions = new List<Permission> { Permission.UserAdmin }
68+
};
69+
70+
var user1 = new User
71+
{
72+
Id = "user1",
73+
UserName = "admin1@test.com",
74+
NormalizedUserName = "ADMIN1@TEST.COM"
75+
};
76+
77+
var user2 = new User
78+
{
79+
Id = "user2",
80+
UserName = "admin2@test.com",
81+
NormalizedUserName = "ADMIN2@TEST.COM"
82+
};
83+
84+
var userRole1 = new UserRole { UserId = user1.Id, RoleId = adminRole1.Id };
85+
var userRole2 = new UserRole { UserId = user2.Id, RoleId = adminRole2.Id };
86+
87+
Db.Roles.AddRange(adminRole1, adminRole2);
88+
Db.Users.AddRange(user1, user2);
89+
Db.UserRoles.AddRange(userRole1, userRole2);
90+
await Db.SaveChangesAsync();
91+
92+
RefreshServices();
93+
94+
var roleToUpdate = await Db.Roles.FindAsync(adminRole1.Id);
95+
var behaviors = new Role.Behaviors(Mocker.Get<RoleManager<Role>>(), Mocker.Get<CrudContext<AppDbContext>>());
96+
97+
// Act
98+
roleToUpdate!.Permissions = new List<Permission> { Permission.Admin }; // Remove UserAdmin permission
99+
var result = behaviors.BeforeSave(SaveKind.Update, adminRole1, roleToUpdate);
100+
101+
// Assert
102+
Assert.True(result.WasSuccessful);
103+
}
104+
105+
[Fact]
106+
public async Task Role_CannotDeleteRole_WhenItWouldLeaveNoAdmins()
107+
{
108+
// Arrange
109+
var adminRole = new Role
110+
{
111+
Id = "admin-role",
112+
Name = "Admin",
113+
NormalizedName = "ADMIN",
114+
Permissions = new List<Permission> { Permission.UserAdmin }
115+
};
116+
117+
var user = new User
118+
{
119+
Id = "user1",
120+
UserName = "admin@test.com",
121+
NormalizedUserName = "ADMIN@TEST.COM"
122+
};
123+
124+
var userRole = new UserRole { UserId = user.Id, RoleId = adminRole.Id };
125+
126+
Db.Roles.Add(adminRole);
127+
Db.Users.Add(user);
128+
Db.UserRoles.Add(userRole);
129+
await Db.SaveChangesAsync();
130+
131+
RefreshServices();
132+
133+
var roleToDelete = await Db.Roles.FindAsync(adminRole.Id);
134+
var behaviors = new Role.Behaviors(Mocker.Get<RoleManager<Role>>(), Mocker.Get<CrudContext<AppDbContext>>());
135+
136+
// Act & Assert
137+
var result = behaviors.BeforeDelete(roleToDelete!);
138+
139+
Assert.False(result.WasSuccessful);
140+
Assert.Contains("no user administrators", result.Message);
141+
}
142+
143+
[Fact]
144+
public async Task Role_CanDeleteRole_WhenOtherAdminsExist()
145+
{
146+
// Arrange
147+
var adminRole1 = new Role
148+
{
149+
Id = "admin-role-1",
150+
Name = "Admin1",
151+
NormalizedName = "ADMIN1",
152+
Permissions = new List<Permission> { Permission.UserAdmin }
153+
};
154+
155+
var adminRole2 = new Role
156+
{
157+
Id = "admin-role-2",
158+
Name = "Admin2",
159+
NormalizedName = "ADMIN2",
160+
Permissions = new List<Permission> { Permission.UserAdmin }
161+
};
162+
163+
var user1 = new User
164+
{
165+
Id = "user1",
166+
UserName = "admin1@test.com",
167+
NormalizedUserName = "ADMIN1@TEST.COM"
168+
};
169+
170+
var user2 = new User
171+
{
172+
Id = "user2",
173+
UserName = "admin2@test.com",
174+
NormalizedUserName = "ADMIN2@TEST.COM"
175+
};
176+
177+
var userRole1 = new UserRole { UserId = user1.Id, RoleId = adminRole1.Id };
178+
var userRole2 = new UserRole { UserId = user2.Id, RoleId = adminRole2.Id };
179+
180+
Db.Roles.AddRange(adminRole1, adminRole2);
181+
Db.Users.AddRange(user1, user2);
182+
Db.UserRoles.AddRange(userRole1, userRole2);
183+
await Db.SaveChangesAsync();
184+
185+
RefreshServices();
186+
187+
var roleToDelete = await Db.Roles.FindAsync(adminRole1.Id);
188+
var behaviors = new Role.Behaviors(Mocker.Get<RoleManager<Role>>(), Mocker.Get<CrudContext<AppDbContext>>());
189+
190+
// Act
191+
var result = behaviors.BeforeDelete(roleToDelete!);
192+
193+
// Assert
194+
Assert.True(result.WasSuccessful);
195+
}
196+
197+
[Fact]
198+
public async Task UserRole_CannotDeleteUserRole_WhenItWouldLeaveNoAdmins()
199+
{
200+
// Arrange
201+
var adminRole = new Role
202+
{
203+
Id = "admin-role",
204+
Name = "Admin",
205+
NormalizedName = "ADMIN",
206+
Permissions = new List<Permission> { Permission.UserAdmin }
207+
};
208+
209+
var user = new User
210+
{
211+
Id = "user1",
212+
UserName = "admin@test.com",
213+
NormalizedUserName = "ADMIN@TEST.COM"
214+
};
215+
216+
var userRole = new UserRole { UserId = user.Id, RoleId = adminRole.Id, Role = adminRole, User = user };
217+
218+
Db.Roles.Add(adminRole);
219+
Db.Users.Add(user);
220+
Db.UserRoles.Add(userRole);
221+
await Db.SaveChangesAsync();
222+
223+
RefreshServices();
224+
225+
var userRoleToDelete = await Db.UserRoles
226+
.Include(ur => ur.Role)
227+
.FirstAsync(ur => ur.UserId == user.Id && ur.RoleId == adminRole.Id);
228+
var behaviors = new UserRole.Behaviors(Mocker.Get<CrudContext<AppDbContext>>(), Mocker.Get<SignInManager<User>>());
229+
230+
// Act & Assert
231+
var result = behaviors.BeforeDelete(userRoleToDelete);
232+
233+
Assert.False(result.WasSuccessful);
234+
Assert.Contains("no user administrators", result.Message);
235+
}
236+
237+
[Fact]
238+
public async Task UserRole_CanDeleteUserRole_WhenOtherAdminsExist()
239+
{
240+
// Arrange
241+
var adminRole = new Role
242+
{
243+
Id = "admin-role",
244+
Name = "Admin",
245+
NormalizedName = "ADMIN",
246+
Permissions = new List<Permission> { Permission.UserAdmin }
247+
};
248+
249+
var user1 = new User
250+
{
251+
Id = "user1",
252+
UserName = "admin1@test.com",
253+
NormalizedUserName = "ADMIN1@TEST.COM"
254+
};
255+
256+
var user2 = new User
257+
{
258+
Id = "user2",
259+
UserName = "admin2@test.com",
260+
NormalizedUserName = "ADMIN2@TEST.COM"
261+
};
262+
263+
var userRole1 = new UserRole { UserId = user1.Id, RoleId = adminRole.Id, Role = adminRole, User = user1 };
264+
var userRole2 = new UserRole { UserId = user2.Id, RoleId = adminRole.Id, Role = adminRole, User = user2 };
265+
266+
Db.Roles.Add(adminRole);
267+
Db.Users.AddRange(user1, user2);
268+
Db.UserRoles.AddRange(userRole1, userRole2);
269+
await Db.SaveChangesAsync();
270+
271+
RefreshServices();
272+
273+
var userRoleToDelete = await Db.UserRoles
274+
.Include(ur => ur.Role)
275+
.FirstAsync(ur => ur.UserId == user1.Id && ur.RoleId == adminRole.Id);
276+
var behaviors = new UserRole.Behaviors(Mocker.Get<CrudContext<AppDbContext>>(), Mocker.Get<SignInManager<User>>());
277+
278+
// Act
279+
var result = behaviors.BeforeDelete(userRoleToDelete);
280+
281+
// Assert
282+
Assert.True(result.WasSuccessful);
283+
}
284+
285+
#if Tenancy
286+
[Fact]
287+
public async Task Role_CanRemoveUserAdminPermission_WhenGlobalAdminExists()
288+
{
289+
// Arrange
290+
var adminRole = new Role
291+
{
292+
Id = "admin-role",
293+
Name = "Admin",
294+
NormalizedName = "ADMIN",
295+
Permissions = new List<Permission> { Permission.UserAdmin }
296+
};
297+
298+
var regularUser = new User
299+
{
300+
Id = "user1",
301+
UserName = "admin@test.com",
302+
NormalizedUserName = "ADMIN@TEST.COM"
303+
};
304+
305+
var globalAdmin = new User
306+
{
307+
Id = "user2",
308+
UserName = "global@test.com",
309+
NormalizedUserName = "GLOBAL@TEST.COM",
310+
IsGlobalAdmin = true
311+
};
312+
313+
var userRole = new UserRole { UserId = regularUser.Id, RoleId = adminRole.Id };
314+
315+
Db.Roles.Add(adminRole);
316+
Db.Users.AddRange(regularUser, globalAdmin);
317+
Db.UserRoles.Add(userRole);
318+
await Db.SaveChangesAsync();
319+
320+
RefreshServices();
321+
322+
var roleToUpdate = await Db.Roles.FindAsync(adminRole.Id);
323+
var behaviors = new Role.Behaviors(Mocker.Get<RoleManager<Role>>(), Mocker.Get<CrudContext<AppDbContext>>());
324+
325+
// Act
326+
roleToUpdate!.Permissions = new List<Permission> { Permission.Admin }; // Remove UserAdmin permission
327+
var result = behaviors.BeforeSave(SaveKind.Update, adminRole, roleToUpdate);
328+
329+
// Assert
330+
Assert.True(result.WasSuccessful);
331+
}
332+
333+
[Fact]
334+
public async Task UserRole_CanDeleteUserRole_WhenGlobalAdminExists()
335+
{
336+
// Arrange
337+
var adminRole = new Role
338+
{
339+
Id = "admin-role",
340+
Name = "Admin",
341+
NormalizedName = "ADMIN",
342+
Permissions = new List<Permission> { Permission.UserAdmin }
343+
};
344+
345+
var regularUser = new User
346+
{
347+
Id = "user1",
348+
UserName = "admin@test.com",
349+
NormalizedUserName = "ADMIN@TEST.COM"
350+
};
351+
352+
var globalAdmin = new User
353+
{
354+
Id = "user2",
355+
UserName = "global@test.com",
356+
NormalizedUserName = "GLOBAL@TEST.COM",
357+
IsGlobalAdmin = true
358+
};
359+
360+
var userRole = new UserRole { UserId = regularUser.Id, RoleId = adminRole.Id, Role = adminRole, User = regularUser };
361+
362+
Db.Roles.Add(adminRole);
363+
Db.Users.AddRange(regularUser, globalAdmin);
364+
Db.UserRoles.Add(userRole);
365+
await Db.SaveChangesAsync();
366+
367+
RefreshServices();
368+
369+
var userRoleToDelete = await Db.UserRoles
370+
.Include(ur => ur.Role)
371+
.FirstAsync(ur => ur.UserId == regularUser.Id && ur.RoleId == adminRole.Id);
372+
var behaviors = new UserRole.Behaviors(Mocker.Get<CrudContext<AppDbContext>>(), Mocker.Get<SignInManager<User>>());
373+
374+
// Act
375+
var result = behaviors.BeforeDelete(userRoleToDelete);
376+
377+
// Assert
378+
Assert.True(result.WasSuccessful);
379+
}
380+
#endif
381+
}

0 commit comments

Comments
 (0)