Skip to content

Commit 7b08676

Browse files
authored
[UserController] Update security, attributes, tests (#3850)
1 parent bfd7907 commit 7b08676

File tree

6 files changed

+302
-112
lines changed

6 files changed

+302
-112
lines changed

Backend.Tests/Controllers/UserControllerTests.cs

Lines changed: 126 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace Backend.Tests.Controllers
1212
public class UserControllerTests : IDisposable
1313
{
1414
private IUserRepository _userRepo = null!;
15+
private IPasswordResetService _passwordResetService = null!;
1516
private IPermissionService _permissionService = null!;
1617
private UserController _userController = null!;
1718

@@ -33,9 +34,10 @@ protected virtual void Dispose(bool disposing)
3334
public void Setup()
3435
{
3536
_userRepo = new UserRepositoryMock();
37+
_passwordResetService = new PasswordResetServiceMock();
3638
_permissionService = new PermissionServiceMock(_userRepo);
3739
_userController = new UserController(_userRepo, _permissionService,
38-
new CaptchaServiceMock(), new EmailServiceMock(), new PasswordResetServiceMock());
40+
new CaptchaServiceMock(), new EmailServiceMock(), _passwordResetService);
3941
}
4042

4143
private static User RandomUser()
@@ -49,6 +51,62 @@ private static User RandomUser()
4951
return user;
5052
}
5153

54+
[Test]
55+
public void TestVerifyCaptchaToken()
56+
{
57+
// No permissions should be required to verify CAPTCHA.
58+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
59+
60+
var result = _userController.VerifyCaptchaToken("token").Result;
61+
Assert.That(result, Is.TypeOf<OkResult>());
62+
}
63+
64+
[Test]
65+
public void TestResetPasswordRequest()
66+
{
67+
// No permissions should be required to request a password reset.
68+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
69+
70+
// Returns Ok regardless of if user exists.
71+
var noUserResult = _userController.ResetPasswordRequest(new()).Result;
72+
Assert.That(noUserResult, Is.TypeOf<OkResult>());
73+
var username = (_userRepo.Create(new() { Username = "Imarealboy" }).Result)!.Username;
74+
var yesUserResult = _userController.ResetPasswordRequest(new() { EmailOrUsername = username }).Result;
75+
Assert.That(yesUserResult, Is.TypeOf<OkResult>());
76+
}
77+
78+
[Test]
79+
public void TestValidateResetToken()
80+
{
81+
// No permissions should be required to validate a password reset token.
82+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
83+
84+
((PasswordResetServiceMock)_passwordResetService).SetNextBoolResponse(false);
85+
var falseResult = _userController.ValidateResetToken("token").Result;
86+
Assert.That(falseResult, Is.TypeOf<OkObjectResult>());
87+
Assert.That(((OkObjectResult)falseResult).Value, Is.EqualTo(false));
88+
89+
((PasswordResetServiceMock)_passwordResetService).SetNextBoolResponse(true);
90+
var trueResult = _userController.ValidateResetToken("token").Result;
91+
Assert.That(trueResult, Is.TypeOf<OkObjectResult>());
92+
Assert.That(((OkObjectResult)trueResult).Value, Is.EqualTo(true));
93+
}
94+
95+
[Test]
96+
public void TestResetPassword()
97+
{
98+
// No permissions should be required to reset password via a token.
99+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
100+
101+
((PasswordResetServiceMock)_passwordResetService).SetNextBoolResponse(false);
102+
var falseResult = _userController.ResetPassword(new()).Result;
103+
Assert.That(falseResult, Is.TypeOf<ForbidResult>());
104+
105+
((PasswordResetServiceMock)_passwordResetService).SetNextBoolResponse(true);
106+
var trueResult = _userController.ResetPassword(new()).Result;
107+
Assert.That(trueResult, Is.TypeOf<OkResult>());
108+
}
109+
52110
[Test]
53111
public void TestGetAllUsers()
54112
{
@@ -62,6 +120,37 @@ public void TestGetAllUsers()
62120
user => Assert.That(users, Does.Contain(user).UsingPropertiesComparer()));
63121
}
64122

123+
[Test]
124+
public void TestGetAllUsersNoPermission()
125+
{
126+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
127+
var result = _userController.GetAllUsers().Result;
128+
Assert.That(result, Is.InstanceOf<ForbidResult>());
129+
}
130+
131+
[Test]
132+
public void TestAuthenticateBadCredentials()
133+
{
134+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
135+
var result = _userController.Authenticate(new() { EmailOrUsername = "no", Password = "no" }).Result;
136+
Assert.That(result, Is.InstanceOf<UnauthorizedObjectResult>());
137+
}
138+
139+
[Test]
140+
public void TestGetCurrentUserNoneAuthenticated()
141+
{
142+
var result = _userController.GetCurrentUser().Result;
143+
Assert.That(result, Is.InstanceOf<ForbidResult>());
144+
}
145+
146+
[Test]
147+
public void TestGetUserNoPermission()
148+
{
149+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
150+
var result = _userController.GetUser("any-user").Result;
151+
Assert.That(result, Is.InstanceOf<ForbidResult>());
152+
}
153+
65154
[Test]
66155
public void TestGetUser()
67156
{
@@ -72,14 +161,14 @@ public void TestGetUser()
72161

73162
var result = _userController.GetUser(user.Id).Result;
74163
Assert.That(result, Is.InstanceOf<ObjectResult>());
75-
Assert.That(((ObjectResult)result).Value, Is.EqualTo(user).UsingPropertiesComparer());
164+
Assert.That(((ObjectResult)result).Value, Is.EqualTo(new UserStub(user)).UsingPropertiesComparer());
76165
}
77166

78167
[Test]
79168
public void TestGetMissingUser()
80169
{
81170
var result = _userController.GetUser("INVALID_USER_ID").Result;
82-
Assert.That(result, Is.InstanceOf<NotFoundObjectResult>());
171+
Assert.That(result, Is.InstanceOf<NotFoundResult>());
83172
}
84173

85174
[Test]
@@ -120,7 +209,7 @@ public void TestGetUserIdByEmailOrUsernameNoPermission()
120209
{
121210
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
122211
const string email = "[email protected]";
123-
var _ = _userRepo.Create(
212+
_ = _userRepo.Create(
124213
new User { Email = email, Username = Util.RandString(10), Password = Util.RandString(10) }
125214
).Result ?? throw new UserCreationException();
126215

@@ -142,30 +231,29 @@ public void TestCreateUserBadUsername()
142231
{
143232
var user = RandomUser();
144233
_userRepo.Create(user);
234+
145235
var user2 = RandomUser();
146236
user2.Username = " ";
147-
var result = _userController.CreateUser(user).Result;
148-
Assert.That(result, Is.TypeOf<BadRequestObjectResult>());
237+
Assert.That(_userController.CreateUser(user2).Result, Is.TypeOf<BadRequestObjectResult>());
149238
user2.Username = user.Username;
150-
result = _userController.CreateUser(user).Result;
151-
Assert.That(result, Is.TypeOf<BadRequestObjectResult>());
239+
Assert.That(_userController.CreateUser(user2).Result, Is.TypeOf<BadRequestObjectResult>());
152240
user2.Username = user.Email;
153-
result = _userController.CreateUser(user).Result;
154-
Assert.That(result, Is.TypeOf<BadRequestObjectResult>());
241+
Assert.That(_userController.CreateUser(user2).Result, Is.TypeOf<BadRequestObjectResult>());
155242
}
156243

157244
[Test]
158245
public void TestCreateUserBadEmail()
159246
{
160247
var user = RandomUser();
161248
_userRepo.Create(user);
249+
162250
var user2 = RandomUser();
163251
user2.Email = " ";
164-
var result = _userController.CreateUser(user).Result;
165-
Assert.That(result, Is.TypeOf<BadRequestObjectResult>());
252+
Assert.That(_userController.CreateUser(user2).Result, Is.TypeOf<BadRequestObjectResult>());
166253
user2.Email = user.Email;
167-
result = _userController.CreateUser(user).Result;
168-
Assert.That(result, Is.TypeOf<BadRequestObjectResult>());
254+
Assert.That(_userController.CreateUser(user2).Result, Is.TypeOf<BadRequestObjectResult>());
255+
user2.Email = user.Username;
256+
Assert.That(_userController.CreateUser(user2).Result, Is.TypeOf<BadRequestObjectResult>());
169257
}
170258

171259
[Test]
@@ -196,6 +284,14 @@ public void TestUpdateUserCantUpdateIsAdmin()
196284
Assert.That(users, Does.Contain(modUser).UsingPropertiesComparer());
197285
}
198286

287+
[Test]
288+
public void TestUpdateUserNoPermission()
289+
{
290+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
291+
var result = _userController.UpdateUser("any-user", new()).Result;
292+
Assert.That(result, Is.InstanceOf<ForbidResult>());
293+
}
294+
199295
[Test]
200296
public void TestDeleteUser()
201297
{
@@ -206,6 +302,21 @@ public void TestDeleteUser()
206302
Assert.That(_userRepo.GetAllUsers().Result, Is.Empty);
207303
}
208304

305+
[Test]
306+
public void TestDeleteUserNoUser()
307+
{
308+
var result = _userController.DeleteUser("not-a-user").Result;
309+
Assert.That(result, Is.InstanceOf<NotFoundResult>());
310+
}
311+
312+
[Test]
313+
public void TestDeleteUserNoPermission()
314+
{
315+
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
316+
var result = _userController.DeleteUser("anything").Result;
317+
Assert.That(result, Is.InstanceOf<ForbidResult>());
318+
}
319+
209320
[Test]
210321
public void TestIsEmailOrUsernameAvailable()
211322
{
@@ -233,7 +344,7 @@ public void TestIsEmailOrUsernameAvailable()
233344
}
234345

235346
[Test]
236-
public void TestIsUserSiteAdminNotAuthorized()
347+
public void TestIsUserSiteAdminNoPermission()
237348
{
238349
_userController.ControllerContext.HttpContext = PermissionServiceMock.UnauthorizedHttpContext();
239350
var result = _userController.IsUserSiteAdmin().Result;

Backend.Tests/Mocks/PasswordResetServiceMock.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ namespace Backend.Tests.Mocks
66
{
77
internal sealed class PasswordResetServiceMock : IPasswordResetService
88
{
9+
private bool _boolResponse;
10+
internal void SetNextBoolResponse(bool response)
11+
{
12+
_boolResponse = response;
13+
}
14+
915
public Task<PasswordReset> CreatePasswordReset(string email)
1016
{
1117
return Task.FromResult(new PasswordReset(15, email));
@@ -18,14 +24,12 @@ public Task ExpirePasswordReset(string email)
1824

1925
public Task<bool> ValidateToken(string token)
2026
{
21-
// TODO: More sophisticated mock
22-
return Task.FromResult(true);
27+
return Task.FromResult(_boolResponse);
2328
}
2429

2530
public Task<bool> ResetPassword(string token, string password)
2631
{
27-
// TODO: More sophisticated mock
28-
return Task.FromResult(true);
32+
return Task.FromResult(_boolResponse);
2933
}
3034
}
3135
}

0 commit comments

Comments
 (0)