Compare commits
2 Commits
e0bf9f5edc
...
9a6ba0b70b
Author | SHA1 | Date | |
---|---|---|---|
9a6ba0b70b | |||
86343ae3a6 |
BIN
data/giproclientsandfilesdb.mv.db
Normal file
BIN
data/giproclientsandfilesdb.mv.db
Normal file
Binary file not shown.
20
pom.xml
20
pom.xml
@ -21,6 +21,26 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.persistence</groupId>
|
||||
<artifactId>jakarta.persistence-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
|
21
src/main/java/com/gipro/giprolab/config/Constants.java
Normal file
21
src/main/java/com/gipro/giprolab/config/Constants.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.gipro.giprolab.config;
|
||||
|
||||
public class Constants {
|
||||
public static final String API_1_0 = "/api/1.0/";
|
||||
|
||||
public static final String MAIL_ACTIVATE = "Account activation";
|
||||
public static final String MAIL_RESET = "Password reset";
|
||||
public static final int MIN_PASSWORD_LENGTH = 6;
|
||||
|
||||
public static final String LOGIN_REGEX = "^[_'.@A-Za-z0-9-]*$";
|
||||
|
||||
public static final String COOKIES_NAME = "JSESSIONID";
|
||||
public static final String LOGOUT_URL = "/login?logout";
|
||||
public static final String SESSION_ID_ATTR = "sessionId";
|
||||
public static final int SESSION_TIMEOUT_SECONDS = 30 * 60;
|
||||
|
||||
public static final String PASSWORD_RESET_REQUEST_PAGE = "/resetRequest";
|
||||
public static final String PASSWORD_RESET_PAGE = "/reset";
|
||||
|
||||
public static final String SYSTEM_ENDPOINT_URL = "/metrics";
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.gipro.giprolab.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
@Configuration
|
||||
public class PasswordEncoderConfiguration {
|
||||
@Bean
|
||||
public BCryptPasswordEncoder bCryptPasswordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.gipro.giprolab.config;
|
||||
|
||||
import com.gipro.giprolab.models.UserRoleConstants;
|
||||
import com.gipro.giprolab.services.UserService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.BeanInitializationException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
|
||||
public class SecurityConfiguration {
|
||||
private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class);
|
||||
private final UserService userService;
|
||||
private final BCryptPasswordEncoder bCryptPasswordEncoder;
|
||||
|
||||
public SecurityConfiguration(UserService userService,
|
||||
BCryptPasswordEncoder bCryptPasswordEncoder) {
|
||||
this.userService = userService;
|
||||
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||
log.debug("Security enabled");
|
||||
http
|
||||
.authorizeHttpRequests((authorize) -> authorize
|
||||
//.requestMatchers(UserController.ACTIVATE_URL).permitAll()
|
||||
.requestMatchers(Constants.PASSWORD_RESET_REQUEST_PAGE).permitAll()
|
||||
.requestMatchers(Constants.PASSWORD_RESET_PAGE).permitAll()
|
||||
//.requestMatchers(UserController.URL + UserController.REGISTER_URL).permitAll()
|
||||
//.requestMatchers(UserController.URL + UserController.ACTIVATE_URL).permitAll()
|
||||
//.requestMatchers(UserController.URL + UserController.PASSWORD_RESET_REQUEST_URL).permitAll()
|
||||
//.requestMatchers(UserController.URL + UserController.PASSWORD_RESET_URL).permitAll()
|
||||
.requestMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN)
|
||||
.anyRequest().authenticated())
|
||||
.formLogin(fl -> fl
|
||||
.loginPage("/login")
|
||||
//.successHandler(authenticationSuccessHandler)
|
||||
.permitAll())
|
||||
.csrf(AbstractHttpConfigurer::disable)
|
||||
.logout(l -> l
|
||||
//.logoutSuccessHandler(logoutSuccessHandler)
|
||||
.logoutSuccessUrl(Constants.LOGOUT_URL)
|
||||
.invalidateHttpSession(false)
|
||||
.clearAuthentication(true)
|
||||
.deleteCookies(Constants.COOKIES_NAME)
|
||||
.permitAll());
|
||||
return http.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public WebSecurityCustomizer webSecurityCustomizer() {
|
||||
return (web) -> web.ignoring().requestMatchers("/css/**", "/js/**", "/templates/**", "/webjars/**");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void configureGlobal(AuthenticationManagerBuilder auth) {
|
||||
try {
|
||||
auth.userDetailsService(userService).passwordEncoder(bCryptPasswordEncoder);
|
||||
} catch (Exception e) {
|
||||
throw new BeanInitializationException("Security configuration failed", e);
|
||||
}
|
||||
}
|
||||
}
|
83
src/main/java/com/gipro/giprolab/core/BaseEntity.java
Normal file
83
src/main/java/com/gipro/giprolab/core/BaseEntity.java
Normal file
@ -0,0 +1,83 @@
|
||||
package com.gipro.giprolab.core;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class BaseEntity implements Serializable, Comparable {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.TABLE)
|
||||
private Integer id;
|
||||
|
||||
@Version
|
||||
private Integer version;
|
||||
|
||||
public BaseEntity() {
|
||||
}
|
||||
|
||||
public BaseEntity(Integer id, Integer version) {
|
||||
this.id = id;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Integer getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!getClass().isAssignableFrom(obj.getClass())) {
|
||||
return false;
|
||||
}
|
||||
BaseEntity other = (BaseEntity) obj;
|
||||
if (id == null) {
|
||||
if (other.id != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!id.equals(other.id)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (id == null ? 0 : id.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + "{" +
|
||||
"id=" + id +
|
||||
", version=" + version +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Object o) {
|
||||
return id != null ? id.compareTo(((BaseEntity) o).getId()) : -1;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.id = null;
|
||||
this.version = null;
|
||||
}
|
||||
}
|
21
src/main/java/com/gipro/giprolab/core/PageableItems.java
Normal file
21
src/main/java/com/gipro/giprolab/core/PageableItems.java
Normal file
@ -0,0 +1,21 @@
|
||||
package com.gipro.giprolab.core;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class PageableItems<T> {
|
||||
private final long count;
|
||||
private final Collection<T> items;
|
||||
|
||||
public PageableItems(long count, Collection<T> items) {
|
||||
this.count = count;
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public long getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public Collection<T> getItems() {
|
||||
return items;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class EntityIdIsNullException extends RuntimeException {
|
||||
public EntityIdIsNullException() {
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class EntityNotFoundException extends RuntimeException {
|
||||
public EntityNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserActivationError extends RuntimeException {
|
||||
public UserActivationError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserEmailExistsException extends RuntimeException {
|
||||
public UserEmailExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserIdExistsException extends RuntimeException {
|
||||
public UserIdExistsException() {
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserIsUndeadException extends RuntimeException {
|
||||
public UserIsUndeadException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserLoginExistsException extends RuntimeException {
|
||||
public UserLoginExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserNotActivatedException extends RuntimeException {
|
||||
public UserNotActivatedException() {
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserNotFoundException extends RuntimeException {
|
||||
public UserNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserPasswordsNotValidOrNotMatchException extends RuntimeException {
|
||||
public UserPasswordsNotValidOrNotMatchException() {
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.error;
|
||||
|
||||
public class UserResetKeyError extends RuntimeException {
|
||||
public UserResetKeyError(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
170
src/main/java/com/gipro/giprolab/models/User.java
Normal file
170
src/main/java/com/gipro/giprolab/models/User.java
Normal file
@ -0,0 +1,170 @@
|
||||
package com.gipro.giprolab.models;
|
||||
|
||||
import com.gipro.giprolab.config.Constants;
|
||||
import com.gipro.giprolab.core.BaseEntity;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "is_users")
|
||||
public class User extends BaseEntity {
|
||||
@NotNull
|
||||
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||
@Size(min = 1, max = 50)
|
||||
@Column(length = 50, unique = true, nullable = false)
|
||||
private String login;
|
||||
|
||||
@NotNull
|
||||
@Size(min = 60, max = 60)
|
||||
@Column(name = "password_hash", length = 60, nullable = false)
|
||||
private String password;
|
||||
|
||||
@NotNull
|
||||
@Size(max = 50)
|
||||
@Column(name = "first_name", length = 50, nullable = false)
|
||||
private String firstName;
|
||||
|
||||
@NotNull
|
||||
@Size(max = 50)
|
||||
@Column(name = "last_name", length = 50, nullable = false)
|
||||
private String lastName;
|
||||
|
||||
@NotNull
|
||||
@Email
|
||||
@Size(min = 5, max = 100)
|
||||
@Column(length = 100, nullable = false, unique = true)
|
||||
private String email;
|
||||
|
||||
@NotNull
|
||||
@Column(nullable = false)
|
||||
private boolean activated;
|
||||
|
||||
@Size(max = 20)
|
||||
@Column(name = "activation_key", length = 20)
|
||||
private String activationKey;
|
||||
|
||||
@Column(name = "activation_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date activationDate;
|
||||
|
||||
@Size(max = 20)
|
||||
@Column(name = "reset_key", length = 20)
|
||||
private String resetKey;
|
||||
|
||||
@Column(name = "reset_date")
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
private Date resetDate;
|
||||
|
||||
@ManyToMany
|
||||
@JoinTable(
|
||||
name = "is_user_role",
|
||||
joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
|
||||
inverseJoinColumns = {@JoinColumn(name = "user_role_name", referencedColumnName = "name")})
|
||||
private Set<UserRole> roles;
|
||||
|
||||
public User() {
|
||||
roles = new HashSet<>();
|
||||
activated = false;
|
||||
activationDate = new Date();
|
||||
resetDate = null;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login.toLowerCase();
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public boolean getActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public String getActivationKey() {
|
||||
return activationKey;
|
||||
}
|
||||
|
||||
public void setActivationKey(String activationKey) {
|
||||
this.activationKey = activationKey;
|
||||
}
|
||||
|
||||
public Date getActivationDate() {
|
||||
return activationDate;
|
||||
}
|
||||
|
||||
public void setActivationDate(Date activationDate) {
|
||||
this.activationDate = activationDate;
|
||||
}
|
||||
|
||||
public String getResetKey() {
|
||||
return resetKey;
|
||||
}
|
||||
|
||||
public void setResetKey(String resetKey) {
|
||||
this.resetKey = resetKey;
|
||||
}
|
||||
|
||||
public Date getResetDate() {
|
||||
return resetDate;
|
||||
}
|
||||
|
||||
public void setResetDate(Date resetDate) {
|
||||
this.resetDate = resetDate;
|
||||
}
|
||||
|
||||
public Set<UserRole> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Set<UserRole> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public void setActivated(boolean activated) {
|
||||
this.activated = activated;
|
||||
}
|
||||
}
|
159
src/main/java/com/gipro/giprolab/models/UserDto.java
Normal file
159
src/main/java/com/gipro/giprolab/models/UserDto.java
Normal file
@ -0,0 +1,159 @@
|
||||
package com.gipro.giprolab.models;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.gipro.giprolab.config.Constants;
|
||||
import jakarta.validation.constraints.*;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
public class UserDto {
|
||||
private Integer id;
|
||||
|
||||
@NotEmpty
|
||||
@Pattern(regexp = Constants.LOGIN_REGEX)
|
||||
@Size(min = 4, max = 50)
|
||||
private String login;
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 2, max = 50)
|
||||
private String firstName;
|
||||
|
||||
@NotBlank
|
||||
@Size(min = 2, max = 50)
|
||||
private String lastName;
|
||||
|
||||
@Email
|
||||
@NotBlank
|
||||
@Size(min = 5, max = 100)
|
||||
private String email;
|
||||
|
||||
private boolean activated;
|
||||
|
||||
private LinkedHashSet<UserRoleDto> roles;
|
||||
|
||||
@Size(max = 50)
|
||||
private String oldPassword;
|
||||
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String password;
|
||||
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String passwordConfirm;
|
||||
|
||||
public UserDto() {
|
||||
activated = false;
|
||||
roles = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
public UserDto(User user) {
|
||||
this();
|
||||
this.id = user.getId();
|
||||
this.login = user.getLogin();
|
||||
this.firstName = user.getFirstName();
|
||||
this.lastName = user.getLastName();
|
||||
this.email = user.getEmail();
|
||||
this.activated = user.getActivated();
|
||||
this.roles.addAll(user.getRoles().stream()
|
||||
.map(UserRoleDto::new)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getLogin() {
|
||||
return login;
|
||||
}
|
||||
|
||||
public void setLogin(String login) {
|
||||
this.login = login;
|
||||
}
|
||||
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public boolean isActivated() {
|
||||
return activated;
|
||||
}
|
||||
|
||||
public void setActivated(boolean activated) {
|
||||
this.activated = activated;
|
||||
}
|
||||
|
||||
public Set<UserRoleDto> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Collection<UserRoleDto> roles) {
|
||||
this.roles.clear();
|
||||
this.roles.addAll(roles);
|
||||
}
|
||||
|
||||
public String getOldPassword() {
|
||||
return oldPassword;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isPasswordsValid() {
|
||||
if (StringUtils.isEmpty(password) || StringUtils.isEmpty(passwordConfirm)) {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(password, passwordConfirm);
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isOldPasswordValid() {
|
||||
return !StringUtils.isEmpty(oldPassword);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() + " {" +
|
||||
"id=" + id +
|
||||
", login='" + login + '\'' +
|
||||
", firstName='" + firstName + '\'' +
|
||||
", lastName='" + lastName + '\'' +
|
||||
", email='" + email + '\'' +
|
||||
", activated=" + activated +
|
||||
", roles=" + roles +
|
||||
", password='" + password + '\'' +
|
||||
", passwordConfirm='" + passwordConfirm + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.gipro.giprolab.models;
|
||||
|
||||
import com.gipro.giprolab.config.Constants;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class UserResetPasswordDto {
|
||||
@NotEmpty
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String password;
|
||||
|
||||
@NotEmpty
|
||||
@Size(min = Constants.MIN_PASSWORD_LENGTH, max = 50)
|
||||
private String passwordConfirm;
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getPasswordConfirm() {
|
||||
return passwordConfirm;
|
||||
}
|
||||
|
||||
public boolean isPasswordsValid() {
|
||||
return Objects.equals(password, passwordConfirm);
|
||||
}
|
||||
}
|
50
src/main/java/com/gipro/giprolab/models/UserRole.java
Normal file
50
src/main/java/com/gipro/giprolab/models/UserRole.java
Normal file
@ -0,0 +1,50 @@
|
||||
package com.gipro.giprolab.models;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
@Entity
|
||||
@Table(name = "is_user_roles")
|
||||
public class UserRole {
|
||||
@Id
|
||||
@NotNull
|
||||
@Size(max = 50)
|
||||
@Column(length = 50, nullable = false)
|
||||
private String name;
|
||||
|
||||
public UserRole() {
|
||||
}
|
||||
|
||||
public UserRole(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
UserRole role = (UserRole) o;
|
||||
return !(name != null ? !name.equals(role.name) : role.name != null);
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name != null ? name.hashCode() : 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.gipro.giprolab.models;
|
||||
|
||||
public class UserRoleConstants {
|
||||
public static final String ADMIN = "ROLE_ADMIN";
|
||||
public static final String USER = "ROLE_USER";
|
||||
}
|
20
src/main/java/com/gipro/giprolab/models/UserRoleDto.java
Normal file
20
src/main/java/com/gipro/giprolab/models/UserRoleDto.java
Normal file
@ -0,0 +1,20 @@
|
||||
package com.gipro.giprolab.models;
|
||||
|
||||
public class UserRoleDto {
|
||||
private String id;
|
||||
|
||||
public UserRoleDto() {
|
||||
}
|
||||
|
||||
public UserRoleDto(UserRole role) {
|
||||
this.id = role.getName();
|
||||
}
|
||||
|
||||
public UserRoleDto(String name) {
|
||||
this.id = name;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.gipro.giprolab.repositories;
|
||||
|
||||
import com.gipro.giprolab.models.User;
|
||||
import org.springframework.data.jpa.repository.EntityGraph;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public interface UserRepository extends JpaRepository<User, Integer> {
|
||||
User findOneByActivationKey(String activationKey);
|
||||
|
||||
List<User> findAllByActivatedIsFalseAndActivationDateBefore(Date date);
|
||||
|
||||
User findOneByResetKey(String resetKey);
|
||||
|
||||
List<User> findAllByResetKeyNotNullAndResetDateBefore(Date date);
|
||||
|
||||
User findOneByEmailIgnoreCase(String email);
|
||||
|
||||
User findOneByLoginIgnoreCase(String login);
|
||||
|
||||
@EntityGraph(attributePaths = "roles")
|
||||
User findOneWithRolesById(int id);
|
||||
|
||||
@EntityGraph(attributePaths = "roles")
|
||||
User findOneWithRolesByLogin(String login);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.gipro.giprolab.repositories;
|
||||
|
||||
import com.gipro.giprolab.models.UserRole;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface UserRoleRepository extends JpaRepository<UserRole, String> {
|
||||
}
|
56
src/main/java/com/gipro/giprolab/services/UserMapper.java
Normal file
56
src/main/java/com/gipro/giprolab/services/UserMapper.java
Normal file
@ -0,0 +1,56 @@
|
||||
package com.gipro.giprolab.services;
|
||||
|
||||
import com.gipro.giprolab.models.User;
|
||||
import com.gipro.giprolab.models.UserDto;
|
||||
import com.gipro.giprolab.models.UserRole;
|
||||
import com.gipro.giprolab.models.UserRoleDto;
|
||||
import com.gipro.giprolab.repositories.UserRoleRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
public class UserMapper {
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
|
||||
public UserMapper(UserRoleRepository userRoleRepository) {
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
}
|
||||
|
||||
public Set<UserRole> rolesFromDto(Set<UserRoleDto> strings) {
|
||||
return Optional.ofNullable(strings).orElse(Collections.emptySet()).stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(role -> userRoleRepository.findById(role.getId()).orElse(null))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public UserDto userEntityToUserDto(User userEntity) {
|
||||
if (userEntity == null) {
|
||||
return null;
|
||||
}
|
||||
return new UserDto(userEntity);
|
||||
}
|
||||
|
||||
public User userDtoToUserEntity(UserDto userDto) {
|
||||
if (userDto == null) {
|
||||
return null;
|
||||
}
|
||||
final User user = new User();
|
||||
user.setId(userDto.getId());
|
||||
user.setLogin(userDto.getLogin());
|
||||
user.setFirstName(userDto.getFirstName());
|
||||
user.setLastName(userDto.getLastName());
|
||||
user.setEmail(userDto.getEmail());
|
||||
user.setActivated(userDto.isActivated());
|
||||
final Set<UserRole> roles = this.rolesFromDto(userDto.getRoles());
|
||||
if (!roles.isEmpty()) {
|
||||
user.setRoles(roles);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
}
|
262
src/main/java/com/gipro/giprolab/services/UserService.java
Normal file
262
src/main/java/com/gipro/giprolab/services/UserService.java
Normal file
@ -0,0 +1,262 @@
|
||||
package com.gipro.giprolab.services;
|
||||
|
||||
import com.gipro.giprolab.core.BaseEntity;
|
||||
import com.gipro.giprolab.core.PageableItems;
|
||||
import com.gipro.giprolab.error.*;
|
||||
import com.gipro.giprolab.models.*;
|
||||
import com.gipro.giprolab.repositories.UserRepository;
|
||||
import com.gipro.giprolab.repositories.UserRoleRepository;
|
||||
import com.gipro.giprolab.util.UserUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional
|
||||
public class UserService implements UserDetailsService {
|
||||
private final Logger log = LoggerFactory.getLogger(UserService.class);
|
||||
private final UserRepository userRepository;
|
||||
private final PasswordEncoder passwordEncoder;
|
||||
private final UserRoleRepository userRoleRepository;
|
||||
|
||||
private final UserMapper userMapper;
|
||||
|
||||
public UserService(UserRepository userRepository,
|
||||
PasswordEncoder passwordEncoder,
|
||||
UserRoleRepository userRoleRepository,
|
||||
UserMapper userMapper) {
|
||||
this.userRepository = userRepository;
|
||||
this.passwordEncoder = passwordEncoder;
|
||||
this.userRoleRepository = userRoleRepository;
|
||||
this.userMapper = userMapper;
|
||||
}
|
||||
|
||||
private User getUserByEmail(String email) {
|
||||
return userRepository.findOneByEmailIgnoreCase(email);
|
||||
}
|
||||
|
||||
private User getUserByActivationKey(String activationKey) {
|
||||
return userRepository.findOneByActivationKey(activationKey);
|
||||
}
|
||||
|
||||
public User getUserByLogin(String login) {
|
||||
return userRepository.findOneByLoginIgnoreCase(login);
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public UserDto getUserWithRolesById(Integer userId) {
|
||||
final User userEntity = userRepository.findOneWithRolesById(userId);
|
||||
if (userEntity == null) {
|
||||
throw new UserNotFoundException(userId.toString());
|
||||
}
|
||||
return userMapper.userEntityToUserDto(userEntity);
|
||||
}
|
||||
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public PageableItems<UserRole> getUserRoles() {
|
||||
final List<UserRole> roles = userRoleRepository.findAll();
|
||||
return new PageableItems<>(roles.size(), roles);
|
||||
}
|
||||
|
||||
public UserDto createUser(UserDto userDto) {
|
||||
if (userDto.getId() != null) {
|
||||
throw new UserIdExistsException();
|
||||
}
|
||||
if (getUserByLogin(userDto.getLogin()) != null) {
|
||||
throw new UserLoginExistsException(userDto.getLogin());
|
||||
}
|
||||
if (getUserByEmail(userDto.getEmail()) != null) {
|
||||
throw new UserEmailExistsException(userDto.getEmail());
|
||||
}
|
||||
if (!userDto.isPasswordsValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
User user = userMapper.userDtoToUserEntity(userDto);
|
||||
user.setActivated(false);
|
||||
user.setActivationKey(UserUtils.generateActivationKey());
|
||||
user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER)));
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
user = userRepository.save(user);
|
||||
//TODO: mailService.sendActivationEmail(user);
|
||||
log.debug("Created Information for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
public UserDto activateUser(String activationKey) {
|
||||
final User user = getUserByActivationKey(activationKey);
|
||||
if (user == null) {
|
||||
throw new UserActivationError(activationKey);
|
||||
}
|
||||
user.setActivated(true);
|
||||
user.setActivationKey(null);
|
||||
user.setActivationDate(null);
|
||||
log.debug("Activated user: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(userRepository.save(user));
|
||||
}
|
||||
|
||||
public UserDto updateUser(UserDto userDto) {
|
||||
if (userDto.getId() == null) {
|
||||
throw new EntityIdIsNullException();
|
||||
}
|
||||
if (!Objects.equals(
|
||||
Optional.ofNullable(getUserByEmail(userDto.getEmail()))
|
||||
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||
userDto.getId())) {
|
||||
throw new UserEmailExistsException(userDto.getEmail());
|
||||
}
|
||||
if (!Objects.equals(
|
||||
Optional.ofNullable(getUserByLogin(userDto.getLogin()))
|
||||
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||
userDto.getId())) {
|
||||
throw new UserLoginExistsException(userDto.getLogin());
|
||||
}
|
||||
User user = userRepository.findById(userDto.getId()).orElse(null);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userDto.getId().toString());
|
||||
}
|
||||
user.setLogin(userDto.getLogin());
|
||||
user.setFirstName(userDto.getFirstName());
|
||||
user.setLastName(userDto.getLastName());
|
||||
user.setEmail(userDto.getEmail());
|
||||
if (userDto.isActivated() != user.getActivated()) {
|
||||
if (userDto.isActivated()) {
|
||||
user.setActivationKey(null);
|
||||
user.setActivationDate(null);
|
||||
} else {
|
||||
user.setActivationKey(UserUtils.generateActivationKey());
|
||||
user.setActivationDate(new Date());
|
||||
}
|
||||
}
|
||||
user.setActivated(userDto.isActivated());
|
||||
final Set<UserRole> roles = userMapper.rolesFromDto(userDto.getRoles());
|
||||
user.setRoles(roles.isEmpty()
|
||||
? Collections.singleton(new UserRole(UserRoleConstants.USER))
|
||||
: roles);
|
||||
if (!StringUtils.isEmpty(userDto.getOldPassword())) {
|
||||
if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
log.debug("Changed password for User: {}", user.getLogin());
|
||||
}
|
||||
user = userRepository.save(user);
|
||||
log.debug("Changed Information for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
public UserDto updateUserInformation(UserDto userDto) {
|
||||
if (userDto.getId() == null) {
|
||||
throw new EntityIdIsNullException();
|
||||
}
|
||||
if (!Objects.equals(
|
||||
Optional.ofNullable(getUserByEmail(userDto.getEmail()))
|
||||
.map(BaseEntity::getId).orElse(userDto.getId()),
|
||||
userDto.getId())) {
|
||||
throw new UserEmailExistsException(userDto.getEmail());
|
||||
}
|
||||
User user = userRepository.findById(userDto.getId()).orElse(null);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(userDto.getId().toString());
|
||||
}
|
||||
user.setFirstName(userDto.getFirstName());
|
||||
user.setLastName(userDto.getLastName());
|
||||
user.setEmail(userDto.getEmail());
|
||||
user = userRepository.save(user);
|
||||
log.debug("Updated Information for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
public UserDto changeUserPassword(UserDto userDto) {
|
||||
if (userDto.getId() == null) {
|
||||
throw new EntityIdIsNullException();
|
||||
}
|
||||
if (!userDto.isPasswordsValid() || !userDto.isOldPasswordValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
final User user = getCurrentUser();
|
||||
|
||||
if (!passwordEncoder.matches(userDto.getOldPassword(), user.getPassword())) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(userDto.getPassword()));
|
||||
log.debug("Changed password for User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(userRepository.save(user));
|
||||
}
|
||||
|
||||
public User getCurrentUser() {
|
||||
String login = UserUtils.getCurrentUserLogin();
|
||||
User user = userRepository.findOneByLoginIgnoreCase(login);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(login);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public boolean requestUserPasswordReset(String email) {
|
||||
User user = userRepository.findOneByEmailIgnoreCase(email);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(email);
|
||||
}
|
||||
if (!user.getActivated()) {
|
||||
throw new UserNotActivatedException();
|
||||
}
|
||||
user.setResetKey(UserUtils.generateResetKey());
|
||||
user.setResetDate(new Date());
|
||||
user = userRepository.save(user);
|
||||
// mailService.sendPasswordResetMail(user);
|
||||
log.debug("Created Reset Password Request for User: {}", user.getLogin());
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean completeUserPasswordReset(String key, UserResetPasswordDto userResetPasswordDto) {
|
||||
if (!userResetPasswordDto.isPasswordsValid()) {
|
||||
throw new UserPasswordsNotValidOrNotMatchException();
|
||||
}
|
||||
User user = userRepository.findOneByResetKey(key);
|
||||
if (user == null) {
|
||||
throw new UserResetKeyError(key);
|
||||
}
|
||||
user.setPassword(passwordEncoder.encode(userResetPasswordDto.getPassword()));
|
||||
user.setResetKey(null);
|
||||
user.setResetDate(null);
|
||||
user = userRepository.save(user);
|
||||
log.debug("Reset Password for User: {}", user.getLogin());
|
||||
return true;
|
||||
}
|
||||
|
||||
public UserDto deleteUser(Integer userId) {
|
||||
final User user = userRepository.findById(userId).orElse(null);
|
||||
userRepository.delete(user);
|
||||
log.debug("Deleted User: {}", user.getLogin());
|
||||
return userMapper.userEntityToUserDto(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserDetails loadUserByUsername(String username) {
|
||||
final User user = userRepository.findOneByLoginIgnoreCase(username);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException(username);
|
||||
}
|
||||
if (!user.getActivated()) {
|
||||
throw new UserNotActivatedException();
|
||||
}
|
||||
return new org.springframework.security.core.userdetails.User(user.getLogin(),
|
||||
user.getPassword(),
|
||||
Optional.ofNullable(user.getRoles()).orElse(Collections.emptySet()).stream()
|
||||
.map(role -> new SimpleGrantedAuthority(role.getName()))
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
}
|
35
src/main/java/com/gipro/giprolab/util/UserUtils.java
Normal file
35
src/main/java/com/gipro/giprolab/util/UserUtils.java
Normal file
@ -0,0 +1,35 @@
|
||||
package com.gipro.giprolab.util;
|
||||
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
|
||||
public class UserUtils {
|
||||
private static final int DEF_COUNT = 20;
|
||||
|
||||
public static String generateActivationKey() {
|
||||
return RandomStringUtils.randomNumeric(DEF_COUNT);
|
||||
}
|
||||
|
||||
public static String generateResetKey() {
|
||||
return RandomStringUtils.randomNumeric(DEF_COUNT);
|
||||
}
|
||||
|
||||
public static String getCurrentUserLogin() {
|
||||
final SecurityContext securityContext = SecurityContextHolder.getContext();
|
||||
if (securityContext == null) {
|
||||
return null;
|
||||
}
|
||||
final Authentication authentication = securityContext.getAuthentication();
|
||||
if (authentication.getPrincipal() instanceof UserDetails) {
|
||||
final UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
|
||||
return springSecurityUser.getUsername();
|
||||
}
|
||||
if (authentication.getPrincipal() instanceof String) {
|
||||
return (String) authentication.getPrincipal();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,6 +1,15 @@
|
||||
spring.jpa.hibernate.ddl-auto=update
|
||||
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/giproclientsandfilesdb
|
||||
spring.datasource.username=root
|
||||
spring.datasource.password=
|
||||
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
#spring.jpa.hibernate.ddl-auto=update
|
||||
#spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/giproclientsandfilesdb
|
||||
#spring.datasource.username=root
|
||||
#spring.datasource.password=
|
||||
#spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||
#spring.jpa.show-sql: true
|
||||
spring.main.allow-bean-definition-overriding=true
|
||||
spring.jackson.serialization.fail-on-empty-beans=false
|
||||
spring.datasource.url=jdbc:h2:file:./data/giproclientsandfilesdb
|
||||
spring.datasource.driverClassName=org.h2.Driver
|
||||
spring.datasource.username=sa
|
||||
spring.datasource.password=password
|
||||
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
|
||||
spring.h2.console.enabled=true
|
||||
spring.jpa.hibernate.ddl-auto=update
|
Loading…
Reference in New Issue
Block a user