Added functionality to change password
This commit is contained in:
parent
0b88ebd2e8
commit
93ee75d021
@ -2,7 +2,7 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="SshConfigs">
|
<component name="SshConfigs">
|
||||||
<configs>
|
<configs>
|
||||||
<sshConfig host="4.72.148.132.host.secureserver.net" id="803b7675-648e-4d0f-8433-0abf8725c35f" keyPath="$USER_HOME$/downloads/id" port="22" nameFormat="DESCRIPTIVE" username="dotnet" useOpenSSHConfig="false" />
|
<sshConfig host="4.72.148.132.host.secureserver.net" id="803b7675-648e-4d0f-8433-0abf8725c35f" keyPath="$USER_HOME$/.ssh/dotnetSanAntonioSeniorGolf" port="22" nameFormat="DESCRIPTIVE" username="dotnet" useOpenSSHConfig="false" />
|
||||||
<sshConfig host="4.72.148.132.host.secureserver.net" id="7db7f912-3e0a-4cfc-90fe-563abe88fa2c" keyPath="$USER_HOME$/.ssh/sanantonioseniorgolf" port="22" nameFormat="DESCRIPTIVE" username="oaksana" useOpenSSHConfig="true" />
|
<sshConfig host="4.72.148.132.host.secureserver.net" id="7db7f912-3e0a-4cfc-90fe-563abe88fa2c" keyPath="$USER_HOME$/.ssh/sanantonioseniorgolf" port="22" nameFormat="DESCRIPTIVE" username="oaksana" useOpenSSHConfig="true" />
|
||||||
<sshConfig host="192.168.1.52" id="e58264ea-75c0-4f9e-aa5c-b6ece113fffb" keyPath="$USER_HOME$/.ssh/dotnet" port="22" nameFormat="DESCRIPTIVE" username="dotnet" />
|
<sshConfig host="192.168.1.52" id="e58264ea-75c0-4f9e-aa5c-b6ece113fffb" keyPath="$USER_HOME$/.ssh/dotnet" port="22" nameFormat="DESCRIPTIVE" username="dotnet" />
|
||||||
</configs>
|
</configs>
|
||||||
|
@ -14,5 +14,7 @@ namespace API.Authentication.GrantNames
|
|||||||
public const string CanUpdatePermission = "api.user.update.permission";
|
public const string CanUpdatePermission = "api.user.update.permission";
|
||||||
public const string CanDeleteAny = "api.user.delete.any";
|
public const string CanDeleteAny = "api.user.delete.any";
|
||||||
public const string CanDelete = "api.user.delete";
|
public const string CanDelete = "api.user.delete";
|
||||||
|
public const string CanChangePasswordSelf = "api.user.update.password.self";
|
||||||
|
public const string CanChangePasswordOthers = "api.user.update.password.others";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,6 @@ namespace API.Authentication.Interfaces
|
|||||||
{
|
{
|
||||||
public interface IUserAuthentication : IGenericAuthentication<UserDTO, User>
|
public interface IUserAuthentication : IGenericAuthentication<UserDTO, User>
|
||||||
{
|
{
|
||||||
|
bool canChangePassword(User destUser, User changingUser, bool oldPasswordMatchNew);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,6 @@ namespace API.Authentication
|
|||||||
{
|
{
|
||||||
if (!_grantManager.hasGrant(user.permissionId, UserGrantNames.CanUpdateSelf)
|
if (!_grantManager.hasGrant(user.permissionId, UserGrantNames.CanUpdateSelf)
|
||||||
|| !_grantManager.hasGrant(user.permissionId, UserGrantNames.CanUpdateAny)
|
|| !_grantManager.hasGrant(user.permissionId, UserGrantNames.CanUpdateAny)
|
||||||
|| !_grantManager.getULongValues(user.permissionId, UserGrantNames.CanUpdate).Exists(x => x == model.id)
|
|
||||||
)
|
)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -82,5 +81,10 @@ namespace API.Authentication
|
|||||||
_grantManager.getULongValues(user.permissionId, UserGrantNames.CanDelete).Exists(x => x == model.id))
|
_grantManager.getULongValues(user.permissionId, UserGrantNames.CanDelete).Exists(x => x == model.id))
|
||||||
&& model.id != user.id;
|
&& model.id != user.id;
|
||||||
}
|
}
|
||||||
|
public bool canChangePassword(User destUser, User changingUser, bool oldPasswordMatchNew)
|
||||||
|
{
|
||||||
|
return (destUser.id == changingUser.id && _grantManager.hasGrant(changingUser.permissionId, UserGrantNames.CanChangePasswordSelf) && oldPasswordMatchNew) ||
|
||||||
|
_grantManager.hasGrant(changingUser.permissionId, UserGrantNames.CanChangePasswordOthers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using API.DTO.Base;
|
using API.DTO.Base;
|
||||||
|
using API.DTO.Base.Update;
|
||||||
using API.DTO.Login;
|
using API.DTO.Login;
|
||||||
using API.Errors;
|
using API.Errors;
|
||||||
using API.Services;
|
using API.Services;
|
||||||
@ -78,6 +79,20 @@ namespace API.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPut("changePassword")]
|
||||||
|
public ActionResult<UserDTO> changePassword(UserPasswordUpdateDTO passwordUpdateDTO)
|
||||||
|
{
|
||||||
|
User? user = getUser(User);
|
||||||
|
if (user == null)
|
||||||
|
return Unauthorized();
|
||||||
|
|
||||||
|
UserDTO? result = _userManager.changePassword(passwordUpdateDTO, user);
|
||||||
|
if (result == null)
|
||||||
|
return Forbid();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
[NonAction]
|
[NonAction]
|
||||||
public User? getUser(ClaimsPrincipal user)
|
public User? getUser(ClaimsPrincipal user)
|
||||||
{
|
{
|
||||||
|
16
API/DTO/Base/Update/UserPasswordUpdateDTO.cs
Normal file
16
API/DTO/Base/Update/UserPasswordUpdateDTO.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using DAL.Values;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace API.DTO.Base.Update
|
||||||
|
{
|
||||||
|
public class UserPasswordUpdateDTO
|
||||||
|
{
|
||||||
|
public PhoneNumber phoneNumber { get; set; } = null!;
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string? oldPassword { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(100)]
|
||||||
|
public string newPassword { get; set; } = null!;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using API.DTO.Base;
|
using API.DTO.Base;
|
||||||
|
using API.DTO.Base.Update;
|
||||||
using API.DTO.Login;
|
using API.DTO.Login;
|
||||||
using DAL.Models;
|
using DAL.Models;
|
||||||
|
|
||||||
@ -9,5 +10,7 @@ namespace API.Services.Interfaces
|
|||||||
UserDTO? authenticateUser(UserLoginDTO loginDTO);
|
UserDTO? authenticateUser(UserLoginDTO loginDTO);
|
||||||
|
|
||||||
UserDTO? registerUser(UserRegisterDTO registerDTO, User? user = null, ulong? permissionId = null);
|
UserDTO? registerUser(UserRegisterDTO registerDTO, User? user = null, ulong? permissionId = null);
|
||||||
|
|
||||||
|
UserDTO? changePassword(UserPasswordUpdateDTO passwordUpdateDTO, User changingUser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using API.DTO.Base;
|
using API.DTO.Base;
|
||||||
|
using API.DTO.Base.Update;
|
||||||
using API.DTO.Login;
|
using API.DTO.Login;
|
||||||
using API.Hashing.Interfaces;
|
using API.Hashing.Interfaces;
|
||||||
using API.Services.Interfaces;
|
using API.Services.Interfaces;
|
||||||
@ -84,5 +85,49 @@ namespace API.Services
|
|||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserDTO? changePassword(UserPasswordUpdateDTO userPasswordUpdateDTO, User changingUser)
|
||||||
|
{
|
||||||
|
User? destUser = _userService.getNoAuthentication(x => x.phoneNumber.Equals(userPasswordUpdateDTO.phoneNumber)).FirstOrDefault();
|
||||||
|
if (destUser == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
IHashingAlgorithm? hashingAlgorithm = _hashingFactory.getAlgorithm(_preferredHashingType);
|
||||||
|
if (hashingAlgorithm == null){
|
||||||
|
_logger.Log(LogLevel.Error, "Preferred hashing type '{hashingType}' that isn't recognized by factory '{factory}'.", _preferredHashingType, nameof(_hashingFactory));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] newSalt;
|
||||||
|
string hashedNewPassword = hashingAlgorithm.hash(userPasswordUpdateDTO.newPassword, out newSalt);
|
||||||
|
|
||||||
|
bool oldPasswordMatchNew = false;
|
||||||
|
if (userPasswordUpdateDTO.oldPassword != null)
|
||||||
|
{
|
||||||
|
IHashingAlgorithm? userHashingAlgorithm = _hashingFactory.getAlgorithm(destUser.hashingType);
|
||||||
|
if (userHashingAlgorithm == null)
|
||||||
|
{
|
||||||
|
_logger.Log(LogLevel.Warning, "User id '{id}' has a hashing type '{hashingType}' that isn't recognized by factory '{factory}'. Not logging in.", destUser.id, destUser.hashingType, nameof(_hashingFactory));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
string hashedOldPassword = userHashingAlgorithm.hash(userPasswordUpdateDTO.oldPassword, destUser.salt);
|
||||||
|
|
||||||
|
if (hashedOldPassword.Equals(destUser.password))
|
||||||
|
{
|
||||||
|
oldPasswordMatchNew = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
User? updatedUser = _userService.changePassword(destUser, changingUser, hashedNewPassword, newSalt, oldPasswordMatchNew);
|
||||||
|
|
||||||
|
if (updatedUser == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
UserDTO dto = new UserDTO();
|
||||||
|
dto.adaptFromModel(updatedUser);
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using API.Authentication.Interfaces;
|
using API.Authentication.Interfaces;
|
||||||
using API.DTO.Base;
|
using API.DTO.Base;
|
||||||
|
using API.DTO.Base.Update;
|
||||||
using API.DTO.Login;
|
using API.DTO.Login;
|
||||||
using DAL.Contexts;
|
using DAL.Contexts;
|
||||||
using DAL.Models;
|
using DAL.Models;
|
||||||
@ -56,5 +57,16 @@ namespace API.Services
|
|||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public User? changePassword(User destUser, User changingUser, string hashedNewPassword, byte[] newSalt, bool oldPasswordMatchNew)
|
||||||
|
{
|
||||||
|
if (!_auth.canChangePassword(destUser, changingUser, oldPasswordMatchNew))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
destUser.password = hashedNewPassword;
|
||||||
|
destUser.salt = newSalt;
|
||||||
|
|
||||||
|
return update(destUser, changingUser);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -207,4 +207,20 @@ VALUES ('api.signup.delete', 1, NOW(), 1);
|
|||||||
INSERT INTO grants (name, permissionId, updated, updater)
|
INSERT INTO grants (name, permissionId, updated, updater)
|
||||||
VALUES ('api.signup.add.others', 1, NOW(), 1);
|
VALUES ('api.signup.add.others', 1, NOW(), 1);
|
||||||
|
|
||||||
|
INSERT INTO grants (name, permissionId, updated, updater)
|
||||||
|
VALUES ('api.user.update.password.self', 1, NOW(), 1);
|
||||||
|
|
||||||
|
INSERT INTO grants (name, permissionId, updated, updater)
|
||||||
|
VALUES ('api.user.update.password.others', 1, NOW(), 1);
|
||||||
|
|
||||||
|
|
||||||
|
INSERT INTO grants (name, permissionId, updated, updater)
|
||||||
|
VALUES ('api.signup.delete.self', 1, NOW(), 1);
|
||||||
|
|
||||||
|
INSERT INTO grants (name, permissionId, updated, updater)
|
||||||
|
VALUES ('api.user.update.password.self', 1, NOW(), 1);
|
||||||
|
|
||||||
|
INSERT INTO grants (name, permissionId, updated, updater)
|
||||||
|
VALUES ('api.user.update.password.others', 1, NOW(), 1);
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AActionMethodExecutor_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Fdb7395f4add94e6d10e515b3e55373f2821f8323de7dc8e314d78feefacf5584_003FActionMethodExecutor_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAuthenticationHttpContextExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Feb8b15bbf6ed38ef49b0b77ac383bba3c22ce8dbcff5a4798cdebe3013d9ea_003FAuthenticationHttpContextExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAuthenticationHttpContextExtensions_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003Feb8b15bbf6ed38ef49b0b77ac383bba3c22ce8dbcff5a4798cdebe3013d9ea_003FAuthenticationHttpContextExtensions_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F3bd4df5aff92cabbc4d630be64227073db1b8539b3a1e47786b4b189d7cdb7_003FDbContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ADbContext_002Ecs_002Fl_003A_002E_002E_003F_002E_002E_003F_002E_002E_003F_002E_002E_003F_002Econfig_003FJetBrains_003FRider2024_002E2_003Fresharper_002Dhost_003FSourcesCache_003F3bd4df5aff92cabbc4d630be64227073db1b8539b3a1e47786b4b189d7cdb7_003FDbContext_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
|
||||||
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d51071ba_002D6946_002D464f_002Db1ff_002D8183035b48e5/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
<s:String x:Key="/Default/Environment/UnitTesting/UnitTestSessionStore/Sessions/=d51071ba_002D6946_002D464f_002Db1ff_002D8183035b48e5/@EntryIndexedValue"><SessionState ContinuousTestingMode="0" IsActive="True" Name="Test1" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user