5 Commits a28da91827 ... bb43aecb1e

Auteur SHA1 Message Date
  Eldar Mukhtarov bb43aecb1e Setup Security Configuration & Auth Message il y a 10 mois
  Eldar Mukhtarov 44caf98610 Fix Relational Bigs il y a 10 mois
  Eldar Mukhtarov 19f6d7085f Role Setup il y a 10 mois
  Eldar Mukhtarov d52ae0d60c Grade CRUD and Relationships Setup il y a 10 mois
  Eldar Mukhtarov b209e4391b Setup Subject CRUD and Teacher-Subject Relation il y a 10 mois
27 fichiers modifiés avec 907 ajouts et 52 suppressions
  1. 5 5
      project/backend_springboot/pom.xml
  2. 77 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/GradeController.java
  3. 11 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/StudentController.java
  4. 80 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/SubjectController.java
  5. 10 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/TeacherController.java
  6. 31 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/request/LoginForm.java
  7. 62 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/request/SignUpForm.java
  8. 47 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/response/JwtResponse.java
  9. 18 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/response/ResponseMessage.java
  10. 11 9
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Account.java
  11. 55 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Grade.java
  12. 38 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Role.java
  13. 7 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/RoleName.java
  14. 17 16
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Student.java
  15. 39 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Subject.java
  16. 17 16
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Teacher.java
  17. 14 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/GradeRepository.java
  18. 13 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/RoleRepository.java
  19. 1 1
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/StudentRepository.java
  20. 11 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/SubjectRepository.java
  21. 80 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/WebSecurityConfig.java
  22. 21 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/jwt/JwtAuthEntryPoint.java
  23. 54 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/jwt/JwtAuthTokenFilter.java
  24. 65 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/jwt/JwtProvider.java
  25. 26 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/services/UserDetailsServiceImpl.java
  26. 92 0
      project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/services/UserPrinciple.java
  27. 5 5
      project/backend_springboot/src/main/resources/application.properties

+ 5 - 5
project/backend_springboot/pom.xml

@@ -67,11 +67,11 @@
 			<artifactId>jackson-dataformat-xml</artifactId>
 			<version>2.18.3</version>
 		</dependency>
-		<!-- spring security ____________________________________________ UNCOMMENT LATER ON -->
-<!--		<dependency>-->
-<!--			<groupId>org.springframework.boot</groupId>-->
-<!--			<artifactId>spring-boot-starter-security</artifactId>-->
-<!--		</dependency>-->
+		<!-- spring security -->
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-security</artifactId>
+		</dependency>
 		<!-- JWT -->
 		<dependency>
 			<groupId>io.jsonwebtoken</groupId>

+ 77 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/GradeController.java

@@ -0,0 +1,77 @@
+package pl.dmcs.eldarmuk.backend_springboot.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmcs.eldarmuk.backend_springboot.model.Account;
+import pl.dmcs.eldarmuk.backend_springboot.model.Grade;
+import pl.dmcs.eldarmuk.backend_springboot.model.Student;
+import pl.dmcs.eldarmuk.backend_springboot.repository.AccountRepository;
+import pl.dmcs.eldarmuk.backend_springboot.repository.GradeRepository;
+import pl.dmcs.eldarmuk.backend_springboot.repository.StudentRepository;
+
+import java.util.List;
+
+@RestController
+@CrossOrigin(origins = "http://localhost:4200")
+@RequestMapping("/grades")
+public class GradeController {
+    private GradeRepository gradeRepository;
+    private AccountRepository accountRepository;
+    private StudentRepository studentRepository;
+
+    @Autowired
+    public GradeController(GradeRepository gradeRepository, AccountRepository accountRepository, StudentRepository studentRepository) {
+        this.gradeRepository = gradeRepository;
+        this.accountRepository = accountRepository;
+        this.studentRepository = studentRepository;
+    }
+
+    @GetMapping(value = "/subject/{id}")
+    public List<Grade> findGradesBySubject(@PathVariable("id") long id){
+        return gradeRepository.findGradesBySubjectId(id);
+    }
+
+    @GetMapping(value = "/student/{username}")
+    public ResponseEntity<List<Grade>> findGradesByStudent(@PathVariable("username") String username){
+        try {
+            Account account = accountRepository.findByUsername(username)
+                    .orElseThrow(() -> new RuntimeException("Error: User not found!"));
+            Student student = studentRepository.findByAccountUsername(account.getUsername());
+            List<Grade> grades = gradeRepository.findGradesByStudentId(student.getId());
+            return new ResponseEntity<>(grades, HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+        }
+    }
+
+    @PostMapping
+    public ResponseEntity<Grade> addGrade(@RequestBody Grade grade){
+        try {
+            gradeRepository.save(grade);
+            return new ResponseEntity<>(grade, HttpStatus.CREATED);
+        } catch (Exception e) {
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    @PatchMapping(value = "/edit/{id}")
+    public ResponseEntity<Grade> editGrade(@PathVariable("id") long id, @RequestBody java.util.Map<String, Integer> body){
+        try {
+            Grade currentGrade = gradeRepository.findById(id);
+            if (currentGrade == null){
+                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+            }
+            Integer grade = body.get("grade");
+            if (grade == null) {
+                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+            }
+            currentGrade.setGrade(grade);
+            gradeRepository.save(currentGrade);
+            return new ResponseEntity<>(currentGrade, HttpStatus.OK);
+        } catch (Exception e) {
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+}

+ 11 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/StudentController.java

@@ -1,6 +1,7 @@
 package pl.dmcs.eldarmuk.backend_springboot.controller;
 
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
 
@@ -25,6 +26,16 @@ public class StudentController {
         return studentRepository.findAll();
     }
 
+    @PostMapping
+    public ResponseEntity<Student> createStudent(@RequestBody Student student) {
+        try {
+            Student savedStudent = studentRepository.save(student);
+            return new ResponseEntity<>(savedStudent, HttpStatus.CREATED);
+        } catch (Exception e) {
+            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
     @DeleteMapping("/{id}")
     public ResponseEntity<?> deleteStudent(@PathVariable("id") long id) {
         try {

+ 80 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/SubjectController.java

@@ -0,0 +1,80 @@
+package pl.dmcs.eldarmuk.backend_springboot.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import pl.dmcs.eldarmuk.backend_springboot.model.Subject;
+import pl.dmcs.eldarmuk.backend_springboot.model.Teacher;
+import pl.dmcs.eldarmuk.backend_springboot.repository.AccountRepository;
+import pl.dmcs.eldarmuk.backend_springboot.repository.SubjectRepository;
+import pl.dmcs.eldarmuk.backend_springboot.repository.TeacherRepository;
+
+import java.util.List;
+
+@RestController
+@CrossOrigin(origins = "http://localhost:4200")
+@RequestMapping("/subjects")
+public class SubjectController {
+    private SubjectRepository subjectRepository;
+    private AccountRepository accountRepository;
+    private TeacherRepository teacherRepository;
+
+    @Autowired
+    public SubjectController(SubjectRepository subjectRepository, AccountRepository accountRepository, TeacherRepository teacherRepository) {
+        this.subjectRepository = subjectRepository;
+        this.accountRepository = accountRepository;
+        this.teacherRepository = teacherRepository;
+    }
+
+    @GetMapping
+    public List<Subject> findAllSubjects() {
+        return subjectRepository.findAll();
+    }
+
+    @GetMapping(value = "/{id}")
+    public ResponseEntity<Subject> findSubject(@PathVariable("id") long id){
+        try {
+            Subject subject = subjectRepository.findById(id);
+            if (subject == null){
+                System.out.println("Subject with id " + id + " not found");
+                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+            }
+            return new ResponseEntity<>(subject, HttpStatus.OK);
+        } catch (Exception e) {
+            System.out.println("Error finding subject: " + e.getMessage());
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    @PostMapping
+    public ResponseEntity<?> addSubject(@RequestBody Subject subject){
+        try {
+            if(subjectRepository.existsByName(subject.getName())){
+                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+            }
+            subjectRepository.save(subject);
+            return new ResponseEntity<>(subject, HttpStatus.CREATED);
+        } catch (Exception e) {
+            System.out.println("Error adding subject: " + e.getMessage());
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+    @DeleteMapping(value = "/{id}")
+    public ResponseEntity<Subject> deleteSubject(@PathVariable("id") long id){
+        try {
+            Subject subject = subjectRepository.findById(id);
+            if (subject == null){
+                System.out.println("Subject with id " + id + " not found");
+                return new ResponseEntity<>(HttpStatus.NOT_FOUND);
+            }
+            subjectRepository.delete(subject);
+            return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+        } catch (Exception e) {
+            System.out.println("Error deleting subject: " + e.getMessage());
+            return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
+
+}

+ 10 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/controller/TeacherController.java

@@ -37,6 +37,16 @@ public class TeacherController {
             return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
         }
     }
+    
+    @PostMapping
+    public ResponseEntity<Teacher> createTeacher(@RequestBody Teacher teacher) {
+        try {
+            Teacher savedTeacher = teacherRepository.save(teacher);
+            return new ResponseEntity<>(savedTeacher, HttpStatus.CREATED);
+        } catch (Exception e) {
+            return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
+        }
+    }
 
     @DeleteMapping("/{id}")
     public ResponseEntity<?> deleteTeacher(@PathVariable("id") long id) {

+ 31 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/request/LoginForm.java

@@ -0,0 +1,31 @@
+package pl.dmcs.eldarmuk.backend_springboot.message.request;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+public class LoginForm {
+
+    @NotBlank
+    @Size(min=3, max = 60)
+    private String username;
+
+    @NotBlank
+    @Size(min = 6, max = 40)
+    private String password;
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+}

+ 62 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/request/SignUpForm.java

@@ -0,0 +1,62 @@
+package pl.dmcs.eldarmuk.backend_springboot.message.request;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+import java.util.Set;
+
+public class SignUpForm {
+
+    @NotBlank
+    @Size(min = 3, max = 50)
+    private String username;
+
+    private Set<String> role;
+
+    @NotBlank
+    @Size(min = 6, max = 40)
+    private String password;
+
+    private String firstname;
+    private String lastname;
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public Set<String> getRole() {
+        return role;
+    }
+
+    public void setRole(Set<String> role) {
+        this.role = role;
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    public String getFirstname() {
+        return firstname;
+    }
+
+    public void setFirstname(String firstname) {
+        this.firstname = firstname;
+    }
+
+    public String getLastname() {
+        return lastname;
+    }
+
+    public void setLastname(String lastname) {
+        this.lastname = lastname;
+    }
+}

+ 47 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/response/JwtResponse.java

@@ -0,0 +1,47 @@
+package pl.dmcs.eldarmuk.backend_springboot.message.response;
+
+import org.springframework.security.core.GrantedAuthority;
+
+import java.util.Collection;
+
+public class JwtResponse {
+
+    private String token;
+    private String type = "Bearer";
+    private String username;
+    private Collection<? extends GrantedAuthority> authorities;
+
+    public JwtResponse(String token, String username, Collection<? extends GrantedAuthority> authorities) {
+        this.token = token;
+        this.username = username;
+        this.authorities = authorities;
+    }
+
+    public String getAccessToken() {
+        return token;
+    }
+
+    public void setAccessToken(String accessToken) {
+        this.token = accessToken;
+    }
+
+    public String getTokenType() {
+        return type;
+    }
+
+    public void setTokenType(String tokenType) {
+        this.type = tokenType;
+    }
+
+    public String getUsername() {
+        return username;
+    }
+
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return authorities;
+    }
+}

+ 18 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/message/response/ResponseMessage.java

@@ -0,0 +1,18 @@
+package pl.dmcs.eldarmuk.backend_springboot.message.response;
+
+public class ResponseMessage {
+
+    private String message;
+
+    public ResponseMessage(String message) {
+        this.message = message;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}

+ 11 - 9
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Account.java

@@ -1,13 +1,13 @@
 package pl.dmcs.eldarmuk.backend_springboot.model;
 
+import jakarta.persistence.*;
 import org.hibernate.validator.constraints.Length;
 
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
 import jakarta.validation.constraints.NotBlank;
 
+import java.util.HashSet;
+import java.util.Set;
+
 @Entity
 public class Account {
     @Id
@@ -21,7 +21,9 @@ public class Account {
     @NotBlank
     @Length(min = 6, max = 100)
     private String password;
-    private String email;
+
+    @ManyToMany(fetch = FetchType.EAGER)
+    private Set<Role> roles = new HashSet<>();
 
     public Account() {}
 
@@ -54,11 +56,11 @@ public class Account {
         this.password = password;
     }
 
-    public String getEmail() {
-        return email;
+    public Set<Role> getRoles() {
+        return roles;
     }
 
-    public void setEmail(String email) {
-        this.email = email;
+    public void setRoles(Set<Role> roles) {
+        this.roles = roles;
     }
 }

+ 55 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Grade.java

@@ -0,0 +1,55 @@
+package pl.dmcs.eldarmuk.backend_springboot.model;
+
+import jakarta.persistence.*;
+
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+
+@Entity
+public class Grade {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    @Min(2)
+    @Max(5)
+    private int grade;
+
+    @ManyToOne
+    private Student student;
+
+    @ManyToOne
+    private Subject subject;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public int getGrade() {
+        return grade;
+    }
+
+    public void setGrade(int grade) {
+        this.grade = grade;
+    }
+
+    public Student getStudent() {
+        return student;
+    }
+
+    public void setStudent(Student student) {
+        this.student = student;
+    }
+
+    public Subject getSubject() {
+        return subject;
+    }
+
+    public void setSubject(Subject subject) {
+        this.subject = subject;
+    }
+}

+ 38 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Role.java

@@ -0,0 +1,38 @@
+package pl.dmcs.eldarmuk.backend_springboot.model;
+
+import jakarta.persistence.*;
+import org.hibernate.annotations.NaturalId;
+
+@Entity
+public class Role {
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    @Enumerated(EnumType.STRING)
+    @NaturalId
+    private RoleName name;
+
+    public Role() {}
+
+    public Role(RoleName name) {
+        this.name = name;
+    }
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public RoleName getName() {
+        return name;
+    }
+
+    public void setName(RoleName name) {
+        this.name = name;
+    }
+}

+ 7 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/RoleName.java

@@ -0,0 +1,7 @@
+package pl.dmcs.eldarmuk.backend_springboot.model;
+
+public enum RoleName {
+    ROLE_STUDENT,
+    ROLE_TEACHER,
+    ROLE_ADMIN
+}

+ 17 - 16
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Student.java

@@ -1,11 +1,9 @@
 package pl.dmcs.eldarmuk.backend_springboot.model;
 
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.OneToOne;
-import jakarta.persistence.CascadeType;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+
+import java.util.List;
 
 @Entity
 public class Student {
@@ -14,18 +12,21 @@ public class Student {
     private Long id;
     private String firstName;
     private String lastName;
-    private String email;
 
     @OneToOne(cascade = CascadeType.ALL)
+    @JoinColumn(name = "account_id")
     private Account account;
 
+    @OneToMany(mappedBy = "student", cascade = CascadeType.ALL)
+    @JsonIgnore
+    private List<Grade> grades;
+
     public Student() {}
 
     public Student(Long id, String firstName, String lastName, String email) {
         this.id = id;
         this.firstName = firstName;
         this.lastName = lastName;
-        this.email = email;
     }
 
     public Long getId() {
@@ -52,14 +53,6 @@ public class Student {
         this.lastName = lastName;
     }
 
-    public String getEmail() {
-        return email;
-    }
-
-    public void setEmail(String email) {
-        this.email = email;
-    }
-
     public Account getAccount() {
         return account;
     }
@@ -67,4 +60,12 @@ public class Student {
     public void setAccount(Account account) {
         this.account = account;
     }
+
+    public List<Grade> getGrades() {
+        return grades;
+    }
+
+    public void setGrades(List<Grade> grades) {
+        this.grades = grades;
+    }
 }

+ 39 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Subject.java

@@ -0,0 +1,39 @@
+package pl.dmcs.eldarmuk.backend_springboot.model;
+
+import jakarta.persistence.*;
+
+@Entity
+public class Subject {
+    @Id
+    @GeneratedValue(strategy = GenerationType.IDENTITY)
+    private long id;
+
+    private String name;
+
+    @ManyToOne
+    private Teacher teacher;
+
+    public long getId() {
+        return id;
+    }
+
+    public void setId(long id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Teacher getTeacher() {
+        return teacher;
+    }
+
+    public void setTeacher(Teacher teacher) {
+        this.teacher = teacher;
+    }
+}

+ 17 - 16
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/model/Teacher.java

@@ -1,11 +1,9 @@
 package pl.dmcs.eldarmuk.backend_springboot.model;
 
-import jakarta.persistence.CascadeType;
-import jakarta.persistence.Entity;
-import jakarta.persistence.GeneratedValue;
-import jakarta.persistence.GenerationType;
-import jakarta.persistence.Id;
-import jakarta.persistence.OneToOne;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+
+import java.util.List;
 
 @Entity
 public class Teacher {
@@ -14,18 +12,21 @@ public class Teacher {
     private Long id;
     private String firstName;
     private String lastName;
-    private String email;
 
     @OneToOne(cascade = CascadeType.ALL)
+    @JoinColumn(name = "account_id")
     private Account account;
 
+    @OneToMany(mappedBy="teacher", cascade = CascadeType.ALL)
+    @JsonIgnore
+    private List<Subject> subjects;
+
     public Teacher() {}
 
     public Teacher(Long id, String firstName, String lastName, String email) {
         this.id = id;
         this.firstName = firstName;
         this.lastName = lastName;
-        this.email = email;
     }
 
     public Long getId() {
@@ -52,14 +53,6 @@ public class Teacher {
         this.lastName = lastName;
     }
 
-    public String getEmail() {
-        return email;
-    }
-
-    public void setEmail(String email) {
-        this.email = email;
-    }
-
     public Account getAccount() {
         return account;
     }
@@ -67,4 +60,12 @@ public class Teacher {
     public void setAccount(Account account) {
         this.account = account;
     }
+
+    public List<Subject> getSubjects() {
+        return subjects;
+    }
+
+    public void setSubjects(List<Subject> subjects) {
+        this.subjects = subjects;
+    }
 }

+ 14 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/GradeRepository.java

@@ -0,0 +1,14 @@
+package pl.dmcs.eldarmuk.backend_springboot.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import pl.dmcs.eldarmuk.backend_springboot.model.Grade;
+
+import java.util.List;
+
+@Repository
+public interface GradeRepository extends JpaRepository<Grade, Long> {
+    Grade findById(long id);
+    List<Grade> findGradesByStudentId(long id);
+    List<Grade> findGradesBySubjectId(long id);
+}

+ 13 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/RoleRepository.java

@@ -0,0 +1,13 @@
+package pl.dmcs.eldarmuk.backend_springboot.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import pl.dmcs.eldarmuk.backend_springboot.model.Role;
+import pl.dmcs.eldarmuk.backend_springboot.model.RoleName;
+
+import java.util.Optional;
+
+@Repository
+public interface RoleRepository extends JpaRepository<Role, Long> {
+    Optional<Role> findByName(RoleName roleName);
+}

+ 1 - 1
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/StudentRepository.java

@@ -1,8 +1,8 @@
 package pl.dmcs.eldarmuk.backend_springboot.repository;
 
+import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.stereotype.Repository;
 import pl.dmcs.eldarmuk.backend_springboot.model.Student;
-import org.springframework.data.jpa.repository.JpaRepository;
 
 @Repository
 public interface StudentRepository extends JpaRepository<Student, Long> {

+ 11 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/repository/SubjectRepository.java

@@ -0,0 +1,11 @@
+package pl.dmcs.eldarmuk.backend_springboot.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import pl.dmcs.eldarmuk.backend_springboot.model.Subject;
+
+@Repository
+public interface SubjectRepository extends JpaRepository<Subject, Long> {
+    Subject findById(long id);
+    boolean existsByName(String name);
+}

+ 80 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/WebSecurityConfig.java

@@ -0,0 +1,80 @@
+package pl.dmcs.eldarmuk.backend_springboot.security;
+
+import jakarta.servlet.DispatcherType;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
+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.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import pl.dmcs.eldarmuk.backend_springboot.security.jwt.JwtAuthEntryPoint;
+import pl.dmcs.eldarmuk.backend_springboot.security.jwt.JwtAuthTokenFilter;
+import pl.dmcs.eldarmuk.backend_springboot.security.services.UserDetailsServiceImpl;
+
+@Configuration
+@EnableWebSecurity
+@EnableMethodSecurity
+public class WebSecurityConfig {
+
+    @Autowired
+    UserDetailsServiceImpl userDetailsService;
+
+    @Autowired
+    private JwtAuthEntryPoint unauthorizedHandler;
+
+    @Bean
+    public JwtAuthTokenFilter authenticationJwtTokenFilter() {
+        return new JwtAuthTokenFilter();
+    }
+
+    @Bean
+    DaoAuthenticationProvider authProvider(){
+        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
+        authProvider.setUserDetailsService(userDetailsService);
+        authProvider.setPasswordEncoder(passwordEncoder());
+        return authProvider;
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
+    @Bean
+    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
+
+        http
+                .cors(Customizer.withDefaults())
+                .csrf(AbstractHttpConfigurer::disable)
+                .authorizeHttpRequests((auth) -> auth
+                        .requestMatchers("/auth/**").permitAll()
+                        .requestMatchers("/students/**").permitAll()
+                        .requestMatchers("/teachers/**").permitAll()
+                        .requestMatchers("/subjects/**").permitAll()
+                        .requestMatchers("/grades/**").permitAll()
+                        .requestMatchers("/error").permitAll() // this enables the body in the exception responses
+                        .anyRequest().authenticated()
+                )
+                .exceptionHandling(unauthorized -> unauthorized
+                        .authenticationEntryPoint(unauthorizedHandler)
+                )
+                .sessionManagement(session -> session
+                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS));
+
+        http.addFilterBefore(authenticationJwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
+        return http.build();
+    }
+}
+
+
+
+
+

+ 21 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/jwt/JwtAuthEntryPoint.java

@@ -0,0 +1,21 @@
+package pl.dmcs.eldarmuk.backend_springboot.security.jwt;
+
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+@Component
+public class JwtAuthEntryPoint implements AuthenticationEntryPoint {
+
+    @Override
+    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
+                         AuthenticationException e) throws IOException, ServletException {
+        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error -> Unauthorized");
+    }
+}
+

+ 54 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/jwt/JwtAuthTokenFilter.java

@@ -0,0 +1,54 @@
+package pl.dmcs.eldarmuk.backend_springboot.security.jwt;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
+import org.springframework.web.filter.OncePerRequestFilter;
+import pl.dmcs.eldarmuk.backend_springboot.security.services.UserDetailsServiceImpl;
+
+import java.io.IOException;
+
+public class JwtAuthTokenFilter extends OncePerRequestFilter {
+
+    @Autowired
+    private JwtProvider tokenProvider;
+
+    @Autowired
+    private UserDetailsServiceImpl userDetailsService;
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
+                                    FilterChain filterChain) throws ServletException, IOException {
+        try {
+            String jwt = getJwt(httpServletRequest);
+            if (jwt != null && tokenProvider.validateJwtToken(jwt)) {
+                String username = tokenProvider.getUserNameFromJwtToken(jwt);
+
+                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
+                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
+                        userDetails, null, userDetails.getAuthorities());
+                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
+
+                SecurityContextHolder.getContext().setAuthentication(authentication);
+            }
+        } catch (Exception e) {
+            logger.error("Can NOT set user authentication -> Message: {}", e);
+        }
+        filterChain.doFilter(httpServletRequest, httpServletResponse);
+    }
+
+    private String getJwt(HttpServletRequest request) {
+        String authHeader = request.getHeader("Authorization");
+        if (authHeader != null && authHeader.startsWith("Bearer ")) {
+            return authHeader.replace("Bearer ", "");
+        }
+        return null;
+    }
+}
+

+ 65 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/jwt/JwtProvider.java

@@ -0,0 +1,65 @@
+package pl.dmcs.eldarmuk.backend_springboot.security.jwt;
+
+import io.jsonwebtoken.*;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.Authentication;
+import org.springframework.stereotype.Component;
+import pl.dmcs.eldarmuk.backend_springboot.security.services.UserPrinciple;
+
+import java.util.Date;
+
+@Component
+public class JwtProvider {
+
+    @Value("${pl.dmcs.eldarmuk.jwtSecret}")
+    private String jwtSecret;
+
+    @Value("${pl.dmcs.eldarmuk.jwtExpiration}")
+    private int jwtExpiration;
+
+    public String generateJwtToken(Authentication authentication) {
+        UserPrinciple userPrinciple = (UserPrinciple) authentication.getPrincipal();
+
+        return Jwts.builder()
+                .setSubject(userPrinciple.getUsername())
+                .setIssuedAt(new Date())
+                .setExpiration(new Date((new Date()).getTime() + jwtExpiration*1000))
+                .signWith(SignatureAlgorithm.HS512, jwtSecret)
+                .compact();
+    }
+
+    public boolean validateJwtToken(String authToken) {
+        try {
+            Jwts.parser().setSigningKey(jwtSecret).build().parseSignedClaims(authToken);
+            return true;
+        } catch (SignatureException e) {
+            System.out.println("Invalid JWT signature -> Message: {} " + e);
+        } catch (MalformedJwtException e) {
+            System.out.println("Invalid JWT token -> Message: {}" + e);
+        } catch (ExpiredJwtException e) {
+            System.out.println("Expired JWT token -> Message: {}" + e);
+        } catch (UnsupportedJwtException e) {
+            System.out.println("Unsupported JWT token -> Message: {}" + e);
+        } catch (IllegalArgumentException e) {
+            System.out.println("JWT claims string is empty -> Message: {}" + e);
+        }
+
+        return false;
+    }
+
+    public String getUserNameFromJwtToken(String token) {
+        return Jwts.parser()
+                .setSigningKey(jwtSecret)
+                .build()
+                .parseClaimsJws(token)
+                .getBody().getSubject();
+    }
+
+}
+
+
+
+
+
+
+

+ 26 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/services/UserDetailsServiceImpl.java

@@ -0,0 +1,26 @@
+package pl.dmcs.eldarmuk.backend_springboot.security.services;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import pl.dmcs.eldarmuk.backend_springboot.model.Account;
+import pl.dmcs.eldarmuk.backend_springboot.repository.AccountRepository;
+
+@Service
+public class UserDetailsServiceImpl  implements UserDetailsService {
+    @Autowired
+    AccountRepository accountRepository;
+
+    @Override
+    @Transactional
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+
+        Account user = accountRepository.findByUsername(username).orElseThrow(
+                () -> new UsernameNotFoundException("User Not Found with -> username: " + username));
+        return UserPrinciple.build(user);
+    }
+}
+

+ 92 - 0
project/backend_springboot/src/main/java/pl/dmcs/eldarmuk/backend_springboot/security/services/UserPrinciple.java

@@ -0,0 +1,92 @@
+package pl.dmcs.eldarmuk.backend_springboot.security.services;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+import pl.dmcs.eldarmuk.backend_springboot.model.Account;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class UserPrinciple implements UserDetails {
+
+    private Long id;
+
+    private String username;
+
+    @JsonIgnore
+    private String password;
+
+    private Collection<? extends GrantedAuthority> authorities;
+
+    public UserPrinciple(Long id, String username, String password, Collection<? extends GrantedAuthority> authorities) {
+        this.id = id;
+        this.username = username;
+        this.password = password;
+        this.authorities = authorities;
+    }
+
+    public static UserPrinciple build(Account user) {
+        List<GrantedAuthority> authorities = user.getRoles().stream()
+            .map(role -> new SimpleGrantedAuthority(role.getName().name()))
+            .collect(Collectors.toList());
+
+        return new UserPrinciple(
+            user.getId(),
+            user.getUsername(),
+            user.getPassword(),
+            authorities
+        );
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    @Override
+    public String getUsername() {
+        return username;
+    }
+
+    @Override
+    public String getPassword() {
+        return password;
+    }
+
+    @Override
+    public Collection<? extends GrantedAuthority> getAuthorities() {
+        return authorities;
+    }
+
+    @Override
+    public boolean isAccountNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isAccountNonLocked() {
+        return true;
+    }
+
+    @Override
+    public boolean isCredentialsNonExpired() {
+        return true;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) return true;
+        if (obj == null || getClass() != obj.getClass()) return false;
+
+        UserPrinciple user = (UserPrinciple) obj;
+        return Objects.equals(id, user.id);
+    }
+}

+ 5 - 5
project/backend_springboot/src/main/resources/application.properties

@@ -28,14 +28,14 @@ spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
 spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
 
 # Fix Postgres JPA Error (Method org.postgresql.jdbc.PgConnection.createClob() is not yet implemented).
-#spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
+spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
 
 #To change logging levels in the app
-# logging.level.org.springframework.web = trace
-# logging.level.org.hibernate = trace
+ logging.level.org.springframework.web = trace
+ logging.level.org.hibernate = trace
 
 # IMPORTANT: to use data.sql file has to be uncommented
 #spring.sql.init.mode = always
 
-#pl.dmcs.eldarmuk.jwtSecret = jwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKey
-#pl.dmcs.eldarmuk.jwtExpiration = 3600
+pl.dmcs.eldarmuk.jwtSecret = jwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKeyjwtSecretKey
+pl.dmcs.eldarmuk.jwtExpiration = 3600