root/resilient#3 Melhorias no registo de utilizadores
This commit is contained in:
parent
2aa75f0e13
commit
af63377a1e
11 changed files with 186 additions and 15 deletions
|
@ -76,6 +76,12 @@ public class User extends AbstractAuditingEntity<Long> implements Serializable {
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private boolean activated = false;
|
private boolean activated = false;
|
||||||
|
|
||||||
|
@Column(name = "allow_user_pwd_auth")
|
||||||
|
private boolean allowUserPwdAuthentication = false;
|
||||||
|
|
||||||
|
@Column(name = "allow_saml_auth")
|
||||||
|
private boolean allowSamlAuthentication = false;
|
||||||
|
|
||||||
@Size(min = 2, max = 10)
|
@Size(min = 2, max = 10)
|
||||||
@Column(name = "lang_key", length = 10)
|
@Column(name = "lang_key", length = 10)
|
||||||
private String langKey;
|
private String langKey;
|
||||||
|
@ -183,6 +189,22 @@ public class User extends AbstractAuditingEntity<Long> implements Serializable {
|
||||||
return activationKey;
|
return activationKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAllowSamlAuthentication(boolean allowSamlAuthentication) {
|
||||||
|
this.allowSamlAuthentication = allowSamlAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAllowSamlAuthentication() {
|
||||||
|
return allowSamlAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowUserPwdAuthentication(boolean allowUserPwdAuthentication) {
|
||||||
|
this.allowUserPwdAuthentication = allowUserPwdAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getAllowUserPwdAuthentication() {
|
||||||
|
return allowUserPwdAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
public void setActivationKey(String activationKey) {
|
public void setActivationKey(String activationKey) {
|
||||||
this.activationKey = activationKey;
|
this.activationKey = activationKey;
|
||||||
}
|
}
|
||||||
|
@ -260,8 +282,19 @@ public class User extends AbstractAuditingEntity<Long> implements Serializable {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "User{" + "login='" + login + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\''
|
//@formatter:off
|
||||||
+ ", email='" + email + '\'' + ", imageUrl='" + imageUrl + '\'' + ", activated='" + activated + '\''
|
return "User{"
|
||||||
+ ", langKey='" + langKey + '\'' + ", activationKey='" + activationKey + '\'' + "}";
|
+ "login='" + login + '\''
|
||||||
|
+ ", firstName='" + firstName + '\''
|
||||||
|
+ ", lastName='" + lastName + '\''
|
||||||
|
+ ", email='" + email + '\''
|
||||||
|
+ ", imageUrl='" + imageUrl + '\''
|
||||||
|
+ ", activated='" + activated + '\''
|
||||||
|
+ ", allowSamlAuthentication='" + allowSamlAuthentication + '\''
|
||||||
|
+ ", allowUserPwdAuthentication='" + allowUserPwdAuthentication + '\''
|
||||||
|
+ ", langKey='" + langKey + '\''
|
||||||
|
+ ", activationKey='" + activationKey + '\''
|
||||||
|
+ "}";
|
||||||
|
//@formatter:on
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.oguerreiro.resilient.security;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import org.hibernate.Hibernate;
|
import org.hibernate.Hibernate;
|
||||||
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
|
import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator;
|
||||||
|
@ -66,16 +67,44 @@ public class DomainUserDetailsService implements UserDetailsService {
|
||||||
public UserDetails loadUserByUsername(final String login) {
|
public UserDetails loadUserByUsername(final String login) {
|
||||||
log.debug("Authenticating {}", login);
|
log.debug("Authenticating {}", login);
|
||||||
|
|
||||||
|
UserDetails userDetails = null;
|
||||||
|
AtomicBoolean allowUserPwdAuthentication = new AtomicBoolean(false);
|
||||||
|
|
||||||
if (new EmailValidator().isValid(login, null)) {
|
if (new EmailValidator().isValid(login, null)) {
|
||||||
return userRepository.findOneWithAuthoritiesByEmailIgnoreCase(login).map(
|
//@formatter:off
|
||||||
user -> createResilientSecurityUser(login, user)).orElseThrow(
|
userDetails = userRepository.findOneWithAuthoritiesByEmailIgnoreCase(login)
|
||||||
() -> new UsernameNotFoundException("User with email " + login + " was not found in the database"));
|
.map(user -> {
|
||||||
|
allowUserPwdAuthentication.set(user.getAllowUserPwdAuthentication());
|
||||||
|
return createResilientSecurityUser(login, user);
|
||||||
|
})
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new UsernameNotFoundException("User with email " + login + " was not found in the database")
|
||||||
|
);
|
||||||
|
//@formatter:on
|
||||||
|
} else {
|
||||||
|
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
|
||||||
|
//@formatter:off
|
||||||
|
userDetails = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin)
|
||||||
|
.map(user -> {
|
||||||
|
allowUserPwdAuthentication.set(user.getAllowUserPwdAuthentication());
|
||||||
|
return createResilientSecurityUser(lowercaseLogin, user);
|
||||||
|
})
|
||||||
|
.orElseThrow(
|
||||||
|
() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database")
|
||||||
|
);
|
||||||
|
//@formatter:on
|
||||||
}
|
}
|
||||||
|
|
||||||
String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
|
if (userDetails == null) {
|
||||||
return userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin).map(
|
throw new UsernameNotFoundException("User " + login + " was not found in the database");
|
||||||
user -> createResilientSecurityUser(lowercaseLogin, user)).orElseThrow(
|
}
|
||||||
() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database"));
|
|
||||||
|
if (!allowUserPwdAuthentication.get()) {
|
||||||
|
// The user can't login with User + Password
|
||||||
|
throw new UsernameNotFoundException("User " + login + " not allowed for login with User + Password.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return userDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin,
|
private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin,
|
||||||
|
|
|
@ -84,6 +84,13 @@ public class Saml2AuthenticationHandler implements AuthenticationSuccessHandler,
|
||||||
|
|
||||||
User user = userRepository.findOneByLogin(username).orElse(null);
|
User user = userRepository.findOneByLogin(username).orElse(null);
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
|
// This user can login with SAML2 ?
|
||||||
|
if (!user.getAllowSamlAuthentication()) {
|
||||||
|
log.error("The user '" + username + "' it's not allowed to login with SAML2 authentication.");
|
||||||
|
this.invalidateLogin(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
// User found. Setup permissions from resilient config
|
// User found. Setup permissions from resilient config
|
||||||
authorities = user.getAuthorities().stream()
|
authorities = user.getAuthorities().stream()
|
||||||
|
|
|
@ -201,6 +201,8 @@ public class UserService {
|
||||||
}
|
}
|
||||||
user.setImageUrl(userDTO.getImageUrl());
|
user.setImageUrl(userDTO.getImageUrl());
|
||||||
user.setActivated(userDTO.isActivated());
|
user.setActivated(userDTO.isActivated());
|
||||||
|
user.setAllowSamlAuthentication(userDTO.isAllowSamlAuthentication());
|
||||||
|
user.setAllowUserPwdAuthentication(userDTO.isAllowUserPwdAuthentication());
|
||||||
user.setLangKey(userDTO.getLangKey());
|
user.setLangKey(userDTO.getLangKey());
|
||||||
Set<Authority> managedAuthorities = user.getAuthorities();
|
Set<Authority> managedAuthorities = user.getAuthorities();
|
||||||
managedAuthorities.clear();
|
managedAuthorities.clear();
|
||||||
|
|
|
@ -47,6 +47,8 @@ public class AdminUserDTO implements Serializable {
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
|
|
||||||
private boolean activated = false;
|
private boolean activated = false;
|
||||||
|
private boolean allowUserPwdAuthentication = false;
|
||||||
|
private boolean allowSamlAuthentication = false;
|
||||||
|
|
||||||
@Size(min = 2, max = 10)
|
@Size(min = 2, max = 10)
|
||||||
private String langKey;
|
private String langKey;
|
||||||
|
@ -76,6 +78,8 @@ public class AdminUserDTO implements Serializable {
|
||||||
this.lastName = user.getLastName();
|
this.lastName = user.getLastName();
|
||||||
this.email = user.getEmail();
|
this.email = user.getEmail();
|
||||||
this.activated = user.isActivated();
|
this.activated = user.isActivated();
|
||||||
|
this.allowSamlAuthentication = user.getAllowSamlAuthentication();
|
||||||
|
this.allowUserPwdAuthentication = user.getAllowUserPwdAuthentication();
|
||||||
this.imageUrl = user.getImageUrl();
|
this.imageUrl = user.getImageUrl();
|
||||||
this.langKey = user.getLangKey();
|
this.langKey = user.getLangKey();
|
||||||
this.createdBy = user.getCreatedBy();
|
this.createdBy = user.getCreatedBy();
|
||||||
|
@ -221,13 +225,43 @@ public class AdminUserDTO implements Serializable {
|
||||||
this.parentOrganization = parentOrganization;
|
this.parentOrganization = parentOrganization;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isAllowUserPwdAuthentication() {
|
||||||
|
return allowUserPwdAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowUserPwdAuthentication(boolean allowUserPwdAuthentication) {
|
||||||
|
this.allowUserPwdAuthentication = allowUserPwdAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAllowSamlAuthentication() {
|
||||||
|
return allowSamlAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAllowSamlAuthentication(boolean allowSamlAuthentication) {
|
||||||
|
this.allowSamlAuthentication = allowSamlAuthentication;
|
||||||
|
}
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "AdminUserDTO{" + "login='" + login + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName
|
//@formatter:off
|
||||||
+ '\'' + ", email='" + email + '\'' + ", imageUrl='" + imageUrl + '\'' + ", activated=" + activated
|
return "AdminUserDTO{"
|
||||||
+ ", langKey='" + langKey + '\'' + ", createdBy=" + createdBy + ", createdDate=" + createdDate
|
+ "login='" + login + '\''
|
||||||
+ ", lastModifiedBy='" + lastModifiedBy + '\'' + ", lastModifiedDate=" + lastModifiedDate + ", authorities="
|
+ ", firstName='" + firstName + '\''
|
||||||
+ authorities + "}";
|
+ ", lastName='" + lastName + '\''
|
||||||
|
+ ", email='" + email + '\''
|
||||||
|
+ ", imageUrl='" + imageUrl + '\''
|
||||||
|
+ ", activated=" + activated
|
||||||
|
+ ", allowSamlAuthentication=" + allowSamlAuthentication
|
||||||
|
+ ", allowUserPwdAuthentication=" + allowUserPwdAuthentication
|
||||||
|
+ ", langKey='" + langKey + '\''
|
||||||
|
+ ", createdBy=" + createdBy
|
||||||
|
+ ", createdDate=" + createdDate
|
||||||
|
+ ", lastModifiedBy='" + lastModifiedBy + '\''
|
||||||
|
+ ", lastModifiedDate=" + lastModifiedDate
|
||||||
|
+ ", authorities=" + authorities
|
||||||
|
+ "}";
|
||||||
|
//@formatter:
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<databaseChangeLog
|
||||||
|
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
|
||||||
|
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd
|
||||||
|
http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Alter the entity User.
|
||||||
|
Add flags for allowUserPwdAuth & allowSamlAuth
|
||||||
|
-->
|
||||||
|
<changeSet id="20250604123000-1" author="omg">
|
||||||
|
<!-- User can authenticate with USER+PWD -->
|
||||||
|
<addColumn tableName="jhi_user">
|
||||||
|
<column name="allow_user_pwd_auth" type="boolean" valueBoolean="false"/>
|
||||||
|
</addColumn>
|
||||||
|
|
||||||
|
<!-- User can authenticate with IDP (SAML2) -->
|
||||||
|
<addColumn tableName="jhi_user">
|
||||||
|
<column name="allow_saml_auth" type="boolean" valueBoolean="false"/>
|
||||||
|
</addColumn>
|
||||||
|
</changeSet>
|
||||||
|
|
||||||
|
<changeSet id="20250604123000-2" author="omg">
|
||||||
|
<!-- Update allow_user_pwd_auth to TRUE -->
|
||||||
|
<update tableName="jhi_users">
|
||||||
|
<column name="allow_user_pwd" valueBoolean="true"/>
|
||||||
|
</update>
|
||||||
|
</changeSet>
|
||||||
|
</databaseChangeLog>
|
|
@ -104,5 +104,6 @@
|
||||||
<include file="config/liquibase/changelog/20252104104000_added_entity_ResilientLog.xml" relativeToChangelogFile="false"/>
|
<include file="config/liquibase/changelog/20252104104000_added_entity_ResilientLog.xml" relativeToChangelogFile="false"/>
|
||||||
<include file="config/liquibase/changelog/20250502222301_added_entity_DashboardComponentOrganization.xml" relativeToChangelogFile="false"/>
|
<include file="config/liquibase/changelog/20250502222301_added_entity_DashboardComponentOrganization.xml" relativeToChangelogFile="false"/>
|
||||||
<include file="config/liquibase/changelog/20250506120000_altered_entity_ResilientLog.xml" relativeToChangelogFile="false"/>
|
<include file="config/liquibase/changelog/20250506120000_altered_entity_ResilientLog.xml" relativeToChangelogFile="false"/>
|
||||||
|
<include file="config/liquibase/changelog/20250604123000_altered_entity_User.xml" relativeToChangelogFile="false"/>
|
||||||
|
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -137,6 +137,34 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<label class="form-check-label" for="allowUserPwdAuthentication">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
[attr.disabled]="editForm.value.id === undefined ? 'disabled' : null"
|
||||||
|
type="checkbox"
|
||||||
|
id="allowUserPwdAuthentication"
|
||||||
|
name="allowUserPwdAuthentication"
|
||||||
|
formControlName="allowUserPwdAuthentication"
|
||||||
|
/>
|
||||||
|
<span jhiTranslate="userManagement.allowUserPwdAuthentication">Permite autenticação Utilizador+Password</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check">
|
||||||
|
<label class="form-check-label" for="allowSamlAuthentication">
|
||||||
|
<input
|
||||||
|
class="form-check-input"
|
||||||
|
[attr.disabled]="editForm.value.id === undefined ? 'disabled' : null"
|
||||||
|
type="checkbox"
|
||||||
|
id="allowSamlAuthentication"
|
||||||
|
name="allowSamlAuthentication"
|
||||||
|
formControlName="allowSamlAuthentication"
|
||||||
|
/>
|
||||||
|
<span jhiTranslate="userManagement.allowSamlAuthentication">Permite autenticação SAML2</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn btn-secondary" (click)="previousState()">
|
<button type="button" class="btn btn-secondary" (click)="previousState()">
|
||||||
<fa-icon icon="ban"></fa-icon> <span jhiTranslate="entity.action.cancel">Cancelar</span>
|
<fa-icon icon="ban"></fa-icon> <span jhiTranslate="entity.action.cancel">Cancelar</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -52,6 +52,8 @@ export default class UserManagementUpdateComponent implements OnInit {
|
||||||
langKey: new FormControl(userTemplate.langKey, { nonNullable: true }),
|
langKey: new FormControl(userTemplate.langKey, { nonNullable: true }),
|
||||||
authorities: new FormControl(userTemplate.authorities, { nonNullable: true }),
|
authorities: new FormControl(userTemplate.authorities, { nonNullable: true }),
|
||||||
securityGroup: new FormControl(userTemplate.securityGroup, { nonNullable: true }),
|
securityGroup: new FormControl(userTemplate.securityGroup, { nonNullable: true }),
|
||||||
|
allowSamlAuthentication: new FormControl(userTemplate.allowSamlAuthentication, { nonNullable: true }),
|
||||||
|
allowUserPwdAuthentication: new FormControl(userTemplate.allowUserPwdAuthentication, { nonNullable: true }),
|
||||||
});
|
});
|
||||||
|
|
||||||
private userService = inject(UserManagementService);
|
private userService = inject(UserManagementService);
|
||||||
|
|
|
@ -14,6 +14,8 @@ export interface IUser {
|
||||||
lastModifiedBy?: string;
|
lastModifiedBy?: string;
|
||||||
lastModifiedDate?: Date;
|
lastModifiedDate?: Date;
|
||||||
securityGroup?: ISecurityGroup;
|
securityGroup?: ISecurityGroup;
|
||||||
|
allowUserPwdAuthentication?: boolean;
|
||||||
|
allowSamlAuthentication?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class User implements IUser {
|
export class User implements IUser {
|
||||||
|
|
|
@ -20,6 +20,8 @@
|
||||||
"lastName": "Último nome",
|
"lastName": "Último nome",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"activated": "Ativo",
|
"activated": "Ativo",
|
||||||
|
"allowSamlAuthentication": "Permite autenticação por SAML2 ?",
|
||||||
|
"allowUserPwdAuthentication": "Permite autenticação por Utlizador/Password ?",
|
||||||
"deactivated": "Inativo",
|
"deactivated": "Inativo",
|
||||||
"profiles": "Perfis",
|
"profiles": "Perfis",
|
||||||
"langKey": "Linguagem",
|
"langKey": "Linguagem",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue