diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index ae0a6c9..f105250 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -21,6 +21,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-mail") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-data-jpa") + implementation("org.springframework.boot:spring-boot-starter-security") implementation("com.kohlschutter.junixsocket:junixsocket-core:2.9.0") // implementation("org.springframework.session:spring-session-jdbc") developmentOnly("org.springframework.boot:spring-boot-devtools") diff --git a/backend/src/main/java/ovh/herisson/Clyde/ClydeApplication.java b/backend/src/main/java/ovh/herisson/Clyde/ClydeApplication.java index aa2882d..364eba7 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/ClydeApplication.java +++ b/backend/src/main/java/ovh/herisson/Clyde/ClydeApplication.java @@ -2,8 +2,9 @@ package ovh.herisson.Clyde; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; -@SpringBootApplication +@SpringBootApplication(exclude = { SecurityAutoConfiguration.class }) public class ClydeApplication { public static void main(String[] args) { diff --git a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/LoginController.java b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/LoginController.java new file mode 100644 index 0000000..d47885f --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/LoginController.java @@ -0,0 +1,31 @@ +package ovh.herisson.Clyde.EndPoints; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ovh.herisson.Clyde.Responses.UnauthorizedResponse; +import ovh.herisson.Clyde.Services.AuthenticatorService; + +import java.util.Date; + +@RestController +@CrossOrigin(origins = "http://localhost:5173") +public class LoginController { + private final AuthenticatorService authServ; + public LoginController(AuthenticatorService authServ){ + this.authServ = authServ; + } + @PostMapping("/login") + public ResponseEntity login(@RequestParam String identifier, String password, Date expirationDate){ + + String sessionToken = authServ.login(identifier,password,expirationDate); + if (sessionToken == null){ + return new UnauthorizedResponse<>("Identifier or Password incorrect"); + } + + HttpHeaders responseHeaders = new HttpHeaders(); + responseHeaders.set("Set-Cookie",String.format("session_token=%s",sessionToken)); + return ResponseEntity.ok().headers(responseHeaders).build(); + } +} + + diff --git a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/MockController.java b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/MockController.java new file mode 100644 index 0000000..f7825f6 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/MockController.java @@ -0,0 +1,65 @@ +package ovh.herisson.Clyde.EndPoints; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import ovh.herisson.Clyde.Repositories.TokenRepository; +import ovh.herisson.Clyde.Repositories.UserRepository; +import ovh.herisson.Clyde.Tables.Role; +import ovh.herisson.Clyde.Tables.Token; +import ovh.herisson.Clyde.Tables.User; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; + +@RestController +@CrossOrigin(origins = "http://localhost:5173") + +public class MockController { + private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + public final UserRepository userRepo; + public final TokenRepository tokenRepo; + + ArrayList mockUsers; + + + public MockController(UserRepository userRepo, TokenRepository tokenRepo){ + this.tokenRepo = tokenRepo; + this.userRepo = userRepo; + } + + /** Saves an example of each user type by : + * email : FooRole@FooRole.com, password : FooRole and token : FooRole + * For example the admin as "admin@admin.com" as email and "admin" as both password and token + * They all have silly names + */ + + @PostMapping("/mock") + public void postMock(){ + + User herobrine = new User("brine","hero","admin@admin.com","in your WalLs","ShadowsLand",new Date(0), "none",Role.Admin,passwordEncoder.encode("admin")); + User joe = new User("Mama","Joe","student@student.com","roundabout","DaWarudo",new Date(0), "None",Role.Student,passwordEncoder.encode("student")); + User meh = new User("Inspiration","lackOf","secretary@secretary.com","a Box","the street",new Date(0),"none", Role.Teacher,passwordEncoder.encode("secretary")); + User joke = new User("CthemBalls","Lemme","teacher@teacher.com","lab","faculty",new Date(0), "none",Role.Teacher,passwordEncoder.encode("teacher")); + + mockUsers = new ArrayList(Arrays.asList(herobrine,joe,meh,joke)); + + userRepo.saveAll(mockUsers); + + for (User user: mockUsers){ + tokenRepo.save(new Token(user,user.getPassword())); + } + } + + @DeleteMapping("/mock") + public void deleteMock(){ + for (User user:mockUsers){ + tokenRepo.deleteAll(tokenRepo.getByUser(user)); + } + userRepo.deleteAll(mockUsers); + } +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/UserController.java b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/UserController.java index affb301..ba54926 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/EndPoints/UserController.java +++ b/backend/src/main/java/ovh/herisson/Clyde/EndPoints/UserController.java @@ -2,10 +2,12 @@ package ovh.herisson.Clyde.EndPoints; import org.springframework.http.HttpStatus; -import org.springframework.http.HttpStatusCode; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import ovh.herisson.Clyde.Repositories.UserRepository; +import ovh.herisson.Clyde.Responses.UnauthorizedResponse; +import ovh.herisson.Clyde.Services.AuthenticatorService; +import ovh.herisson.Clyde.Services.UserService; import ovh.herisson.Clyde.Tables.User; @@ -13,30 +15,31 @@ import ovh.herisson.Clyde.Tables.User; @CrossOrigin(origins = "http://localhost:5173") public class UserController { - private final UserRepository userRepo; - - public UserController(UserRepository userRepo){ - this.userRepo = userRepo; + private final UserService userService; + private final AuthenticatorService authServ; + public UserController(UserService userService, AuthenticatorService authServ){ + this.userService = userService; + this.authServ = authServ; } @GetMapping("/user") - public ResponseEntity getUsers(@RequestHeader("Authorization") String token){ - //TODO - // Get the token thru the data base - // tokenRepo.findToken(token) => User userFromToken - // si role != secretary => return error : ResponseEntity(null, HttpStatus.UNAUTHORIZED) - return new ResponseEntity(/**userRepo.findById(userFromToken.id),**/ HttpStatus.OK); + public ResponseEntity getUser(@RequestHeader("Authorization") String token){ + User user = authServ.getUserFromToken(token); + if (user == null) { + return new UnauthorizedResponse<>(null); + } + return new ResponseEntity<>(user, HttpStatus.OK); } @PostMapping("/user") public ResponseEntity postUser(@RequestBody User user){ - userRepo.save(user); + userService.save(user); return new ResponseEntity(String.format("Account created with ID:%s",user.getRegNo()),HttpStatus.CREATED); } @GetMapping("/users") - public Iterable getAllUsers(){//TODO ne l'accepter que si c'est le secrétariat - return userRepo.findAll(); + public Iterable getAllUsers(){ + return userService.getAll(); } diff --git a/backend/src/main/java/ovh/herisson/Clyde/JdbcConfig.java b/backend/src/main/java/ovh/herisson/Clyde/JdbcConfig.java index 2fb978c..b6525dc 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/JdbcConfig.java +++ b/backend/src/main/java/ovh/herisson/Clyde/JdbcConfig.java @@ -6,8 +6,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class JdbcConfig { diff --git a/backend/src/main/java/ovh/herisson/Clyde/Repositories/TokenRepository.java b/backend/src/main/java/ovh/herisson/Clyde/Repositories/TokenRepository.java new file mode 100644 index 0000000..d375e7a --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Repositories/TokenRepository.java @@ -0,0 +1,12 @@ +package ovh.herisson.Clyde.Repositories; + +import org.springframework.data.repository.CrudRepository; +import ovh.herisson.Clyde.Tables.Token; +import ovh.herisson.Clyde.Tables.User; + +public interface TokenRepository extends CrudRepository { + + Token getByToken(String token); + + Iterable getByUser(User user); +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Repositories/UserRepository.java b/backend/src/main/java/ovh/herisson/Clyde/Repositories/UserRepository.java index 38f9ae0..af2bd08 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Repositories/UserRepository.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Repositories/UserRepository.java @@ -10,6 +10,8 @@ public interface UserRepository extends CrudRepository { User findById(long id); + User findByEmail(String email); + /** @Query(value = "select a.* from Users a ",nativeQuery = true) Iterable findAllUsers();**/ diff --git a/backend/src/main/java/ovh/herisson/Clyde/Responses/UnauthorizedResponse.java b/backend/src/main/java/ovh/herisson/Clyde/Responses/UnauthorizedResponse.java new file mode 100644 index 0000000..305dd4d --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Responses/UnauthorizedResponse.java @@ -0,0 +1,12 @@ +package ovh.herisson.Clyde.Responses; + + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; + + +public class UnauthorizedResponse extends ResponseEntity { + public UnauthorizedResponse(T message) { + super(message,HttpStatus.UNAUTHORIZED); + } +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/AuthenticatorService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/AuthenticatorService.java new file mode 100644 index 0000000..f3ae072 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/AuthenticatorService.java @@ -0,0 +1,32 @@ +package ovh.herisson.Clyde.Services; + +import org.springframework.stereotype.Service; +import ovh.herisson.Clyde.Tables.User; + +import java.util.Date; + +@Service +public class AuthenticatorService { + + private final TokenService tokenService; + private final UserService userService; + + public AuthenticatorService(TokenService tokenService, UserService userService){ + this.tokenService = tokenService; + this.userService = userService; + } + + public User getUserFromToken(String token){ + return tokenService.getUserFromToken(token); + } + + + public String login(String identifier, String password, Date expirationDate){ + User user = userService.getUser(identifier); + if (user == null){return null;} + if (!userService.checkPassword(user,password)){return null;} + String token = tokenService.generateNewToken(); + tokenService.saveToken(token,user,expirationDate); + return token; + } +} diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/TokenService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/TokenService.java new file mode 100644 index 0000000..e619fd8 --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/TokenService.java @@ -0,0 +1,37 @@ +package ovh.herisson.Clyde.Services; + +import org.springframework.stereotype.Service; +import ovh.herisson.Clyde.Repositories.TokenRepository; +import ovh.herisson.Clyde.Tables.Token; +import ovh.herisson.Clyde.Tables.User; + +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Date; + +@Service +public class TokenService { + + TokenRepository tokenRepo; + + public TokenService(TokenRepository tokenRepo){ + this.tokenRepo = tokenRepo; + } + + + public String generateNewToken(){ + byte[] bytes = new byte[64]; + new SecureRandom().nextBytes(bytes); + String token = new String(bytes, StandardCharsets.US_ASCII); + System.out.println(token); + return token; + } + + public User getUserFromToken(String token){ + return tokenRepo.getByToken(token).getUser(); + } + + public void saveToken(String token, User user, Date expirationDate){// todo faire qlq chose de l'expDate + tokenRepo.save(new Token(user,token)); + } +} \ No newline at end of file diff --git a/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java b/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java new file mode 100644 index 0000000..f16c68f --- /dev/null +++ b/backend/src/main/java/ovh/herisson/Clyde/Services/UserService.java @@ -0,0 +1,50 @@ +package ovh.herisson.Clyde.Services; + +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.stereotype.Service; +import ovh.herisson.Clyde.Repositories.UserRepository; +import ovh.herisson.Clyde.Tables.Role; +import ovh.herisson.Clyde.Tables.User; + +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +@Service +public class UserService { + + private final UserRepository userRepo; + private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + public UserService(UserRepository userRepo){ + this.userRepo = userRepo; + } + + + public User getUser(String identifier){ + if (identifier == null) return null; + try { + int id = Integer.parseInt(identifier); + return userRepo.findById(id); + } + catch (NumberFormatException nfe){ + return userRepo.findByEmail(identifier); + } + } + + + public boolean checkPassword(User user, String tryingPassword){ + return passwordEncoder.matches(tryingPassword, user.getPassword()); + } + + public void save(User user){ + userRepo.save(user); + } + + public Iterable getAll(){ + return userRepo.findAll(); + } + +} \ No newline at end of file diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/CursusCourse.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/CursusCourse.java index e48e6a3..ecdd857 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/CursusCourse.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/CursusCourse.java @@ -8,15 +8,17 @@ public class CursusCourse { @GeneratedValue(strategy = GenerationType.AUTO) private int id; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Cursus") - private int cursusId; + private Cursus cursus; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Course") - private int courseId; + private Course course; - public CursusCourse(int cursusId, int courseId){ - this.cursusId = cursusId; - this.courseId = courseId; + public CursusCourse(Cursus cursus, Course course){ + this.cursus = cursus; + this.course = course; } public CursusCourse() {} @@ -25,19 +27,19 @@ public class CursusCourse { return id; } - public int getCourseId() { - return courseId; + public Course getCourse() { + return course; } - public void setCourseId(int courseId){ - this.courseId = courseId; + public void setCourse(Course course){ + this.course = course; } - public int getCursusId() { - return cursusId; + public Cursus getCursus() { + return cursus; } - public void setCursusId(int cursusId) { - this.cursusId = cursusId; + public void setCursus(Cursus cursus) { + this.cursus = cursus; } } diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Secretary.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Secretary.java index cf533a0..cc34841 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/Secretary.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Secretary.java @@ -8,12 +8,13 @@ public class Secretary { @GeneratedValue(strategy = GenerationType.AUTO) private int id; + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Users") - private int regNo; + private User user; private String faculty; - public Secretary(int regNo, String faculty){ - this.regNo = regNo; + public Secretary(User user, String faculty){ + this.user = user; this.faculty = faculty; } @@ -23,12 +24,12 @@ public class Secretary { return id; } - public int getRegNo() { - return regNo; + public User getUser() { + return user; } - public void setRegNo(int regNo) { - this.regNo = regNo; + public void setUser(User user) { + this.user = user; } public String getFaculty() { diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/TeacherGivenCourse.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/TeacherGivenCourse.java index 9cb931e..a1ee138 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/TeacherGivenCourse.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/TeacherGivenCourse.java @@ -8,18 +8,21 @@ public class TeacherGivenCourse { @GeneratedValue(strategy = GenerationType.AUTO) private int id; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Users") - private int regNo; + private User user; + + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Course") - private int courseId; + private Course course; //This flag helps make the difference between an assistant or a Teacher (who owns the course) private boolean owned; - public TeacherGivenCourse(int regNo, int courseId, boolean owned){ - this.regNo = regNo; - this.courseId = courseId; + public TeacherGivenCourse(User user, Course course, boolean owned){ + this.user = user; + this.course = course; this.owned = owned; } @@ -29,20 +32,20 @@ public class TeacherGivenCourse { return id; } - public int getRegNo() { - return regNo; + public User getUser() { + return user; } - public void setRegNo(int regNo) { - this.regNo = regNo; + public void setUser(User user) { + this.user = user; } - public int getCourseId() { - return courseId; + public Course getCourse() { + return course; } - public void setCourseId(int courseId) { - this.courseId = courseId; + public void setCourse(Course course) { + this.course = course; } public boolean isOwned() { diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/Token.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/Token.java index ec59cbf..5b61cb9 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/Token.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/Token.java @@ -8,12 +8,13 @@ public class Token { @Id private int id; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name ="Users") - private int regNo; + private User user; private String token; - public Token(int regNo, String token){ - this.regNo = regNo; + public Token(User user, String token){ + this.user = user; this.token = token; } @@ -21,13 +22,12 @@ public class Token { public int getId() { return id; } - - public int getRegNo() { - return regNo; + public User getUser() { + return user; } - public void setRegNo(int regNo) { - this.regNo = regNo; + public void setUser(User regNo) { + this.user = regNo; } public String getToken(){ diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java index 746ac3b..95467a4 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/User.java @@ -20,15 +20,17 @@ public class User { private String address; private String country; private Date birthDate; + private String profilePictureUrl; private ovh.herisson.Clyde.Tables.Role role; private String password; - public User(String lastName, String firstName, String email, String address, String country, Date birthDate, Role role, String password){ + public User(String lastName, String firstName, String email, String address, String country, Date birthDate, String profilePictureUrl, Role role, String password){ this.lastName = lastName; this.firstName = firstName; this.email = email; this.address = address; this.country = country; this.birthDate = birthDate; + this.profilePictureUrl = profilePictureUrl; this.role = role; this.password = password; } @@ -86,6 +88,11 @@ public class User { this.birthDate = birthDate; } + public String getProfilePictureUrl(){return this.profilePictureUrl;} + + public void setProfilePictureUrl(String profilePictureUrl){ + this.profilePictureUrl = profilePictureUrl; + } public ovh.herisson.Clyde.Tables.Role getRole() { return role; } diff --git a/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCursus.java b/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCursus.java index 4de1559..a5c5153 100644 --- a/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCursus.java +++ b/backend/src/main/java/ovh/herisson/Clyde/Tables/UserCursus.java @@ -7,15 +7,19 @@ public class UserCursus { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; + + //Un étudiant peut avoir plusieurs cursus + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Users") - private int regNo; + private User user; + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(name = "Cursus") - private int cursusId; + private Cursus cursus; - public UserCursus(int regNo, int cursusId){ - this.regNo = regNo; - this.cursusId = cursusId; + public UserCursus(User user, Cursus cursus){ + this.user = user; + this.cursus = cursus; } public UserCursus() {} @@ -24,19 +28,19 @@ public class UserCursus { return id; } - public int getRegNo() { - return regNo; + public User getUser() { + return user; } - public void setRegNo(int regNo) { - this.regNo = regNo; + public void setUser(User user) { + this.user = user; } - public int getCursusId() { - return cursusId; + public Cursus getCursus() { + return cursus; } - public void setCursusId(int cursusId) { - this.cursusId = cursusId; + public void setCursus(Cursus cursus) { + this.cursus = cursus; } }