Skip to content

Commit 80553c5

Browse files
Merge pull request #325 from TransactionProcessing/task/#29_changeandforgotpassword
Change Password Implemented
2 parents 364d5e2 + 1ae8a3a commit 80553c5

19 files changed

Lines changed: 683 additions & 45 deletions

File tree

SecurityService.BusinessLogic/ISecurityServiceManager.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ namespace SecurityService.BusinessLogic
1111

1212
public interface ISecurityServiceManager
1313
{
14+
Task<(Boolean, String)> ChangePassword(String userName,
15+
String currentPassword,
16+
String newPassword,
17+
String clientId,
18+
CancellationToken cancellationToken);
19+
1420
Task ProcessPasswordResetRequest(String username,
1521
String emailAddress,
1622
String clientId,

SecurityService.BusinessLogic/SecurityServiceManager.cs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,52 @@ public SecurityServiceManager(IPasswordHasher<IdentityUser> passwordHasher,
8686
this.MessagingServiceClient = messagingServiceClient;
8787
this.IdentityServerTools = identityServerTools;
8888
}
89+
90+
public async Task<(Boolean, String)> ChangePassword(String userName,
91+
String currentPassword,
92+
String newPassword,
93+
String clientId,
94+
CancellationToken cancellationToken) {
95+
// Find the user based on the user name passed in
96+
IdentityUser user = await this.UserManager.FindByNameAsync(userName);
97+
98+
if (user == null)
99+
{
100+
// TODO: Redirect to a success page so the user doesnt know if the username is correct or not,
101+
// this prevents giving away info to a potential hacker...
102+
// TODO: maybe log something here...
103+
return (false,String.Empty);
104+
}
105+
106+
IdentityResult result = await this.UserManager.ChangePasswordAsync(user, currentPassword, newPassword);
107+
108+
if (result.Succeeded == false) {
109+
// Log any errors
110+
Logger.LogWarning($"Errors during password change for user [{userName} and Client [{clientId}]");
111+
foreach (IdentityError identityError in result.Errors)
112+
{
113+
Logger.LogWarning($"Code {identityError.Code} Description {identityError.Description}");
114+
}
115+
}
116+
117+
// build the redirect uri
118+
Duende.IdentityServer.EntityFramework.Entities.Client client = await this.ConfigurationDbContext.Clients.SingleOrDefaultAsync(c => c.ClientId == clientId);
119+
120+
if (client == null)
121+
{
122+
Logger.LogWarning($"Client not found for clientId {clientId}");
123+
// TODO: need to redirect somewhere...
124+
return (false,String.Empty);
125+
}
126+
127+
Logger.LogWarning($"Client uri {client.ClientUri}");
128+
return (true,client.ClientUri);
129+
}
89130

90131
public async Task ProcessPasswordResetRequest(String username,
91-
String emailAddress,
92-
String clientId,
93-
CancellationToken cancellationToken) {
132+
String emailAddress,
133+
String clientId,
134+
CancellationToken cancellationToken) {
94135
// Find the user based on the user name passed in
95136
IdentityUser user = await this.UserManager.FindByNameAsync(username);
96137

@@ -101,8 +142,7 @@ public async Task ProcessPasswordResetRequest(String username,
101142
return;
102143
}
103144

104-
// TODO: User has been found so send an email with reset details
105-
// TODO: For now we will write this to a text file
145+
// User has been found so send an email with reset details
106146
String resetToken = await this.UserManager.GeneratePasswordResetTokenAsync(user);
107147
resetToken = UrlEncoder.Default.Encode(resetToken);
108148
String baseAddress = ConfigurationReader.GetValue("ServiceOptions", "PublicOrigin");
@@ -202,7 +242,7 @@ public async Task<String> ProcessPasswordResetConfirmation(String username,
202242
}
203243
}
204244

205-
// TODO: build the redirect uri
245+
// build the redirect uri
206246
Duende.IdentityServer.EntityFramework.Entities.Client client = await this.ConfigurationDbContext.Clients.SingleOrDefaultAsync(c => c.ClientId == clientId);
207247

208248
if (client == null) {

SecurityService.IntegrationTests/SecurityService.IntegrationTests.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@
6161
</Compile>
6262
</ItemGroup>
6363

64+
<ItemGroup>
65+
<None Update="xunit.runner.json">
66+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
67+
</None>
68+
</ItemGroup>
69+
6470
<ItemGroup>
6571
<SpecFlowFeatureFiles Update="ApiScopes\ApiScope.feature">
6672
<Generator>SpecFlowSingleFileGenerator</Generator>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"maxParallelThreads" : 1
3+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@base @shared @userlogin @changepassword
2+
Feature: Change Password
3+
4+
Background:
5+
6+
Given I create the following roles
7+
| Role Name |
8+
| Estate |
9+
10+
Given I create the following api resources
11+
| Name | DisplayName | Secret | Scopes | UserClaims |
12+
| estateManagement | Estate Managememt REST | Secret1 | estateManagement | MerchantId,EstateId,role |
13+
14+
Given I create the following identity resources
15+
| Name | DisplayName | Description | UserClaims |
16+
| openid | Your user identifier | | sub |
17+
| profile | User profile | Your user profile information (first name, last name, etc.) | name,role,email,given_name,middle_name,family_name,EstateId,MerchantId |
18+
| email | Email | Email and Email Verified Flags | email_verified,email |
19+
20+
Given I create the following clients
21+
| ClientId | Name | Secret | Scopes | GrantTypes | RedirectUris | PostLogoutRedirectUris | RequireConsent | AllowOfflineAccess | ClientUri |
22+
| estateUIClient | Merchant Client | Secret1 | estateManagement,openid,email,profile | hybrid | https://[url]:[port]/signin-oidc | https://[url]:[port]/signout-oidc | false | true | https://[url]:[port] |
23+
24+
Given I create the following users
25+
| Email Address | Password | Phone Number | Given Name | Middle Name | Family Name | Claims | Roles |
26+
| estateuser@testestate1.co.uk | 123456 | 123456789 | Test | | User 1 | EstateId:1 | Estate |
27+
28+
@PRTest
29+
Scenario: Change Passwword
30+
Given I am on the application home page
31+
When I click the 'Privacy' link
32+
Then I am presented with a login screen
33+
When I login with the username 'estateuser@testestate1.co.uk' and password '123456'
34+
Then I am presented with the privacy screen
35+
When I click the 'ChangePassword' link
36+
Then I am presented with a change password screen
37+
When I enter my old password '123456'
38+
When I enter my new password 'Pa55word!'
39+
And I confirm my new password 'Pa55word!'
40+
And I click the change password button
41+
Then I am returned to the application home page

0 commit comments

Comments
 (0)