diff --git a/server/pom.xml b/server/pom.xml index 15decd5..16f199e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -88,6 +88,16 @@ org.springframework.boot spring-boot-starter-actuator + + org.apache.commons + commons-lang3 + 3.14.0 + + + org.apache.commons + commons-collections4 + 4.4 + diff --git a/server/src/main/java/ru/tubryansk/tdms/TdmsApplication.java b/server/src/main/java/ru/tubryansk/tdms/TdmsApplication.java index 396b31a..597ecfe 100644 --- a/server/src/main/java/ru/tubryansk/tdms/TdmsApplication.java +++ b/server/src/main/java/ru/tubryansk/tdms/TdmsApplication.java @@ -11,8 +11,6 @@ import org.springframework.context.ConfigurableApplicationContext; @Slf4j public class TdmsApplication { public static void main(String[] args) { - ConfigurableApplicationContext context = SpringApplication.run(TdmsApplication.class, args); - String staticLocation = context.getEnvironment().getProperty("spring.web.resources.static-locations"); - log.info("Static location: {}", staticLocation); + SpringApplication.run(TdmsApplication.class, args); } } diff --git a/server/src/main/java/ru/tubryansk/tdms/config/SecurityConfiguration.java b/server/src/main/java/ru/tubryansk/tdms/config/SecurityConfiguration.java index dd3e0b2..443463e 100644 --- a/server/src/main/java/ru/tubryansk/tdms/config/SecurityConfiguration.java +++ b/server/src/main/java/ru/tubryansk/tdms/config/SecurityConfiguration.java @@ -3,9 +3,14 @@ package ru.tubryansk.tdms.config; import jakarta.servlet.http.HttpSessionEvent; import jakarta.servlet.http.HttpSessionListener; +import org.apache.commons.lang3.StringUtils; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.ProviderManager; @@ -26,24 +31,31 @@ import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; +import java.time.Duration; +import java.util.List; + import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY; @Configuration public class SecurityConfiguration { @Bean - public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, AuthenticationManager authenticationManager) throws Exception { + public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity, + AuthenticationManager authenticationManager, + @Qualifier("corsConfig") CorsConfigurationSource cors) throws Exception { return httpSecurity .authorizeHttpRequests(this::configureHttpAuthorization) .csrf(AbstractHttpConfigurer::disable) - .cors(a -> a.configurationSource(corsConfiguration())) + .cors(a -> a.configurationSource(cors)) .authenticationManager(authenticationManager) .sessionManagement(this::configureSessionManagement) .build(); } @Bean - public CorsConfigurationSource corsConfiguration() { + @Profile("dev") + @Qualifier("corsConfig") + public CorsConfigurationSource corsConfigurationDev() { return request -> { CorsConfiguration corsConfiguration = new CorsConfiguration(); corsConfiguration.applyPermitDefaultValues(); @@ -54,15 +66,34 @@ public class SecurityConfiguration { }; } + @Bean + @Profile("!dev") + @Qualifier("corsConfig") + public CorsConfigurationSource corsConfigurationProd(@Value("${application.domain}") String domain, + @Value("${application.port}") String port, + @Value("${application.protocol}") String protocol) { + return request -> { + String url = StringUtils.join(protocol, "://", domain, ":", port); + CorsConfiguration corsConfiguration = new CorsConfiguration(); + corsConfiguration.setMaxAge(Duration.ofHours(1)); + corsConfiguration.addAllowedOrigin(url); + corsConfiguration.setAllowedMethods(List.of(HttpMethod.GET.name(), HttpMethod.POST.name())); + // corsConfiguration.setAllowedHeaders(); + return corsConfiguration; + }; + } + private void configureHttpAuthorization(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry httpAuthorization) { /* API ROUTES */ - httpAuthorization.requestMatchers("/api/v1/diploma-topic/**").permitAll(); - httpAuthorization.requestMatchers("/api/v1/user/**").permitAll(); + httpAuthorization.requestMatchers("/api/v1/user/logout").authenticated(); + httpAuthorization.requestMatchers("/api/v1/user/login").anonymous(); + httpAuthorization.requestMatchers("/api/v1/user/current").permitAll(); + + httpAuthorization.requestMatchers("/api/v1/student/current").permitAll(); + httpAuthorization.requestMatchers("/api/**").denyAll(); /* STATIC ROUTES */ httpAuthorization.requestMatchers("/**").permitAll(); - /* OTHER */ - httpAuthorization.anyRequest().denyAll(); } @Bean @@ -76,18 +107,21 @@ public class SecurityConfiguration { return provider; } - private PasswordEncoder passwordEncoder() { + @Bean + public PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Bean + @Profile("dev") public HttpSessionListener autoAuthenticateUnderAdmin(AuthenticationManager authenticationManager) { return new HttpSessionListener() { @Override public void sessionCreated(HttpSessionEvent se) { - LoggerFactory.getLogger(this.getClass()).info("Session created {}. Authenticated, as izrailev_v_ya_1", se.getSession().getId()); + String username = "akulenko_mikhail"; + LoggerFactory.getLogger(this.getClass()).info("Session created {}. Authenticated, as {}", se.getSession().getId(), username); SecurityContext context = SecurityContextHolder.createEmptyContext(); - UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("izrailev_v_ya_1", "1"); + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, "1"); Authentication authenticated = authenticationManager.authenticate(authentication); context.setAuthentication(authenticated); SecurityContextHolder.setContext(context); diff --git a/server/src/main/java/ru/tubryansk/tdms/controller/DiplomaTopicController.java b/server/src/main/java/ru/tubryansk/tdms/controller/DiplomaTopicController.java index d0157f5..8f9a6ad 100644 --- a/server/src/main/java/ru/tubryansk/tdms/controller/DiplomaTopicController.java +++ b/server/src/main/java/ru/tubryansk/tdms/controller/DiplomaTopicController.java @@ -1,33 +1,13 @@ package ru.tubryansk.tdms.controller; -import jakarta.validation.constraints.PositiveOrZero; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import ru.tubryansk.tdms.dto.DiplomaTopicDTO; -import ru.tubryansk.tdms.service.DiplomaTopicService; - -import java.util.List; @RestController @RequestMapping("/api/v1/diploma-topic/") @Validated public class DiplomaTopicController { - @Autowired - private DiplomaTopicService diplomaTopicService; - - @GetMapping("/get-all") - public List getAll() { - return diplomaTopicService.getAll(); - } - - @GetMapping("/get-by-id/{id:[\\-+]?\\d+}") - public DiplomaTopicDTO getById(@PathVariable @PositiveOrZero Integer id) { - return diplomaTopicService.getById(id); - } } diff --git a/server/src/main/java/ru/tubryansk/tdms/controller/StudentController.java b/server/src/main/java/ru/tubryansk/tdms/controller/StudentController.java new file mode 100644 index 0000000..3bb6523 --- /dev/null +++ b/server/src/main/java/ru/tubryansk/tdms/controller/StudentController.java @@ -0,0 +1,20 @@ +package ru.tubryansk.tdms.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import ru.tubryansk.tdms.dto.StudentDTO; +import ru.tubryansk.tdms.service.StudentService; + +@RestController +@RequestMapping("/api/v1/student/") +public class StudentController { + @Autowired + private StudentService studentService; + + @GetMapping("/current") + public StudentDTO getCurrentStudent() { + return studentService.getCallerStudent().map(StudentDTO::from).orElse(null); + } +} diff --git a/server/src/main/java/ru/tubryansk/tdms/controller/UserController.java b/server/src/main/java/ru/tubryansk/tdms/controller/UserController.java index 66c83d2..cfcf057 100644 --- a/server/src/main/java/ru/tubryansk/tdms/controller/UserController.java +++ b/server/src/main/java/ru/tubryansk/tdms/controller/UserController.java @@ -2,31 +2,32 @@ package ru.tubryansk.tdms.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.validation.annotation.Validated; -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 org.springframework.web.bind.annotation.*; import ru.tubryansk.tdms.dto.UserDTO; -import ru.tubryansk.tdms.entity.User; +import ru.tubryansk.tdms.service.AuthenticationService; import ru.tubryansk.tdms.service.UserService; @RestController -@Validated @RequestMapping("/api/v1/user") @Slf4j public class UserController { + @Autowired + private AuthenticationService authenticationService; @Autowired private UserService userService; @GetMapping("/current") public UserDTO getCurrentUser() { - User principal = userService.getCallerPrincipal(); - return principal != null ? UserDTO.from(principal, true) : UserDTO.fromUnauthenticated(); + return userService.getCallerUser().map(user -> UserDTO.from(user, true)).orElse(UserDTO.unauthenticated()); } @PostMapping("/logout") public void logout() { - userService.logout(); + authenticationService.logout(); + } + + @PostMapping("/login") + public void login(@RequestParam String username, @RequestParam String password) { + authenticationService.login(username, password); } } diff --git a/server/src/main/java/ru/tubryansk/tdms/dto/DiplomaTopicDTO.java b/server/src/main/java/ru/tubryansk/tdms/dto/DiplomaTopicDTO.java deleted file mode 100644 index 93e01bd..0000000 --- a/server/src/main/java/ru/tubryansk/tdms/dto/DiplomaTopicDTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.tubryansk.tdms.dto; - - -import lombok.Builder; -import ru.tubryansk.tdms.entity.DiplomaTopic; - - -@Builder -public record DiplomaTopicDTO(Integer id, String name) { - public static DiplomaTopicDTO fromEntity(DiplomaTopic diplomaTopic) { - return DiplomaTopicDTO.builder() - .id(diplomaTopic.getId()) - .name(diplomaTopic.getName()) - .build(); - } -} diff --git a/server/src/main/java/ru/tubryansk/tdms/dto/GroupDTO.java b/server/src/main/java/ru/tubryansk/tdms/dto/GroupDTO.java new file mode 100644 index 0000000..649cf1d --- /dev/null +++ b/server/src/main/java/ru/tubryansk/tdms/dto/GroupDTO.java @@ -0,0 +1,13 @@ +package ru.tubryansk.tdms.dto; + +import ru.tubryansk.tdms.entity.Group; + +public record GroupDTO(String name, UserDTO principalUser) { + + public static GroupDTO from(Group group) { + return new GroupDTO( + group.getName(), + UserDTO.from(group.getPrincipalUser(), true) + ); + } +} diff --git a/server/src/main/java/ru/tubryansk/tdms/dto/RoleDTO.java b/server/src/main/java/ru/tubryansk/tdms/dto/RoleDTO.java index f34d5f9..dc9ebea 100644 --- a/server/src/main/java/ru/tubryansk/tdms/dto/RoleDTO.java +++ b/server/src/main/java/ru/tubryansk/tdms/dto/RoleDTO.java @@ -1,15 +1,19 @@ package ru.tubryansk.tdms.dto; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import ru.tubryansk.tdms.entity.Role; +import ru.tubryansk.tdms.entity.User; + +import java.util.List; -@Data -@AllArgsConstructor -@NoArgsConstructor -public class RoleDTO { - private Integer id; - private String name; +public record RoleDTO(String name, String authority) { + + public static RoleDTO from(Role role) { + return new RoleDTO(role.getName(), role.getAuthority()); + } + + public static List from(User user) { + return user.getRoles().stream().map(RoleDTO::from).toList(); + } } diff --git a/server/src/main/java/ru/tubryansk/tdms/dto/StudentDTO.java b/server/src/main/java/ru/tubryansk/tdms/dto/StudentDTO.java index 2aad7fb..ddea302 100644 --- a/server/src/main/java/ru/tubryansk/tdms/dto/StudentDTO.java +++ b/server/src/main/java/ru/tubryansk/tdms/dto/StudentDTO.java @@ -4,13 +4,13 @@ package ru.tubryansk.tdms.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import ru.tubryansk.tdms.entity.Student; @Data @AllArgsConstructor @NoArgsConstructor public class StudentDTO { - private Integer id; private Boolean form; private Integer protectionOrder; private String magistracy; @@ -23,8 +23,29 @@ public class StudentDTO { private String note; private Boolean recordBookReturned; private String work; - private Integer userId; - private Integer diplomaTopicId; - private Integer mentorUserId; - private Integer groupId; + private UserDTO user; + private String diplomaTopic; + private UserDTO mentorUser; + private GroupDTO group; + + public static StudentDTO from(Student student) { + return new StudentDTO( + student.getForm(), + student.getProtectionOrder(), + student.getMagistracy(), + student.getDigitalFormatPresent(), + student.getMarkComment(), + student.getMarkPractice(), + student.getPredefenceComment(), + student.getNormalControl(), + student.getAntiPlagiarism(), + student.getNote(), + student.getRecordBookReturned(), + student.getWork(), + UserDTO.from(student.getUser(), true), + student.getDiplomaTopic().getName(), + UserDTO.from(student.getMentorUser(), true), + GroupDTO.from(student.getGroup()) + ); + } } diff --git a/server/src/main/java/ru/tubryansk/tdms/dto/UserDTO.java b/server/src/main/java/ru/tubryansk/tdms/dto/UserDTO.java index f53401f..cb2f0b2 100644 --- a/server/src/main/java/ru/tubryansk/tdms/dto/UserDTO.java +++ b/server/src/main/java/ru/tubryansk/tdms/dto/UserDTO.java @@ -1,8 +1,8 @@ package ru.tubryansk.tdms.dto; +import com.fasterxml.jackson.annotation.JsonInclude; import lombok.Builder; -import ru.tubryansk.tdms.entity.Role; import ru.tubryansk.tdms.entity.User; import java.time.ZonedDateTime; @@ -10,18 +10,19 @@ import java.util.List; @Builder +@JsonInclude(JsonInclude.Include.NON_ABSENT) public record UserDTO( boolean authenticated, String login, String password, String fullName, String email, - String phoneNumber, + String phone, ZonedDateTime createdAt, ZonedDateTime updatedAt, - List authorities) { + List authorities) { - public static UserDTO fromUnauthenticated() { + public static UserDTO unauthenticated() { return UserDTO.builder() .authenticated(false) .build(); @@ -34,10 +35,10 @@ public record UserDTO( .password(anonymize ? "" : user.getPassword()) .fullName(user.getFullName()) .email(user.getMail()) - .phoneNumber(user.getNumberPhone()) + .phone(user.getNumberPhone()) .createdAt(user.getCreatedAt()) .updatedAt(user.getUpdatedAt()) - .authorities(user.getRoles().stream().map(Role::getAuthority).toList()) + .authorities(RoleDTO.from(user)) .build(); } } diff --git a/server/src/main/java/ru/tubryansk/tdms/entity/Student.java b/server/src/main/java/ru/tubryansk/tdms/entity/Student.java index 38eb612..8c5ceef 100644 --- a/server/src/main/java/ru/tubryansk/tdms/entity/Student.java +++ b/server/src/main/java/ru/tubryansk/tdms/entity/Student.java @@ -3,6 +3,8 @@ package ru.tubryansk.tdms.entity; import jakarta.persistence.*; import lombok.Data; +import org.springframework.context.annotation.Scope; +import org.springframework.web.context.annotation.SessionScope; @Data @@ -49,5 +51,4 @@ public class Student { @ManyToOne @JoinColumn(name = "group_id", nullable = false) private Group group; - } diff --git a/server/src/main/java/ru/tubryansk/tdms/repository/DiplomaTopicRepository.java b/server/src/main/java/ru/tubryansk/tdms/repository/DiplomaTopicRepository.java index 49a1306..1ce61e6 100644 --- a/server/src/main/java/ru/tubryansk/tdms/repository/DiplomaTopicRepository.java +++ b/server/src/main/java/ru/tubryansk/tdms/repository/DiplomaTopicRepository.java @@ -3,7 +3,11 @@ package ru.tubryansk.tdms.repository; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import ru.tubryansk.tdms.entity.DiplomaTopic; +import ru.tubryansk.tdms.exception.NotFoundException; @Repository public interface DiplomaTopicRepository extends JpaRepository { + default DiplomaTopic findByIdThrow(Integer id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id)); + } } \ No newline at end of file diff --git a/server/src/main/java/ru/tubryansk/tdms/repository/StudentRepository.java b/server/src/main/java/ru/tubryansk/tdms/repository/StudentRepository.java new file mode 100644 index 0000000..c41a842 --- /dev/null +++ b/server/src/main/java/ru/tubryansk/tdms/repository/StudentRepository.java @@ -0,0 +1,18 @@ +package ru.tubryansk.tdms.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.tubryansk.tdms.entity.Student; +import ru.tubryansk.tdms.entity.User; +import ru.tubryansk.tdms.exception.NotFoundException; + +import java.util.Optional; + +@Repository +public interface StudentRepository extends JpaRepository { + default Student findByIdThrow(Integer id) { + return this.findById(id).orElseThrow(() -> new NotFoundException(Student.class, id)); + } + + Optional findByUser(User user); +} diff --git a/server/src/main/java/ru/tubryansk/tdms/service/AuthenticationService.java b/server/src/main/java/ru/tubryansk/tdms/service/AuthenticationService.java new file mode 100644 index 0000000..6ba7be3 --- /dev/null +++ b/server/src/main/java/ru/tubryansk/tdms/service/AuthenticationService.java @@ -0,0 +1,40 @@ +package ru.tubryansk.tdms.service; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpSession; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import ru.tubryansk.tdms.entity.User; + +import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY; + +@Service +public class AuthenticationService { + @Autowired + private HttpServletRequest request; + @Autowired + private AuthenticationManager authenticationManager; + + public boolean authenticated() { + var authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication.isAuthenticated() && (authentication.getPrincipal() instanceof User); + } + + public void logout() { + HttpSession session = request.getSession(false); + if(session != null) { + session.invalidate(); + } + } + + public void login(String username, String password) { + var context = SecurityContextHolder.createEmptyContext(); + var token = new UsernamePasswordAuthenticationToken(username, password); + var authenticated = authenticationManager.authenticate(token); + context.setAuthentication(authenticated); + request.getSession(true).setAttribute(SPRING_SECURITY_CONTEXT_KEY, context); + } +} diff --git a/server/src/main/java/ru/tubryansk/tdms/service/DiplomaTopicService.java b/server/src/main/java/ru/tubryansk/tdms/service/DiplomaTopicService.java index d43cb9e..bdf5deb 100644 --- a/server/src/main/java/ru/tubryansk/tdms/service/DiplomaTopicService.java +++ b/server/src/main/java/ru/tubryansk/tdms/service/DiplomaTopicService.java @@ -1,31 +1,9 @@ package ru.tubryansk.tdms.service; import jakarta.transaction.Transactional; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import ru.tubryansk.tdms.entity.DiplomaTopic; -import ru.tubryansk.tdms.exception.NotFoundException; -import ru.tubryansk.tdms.repository.DiplomaTopicRepository; -import ru.tubryansk.tdms.dto.DiplomaTopicDTO; - -import java.util.List; -import java.util.stream.Collectors; @Service @Transactional public class DiplomaTopicService { - @Autowired - private DiplomaTopicRepository diplomaTopicRepository; - - public List getAll() { - return diplomaTopicRepository.findAll() - .stream() - .map(DiplomaTopicDTO::fromEntity) - .collect(Collectors.toList()); - } - - public DiplomaTopicDTO getById(Integer id) { - return DiplomaTopicDTO.fromEntity(diplomaTopicRepository.findById(id) - .orElseThrow(() -> new NotFoundException(DiplomaTopic.class, id))); - } } diff --git a/server/src/main/java/ru/tubryansk/tdms/service/LifeCycleService.java b/server/src/main/java/ru/tubryansk/tdms/service/LifeCycleService.java new file mode 100644 index 0000000..dc3e251 --- /dev/null +++ b/server/src/main/java/ru/tubryansk/tdms/service/LifeCycleService.java @@ -0,0 +1,17 @@ +package ru.tubryansk.tdms.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextStartedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class LifeCycleService { + @EventListener(ContextStartedEvent.class) + public void onStartup(ContextStartedEvent event) { + ApplicationContext applicationContext = event.getApplicationContext(); + log.info("Static files location: {}", applicationContext.getEnvironment().getProperty("spring.web.resources.static-locations")); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/tubryansk/tdms/service/StudentService.java b/server/src/main/java/ru/tubryansk/tdms/service/StudentService.java new file mode 100644 index 0000000..2b61f9a --- /dev/null +++ b/server/src/main/java/ru/tubryansk/tdms/service/StudentService.java @@ -0,0 +1,46 @@ +package ru.tubryansk.tdms.service; + +import jakarta.transaction.Transactional; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.tubryansk.tdms.entity.DiplomaTopic; +import ru.tubryansk.tdms.entity.Student; +import ru.tubryansk.tdms.exception.AccessDeniedException; +import ru.tubryansk.tdms.repository.DiplomaTopicRepository; +import ru.tubryansk.tdms.repository.StudentRepository; + +import java.util.Map; +import java.util.Optional; + +@Service +@Transactional +public class StudentService { + @Autowired + private StudentRepository studentRepository; + @Autowired + private DiplomaTopicRepository diplomaTopicRepository; + @Autowired + private Optional student; + @Autowired + private UserService userService; + + /** @param studentToDiplomaTopic Map of @{@link Student} id and @{@link DiplomaTopic} id */ + public void changeDiplomaTopic(Map studentToDiplomaTopic) { + studentToDiplomaTopic.forEach(this::changeDiplomaTopic); + } + + public void changeDiplomaTopic(Integer studentId, Integer diplomaTopicId) { + Student student = studentRepository.findByIdThrow(studentId); + DiplomaTopic diplomaTopic = diplomaTopicRepository.findByIdThrow(diplomaTopicId); + student.setDiplomaTopic(diplomaTopic); + } + + public void changeCallerDiplomaTopic(Integer diplomaTopicId) { + DiplomaTopic diplomaTopic = diplomaTopicRepository.findByIdThrow(diplomaTopicId); + student.ifPresentOrElse(s -> s.setDiplomaTopic(diplomaTopic), () -> {throw new AccessDeniedException();}); + } + + public Optional getCallerStudent() { + return studentRepository.findByUser(userService.getCallerUser().orElse(null)); + } +} diff --git a/server/src/main/java/ru/tubryansk/tdms/service/UserService.java b/server/src/main/java/ru/tubryansk/tdms/service/UserService.java index b782eb2..1c9f934 100644 --- a/server/src/main/java/ru/tubryansk/tdms/service/UserService.java +++ b/server/src/main/java/ru/tubryansk/tdms/service/UserService.java @@ -1,11 +1,8 @@ package ru.tubryansk.tdms.service; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpSession; import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -13,7 +10,7 @@ import org.springframework.stereotype.Service; import ru.tubryansk.tdms.entity.User; import ru.tubryansk.tdms.repository.UserRepository; -import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY; +import java.util.Optional; @Service @Transactional @@ -22,20 +19,7 @@ public class UserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired - private HttpServletRequest httpServletRequest; - - public User getCallerPrincipal() { - if(!authenticated()) { - return null; - } - - return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); - } - - public boolean authenticated() { - var authentication = SecurityContextHolder.getContext().getAuthentication(); - return authentication.isAuthenticated() && !(authentication instanceof AnonymousAuthenticationToken); - } + private AuthenticationService authenticationService; @Override public User loadUserByUsername(String username) throws UsernameNotFoundException { @@ -43,11 +27,10 @@ public class UserService implements UserDetailsService { .orElseThrow(() -> new UsernameNotFoundException("User not found")); } - public void logout() { - HttpSession session = httpServletRequest.getSession(true); - // if(session != null) { - // session.invalidate(); - // } - session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, null); + public Optional getCallerUser() { + if(authenticationService.authenticated()) { + return Optional.of((User) SecurityContextHolder.getContext().getAuthentication().getPrincipal()); + } + return Optional.empty(); } } diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml index f74a198..2666244 100644 --- a/server/src/main/resources/application.yml +++ b/server/src/main/resources/application.yml @@ -7,13 +7,15 @@ application: name: @name@ version: @version@ type: production - port: 80 + port: 443 domain: tdms.tu-bryansk.ru protocol: https spring: application: - name: tdms + name: ${application.name} + main: + allow-circular-references: true datasource: url: ${db.url} username: ${db.user} diff --git a/web/package-lock.json b/web/package-lock.json index 10245d2..e1a0ae9 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,6 +8,11 @@ "name": "web", "version": "1.0.0", "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", "axios": "^1.7.7", "bootstrap": "^5.3.3", "mobx": "^6.13.1", @@ -1733,6 +1738,70 @@ "node": ">=10.0.0" } }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", + "integrity": "sha512-xyX0X9mc0kyz9plIyryrRbl7ngsA9jz77mCZJsUkLl+ZKs0KWObgaEBoSgQiYWAsSmjz/yjl0F++Got0Mdp4Rw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.6.0.tgz", + "integrity": "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-brands-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.6.0.tgz", + "integrity": "sha512-1MPD8lMNW/earme4OQi1IFHtmHUwAKgghXlNwWi9GO7QkTfD+IIaYpIai4m2YJEzqfEji3jFHX1DZI5pbY/biQ==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-regular-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.6.0.tgz", + "integrity": "sha512-Yv9hDzL4aI73BEwSEh20clrY8q/uLxawaQ98lekBx6t9dQKDHcDzzV1p2YtBGTtolYtNqcWdniOnhzB+JPnQEQ==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.6.0.tgz", + "integrity": "sha512-IYv/2skhEDFc2WGUcqvFJkeK39Q+HyPf5GHUrT/l2pKbtgEIv1al1TKd6qStR5OIwQdN1GZP54ci3y4mroJWjA==", + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.6.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/react-fontawesome": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.2.tgz", + "integrity": "sha512-EnkrprPNqI6SXJl//m29hpaNzOp1bruISWaOiRtkMi/xSvHJlzc2j2JAYS7egxt/EbjSNV/k6Xy0AQI6vB2+1g==", + "dependencies": { + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "@fortawesome/fontawesome-svg-core": "~1 || ~6", + "react": ">=16.3" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", diff --git a/web/package.json b/web/package.json index db25241..1fb0428 100644 --- a/web/package.json +++ b/web/package.json @@ -3,10 +3,15 @@ "version": "1.0.0", "private": true, "scripts": { - "build" : "webpack --mode production", + "build": "webpack --mode production", "dev": "webpack-dev-server --mode development" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.6.0", + "@fortawesome/free-brands-svg-icons": "^6.6.0", + "@fortawesome/free-regular-svg-icons": "^6.6.0", + "@fortawesome/free-solid-svg-icons": "^6.6.0", + "@fortawesome/react-fontawesome": "^0.2.2", "axios": "^1.7.7", "bootstrap": "^5.3.3", "mobx": "^6.13.1", diff --git a/web/src/Application.tsx b/web/src/Application.tsx index 5b3bf91..b827e65 100644 --- a/web/src/Application.tsx +++ b/web/src/Application.tsx @@ -1,20 +1,17 @@ -import React from "react"; import ReactDOM from 'react-dom/client' import './index.css' import 'bootstrap/dist/css/bootstrap.min.css'; import {RouterContext, RouterView} from "mobx-state-router"; -import {initApp} from "./utils/init.ts"; -import {MyRouterStore} from "./store/MyRouterStore.ts"; -import { RootStoreContext } from "./store/RootStore.tsx"; +import {initApp} from "./utils/init"; +import {RootStoreContext} from './context/RootStoreContext'; +import {viewMap} from "./router/viewMap"; const rootStore = initApp(); ReactDOM.createRoot(document.getElementById('root')!).render( - - - - - - - + + + + + ); diff --git a/web/src/components/Page/DefaultPage.tsx b/web/src/components/Page/DefaultPage.tsx deleted file mode 100644 index 1188054..0000000 --- a/web/src/components/Page/DefaultPage.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import {Component, ReactNode} from "react"; -import Header from "./Header.tsx"; -import {Container} from "react-bootstrap"; -import Footer from "./Footer.tsx"; - -export abstract class DefaultPage extends Component { - abstract get page(): ReactNode; - // declare context: ContextType - - render() { - return <> -
- - {this.page} - -