From 2a51f5fffce92607af07b2b297011cd0995c7672 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 18:58:24 +0400 Subject: [PATCH 01/16] Add project models --- .../java/ru/ulstu/fc/config/Constants.java | 12 ++ .../ru/ulstu/fc/core/model/BaseEntity.java | 84 +++++++++++++ .../fc/core/model/OffsetablePageRequest.java | 119 ++++++++++++++++++ .../ru/ulstu/fc/project/model/Project.java | 32 +++++ .../ulstu/fc/project/model/ProjectForm.java | 22 ++++ .../controller/InferenceRestController.java | 2 +- 6 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ru/ulstu/fc/config/Constants.java create mode 100644 src/main/java/ru/ulstu/fc/core/model/BaseEntity.java create mode 100644 src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java create mode 100644 src/main/java/ru/ulstu/fc/project/model/Project.java create mode 100644 src/main/java/ru/ulstu/fc/project/model/ProjectForm.java diff --git a/src/main/java/ru/ulstu/fc/config/Constants.java b/src/main/java/ru/ulstu/fc/config/Constants.java new file mode 100644 index 0000000..7d922bb --- /dev/null +++ b/src/main/java/ru/ulstu/fc/config/Constants.java @@ -0,0 +1,12 @@ +package ru.ulstu.fc.config; + +public class Constants { + 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; +} diff --git a/src/main/java/ru/ulstu/fc/core/model/BaseEntity.java b/src/main/java/ru/ulstu/fc/core/model/BaseEntity.java new file mode 100644 index 0000000..bcc4c00 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/BaseEntity.java @@ -0,0 +1,84 @@ +package ru.ulstu.fc.core.model; + +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.Version; +import jakarta.validation.constraints.NotNull; + +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 Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public Integer getVersion() { + return version; + } + + public void setVersion(Integer version) { + this.version = 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(@NotNull BaseEntity o) { + return id != null ? id.compareTo(o.getId()) : -1; + } + + public void reset() { + this.id = null; + this.version = null; + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java b/src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java new file mode 100644 index 0000000..abd3c06 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/OffsetablePageRequest.java @@ -0,0 +1,119 @@ +package ru.ulstu.fc.core.model; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; + +import java.io.Serializable; +import java.util.Optional; + +public class OffsetablePageRequest implements Pageable, Serializable { + private final int offset; + private final int count; + private final Sort sort; + + public OffsetablePageRequest(int page, long pageSize) { + this(pageSize * page, pageSize, Sort.by("id")); + } + + public OffsetablePageRequest(long offset, long count, Sort sort) { + if (offset < 0) { + throw new IllegalArgumentException("Offset value must not be less than zero!"); + } + if (count < 1) { + throw new IllegalArgumentException("Count value must not be less than one!"); + } + this.offset = (int) offset; + this.count = (int) count; + this.sort = sort; + } + + @Override + public Sort getSort() { + return sort; + } + + @Override + public Sort getSortOr(Sort sort) { + return Pageable.super.getSortOr(sort); + } + + @Override + public int getPageSize() { + return count; + } + + @Override + public boolean isPaged() { + return Pageable.super.isPaged(); + } + + @Override + public boolean isUnpaged() { + return Pageable.super.isUnpaged(); + } + + @Override + public int getPageNumber() { + return offset / count; + } + + @Override + public long getOffset() { + return offset; + } + + @Override + public boolean hasPrevious() { + return offset > 0; + } + + @Override + public Optional toOptional() { + return Pageable.super.toOptional(); + } + + @Override + public Pageable next() { + return new OffsetablePageRequest(getOffset() + getPageSize(), getPageSize(), getSort()); + } + + @Override + public Pageable previousOrFirst() { + return hasPrevious() ? previous() : first(); + } + + public Pageable previous() { + return getOffset() == 0 ? this : new OffsetablePageRequest(getOffset() - getPageSize(), getPageSize(), getSort()); + } + + @Override + public Pageable first() { + return new OffsetablePageRequest(0, getPageSize(), getSort()); + } + + @Override + public Pageable withPage(int pageNumber) { + return null; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final OffsetablePageRequest other = (OffsetablePageRequest) obj; + return this.offset == other.offset && this.count == other.count; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + offset; + result = prime * result + count; + return result; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/model/Project.java b/src/main/java/ru/ulstu/fc/project/model/Project.java new file mode 100644 index 0000000..75e310d --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/model/Project.java @@ -0,0 +1,32 @@ +package ru.ulstu.fc.project.model; + +import jakarta.persistence.Entity; +import ru.ulstu.fc.core.model.BaseEntity; + +import java.util.Date; + +@Entity +public class Project extends BaseEntity { + private String name; + private Date createDate = new Date(); + + public Project(ProjectForm projectForm) { + this.name = projectForm.getName(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getCreateDate() { + return createDate; + } + + public void setCreateDate(Date createDate) { + this.createDate = createDate; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java b/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java new file mode 100644 index 0000000..5c06899 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java @@ -0,0 +1,22 @@ +package ru.ulstu.fc.project.model; + +public class ProjectForm { + private Integer id; + private String name; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java index 0f3fb7e..dcb0a8f 100644 --- a/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java +++ b/src/main/java/ru/ulstu/fc/rule/controller/InferenceRestController.java @@ -11,7 +11,7 @@ import ru.ulstu.fc.rule.service.FuzzyInferenceService; import java.util.List; @RestController -@RequestMapping("rest") +@RequestMapping("inferenceRest") public class InferenceRestController { private final FuzzyInferenceService fuzzyInferenceService; From f0c1c26800eebcd438d859d93be88637c8cf70e7 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 18:58:46 +0400 Subject: [PATCH 02/16] Add user models --- .../java/ru/ulstu/fc/core/model/User.java | 71 +++++++++++++++++++ .../java/ru/ulstu/fc/core/model/UserRole.java | 50 +++++++++++++ .../fc/core/model/UserRoleConstants.java | 6 ++ 3 files changed, 127 insertions(+) create mode 100644 src/main/java/ru/ulstu/fc/core/model/User.java create mode 100644 src/main/java/ru/ulstu/fc/core/model/UserRole.java create mode 100644 src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java diff --git a/src/main/java/ru/ulstu/fc/core/model/User.java b/src/main/java/ru/ulstu/fc/core/model/User.java new file mode 100644 index 0000000..2d960b1 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/User.java @@ -0,0 +1,71 @@ +package ru.ulstu.fc.core.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinTable; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; +import ru.ulstu.fc.config.Constants; + +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; + + @ManyToMany + @JoinTable( + name = "is_user_role", + joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")}, + inverseJoinColumns = {@JoinColumn(name = "user_role_name", referencedColumnName = "name")}) + private Set roles; + + public User() { + roles = new HashSet<>(); + } + + public User(String login, String password, Set roles) { + this.login = login; + this.password = password; + this.roles = roles; + } + + 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 Set getRoles() { + return roles; + } + + public void setRoles(Set roles) { + this.roles = roles; + } +} diff --git a/src/main/java/ru/ulstu/fc/core/model/UserRole.java b/src/main/java/ru/ulstu/fc/core/model/UserRole.java new file mode 100644 index 0000000..92ecbb7 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/UserRole.java @@ -0,0 +1,50 @@ +package ru.ulstu.fc.core.model; + +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; + } +} diff --git a/src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java b/src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java new file mode 100644 index 0000000..ccc52ad --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java @@ -0,0 +1,6 @@ +package ru.ulstu.fc.core.model; + +public class UserRoleConstants { + public static final String ADMIN = "ROLE_ADMIN"; + public static final String USER = "ROLE_USER"; +} From b4af3bf238315fd54711eeca5e9278e2d2eeebeb Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 18:59:01 +0400 Subject: [PATCH 03/16] Add project controller --- .../ulstu/fc/project/ProjectController.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/main/java/ru/ulstu/fc/project/ProjectController.java diff --git a/src/main/java/ru/ulstu/fc/project/ProjectController.java b/src/main/java/ru/ulstu/fc/project/ProjectController.java new file mode 100644 index 0000000..35456ef --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/ProjectController.java @@ -0,0 +1,40 @@ +package ru.ulstu.fc.project; + +import io.swagger.v3.oas.annotations.Hidden; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.fc.project.model.ProjectForm; +import ru.ulstu.fc.project.service.ProjectService; + +@Controller +@Hidden +@RequestMapping("project") +public class ProjectController { + private final ProjectService projectService; + + public ProjectController(ProjectService projectService) { + this.projectService = projectService; + } + + @GetMapping("list") + public String getProjects(Model model) { + model.addAttribute("projects", projectService.getCurrentUserProjects()); + return "listProjects"; + } + + @PostMapping("save") + public String save(ProjectForm projectForm, Model model) { + projectService.save(projectForm); + return "redirect:/list"; + } + + @DeleteMapping("delete") + public String delete(ProjectForm projectForm) { + projectService.delete(projectForm); + return "redirect:/list"; + } +} From 0e4cb4fa2e61e0812af9e18339d68810f3b73471 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 18:59:19 +0400 Subject: [PATCH 04/16] #11 -- Add project repository --- .../ulstu/fc/project/repository/ProjectRepository.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java diff --git a/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java b/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java new file mode 100644 index 0000000..cf1b55c --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java @@ -0,0 +1,9 @@ +package ru.ulstu.fc.project.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.ulstu.fc.project.model.Project; + +@Repository +public interface ProjectRepository extends JpaRepository { +} From c372ff511f68ae7ffa7086ddabd440e72e1d0b75 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 18:59:38 +0400 Subject: [PATCH 05/16] #11 -- Add project service --- .../fc/project/service/ProjectService.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/ru/ulstu/fc/project/service/ProjectService.java diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java new file mode 100644 index 0000000..c529d0e --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java @@ -0,0 +1,36 @@ +package ru.ulstu.fc.project.service; + +import org.springframework.stereotype.Service; +import ru.ulstu.fc.project.model.Project; +import ru.ulstu.fc.project.model.ProjectForm; +import ru.ulstu.fc.project.repository.ProjectRepository; + +import java.util.List; + +@Service +public class ProjectService { + private final ProjectRepository projectRepository; + + public ProjectService(ProjectRepository projectRepository) { + this.projectRepository = projectRepository; + } + + public List getCurrentUserProjects() { + return projectRepository.findAll(); + } + + public Project save(ProjectForm projectForm) { + if (projectForm.getId() == null) { + return projectRepository.save(new Project(projectForm)); + } + Project project = projectRepository + .findById(projectForm.getId()) + .orElseThrow(() -> new RuntimeException("Project not found by id")); + project.setName(projectForm.getName()); + return projectRepository.save(project); + } + + public void delete(ProjectForm projectForm) { + projectRepository.deleteById(projectForm.getId()); + } +} From 2131ac462b96c05ec671d6e773aaa71cf3897f59 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 18:59:54 +0400 Subject: [PATCH 06/16] #11 -- Add user session --- .../ru/ulstu/fc/core/model/UserSession.java | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/java/ru/ulstu/fc/core/model/UserSession.java diff --git a/src/main/java/ru/ulstu/fc/core/model/UserSession.java b/src/main/java/ru/ulstu/fc/core/model/UserSession.java new file mode 100644 index 0000000..e217f7e --- /dev/null +++ b/src/main/java/ru/ulstu/fc/core/model/UserSession.java @@ -0,0 +1,104 @@ +package ru.ulstu.fc.core.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.validation.constraints.NotNull; + +import java.util.Date; + +@Entity +@Table(name = "is_user_sessions") +public class UserSession extends BaseEntity { + @NotNull + @Column(name = "session_id", nullable = false, unique = true) + private String sessionId; + + @NotNull + @Column(name = "ip_address", nullable = false) + private String ipAddress; + + @NotNull + @Column(nullable = false) + private String host; + + @NotNull + @Column(name = "login_time", nullable = false) + @Temporal(TemporalType.TIMESTAMP) + private Date loginTime; + + @Column(name = "logout_time") + @Temporal(TemporalType.TIMESTAMP) + private Date logoutTime; + + @ManyToOne(optional = false) + @JoinColumn(name = "user_id") + private User user; + + public UserSession() { + } + + public UserSession(String sessionId, String ipAddress, String host, User user) { + this.sessionId = sessionId; + this.ipAddress = ipAddress; + this.host = host; + this.loginTime = new Date(); + this.user = user; + } + + public String getSessionId() { + return sessionId; + } + + public String getIpAddress() { + return ipAddress; + } + + public String getHost() { + return host; + } + + public Date getLoginTime() { + return loginTime; + } + + public Date getLogoutTime() { + return logoutTime; + } + + public User getUser() { + return user; + } + + public void setSessionId(String sessionId) { + this.sessionId = sessionId; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public void setHost(String host) { + this.host = host; + } + + public void setLoginTime(Date loginTime) { + this.loginTime = loginTime; + } + + public void setLogoutTime(Date logoutTime) { + this.logoutTime = logoutTime; + } + + public void setUser(User user) { + this.user = user; + } + + public void close() { + this.logoutTime = new Date(); + } +} From a4120b3c48cec419f62c964e5849d58484784e17 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 19:29:16 +0400 Subject: [PATCH 07/16] #11 -- Add project rest api --- .../{ => controller}/ProjectController.java | 4 +- .../controller/ProjectRestController.java | 38 +++++++++++++++++++ .../ru/ulstu/fc/project/model/Project.java | 6 +++ .../fc/project/service/ProjectService.java | 16 +++++--- 4 files changed, 56 insertions(+), 8 deletions(-) rename src/main/java/ru/ulstu/fc/project/{ => controller}/ProjectController.java (91%) create mode 100644 src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java diff --git a/src/main/java/ru/ulstu/fc/project/ProjectController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java similarity index 91% rename from src/main/java/ru/ulstu/fc/project/ProjectController.java rename to src/main/java/ru/ulstu/fc/project/controller/ProjectController.java index 35456ef..cda2300 100644 --- a/src/main/java/ru/ulstu/fc/project/ProjectController.java +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.project; +package ru.ulstu.fc.project.controller; import io.swagger.v3.oas.annotations.Hidden; import org.springframework.stereotype.Controller; @@ -28,7 +28,7 @@ public class ProjectController { @PostMapping("save") public String save(ProjectForm projectForm, Model model) { - projectService.save(projectForm); + model.addAttribute("project", projectService.save(projectForm)); return "redirect:/list"; } diff --git a/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java new file mode 100644 index 0000000..b2875a8 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java @@ -0,0 +1,38 @@ +package ru.ulstu.fc.project.controller; + +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.ulstu.fc.project.model.Project; +import ru.ulstu.fc.project.model.ProjectForm; +import ru.ulstu.fc.project.service.ProjectService; + +import java.util.List; + +@RestController +@RequestMapping("projectRest") +public class ProjectRestController { + private final ProjectService projectService; + + public ProjectRestController(ProjectService projectService) { + this.projectService = projectService; + } + + @GetMapping("list") + public List getProjects() { + return projectService.getCurrentUserProjects(); + } + + @PostMapping("save") + public Project save(Project project) { + return projectService.save(project); + } + + @DeleteMapping("delete") + public String delete(ProjectForm projectForm) { + projectService.delete(projectForm); + return "redirect:/list"; + } +} diff --git a/src/main/java/ru/ulstu/fc/project/model/Project.java b/src/main/java/ru/ulstu/fc/project/model/Project.java index 75e310d..7edf932 100644 --- a/src/main/java/ru/ulstu/fc/project/model/Project.java +++ b/src/main/java/ru/ulstu/fc/project/model/Project.java @@ -10,7 +10,13 @@ public class Project extends BaseEntity { private String name; private Date createDate = new Date(); + public Project() { + } + public Project(ProjectForm projectForm) { + if (projectForm.getId() != null) { + setId(projectForm.getId()); + } this.name = projectForm.getName(); } diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java index c529d0e..88e4258 100644 --- a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java @@ -20,14 +20,18 @@ public class ProjectService { } public Project save(ProjectForm projectForm) { - if (projectForm.getId() == null) { - return projectRepository.save(new Project(projectForm)); + return projectRepository.save(new Project(projectForm)); + } + + public Project save(Project projectToSave) { + if (projectToSave.getId() == null) { + return projectRepository.save(projectToSave); } - Project project = projectRepository - .findById(projectForm.getId()) + Project dbProject = projectRepository + .findById(projectToSave.getId()) .orElseThrow(() -> new RuntimeException("Project not found by id")); - project.setName(projectForm.getName()); - return projectRepository.save(project); + dbProject.setName(projectToSave.getName()); + return projectRepository.save(dbProject); } public void delete(ProjectForm projectForm) { From eb0ace5fbe1491b56f8ec1cc120c61ea10a1747b Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 19:41:21 +0400 Subject: [PATCH 08/16] #11 -- Add project user --- .../java/ru/ulstu/fc/project/model/Project.java | 14 ++++++++++++++ .../ulstu/fc/project/service/ProjectService.java | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/main/java/ru/ulstu/fc/project/model/Project.java b/src/main/java/ru/ulstu/fc/project/model/Project.java index 7edf932..129b008 100644 --- a/src/main/java/ru/ulstu/fc/project/model/Project.java +++ b/src/main/java/ru/ulstu/fc/project/model/Project.java @@ -1,14 +1,20 @@ package ru.ulstu.fc.project.model; import jakarta.persistence.Entity; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import ru.ulstu.fc.core.model.BaseEntity; +import ru.ulstu.fc.user.model.User; import java.util.Date; @Entity public class Project extends BaseEntity { + @NotEmpty private String name; private Date createDate = new Date(); + @NotNull + private User user; public Project() { } @@ -35,4 +41,12 @@ public class Project extends BaseEntity { public void setCreateDate(Date createDate) { this.createDate = createDate; } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } } diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java index 88e4258..b53880d 100644 --- a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java @@ -37,4 +37,14 @@ public class ProjectService { public void delete(ProjectForm projectForm) { projectRepository.deleteById(projectForm.getId()); } + + private void checkUserProjectWithThrow(Project project) { + if (!isUserProject(project)) { + throw new RuntimeException("User can not get access to project"); + } + } + + private boolean isUserProject(Project project) { + return (userSevice.getCurrentUser().equals(project.getUser())); + } } From 056ffef87c56dca270cec215e1dd0246ecba434f Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 19:41:43 +0400 Subject: [PATCH 09/16] #11 -- Add user services --- .../ulstu/fc/user/UserNotFoundException.java | 7 ++ .../java/ru/ulstu/fc/user/UserRepository.java | 15 +++ .../ru/ulstu/fc/user/UserRoleRepository.java | 7 ++ .../java/ru/ulstu/fc/user/UserService.java | 97 +++++++++++++++++++ .../fc/user/UserSessionLoginHandler.java | 44 +++++++++ .../fc/user/UserSessionLogoutHandler.java | 48 +++++++++ .../ulstu/fc/user/UserSessionRepository.java | 13 +++ .../ru/ulstu/fc/user/UserSessionService.java | 40 ++++++++ src/main/java/ru/ulstu/fc/user/UserUtils.java | 24 +++++ .../ulstu/fc/{core => user}/model/User.java | 3 +- .../fc/{core => user}/model/UserRole.java | 2 +- .../model/UserRoleConstants.java | 2 +- .../fc/{core => user}/model/UserSession.java | 3 +- 13 files changed, 301 insertions(+), 4 deletions(-) create mode 100644 src/main/java/ru/ulstu/fc/user/UserNotFoundException.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserRepository.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserRoleRepository.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserService.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserSessionRepository.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserSessionService.java create mode 100644 src/main/java/ru/ulstu/fc/user/UserUtils.java rename src/main/java/ru/ulstu/fc/{core => user}/model/User.java (96%) rename src/main/java/ru/ulstu/fc/{core => user}/model/UserRole.java (97%) rename src/main/java/ru/ulstu/fc/{core => user}/model/UserRoleConstants.java (81%) rename src/main/java/ru/ulstu/fc/{core => user}/model/UserSession.java (96%) diff --git a/src/main/java/ru/ulstu/fc/user/UserNotFoundException.java b/src/main/java/ru/ulstu/fc/user/UserNotFoundException.java new file mode 100644 index 0000000..49af789 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserNotFoundException.java @@ -0,0 +1,7 @@ +package ru.ulstu.fc.user; + +public class UserNotFoundException extends RuntimeException { + public UserNotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/UserRepository.java b/src/main/java/ru/ulstu/fc/user/UserRepository.java new file mode 100644 index 0000000..8610464 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserRepository.java @@ -0,0 +1,15 @@ +package ru.ulstu.fc.user; + +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.user.model.User; + +public interface UserRepository extends JpaRepository { + User findOneByLoginIgnoreCase(String login); + + @EntityGraph(attributePaths = "roles") + User findOneWithRolesById(int id); + + @EntityGraph(attributePaths = "roles") + User findOneWithRolesByLogin(String login); +} diff --git a/src/main/java/ru/ulstu/fc/user/UserRoleRepository.java b/src/main/java/ru/ulstu/fc/user/UserRoleRepository.java new file mode 100644 index 0000000..5aec61f --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserRoleRepository.java @@ -0,0 +1,7 @@ +package ru.ulstu.fc.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.user.model.UserRole; + +public interface UserRoleRepository extends JpaRepository { +} diff --git a/src/main/java/ru/ulstu/fc/user/UserService.java b/src/main/java/ru/ulstu/fc/user/UserService.java new file mode 100644 index 0000000..517c4f0 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserService.java @@ -0,0 +1,97 @@ +package ru.ulstu.fc.user; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +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 ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserRole; +import ru.ulstu.fc.user.model.UserRoleConstants; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@Transactional +public class UserService implements UserDetailsService { + private final Logger log = LoggerFactory.getLogger(UserService.class); + private final PasswordEncoder passwordEncoder; + private final UserRepository userRepository; + private final UserRoleRepository userRoleRepository; + @Value("${admin-password}") + private String adminPassword; + + public UserService(PasswordEncoder passwordEncoder, + UserRepository userRepository, + UserRoleRepository userRoleRepository) { + this.passwordEncoder = passwordEncoder; + this.userRepository = userRepository; + this.userRoleRepository = userRoleRepository; + } + + public User getUserByLogin(String login) { + return userRepository.findOneByLoginIgnoreCase(login); + } + + @Override + public UserDetails loadUserByUsername(String username) { + final User user = userRepository.findOneByLoginIgnoreCase(username); + if (user == null) { + throw new UserNotFoundException(username); + } + 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())); + } + + public User createUser(User user) { + if (getUserByLogin(user.getLogin()) != null) { + throw new RuntimeException(user.getLogin()); + } + User dbUser = (user.getId() == null) + ? user + : getUserById(user.getId()); + //user.setRoles(Collections.singleton(new UserRole(UserRoleConstants.USER))); + dbUser.setPassword(passwordEncoder.encode(user.getPassword())); + dbUser.setLogin(user.getLogin()); + dbUser = userRepository.save(dbUser); + log.debug("Created Information for User: {}", dbUser.getLogin()); + return dbUser; + } + + public User getUserById(Integer id) { + return userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found by id")); + } + + private void createDefaultUser(String login, String userRole) { + if (getUserByLogin(login) == null) { + UserRole role = userRoleRepository.save(new UserRole(userRole.toString())); + createUser(new User(login, login.equals("admin") ? adminPassword : login, Set.of(role))); + } + } + + public void initDefaultAdmin() { + createDefaultUser("admin", UserRoleConstants.ADMIN); + } + + public void initDefaultAspirant() { + createDefaultUser("aspirant", UserRoleConstants.ASPIRANT); + } + + public void initDefaultManager() { + createDefaultUser("manager", UserRoleConstants.MANAGER); + } + + public void initDefaultHead() { + createDefaultUser("head", UserRoleConstants.HEAD); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java b/src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java new file mode 100644 index 0000000..ded0a1b --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java @@ -0,0 +1,44 @@ +package ru.ulstu.fc.user; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import ru.ulstu.fc.config.Constants; + +import java.io.IOException; + +@Component +public class UserSessionLoginHandler extends SavedRequestAwareAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + private final Logger log = LoggerFactory.getLogger(UserSessionLoginHandler.class); + private final UserSessionService userSessionService; + + public UserSessionLoginHandler(UserSessionService userSessionService) { + super(); + this.userSessionService = userSessionService; + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + super.onAuthenticationSuccess(request, response, authentication); + final String login = authentication.getName(); + final String ipAddress = IpAddressResolver.getRemoteAddr(request); + final String host = request.getRemoteHost(); + log.debug("Authentication Success for {}@{} ({})", login, ipAddress, host); + HttpSession session = request.getSession(false); + if (session != null) { + final String sessionId = session.getId(); + userSessionService.createUserSession(sessionId, login, ipAddress, host); + session.setAttribute(Constants.SESSION_ID_ATTR, sessionId); + session.setMaxInactiveInterval(Constants.SESSION_TIMEOUT_SECONDS); + } + } +} diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java b/src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java new file mode 100644 index 0000000..2063de8 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java @@ -0,0 +1,48 @@ +package ru.ulstu.fc.user; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler; +import org.springframework.stereotype.Component; +import ru.ulstu.fc.config.Constants; + +import java.io.IOException; + +@Component +public class UserSessionLogoutHandler extends SimpleUrlLogoutSuccessHandler implements LogoutSuccessHandler { + private final Logger log = LoggerFactory.getLogger(UserSessionLogoutHandler.class); + private final UserSessionService userSessionService; + + public UserSessionLogoutHandler(UserSessionService userSessionService) { + this.userSessionService = userSessionService; + setDefaultTargetUrl(Constants.LOGOUT_URL); + } + + @Override + public void onLogoutSuccess(HttpServletRequest request, + HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + if (authentication == null) { + super.onLogoutSuccess(request, response, authentication); + return; + } + final String login = authentication.getName(); + final String ipAddress = IpAddressResolver.getRemoteAddr(request); + final String host = request.getRemoteHost(); + log.debug("Logout Success for {}@{} ({})", login, ipAddress, host); + HttpSession session = request.getSession(false); + if (session != null) { + final String sessionId = session.getAttribute(Constants.SESSION_ID_ATTR).toString(); + userSessionService.closeUserSession(sessionId); + session.removeAttribute(Constants.SESSION_ID_ATTR); + session.invalidate(); + } + super.onLogoutSuccess(request, response, authentication); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionRepository.java b/src/main/java/ru/ulstu/fc/user/UserSessionRepository.java new file mode 100644 index 0000000..ff28f93 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserSessionRepository.java @@ -0,0 +1,13 @@ +package ru.ulstu.fc.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.ulstu.fc.user.model.UserSession; + +import java.util.Date; +import java.util.List; + +public interface UserSessionRepository extends JpaRepository { + UserSession findOneBySessionId(String sessionId); + + List findAllByLogoutTimeIsNullAndLoginTimeBefore(Date date); +} diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionService.java b/src/main/java/ru/ulstu/fc/user/UserSessionService.java new file mode 100644 index 0000000..7f6e5ce --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserSessionService.java @@ -0,0 +1,40 @@ +package ru.ulstu.fc.user; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserSession; + +@Service +@Transactional +public class UserSessionService { + private final Logger log = LoggerFactory.getLogger(UserSessionService.class); + private final UserSessionRepository userSessionRepository; + private final UserService userService; + + public UserSessionService(UserSessionRepository userSessionRepository, UserService userService) { + this.userSessionRepository = userSessionRepository; + this.userService = userService; + } + + public void createUserSession(String sessionId, String login, String ipAddress, String host) { + final User user = userService.getUserByLogin(login); + if (user == null) { + throw new UserNotFoundException(login); + } + userSessionRepository.save(new UserSession(sessionId, ipAddress, host, user)); + log.debug("User session {} created for user {}@{} ({})", sessionId, login, ipAddress, host); + } + + public void closeUserSession(String sessionId) { + final UserSession userSession = userSessionRepository.findOneBySessionId(sessionId); + if (userSession == null) { + throw new IllegalArgumentException(String.format("User session %s not found", sessionId)); + } + userSession.close(); + userSessionRepository.save(userSession); + log.debug("User session {} closed", sessionId); + } +} diff --git a/src/main/java/ru/ulstu/fc/user/UserUtils.java b/src/main/java/ru/ulstu/fc/user/UserUtils.java new file mode 100644 index 0000000..a7169c8 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/UserUtils.java @@ -0,0 +1,24 @@ +package ru.ulstu.fc.user; + +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 { + 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; + } +} diff --git a/src/main/java/ru/ulstu/fc/core/model/User.java b/src/main/java/ru/ulstu/fc/user/model/User.java similarity index 96% rename from src/main/java/ru/ulstu/fc/core/model/User.java rename to src/main/java/ru/ulstu/fc/user/model/User.java index 2d960b1..11b3162 100644 --- a/src/main/java/ru/ulstu/fc/core/model/User.java +++ b/src/main/java/ru/ulstu/fc/user/model/User.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.core.model; +package ru.ulstu.fc.user.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -10,6 +10,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import jakarta.validation.constraints.Size; import ru.ulstu.fc.config.Constants; +import ru.ulstu.fc.core.model.BaseEntity; import java.util.HashSet; import java.util.Set; diff --git a/src/main/java/ru/ulstu/fc/core/model/UserRole.java b/src/main/java/ru/ulstu/fc/user/model/UserRole.java similarity index 97% rename from src/main/java/ru/ulstu/fc/core/model/UserRole.java rename to src/main/java/ru/ulstu/fc/user/model/UserRole.java index 92ecbb7..c6375cd 100644 --- a/src/main/java/ru/ulstu/fc/core/model/UserRole.java +++ b/src/main/java/ru/ulstu/fc/user/model/UserRole.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.core.model; +package ru.ulstu.fc.user.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java b/src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java similarity index 81% rename from src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java rename to src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java index ccc52ad..4eae200 100644 --- a/src/main/java/ru/ulstu/fc/core/model/UserRoleConstants.java +++ b/src/main/java/ru/ulstu/fc/user/model/UserRoleConstants.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.core.model; +package ru.ulstu.fc.user.model; public class UserRoleConstants { public static final String ADMIN = "ROLE_ADMIN"; diff --git a/src/main/java/ru/ulstu/fc/core/model/UserSession.java b/src/main/java/ru/ulstu/fc/user/model/UserSession.java similarity index 96% rename from src/main/java/ru/ulstu/fc/core/model/UserSession.java rename to src/main/java/ru/ulstu/fc/user/model/UserSession.java index e217f7e..354402d 100644 --- a/src/main/java/ru/ulstu/fc/core/model/UserSession.java +++ b/src/main/java/ru/ulstu/fc/user/model/UserSession.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.core.model; +package ru.ulstu.fc.user.model; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -8,6 +8,7 @@ import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; import jakarta.validation.constraints.NotNull; +import ru.ulstu.fc.core.model.BaseEntity; import java.util.Date; From 26cf6d0b0300b750a1391fa56fa66a168c719167 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 19:45:45 +0400 Subject: [PATCH 10/16] #11 -- Add spring security --- build.gradle | 4 +++- .../fc/project/service/ProjectService.java | 3 ++- .../{ => model}/UserNotFoundException.java | 2 +- .../user/{ => repository}/UserRepository.java | 2 +- .../{ => repository}/UserRoleRepository.java | 2 +- .../UserSessionRepository.java | 2 +- .../fc/user/service/IpAddressResolver.java | 22 +++++++++++++++++++ .../fc/user/{ => service}/UserService.java | 17 ++++---------- .../UserSessionLoginHandler.java | 2 +- .../UserSessionLogoutHandler.java | 2 +- .../{ => service}/UserSessionService.java | 4 +++- .../ulstu/fc/user/{ => utils}/UserUtils.java | 2 +- 12 files changed, 41 insertions(+), 23 deletions(-) rename src/main/java/ru/ulstu/fc/user/{ => model}/UserNotFoundException.java (82%) rename src/main/java/ru/ulstu/fc/user/{ => repository}/UserRepository.java (92%) rename src/main/java/ru/ulstu/fc/user/{ => repository}/UserRoleRepository.java (83%) rename src/main/java/ru/ulstu/fc/user/{ => repository}/UserSessionRepository.java (90%) create mode 100644 src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java rename src/main/java/ru/ulstu/fc/user/{ => service}/UserService.java (90%) rename src/main/java/ru/ulstu/fc/user/{ => service}/UserSessionLoginHandler.java (98%) rename src/main/java/ru/ulstu/fc/user/{ => service}/UserSessionLogoutHandler.java (98%) rename src/main/java/ru/ulstu/fc/user/{ => service}/UserSessionService.java (91%) rename src/main/java/ru/ulstu/fc/user/{ => utils}/UserUtils.java (96%) diff --git a/build.gradle b/build.gradle index a7dc0d0..d044239 100644 --- a/build.gradle +++ b/build.gradle @@ -24,11 +24,13 @@ dependencies { implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-jetty' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-thymeleaf' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation' implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa' - implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux' + implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security' implementation group: 'org.json', name: 'json', version: '20220320' implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect' + implementation group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity6' implementation group: 'com.fasterxml.jackson.module', name: 'jackson-module-afterburner' implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-hibernate5' implementation group: 'com.h2database', name:'h2' diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java index b53880d..97ae29a 100644 --- a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java @@ -4,6 +4,7 @@ import org.springframework.stereotype.Service; import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.project.model.ProjectForm; import ru.ulstu.fc.project.repository.ProjectRepository; +import ru.ulstu.fc.user.utils.UserUtils; import java.util.List; @@ -45,6 +46,6 @@ public class ProjectService { } private boolean isUserProject(Project project) { - return (userSevice.getCurrentUser().equals(project.getUser())); + return (UserUtils.getCurrentUserLogin().equals(project.getUser().getLogin())); } } diff --git a/src/main/java/ru/ulstu/fc/user/UserNotFoundException.java b/src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java similarity index 82% rename from src/main/java/ru/ulstu/fc/user/UserNotFoundException.java rename to src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java index 49af789..7840fc6 100644 --- a/src/main/java/ru/ulstu/fc/user/UserNotFoundException.java +++ b/src/main/java/ru/ulstu/fc/user/model/UserNotFoundException.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.model; public class UserNotFoundException extends RuntimeException { public UserNotFoundException(String message) { diff --git a/src/main/java/ru/ulstu/fc/user/UserRepository.java b/src/main/java/ru/ulstu/fc/user/repository/UserRepository.java similarity index 92% rename from src/main/java/ru/ulstu/fc/user/UserRepository.java rename to src/main/java/ru/ulstu/fc/user/repository/UserRepository.java index 8610464..808bac5 100644 --- a/src/main/java/ru/ulstu/fc/user/UserRepository.java +++ b/src/main/java/ru/ulstu/fc/user/repository/UserRepository.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.repository; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; diff --git a/src/main/java/ru/ulstu/fc/user/UserRoleRepository.java b/src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java similarity index 83% rename from src/main/java/ru/ulstu/fc/user/UserRoleRepository.java rename to src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java index 5aec61f..3f03a8d 100644 --- a/src/main/java/ru/ulstu/fc/user/UserRoleRepository.java +++ b/src/main/java/ru/ulstu/fc/user/repository/UserRoleRepository.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.repository; import org.springframework.data.jpa.repository.JpaRepository; import ru.ulstu.fc.user.model.UserRole; diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionRepository.java b/src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java similarity index 90% rename from src/main/java/ru/ulstu/fc/user/UserSessionRepository.java rename to src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java index ff28f93..f4a6de4 100644 --- a/src/main/java/ru/ulstu/fc/user/UserSessionRepository.java +++ b/src/main/java/ru/ulstu/fc/user/repository/UserSessionRepository.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.repository; import org.springframework.data.jpa.repository.JpaRepository; import ru.ulstu.fc.user.model.UserSession; diff --git a/src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java b/src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java new file mode 100644 index 0000000..51a0f36 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/user/service/IpAddressResolver.java @@ -0,0 +1,22 @@ +package ru.ulstu.fc.user.service; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.util.StringUtils; + +public final class IpAddressResolver { + private static final String CLIENT_IP_HEADER = "Client-IP"; + private static final String FORWARDED_FOR_HEADER = "X-Forwarded-For"; + + public static String getRemoteAddr(HttpServletRequest request) { + String headerClientIp = request.getHeader(""); + String headerXForwardedFor = request.getHeader(HttpServletRequest.FORM_AUTH); + if (StringUtils.isEmpty(request.getRemoteAddr()) && !StringUtils.isEmpty(headerClientIp)) { + return headerClientIp; + } + if (!StringUtils.isEmpty(headerXForwardedFor)) { + return headerXForwardedFor; + } + return request.getRemoteAddr(); + } + +} diff --git a/src/main/java/ru/ulstu/fc/user/UserService.java b/src/main/java/ru/ulstu/fc/user/service/UserService.java similarity index 90% rename from src/main/java/ru/ulstu/fc/user/UserService.java rename to src/main/java/ru/ulstu/fc/user/service/UserService.java index 517c4f0..f30ffd9 100644 --- a/src/main/java/ru/ulstu/fc/user/UserService.java +++ b/src/main/java/ru/ulstu/fc/user/service/UserService.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,8 +10,11 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserNotFoundException; import ru.ulstu.fc.user.model.UserRole; import ru.ulstu.fc.user.model.UserRoleConstants; +import ru.ulstu.fc.user.repository.UserRepository; +import ru.ulstu.fc.user.repository.UserRoleRepository; import java.util.Collections; import java.util.Optional; @@ -82,16 +85,4 @@ public class UserService implements UserDetailsService { public void initDefaultAdmin() { createDefaultUser("admin", UserRoleConstants.ADMIN); } - - public void initDefaultAspirant() { - createDefaultUser("aspirant", UserRoleConstants.ASPIRANT); - } - - public void initDefaultManager() { - createDefaultUser("manager", UserRoleConstants.MANAGER); - } - - public void initDefaultHead() { - createDefaultUser("head", UserRoleConstants.HEAD); - } } diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java b/src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java similarity index 98% rename from src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java rename to src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java index ded0a1b..b05710d 100644 --- a/src/main/java/ru/ulstu/fc/user/UserSessionLoginHandler.java +++ b/src/main/java/ru/ulstu/fc/user/service/UserSessionLoginHandler.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.service; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java b/src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java similarity index 98% rename from src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java rename to src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java index 2063de8..98d50fe 100644 --- a/src/main/java/ru/ulstu/fc/user/UserSessionLogoutHandler.java +++ b/src/main/java/ru/ulstu/fc/user/service/UserSessionLogoutHandler.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.service; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/ru/ulstu/fc/user/UserSessionService.java b/src/main/java/ru/ulstu/fc/user/service/UserSessionService.java similarity index 91% rename from src/main/java/ru/ulstu/fc/user/UserSessionService.java rename to src/main/java/ru/ulstu/fc/user/service/UserSessionService.java index 7f6e5ce..037957c 100644 --- a/src/main/java/ru/ulstu/fc/user/UserSessionService.java +++ b/src/main/java/ru/ulstu/fc/user/service/UserSessionService.java @@ -1,11 +1,13 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.model.UserNotFoundException; import ru.ulstu.fc.user.model.UserSession; +import ru.ulstu.fc.user.repository.UserSessionRepository; @Service @Transactional diff --git a/src/main/java/ru/ulstu/fc/user/UserUtils.java b/src/main/java/ru/ulstu/fc/user/utils/UserUtils.java similarity index 96% rename from src/main/java/ru/ulstu/fc/user/UserUtils.java rename to src/main/java/ru/ulstu/fc/user/utils/UserUtils.java index a7169c8..fc610b7 100644 --- a/src/main/java/ru/ulstu/fc/user/UserUtils.java +++ b/src/main/java/ru/ulstu/fc/user/utils/UserUtils.java @@ -1,4 +1,4 @@ -package ru.ulstu.fc.user; +package ru.ulstu.fc.user.utils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; From 07d6a2118f8f8bc63ca65ad46e575b2a202f4372 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 20:13:56 +0400 Subject: [PATCH 11/16] #11 -- Add error pages --- src/main/resources/application.properties | 1 + src/main/resources/templates/error/403.html | 13 +++++++++++++ src/main/resources/templates/error/404.html | 13 +++++++++++++ src/main/resources/templates/error/500.html | 13 +++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 src/main/resources/templates/error/403.html create mode 100644 src/main/resources/templates/error/404.html create mode 100644 src/main/resources/templates/error/500.html diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index bb03f7e..261c422 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,6 +1,7 @@ spring.main.banner-mode=off server.port=8080 server.jetty.connection-idle-timeout=1000s +admin-password=admin # Available levels are: TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF logging.level.ru.ulstu=DEBUG logging.level.sun.rmi.transport=off diff --git a/src/main/resources/templates/error/403.html b/src/main/resources/templates/error/403.html new file mode 100644 index 0000000..9eb069c --- /dev/null +++ b/src/main/resources/templates/error/403.html @@ -0,0 +1,13 @@ + + + + + +
+
Доступ запрещён
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/error/404.html b/src/main/resources/templates/error/404.html new file mode 100644 index 0000000..d599650 --- /dev/null +++ b/src/main/resources/templates/error/404.html @@ -0,0 +1,13 @@ + + + + + +
+
Страница не найдена
+
Вернуться на главную
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/error/500.html b/src/main/resources/templates/error/500.html new file mode 100644 index 0000000..b77a6cf --- /dev/null +++ b/src/main/resources/templates/error/500.html @@ -0,0 +1,13 @@ + + + + + +
+
Ошибка сервера
+
Вернуться на главную
+
+ + \ No newline at end of file From 1f22e12dd48800c58526029b94ed528cb925255d Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 20:14:12 +0400 Subject: [PATCH 12/16] #11 -- Add login page --- src/main/resources/templates/login.html | 35 +++++++++++++++++ src/main/resources/templates/loginError.html | 40 ++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/main/resources/templates/login.html create mode 100644 src/main/resources/templates/loginError.html diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html new file mode 100644 index 0000000..20cb039 --- /dev/null +++ b/src/main/resources/templates/login.html @@ -0,0 +1,35 @@ + + + + + + +
+
+
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/loginError.html b/src/main/resources/templates/loginError.html new file mode 100644 index 0000000..1d08df3 --- /dev/null +++ b/src/main/resources/templates/loginError.html @@ -0,0 +1,40 @@ + + + + + + +
+
+ +
+
+
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+ + \ No newline at end of file From a2ff48a165eae2efbe77e38af4910a8a1b2a6fe8 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Fri, 14 Feb 2025 20:14:41 +0400 Subject: [PATCH 13/16] #11 -- Add security config --- .../ulstu/fc/FuzzyControllerApplication.java | 17 ++++++ .../ru/ulstu/fc/config/MvcConfiguration.java | 13 ++--- .../config/PasswordEncoderConfiguration.java | 13 +++++ .../fc/config/SecurityConfiguration.java | 52 +++++++++++++++++++ .../fc/config/WebClientConfiguration.java | 13 ----- .../project/controller/ProjectController.java | 3 ++ .../controller/ProjectRestController.java | 3 ++ .../ru/ulstu/fc/project/model/Project.java | 6 +-- .../project/repository/ProjectRepository.java | 5 ++ .../fc/project/service/ProjectService.java | 35 +++++++++---- .../ru/ulstu/fc/user/service/UserService.java | 5 ++ 11 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java create mode 100644 src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java delete mode 100644 src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java diff --git a/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java b/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java index f490108..27dcbf9 100644 --- a/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java +++ b/src/main/java/ru/ulstu/fc/FuzzyControllerApplication.java @@ -2,11 +2,28 @@ package ru.ulstu.fc; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.event.EventListener; +import ru.ulstu.fc.user.service.UserService; @SpringBootApplication +@EnableConfigurationProperties public class FuzzyControllerApplication { + private final UserService userService; + + public FuzzyControllerApplication(UserService userService) { + this.userService = userService; + } + public static void main(String[] args) { SpringApplication.run(FuzzyControllerApplication.class, args); } + + @EventListener(ApplicationReadyEvent.class) + public void doSomethingAfterStartup() { + System.out.println("hello world, I have just started up"); + userService.initDefaultAdmin(); + } } diff --git a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java index f4547a4..809147e 100644 --- a/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java +++ b/src/main/java/ru/ulstu/fc/config/MvcConfiguration.java @@ -1,9 +1,3 @@ -/* - * Copyright (C) 2021 Anton Romanov - All Rights Reserved - * You may use, distribute and modify this code, please write to: romanov73@gmail.com. - * - */ - package ru.ulstu.fc.config; import org.springframework.context.annotation.Bean; @@ -11,12 +5,19 @@ import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.CookieLocaleResolver; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; @Configuration public class MvcConfiguration implements WebMvcConfigurer { + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login"); + registry.addViewController("/loginError"); + registry.addViewController("/index"); + } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { diff --git a/src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java b/src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java new file mode 100644 index 0000000..4706fe8 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/config/PasswordEncoderConfiguration.java @@ -0,0 +1,13 @@ +package ru.ulstu.fc.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(); + } +} diff --git a/src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java b/src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java new file mode 100644 index 0000000..6583cd0 --- /dev/null +++ b/src/main/java/ru/ulstu/fc/config/SecurityConfiguration.java @@ -0,0 +1,52 @@ +package ru.ulstu.fc.config; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +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.configurers.AbstractHttpConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer; +import org.springframework.security.web.SecurityFilterChain; +import ru.ulstu.fc.user.model.UserRoleConstants; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfiguration { + private final Logger log = LoggerFactory.getLogger(SecurityConfiguration.class); + private final String[] permittedUrls = new String[]{ + "/login", "/index", + "/public/**", "/organizers", "/webjars/**", + "/h2-console/*", "/h2-console", + "/css/**", "/js/**", "/img/**", + "/templates/**", "/webjars/**"}; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + log.debug("Security enabled"); + + http + .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) + .csrf(AbstractHttpConfigurer::disable) + .authorizeHttpRequests(auth -> + auth.requestMatchers("/").permitAll() + .requestMatchers(permittedUrls).permitAll() + .requestMatchers("/swagger-ui.html").hasAuthority(UserRoleConstants.ADMIN) + .anyRequest().authenticated()) + .formLogin(form -> + form.loginPage("/login") + .failureUrl("/loginError") + .permitAll()) + .logout(logout -> + logout + .logoutSuccessUrl(Constants.LOGOUT_URL) + .invalidateHttpSession(false) + .clearAuthentication(true) + .deleteCookies(Constants.COOKIES_NAME) + .permitAll()); + return http.build(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java b/src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java deleted file mode 100644 index 1761fc4..0000000 --- a/src/main/java/ru/ulstu/fc/config/WebClientConfiguration.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.ulstu.fc.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.reactive.function.client.WebClient; - -@Configuration -public class WebClientConfiguration { - @Bean - public WebClient webClient(WebClient.Builder webClientBuilder) { - return webClientBuilder.build(); - } -} diff --git a/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java index cda2300..35c42d5 100644 --- a/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java @@ -1,6 +1,7 @@ package ru.ulstu.fc.project.controller; import io.swagger.v3.oas.annotations.Hidden; +import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.DeleteMapping; @@ -9,10 +10,12 @@ import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import ru.ulstu.fc.project.model.ProjectForm; import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.user.model.UserRoleConstants; @Controller @Hidden @RequestMapping("project") +@Secured({UserRoleConstants.ADMIN}) public class ProjectController { private final ProjectService projectService; diff --git a/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java index b2875a8..7fcce20 100644 --- a/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectRestController.java @@ -1,5 +1,6 @@ package ru.ulstu.fc.project.controller; +import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; @@ -8,11 +9,13 @@ import org.springframework.web.bind.annotation.RestController; import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.project.model.ProjectForm; import ru.ulstu.fc.project.service.ProjectService; +import ru.ulstu.fc.user.model.UserRoleConstants; import java.util.List; @RestController @RequestMapping("projectRest") +@Secured({UserRoleConstants.ADMIN}) public class ProjectRestController { private final ProjectService projectService; diff --git a/src/main/java/ru/ulstu/fc/project/model/Project.java b/src/main/java/ru/ulstu/fc/project/model/Project.java index 129b008..4725550 100644 --- a/src/main/java/ru/ulstu/fc/project/model/Project.java +++ b/src/main/java/ru/ulstu/fc/project/model/Project.java @@ -1,8 +1,8 @@ package ru.ulstu.fc.project.model; import jakarta.persistence.Entity; +import jakarta.persistence.ManyToOne; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import ru.ulstu.fc.core.model.BaseEntity; import ru.ulstu.fc.user.model.User; @@ -10,10 +10,10 @@ import java.util.Date; @Entity public class Project extends BaseEntity { - @NotEmpty + @NotEmpty(message = "Текст новости не может быть пустым") private String name; private Date createDate = new Date(); - @NotNull + @ManyToOne private User user; public Project() { diff --git a/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java b/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java index cf1b55c..547ac15 100644 --- a/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java +++ b/src/main/java/ru/ulstu/fc/project/repository/ProjectRepository.java @@ -1,9 +1,14 @@ package ru.ulstu.fc.project.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; import ru.ulstu.fc.project.model.Project; +import java.util.List; + @Repository public interface ProjectRepository extends JpaRepository { + + List findAllByUserId(@Param("userId") Integer userId); } diff --git a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java index 97ae29a..39a9cb5 100644 --- a/src/main/java/ru/ulstu/fc/project/service/ProjectService.java +++ b/src/main/java/ru/ulstu/fc/project/service/ProjectService.java @@ -4,48 +4,61 @@ import org.springframework.stereotype.Service; import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.project.model.ProjectForm; import ru.ulstu.fc.project.repository.ProjectRepository; -import ru.ulstu.fc.user.utils.UserUtils; +import ru.ulstu.fc.user.model.User; +import ru.ulstu.fc.user.service.UserService; import java.util.List; @Service public class ProjectService { private final ProjectRepository projectRepository; + private final UserService userService; - public ProjectService(ProjectRepository projectRepository) { + public ProjectService(ProjectRepository projectRepository, + UserService userService) { this.projectRepository = projectRepository; + this.userService = userService; } public List getCurrentUserProjects() { - return projectRepository.findAll(); + return projectRepository.findAllByUserId(userService.getCurrentUser().getId()); + } + + public Project getById(Integer id) { + Project project = projectRepository + .findById(id) + .orElseThrow(() -> new RuntimeException("Project not found by id")); + checkUserProjectWithThrow(project, userService.getCurrentUser()); + return project; } public Project save(ProjectForm projectForm) { - return projectRepository.save(new Project(projectForm)); + return save(new Project(projectForm)); } public Project save(Project projectToSave) { + User currentUser = userService.getCurrentUser(); if (projectToSave.getId() == null) { + projectToSave.setUser(currentUser); return projectRepository.save(projectToSave); } - Project dbProject = projectRepository - .findById(projectToSave.getId()) - .orElseThrow(() -> new RuntimeException("Project not found by id")); + Project dbProject = getById(projectToSave.getId()); dbProject.setName(projectToSave.getName()); return projectRepository.save(dbProject); } public void delete(ProjectForm projectForm) { + getById(projectForm.getId()); projectRepository.deleteById(projectForm.getId()); } - private void checkUserProjectWithThrow(Project project) { - if (!isUserProject(project)) { + private void checkUserProjectWithThrow(Project project, User currentUser) { + if (!isUserProject(project, currentUser)) { throw new RuntimeException("User can not get access to project"); } } - private boolean isUserProject(Project project) { - return (UserUtils.getCurrentUserLogin().equals(project.getUser().getLogin())); + private boolean isUserProject(Project project, User currentUser) { + return (currentUser.equals(project.getUser())); } } diff --git a/src/main/java/ru/ulstu/fc/user/service/UserService.java b/src/main/java/ru/ulstu/fc/user/service/UserService.java index f30ffd9..b94a49c 100644 --- a/src/main/java/ru/ulstu/fc/user/service/UserService.java +++ b/src/main/java/ru/ulstu/fc/user/service/UserService.java @@ -15,6 +15,7 @@ import ru.ulstu.fc.user.model.UserRole; import ru.ulstu.fc.user.model.UserRoleConstants; import ru.ulstu.fc.user.repository.UserRepository; import ru.ulstu.fc.user.repository.UserRoleRepository; +import ru.ulstu.fc.user.utils.UserUtils; import java.util.Collections; import java.util.Optional; @@ -85,4 +86,8 @@ public class UserService implements UserDetailsService { public void initDefaultAdmin() { createDefaultUser("admin", UserRoleConstants.ADMIN); } + + public User getCurrentUser() { + return getUserByLogin(UserUtils.getCurrentUserLogin()); + } } From 7cc6e88c35c506f1cdc84a54b19925dab915a193 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Sat, 15 Feb 2025 14:04:31 +0400 Subject: [PATCH 14/16] #11 -- Add project crud --- .../project/controller/ProjectController.java | 26 +++++++++---- .../ulstu/fc/project/model/ProjectForm.java | 16 ++++++++ src/main/resources/templates/login.html | 3 +- .../resources/templates/project/edit.html | 39 +++++++++++++++++++ .../resources/templates/project/list.html | 22 +++++++++++ 5 files changed, 97 insertions(+), 9 deletions(-) create mode 100644 src/main/resources/templates/project/edit.html create mode 100644 src/main/resources/templates/project/list.html diff --git a/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java index 35c42d5..85c28a8 100644 --- a/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java +++ b/src/main/java/ru/ulstu/fc/project/controller/ProjectController.java @@ -4,10 +4,11 @@ import io.swagger.v3.oas.annotations.Hidden; import org.springframework.security.access.annotation.Secured; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import ru.ulstu.fc.project.model.Project; import ru.ulstu.fc.project.model.ProjectForm; import ru.ulstu.fc.project.service.ProjectService; import ru.ulstu.fc.user.model.UserRoleConstants; @@ -26,18 +27,29 @@ public class ProjectController { @GetMapping("list") public String getProjects(Model model) { model.addAttribute("projects", projectService.getCurrentUserProjects()); - return "listProjects"; + return "project/list"; } - @PostMapping("save") + @GetMapping("/edit/{projectId}") + public String edit(@PathVariable(value = "projectId") Integer id, Model model) { + model.addAttribute("project", + new ProjectForm((id != null && id != 0) + ? projectService.getById(id) + : new Project())); + return "project/edit"; + } + + @PostMapping(value = "save", params = "save") public String save(ProjectForm projectForm, Model model) { model.addAttribute("project", projectService.save(projectForm)); - return "redirect:/list"; + return "redirect:/project/list"; } - @DeleteMapping("delete") + @PostMapping(value = "save", params = "delete") public String delete(ProjectForm projectForm) { - projectService.delete(projectForm); - return "redirect:/list"; + if (projectForm != null && projectForm.getId() != null) { + projectService.delete(projectForm); + } + return "redirect:/project/list"; } } diff --git a/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java b/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java index 5c06899..a03532b 100644 --- a/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java +++ b/src/main/java/ru/ulstu/fc/project/model/ProjectForm.java @@ -1,8 +1,20 @@ package ru.ulstu.fc.project.model; +import java.util.Date; + public class ProjectForm { private Integer id; private String name; + private Date createDate; + + public ProjectForm() { + } + + public ProjectForm(Project project) { + this.id = project.getId(); + this.name = project.getName(); + this.createDate = project.getCreateDate(); + } public Integer getId() { return id; @@ -19,4 +31,8 @@ public class ProjectForm { public void setName(String name) { this.name = name; } + + public Date getCreateDate() { + return createDate; + } } diff --git a/src/main/resources/templates/login.html b/src/main/resources/templates/login.html index 20cb039..d84a8aa 100644 --- a/src/main/resources/templates/login.html +++ b/src/main/resources/templates/login.html @@ -8,8 +8,7 @@
diff --git a/src/main/resources/templates/project/edit.html b/src/main/resources/templates/project/edit.html new file mode 100644 index 0000000..18aefd0 --- /dev/null +++ b/src/main/resources/templates/project/edit.html @@ -0,0 +1,39 @@ + + + + Редактирование проекта + + +
+

Редактирование проекта:

+
+ +
+ + +

+ Не может быть пустым +

+
+
+ +
+ + + Отмена +
+
+ diff --git a/src/main/resources/templates/project/list.html b/src/main/resources/templates/project/list.html new file mode 100644 index 0000000..69e049c --- /dev/null +++ b/src/main/resources/templates/project/list.html @@ -0,0 +1,22 @@ + + + + Список правил + + + + From 8fb60bc079e20a08f3c91a7a2693947fdc8d3a57 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Sat, 15 Feb 2025 14:07:11 +0400 Subject: [PATCH 15/16] #11 -- Fix menu --- src/main/resources/templates/default.html | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index f3c03ed..76767f9 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -24,8 +24,12 @@ -
From fbf1cb50e27cccefdfe583242d7c6ae9faeb35d9 Mon Sep 17 00:00:00 2001 From: Anton Romanov Date: Sat, 15 Feb 2025 14:07:45 +0400 Subject: [PATCH 16/16] #11 -- Fix menu --- src/main/resources/templates/default.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/templates/default.html b/src/main/resources/templates/default.html index 76767f9..e6a830d 100644 --- a/src/main/resources/templates/default.html +++ b/src/main/resources/templates/default.html @@ -21,9 +21,6 @@ +