Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Commit 1021bc46 authored by Joze RIHTARSIC's avatar Joze RIHTARSIC
Browse files

Fix password change when login as CAS

parent 7ce222f5
No related branches found
No related tags found
No related merge requests found
Showing
with 101 additions and 25 deletions
......@@ -92,10 +92,10 @@
<mat-icon>person</mat-icon>
<span>{{currentUser}}</span>
</button>
<button mat-menu-item id="changePassword_id" (click)="changeCurrentUserPassword()">
<button *ngIf="isUserAuthPasswdEnabled" mat-menu-item id="changePassword_id" (click)="changeCurrentUserPassword()">
<span>Change password</span>
</button>
<button mat-menu-item id="getAccessToken_id" (click)="regenerateCurrentUserAccessToken()">
<button *ngIf="isWebServiceUserTokenAuthPasswdEnabled" mat-menu-item id="getAccessToken_id" (click)="regenerateCurrentUserAccessToken()">
<span>Generated access token</span>
</button>
......
......@@ -34,6 +34,14 @@ export class AppComponent {
this.userController = new UserController(this.http, this.lookups, this.dialog);
}
get isWebServiceUserTokenAuthPasswdEnabled(): boolean {
return this.lookups.cachedApplicationConfig?.webServiceAuthTypes?.includes('TOKEN');
}
get isUserAuthPasswdEnabled(): boolean {
return this.lookups.cachedApplicationInfo?.authTypes.includes('PASSWORD');
}
isCurrentUserSystemAdmin(): boolean {
return this.securityService.isCurrentUserInRole([Authority.SYSTEM_ADMIN]);
}
......
......@@ -30,7 +30,7 @@
Token will be generated immediately.
</mat-label>
</mat-card-actions>
<mat-form-field style="width:100%">
<mat-form-field *ngIf="!securityService.getCurrentUser()?.casAuthenticated" style="width:100%">
<input matInput [placeholder]="getPasswordTitle" [type]="hideCurrPwdFiled ? 'password' : 'text'"
formControlName="current-password" required id="cp_id">
<mat-icon matSuffix
......
......@@ -34,7 +34,7 @@ export class AccessTokenGenerationDialogComponent {
@Inject(MAT_DIALOG_DATA) public data: any,
private lookups: GlobalLookups,
private userDetailsService: UserDetailsService,
private securityService: SecurityService,
public securityService: SecurityService,
private fb: FormBuilder
) {
dialogRef.disableClose = true;//disable default close operation
......@@ -48,7 +48,7 @@ export class AccessTokenGenerationDialogComponent {
'username': new FormControl({value: null, readonly: true}, null),
'accessTokenId': new FormControl({value: null, readonly: true}, null),
'accessTokenExpireOn': new FormControl({value: null, readonly: true}, null),
'current-password': new FormControl({value: null, readonly: false}, [Validators.required]),
'current-password': new FormControl({value: null, readonly: false}, this.securityService.getCurrentUser().casAuthenticated?null:[Validators.required]),
});
this.dialogForm.controls['email'].setValue(this.isEmptyEmailAddress ? "Empty email address!" : this.current.emailAddress);
......
......@@ -22,7 +22,7 @@
</mat-card>
<mat-card class="password-panel">
<mat-card-content>
<mat-form-field style="width:100%">
<mat-form-field *ngIf="showCurrentPasswordField" style="width:100%">
<input matInput [placeholder]="getPasswordTitle" [type]="hideCurrPwdFiled ? 'password' : 'text'"
formControlName="current-password" required id="cp_id">
<mat-icon matSuffix
......
......@@ -45,7 +45,8 @@ export class PasswordChangeDialogComponent {
this.forceChange = this.current.forceChangeExpiredPassword;
let currentPasswdFormControl: FormControl = new FormControl({value: null, readonly: false}, [Validators.required]);
let currentPasswdFormControl: FormControl = new FormControl({value: null, readonly: false},
this.securityService.getCurrentUser().casAuthenticated && this.adminUser ? null : [Validators.required]);
let newPasswdFormControl: FormControl = new FormControl({value: null, readonly: false},
[Validators.required, Validators.pattern(this.passwordValidationRegExp), equal(currentPasswdFormControl, false)]);
let confirmNewPasswdFormControl: FormControl = new FormControl({value: null, readonly: false},
......@@ -66,6 +67,10 @@ export class PasswordChangeDialogComponent {
this.dialogForm.controls['confirm-new-password'].setValue('');
}
get showCurrentPasswordField():boolean {
return !this.securityService.getCurrentUser()?.casAuthenticated || !this.adminUser
}
public passwordError = (controlName: string, errorName: string) => {
return this.dialogForm.controls[controlName].hasError(errorName);
}
......
......@@ -7,6 +7,7 @@ export interface User {
accessTokenId?: string;
accessTokenExpireOn?: Date;
authorities: Array<Authority>;
casAuthenticated?: boolean;
defaultPasswordUsed: boolean;
forceChangeExpiredPassword?: boolean;
showPasswordExpirationWarning?: boolean;
......
......@@ -82,7 +82,7 @@
<input matInput placeholder="Cas identifier" [formControl]="userForm.controls['username']"
id="cas-user_id" maxlength="255" disabled>
</mat-form-field>
<button mat-flat-button color="primary" style="width: 100%" id="openCASData"
<button mat-flat-button color="primary" style="width: 100%" id="openCASData" [disabled]="!this.current?.casUserDataUrl"
(click)="openCurrentCasUserData()">
<span>Open CAS user data</span>
</button>
......
......@@ -33,8 +33,8 @@ services:
- "3908:3306"
- "8982:8080"
- "6902:6901"
- "8953:53"
- "5005:5005"
# - "8953:53"
# - "5005:5005"
eulogin-mock-server:
image: edelivery-docker.devops.tech.ec.europa.eu/eulogin/mockserver:6.2.7
......
......@@ -100,8 +100,13 @@ public class UIPropertyService {
property.setValuePattern(property.getValuePattern());
if (changedProps.containsKey(property.getProperty())) {
property.setNewValue(changedProps.get(propertyType.getProperty()).getValue());
property.setUpdateDate(refreshPropertiesTrigger.getNextExecutionDate());
String newVal = changedProps.get(propertyType.getProperty()).getValue();
if (!StringUtils.equals(newVal, property.getValue())) {
property.setNewValue(changedProps.get(propertyType.getProperty()).getValue());
property.setUpdateDate(refreshPropertiesTrigger.getNextExecutionDate());
} else {
LOG.debug("Property [{}] has newer update time, but it has the same value as the current value!");
}
}
return property;
}
......
......@@ -106,18 +106,20 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> {
*
* @param authorizedUserId which is authorized for update
* @param userToUpdateId the user id to be updated
* @param currentPassword authorized password
* @param validatePassword do not validate password if CAS authenticated
* @return generated AccessToken.
*/
@Transactional
public AccessTokenRO generateAccessTokenForUser(Long authorizedUserId, Long userToUpdateId, String currentPassword) {
public AccessTokenRO generateAccessTokenForUser(Long authorizedUserId, Long userToUpdateId, String currentPassword, boolean validateCurrentPassword) {
DBUser dbUser = userDao.find(authorizedUserId);
if (dbUser == null) {
LOG.error("Can not update user password because authorized user with id [{}] does not exist!", authorizedUserId);
throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UserId", "Can not find user id!");
}
if (!BCrypt.checkpw(currentPassword, dbUser.getPassword())) {
throw new BadCredentialsException("Password change failed; Invalid current password!");
if (validateCurrentPassword && !BCrypt.checkpw(currentPassword, dbUser.getPassword())) {
throw new BadCredentialsException("AccessToken generation failed: Invalid current password!");
}
boolean adminUpdate = userToUpdateId != null && authorizedUserId != userToUpdateId;
DBUser dbUserToUpdate = adminUpdate ? userDao.find(userToUpdateId) : dbUser;
......@@ -142,11 +144,27 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> {
* Method regenerate access token for user and returns access token
* In the database the access token value is saved in format BCryptPasswordHash
*
* @param authorizedUserId - authorized user id
* @param authorizedUserId which is authorized for update
* @param userToUpdateId the user id to be updated
* @return generated AccessToken.
*/
@Transactional
public DBUser updateUserPassword(Long authorizedUserId, Long userToUpdateId, String currentPassword, String newPassword) {
public AccessTokenRO generateAccessTokenForUser(Long authorizedUserId, Long userToUpdateId, String currentPassword) {
return generateAccessTokenForUser(authorizedUserId, userToUpdateId, currentPassword, true);
}
/**
* Method updates the user password
*
* @param authorizedUserId - authorized user id
* @param userToUpdateId - user id to update password user id
* @param authorizationPassword - authorization password
* @param newPassword - new password for the userToUpdateId
* @param validateCurrentPassword - validate authorizationPassword - if CAS authenticated skip this part
* @return generated DBUser.
*/
@Transactional
public DBUser updateUserPassword(Long authorizedUserId, Long userToUpdateId, String authorizationPassword, String newPassword, boolean validateCurrentPassword) {
Pattern pattern = configurationService.getPasswordPolicyRexExp();
if (pattern != null && !pattern.matcher(newPassword).matches()) {
......@@ -158,7 +176,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> {
throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UserId", "Can not find user id!");
}
if (!BCrypt.checkpw(currentPassword, dbAuthorizedUser.getPassword())) {
if (validateCurrentPassword && !BCrypt.checkpw(authorizationPassword, dbAuthorizedUser.getPassword())) {
throw new BadCredentialsException("Password change failed; Invalid current password!");
}
......@@ -178,6 +196,20 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> {
return dbUserToUpdate;
}
/**
* Method updates the user password
*
* @param authorizedUserId - authorized user id
* @param userToUpdateId - user id to update password user id
* @param authorizationPassword - authorization password
* @param newPassword - new password for the userToUpdateId
* @return generated DBUser.
*/
@Transactional
public DBUser updateUserPassword(Long authorizedUserId, Long userToUpdateId, String authorizationPassword, String newPassword) {
return updateUserPassword(authorizedUserId, userToUpdateId, authorizationPassword, newPassword, true);
}
@Transactional
public void updateUserList(List<UserRO> lst, OffsetDateTime passwordChange) {
for (UserRO userRO : lst) {
......
......@@ -124,6 +124,7 @@ public class SMPAuthorizationService {
public UserRO getLoggedUserData() {
SMPUserDetails userDetails = getAndValidateUserDetails();
// refresh data from database!
DBUser dbUser = userDao.find(userDetails.getUser().getId());
if (dbUser == null || !dbUser.isActive()) {
......@@ -132,7 +133,9 @@ public class SMPAuthorizationService {
userDetails.getUser().getUsername());
return null;
}
return getUserData(dbUser);
UserRO userRO = getUserData(dbUser);
userRO.setCasAuthenticated(userDetails.isCasAuthenticated());
return userRO;
}
public UserRO getUserData(DBUser user) {
......
......@@ -2,6 +2,7 @@ package eu.europa.ec.edelivery.smp.ui.external;
import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationService;
import eu.europa.ec.edelivery.smp.auth.SMPAuthorizationService;
import eu.europa.ec.edelivery.smp.auth.SMPUserDetails;
import eu.europa.ec.edelivery.smp.data.model.DBUser;
import eu.europa.ec.edelivery.smp.data.ui.AccessTokenRO;
import eu.europa.ec.edelivery.smp.data.ui.PasswordChangeRO;
......@@ -9,7 +10,9 @@ import eu.europa.ec.edelivery.smp.data.ui.UserRO;
import eu.europa.ec.edelivery.smp.logging.SMPLogger;
import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory;
import eu.europa.ec.edelivery.smp.services.ui.UIUserService;
import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.*;
......@@ -40,11 +43,16 @@ public class UserResource {
@PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userId)")
@PostMapping(path = "/{user-id}/generate-access-token", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
public AccessTokenRO generateAccessToken(@PathVariable("user-id") String userId, @RequestBody String password) {
public AccessTokenRO generateAccessToken(@PathVariable("user-id") String userId, @RequestBody(required = false) String password) {
Long entityId = decryptEntityId(userId);
SMPUserDetails currentUser = SessionSecurityUtils.getSessionUserDetails();
LOG.info("Generated access token for user:[{}] with id:[{}] ", userId, entityId);
if (currentUser == null) {
throw new SessionAuthenticationException("User session expired!");
}
return uiUserService.generateAccessTokenForUser(entityId, entityId, password);
// no need to validate password if CAS authenticated
return uiUserService.generateAccessTokenForUser(entityId, entityId, password, !currentUser.isCasAuthenticated());
}
@PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userId)")
......@@ -52,6 +60,7 @@ public class UserResource {
public boolean changePassword(@PathVariable("user-id") String userId, @RequestBody PasswordChangeRO newPassword, HttpServletRequest request, HttpServletResponse response) {
Long entityId = decryptEntityId(userId);
LOG.info("Validating the password of the currently logged in user:[{}] with id:[{}] ", userId, entityId);
// when user changing password the current password must be verified even if cas authenticated
DBUser result = uiUserService.updateUserPassword(entityId, entityId, newPassword.getCurrentPassword(), newPassword.getNewPassword());
if (result!=null) {
LOG.info("Password successfully changed. Logout the user, to be able to login with the new password!");
......
......@@ -12,6 +12,7 @@ import eu.europa.ec.edelivery.smp.services.ui.UIUserService;
import eu.europa.ec.edelivery.smp.services.ui.filters.UserFilter;
import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.web.authentication.session.SessionAuthenticationException;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.*;
......@@ -89,12 +90,18 @@ public class UserAdminResource {
public AccessTokenRO generateAccessTokenForUser(
@PathVariable("user-id") String userId,
@PathVariable("update-user-id") String regenerateForUserId,
@RequestBody String password) {
@RequestBody(required = false) String password) {
Long authorizedUserId = decryptEntityId(userId);
Long changeUserId = decryptEntityId(regenerateForUserId);
LOG.info("Generated access token for user:[{}] with id:[{}] by the system user with id [{}]", userId, regenerateForUserId, authorizedUserId);
return uiUserService.generateAccessTokenForUser(authorizedUserId, changeUserId, password);
SMPUserDetails currentUser = SessionSecurityUtils.getSessionUserDetails();
if (currentUser == null) {
throw new SessionAuthenticationException("User session expired!");
}
// no need to validate password if cas authenticated
return uiUserService.generateAccessTokenForUser(authorizedUserId, changeUserId, password,!currentUser.isCasAuthenticated());
}
......@@ -102,11 +109,17 @@ public class UserAdminResource {
@Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN})
public UserRO changePassword(@PathVariable("user-id") String userId,
@PathVariable("update-user-id") String regenerateForUserId,
@RequestBody PasswordChangeRO newPassword, HttpServletRequest request, HttpServletResponse response) {
@RequestBody PasswordChangeRO newPassword) {
Long authorizedUserId = decryptEntityId(userId);
Long changeUserId = decryptEntityId(regenerateForUserId);
LOG.info("change the password of the currently logged in user:[{}] with id:[{}] ", changeUserId, regenerateForUserId);
DBUser user = uiUserService.updateUserPassword(authorizedUserId, changeUserId, newPassword.getCurrentPassword(), newPassword.getNewPassword());
SMPUserDetails currentUser = SessionSecurityUtils.getSessionUserDetails();
if (currentUser == null) {
throw new SessionAuthenticationException("User session expired!");
}
DBUser user = uiUserService.updateUserPassword(authorizedUserId, changeUserId, newPassword.getCurrentPassword(), newPassword.getNewPassword(),!currentUser.isCasAuthenticated());
return authorizationService.sanitize(uiUserService.convertToRo(user));
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment