Skip to content

Commit 709e969

Browse files
Merge pull request #610 from TransactionProcessing/task/#567_record_user_registration_date
Record the registration date/time of a new user
2 parents 6b193a9 + ffec77b commit 709e969

32 files changed

Lines changed: 1000 additions & 303 deletions

File tree

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
namespace SecurityService.BusinessLogic
22
{
33
using Microsoft.AspNetCore.Identity;
4+
using System;
45

5-
//public class ApplicationUser : IdentityUser
6-
//{
7-
//}
6+
public class ApplicationUser : IdentityUser
7+
{
8+
public DateTime RegistrationDateTime { get; set; }
9+
}
810
}

SecurityService.BusinessLogic/RequestHandlers/UserRequestHandler.cs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,20 @@ public class UserRequestHandler : IRequestHandler<SecurityServiceCommands.Create
4646

4747
private readonly IMessagingServiceClient MessagingServiceClient;
4848

49-
private readonly IPasswordHasher<IdentityUser> PasswordHasher;
49+
private readonly IPasswordHasher<ApplicationUser> PasswordHasher;
5050

5151
private readonly ServiceOptions ServiceOptions;
5252

5353
private TokenResponse TokenResponse;
5454

55-
private readonly UserManager<IdentityUser> UserManager;
55+
private readonly UserManager<ApplicationUser> UserManager;
5656

5757
#endregion
5858

5959
#region Constructors
6060

61-
public UserRequestHandler(IPasswordHasher<IdentityUser> passwordHasher,
62-
UserManager<IdentityUser> userManager,
61+
public UserRequestHandler(IPasswordHasher<ApplicationUser> passwordHasher,
62+
UserManager<ApplicationUser> userManager,
6363
ServiceOptions serviceOptions,
6464
IMessagingServiceClient messagingServiceClient,
6565
IdentityServerTools identityServerTools,
@@ -79,14 +79,15 @@ public UserRequestHandler(IPasswordHasher<IdentityUser> passwordHasher,
7979
public async Task<Result> Handle(SecurityServiceCommands.CreateUserCommand command,
8080
CancellationToken cancellationToken) {
8181
// request is valid now add the user
82-
IdentityUser newIdentityUser = new IdentityUser {
82+
ApplicationUser newIdentityUser = new() {
8383
Id = command.UserId.ToString(),
8484
Email = command.EmailAddress,
8585
UserName = command.UserName,
8686
NormalizedEmail = command.EmailAddress.ToUpper(),
8787
NormalizedUserName = command.UserName.ToUpper(),
8888
SecurityStamp = Guid.NewGuid().ToString("D"),
8989
PhoneNumber = command.PhoneNumber,
90+
RegistrationDateTime = DateTime.Now
9091
};
9192

9293
String passwordValue = String.IsNullOrEmpty(command.Password) ? UserRequestHandler.GenerateRandomPassword(this.UserManager.Options.Password) : command.Password;
@@ -130,7 +131,7 @@ public async Task<Result> Handle(SecurityServiceCommands.CreateUserCommand comma
130131
return Result.Success();
131132
}
132133

133-
private async Task<Result> CreateUser(IdentityUser newIdentityUser) {
134+
private async Task<Result> CreateUser(ApplicationUser newIdentityUser) {
134135
var createResult = await this.UserManager.CreateAsync(newIdentityUser);
135136

136137
if (!createResult.Succeeded)
@@ -140,7 +141,7 @@ private async Task<Result> CreateUser(IdentityUser newIdentityUser) {
140141
return Result.Success();
141142
}
142143

143-
private async Task<Result> AddRolesToUser(IdentityUser newIdentityUser, List<String> roles) {
144+
private async Task<Result> AddRolesToUser(ApplicationUser newIdentityUser, List<String> roles) {
144145
// Add the requested roles to the user
145146
if (roles != null && roles.Any())
146147
{
@@ -155,7 +156,7 @@ private async Task<Result> AddRolesToUser(IdentityUser newIdentityUser, List<Str
155156
return Result.Success();
156157
}
157158

158-
private async Task<Result> AddClaimsToUser(IdentityUser newIdentityUser, SecurityServiceCommands.CreateUserCommand command) {
159+
private async Task<Result> AddClaimsToUser(ApplicationUser newIdentityUser, SecurityServiceCommands.CreateUserCommand command) {
159160
// Add the requested claims
160161
List<Claim> claimsToAdd = new List<Claim>();
161162
if (command.Claims != null)
@@ -198,7 +199,7 @@ private async Task<Result> AddClaimsToUser(IdentityUser newIdentityUser, Securit
198199

199200
public async Task<Result<UserDetails>> Handle(SecurityServiceQueries.GetUserQuery query, CancellationToken cancellationToken){
200201

201-
IdentityUser user = await this.UserManager.FindByIdAsync(query.UserId.ToString());
202+
ApplicationUser user = await this.UserManager.FindByIdAsync(query.UserId.ToString());
202203

203204
if (user == null){
204205
return Result.NotFound($"No user found with user Id {query.UserId}");
@@ -210,6 +211,7 @@ public async Task<Result<UserDetails>> Handle(SecurityServiceQueries.GetUserQuer
210211
response.UserId = query.UserId;
211212
response.SubjectId = query.UserId.ToString();
212213
response.Username = user.UserName;
214+
response.RegistrationDateTime = user.RegistrationDateTime;
213215

214216
// Get the users roles
215217
response.Roles = await this.ConvertUsersRoles(user);
@@ -223,15 +225,15 @@ public async Task<Result<UserDetails>> Handle(SecurityServiceQueries.GetUserQuer
223225
public async Task<Result<List<UserDetails>>> Handle(SecurityServiceQueries.GetUsersQuery query, CancellationToken cancellationToken){
224226
List<UserDetails> response = new List<UserDetails>();
225227

226-
IQueryable<IdentityUser> userQuery = this.UserManager.Users;
228+
IQueryable<ApplicationUser> userQuery = this.UserManager.Users;
227229

228230
if (String.IsNullOrEmpty(query.UserName) == false){
229231
userQuery = userQuery.Where(u => u.UserName.Contains(query.UserName));
230232
}
231233

232-
List<IdentityUser> users = await userQuery.ToListAsyncSafe(cancellationToken);
234+
List<ApplicationUser> users = await userQuery.ToListAsyncSafe(cancellationToken);
233235

234-
foreach (IdentityUser identityUser in users){
236+
foreach (ApplicationUser identityUser in users){
235237
Dictionary<String, String> claims = await this.ConvertUsersClaims(identityUser);
236238
List<String> roles = await this.ConvertUsersRoles(identityUser);
237239

@@ -250,9 +252,9 @@ public async Task<Result<List<UserDetails>>> Handle(SecurityServiceQueries.GetUs
250252
}
251253

252254
public async Task<Result<ChangeUserPasswordResult>> Handle(SecurityServiceCommands.ChangeUserPasswordCommand command, CancellationToken cancellationToken){
253-
255+
254256
// Find the user based on the user name passed in
255-
IdentityUser user = await this.UserManager.FindByNameAsync(command.UserName);
257+
ApplicationUser user = await this.UserManager.FindByNameAsync(command.UserName);
256258

257259
if (user == null){
258260
// this prevents giving away info to a potential hacker...
@@ -286,7 +288,7 @@ public async Task<Result<ChangeUserPasswordResult>> Handle(SecurityServiceComman
286288
}
287289

288290
public async Task<Result> Handle(SecurityServiceCommands.ConfirmUserEmailAddressCommand command, CancellationToken cancellationToken){
289-
IdentityUser identityUser = await this.UserManager.FindByNameAsync(command.UserName);
291+
ApplicationUser identityUser = await this.UserManager.FindByNameAsync(command.UserName);
290292

291293
if (identityUser == null)
292294
{
@@ -311,7 +313,7 @@ public async Task<Result> Handle(SecurityServiceCommands.ConfirmUserEmailAddress
311313

312314
public async Task<Result<String>> Handle(SecurityServiceCommands.ProcessPasswordResetConfirmationCommand command, CancellationToken cancellationToken){
313315
// Find the user based on the user name passed in
314-
IdentityUser user = await this.UserManager.FindByNameAsync(command.Username);
316+
ApplicationUser user = await this.UserManager.FindByNameAsync(command.Username);
315317

316318
if (user == null)
317319
{
@@ -349,7 +351,7 @@ public async Task<Result<String>> Handle(SecurityServiceCommands.ProcessPassword
349351

350352
public async Task<Result> Handle(SecurityServiceCommands.ProcessPasswordResetRequestCommand command, CancellationToken cancellationToken){
351353
// Find the user based on the user name passed in
352-
IdentityUser user = await this.UserManager.FindByNameAsync(command.Username);
354+
ApplicationUser user = await this.UserManager.FindByNameAsync(command.Username);
353355

354356
if (user == null)
355357
{
@@ -373,7 +375,7 @@ public async Task<Result> Handle(SecurityServiceCommands.ProcessPasswordResetReq
373375
}
374376

375377
public async Task<Result> Handle(SecurityServiceCommands.SendWelcomeEmailCommand command, CancellationToken cancellationToken){
376-
IdentityUser i = await this.UserManager.FindByNameAsync(command.Username);
378+
ApplicationUser i = await this.UserManager.FindByNameAsync(command.Username);
377379
await this.UserManager.RemovePasswordAsync(i);
378380
String generatedPassword = UserRequestHandler.GenerateRandomPassword(this.UserManager.Options.Password);
379381
await this.UserManager.AddPasswordAsync(i, generatedPassword);
@@ -388,7 +390,7 @@ public async Task<Result> Handle(SecurityServiceCommands.SendWelcomeEmailCommand
388390
return Result.Success();
389391
}
390392

391-
private SendEmailRequest BuildEmailConfirmationRequest(IdentityUser user,
393+
private SendEmailRequest BuildEmailConfirmationRequest(ApplicationUser user,
392394
String emailConfirmationToken){
393395
StringBuilder mesasgeBuilder = new StringBuilder();
394396

@@ -416,7 +418,7 @@ private SendEmailRequest BuildEmailConfirmationRequest(IdentityUser user,
416418
return request;
417419
}
418420

419-
private SendEmailRequest BuildPasswordResetEmailRequest(IdentityUser user,
421+
private SendEmailRequest BuildPasswordResetEmailRequest(ApplicationUser user,
420422
String resetToken){
421423
StringBuilder mesasgeBuilder = new StringBuilder();
422424

@@ -474,7 +476,7 @@ private SendEmailRequest BuildWelcomeEmail(String emailAddress,
474476
return request;
475477
}
476478

477-
private async Task<Dictionary<String, String>> ConvertUsersClaims(IdentityUser identityUser){
479+
private async Task<Dictionary<String, String>> ConvertUsersClaims(ApplicationUser identityUser){
478480
Dictionary<String, String> response = new Dictionary<String, String>();
479481
IList<Claim> claims = await this.UserManager.GetClaimsAsync(identityUser);
480482
foreach (Claim claim in claims){
@@ -484,7 +486,7 @@ private async Task<Dictionary<String, String>> ConvertUsersClaims(IdentityUser i
484486
return response;
485487
}
486488

487-
private async Task<List<String>> ConvertUsersRoles(IdentityUser identityUser){
489+
private async Task<List<String>> ConvertUsersRoles(ApplicationUser identityUser){
488490
IList<String> roles = await this.UserManager.GetRolesAsync(identityUser);
489491
return roles.ToList();
490492
}

SecurityService.DataTransferObjects/Responses/UserDetails.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ public class UserDetails
6464
[JsonProperty("user_name")]
6565
public String UserName { get; set; }
6666

67+
[JsonProperty("registration_date_time")]
68+
public DateTime RegistrationDateTime { get; set; }
69+
6770
#endregion
6871
}
6972
}

SecurityService.Database/DbContexts/AuthenticationDbContext.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
namespace SecurityService.Database.DbContexts
1+
using SecurityService.BusinessLogic;
2+
3+
namespace SecurityService.Database.DbContexts
24
{
35
using System;
46
using Duende.IdentityServer.EntityFramework.DbContexts;
@@ -8,7 +10,7 @@
810
using Microsoft.EntityFrameworkCore;
911
using Shared.General;
1012

11-
public class AuthenticationDbContext : IdentityDbContext<IdentityUser>
13+
public class AuthenticationDbContext : IdentityDbContext<ApplicationUser>
1214
{
1315
public AuthenticationDbContext(DbContextOptions options)
1416
: base(options)

SecurityService.Database/SecurityService.Database.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.14" />
1212
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.14" />
1313
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.14" />
14-
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.3" />
14+
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.14" />
1515
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.14">
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -28,5 +28,8 @@
2828
<ItemGroup>
2929
<Folder Include="DbContexts\" />
3030
</ItemGroup>
31+
<ItemGroup>
32+
<ProjectReference Include="..\SecurityService.BusinessLogic\SecurityService.BusinessLogic.csproj" />
33+
</ItemGroup>
3134

3235
</Project>

SecurityService.IntegrationTesting.Helpers/ReqnrollExtensions.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,10 +305,12 @@ public static List<UserDetails> ToUserDetails(this DataTableRows tableRows){
305305

306306
String roles = ReqnrollTableHelper.GetStringRowValue(tableRow, "Roles");
307307

308-
309308
if (string.IsNullOrEmpty(roles) == false){
310309
userDetails.Roles = roles.SplitString();
311310
}
311+
312+
DateTime dateTime = ReqnrollTableHelper.GetDateForDateString(ReqnrollTableHelper.GetStringRowValue(tableRow, "RegistrationDate"), DateTime.Now);
313+
userDetails.RegistrationDateTime = dateTime;
312314
userDetailsList.Add(userDetails);
313315
}
314316
return userDetailsList;

SecurityService.IntegrationTesting.Helpers/SecurityServiceSteps.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ public async Task WhenIGetTheUserWithUserNameTheUserDetailsAreReturnedAsFollows(
313313
String? claim = userDetails.Data.Claims[expectedRecordClaim.Key];
314314
claim.ShouldBe(expectedRecordClaim.Value);
315315
}
316+
userDetails.Data.RegistrationDateTime.Date.ShouldBe(expectedRecord.RegistrationDateTime.Date);
316317
}
317318

318319
}

SecurityService.IntegrationTests/SecurityService.IntegrationTests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="8.0.14" />
1212

13+
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.14" />
14+
1315
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.1" />
1416

1517
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />

SecurityService.IntegrationTests/Users/Users.feature

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ Scenario: Get Users
1616
| testuser2@testing.co.uk | 123456789 | Test | | User 2 | | TestRole2 |
1717
| testuser3@testing.co.uk | 123456789 | Test | | User 3 | | TestRole3 |
1818
When I get the user with user name 'testuser1@testing.co.uk' the user details are returned as follows
19-
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |
20-
| testuser1@testing.co.uk | 123456789 | Test | | User 1 | email:testuser1@testing.co.uk, given_name:Test, family_name:User 1 | TestRole1 |
19+
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles | RegistrationDate |
20+
| testuser1@testing.co.uk | 123456789 | Test | | User 1 | email:testuser1@testing.co.uk, given_name:Test, family_name:User 1 | TestRole1 | Today |
2121
When I get the user with user name 'testuser2@testing.co.uk' the user details are returned as follows
22-
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |
23-
| testuser2@testing.co.uk | 123456789 | Test | | User 2 | email:testuser2@testing.co.uk, given_name:Test, family_name:User 2 | TestRole2 |
22+
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |RegistrationDate |
23+
| testuser2@testing.co.uk | 123456789 | Test | | User 2 | email:testuser2@testing.co.uk, given_name:Test, family_name:User 2 | TestRole2 |Today |
2424
When I get the user with user name 'testuser3@testing.co.uk' the user details are returned as follows
25-
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |
26-
| testuser3@testing.co.uk | 123456789 | Test | | User 3 | email:testuser3@testing.co.uk, given_name:Test, family_name:User 3 | TestRole3 |
25+
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |RegistrationDate |
26+
| testuser3@testing.co.uk | 123456789 | Test | | User 3 | email:testuser3@testing.co.uk, given_name:Test, family_name:User 3 | TestRole3 |Today |
2727
When I get the users 3 users details are returned as follows
28-
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |
29-
| testuser1@testing.co.uk | 123456789 | Test | | User 1 | email:testuser1@testing.co.uk, given_name:Test, family_name:User 1 | TestRole1 |
30-
| testuser2@testing.co.uk | 123456789 | Test | | User 2 | email:testuser2@testing.co.uk, given_name:Test, family_name:User 2 | TestRole2 |
31-
| testuser3@testing.co.uk | 123456789 | Test | | User 3 | email:testuser3@testing.co.uk, given_name:Test, family_name:User 3 | TestRole3 |
28+
| Email Address | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |RegistrationDate |
29+
| testuser1@testing.co.uk | 123456789 | Test | | User 1 | email:testuser1@testing.co.uk, given_name:Test, family_name:User 1 | TestRole1 |Today |
30+
| testuser2@testing.co.uk | 123456789 | Test | | User 2 | email:testuser2@testing.co.uk, given_name:Test, family_name:User 2 | TestRole2 |Today |
31+
| testuser3@testing.co.uk | 123456789 | Test | | User 3 | email:testuser3@testing.co.uk, given_name:Test, family_name:User 3 | TestRole3 |Today |

0 commit comments

Comments
 (0)