Skip to content

Commit f56eaa6

Browse files
committed
Finished user authentication and logging-in frontend/backend
1 parent df5fac2 commit f56eaa6

File tree

17 files changed

+225
-69
lines changed

17 files changed

+225
-69
lines changed

src/main/java/net/hackyourfuture/coursehub/SecurityConfig.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package net.hackyourfuture.coursehub;
22

3-
import net.hackyourfuture.coursehub.service.UserAuthenticationService;
43
import org.springframework.context.annotation.Bean;
54
import org.springframework.context.annotation.Configuration;
65
import org.springframework.http.HttpMethod;
@@ -9,7 +8,6 @@
98
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
109
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1110
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
12-
import org.springframework.security.core.userdetails.UserDetailsService;
1311
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1412
import org.springframework.security.crypto.password.PasswordEncoder;
1513
import org.springframework.security.web.SecurityFilterChain;
@@ -30,10 +28,14 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
3028
config.addAllowedMethod("*");
3129
return config;
3230
}))
33-
.authorizeHttpRequests(auth -> auth.requestMatchers("/login", "/register")
31+
.authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.POST, "/login", "/register")
32+
.permitAll()
33+
.requestMatchers(HttpMethod.OPTIONS, "/login", "/register")
3434
.permitAll()
3535
.requestMatchers(HttpMethod.GET, "/courses")
3636
.permitAll()
37+
.requestMatchers(HttpMethod.OPTIONS, "/courses")
38+
.permitAll()
3739
.anyRequest()
3840
.authenticated())
3941
.build();
@@ -44,11 +46,6 @@ public PasswordEncoder passwordEncoder() {
4446
return new BCryptPasswordEncoder();
4547
}
4648

47-
@Bean
48-
public UserDetailsService userDetailsService(UserAuthenticationService userAuthenticationService) {
49-
return userAuthenticationService;
50-
}
51-
5249
@Bean
5350
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration)
5451
throws Exception {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
package net.hackyourfuture.coursehub.data;
22

3-
public record UserAccountEntity(Integer userId, String passwordHash, String emailAddress, Role role) {}
3+
public record UserAccountEntity(Integer userId, String emailAddress, String passwordHash, Role role) {}

src/main/java/net/hackyourfuture/coursehub/service/CourseService.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,13 @@ public List<CourseDto> getAllCourses() {
2727
}
2828
String instructorName = instructor.firstName() + " " + instructor.lastName();
2929
return new CourseDto(
30-
c.name(), c.description(), instructorName, c.startDate(), c.endDate(), c.maxEnrollments());
30+
c.courseId(),
31+
c.name(),
32+
c.description(),
33+
instructorName,
34+
c.startDate(),
35+
c.endDate(),
36+
c.maxEnrollments());
3137
})
3238
.toList();
3339
}
Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,93 @@
11
package net.hackyourfuture.coursehub.web;
22

3+
import jakarta.servlet.http.HttpServletRequest;
4+
import net.hackyourfuture.coursehub.data.InstructorEntity;
5+
import net.hackyourfuture.coursehub.data.StudentEntity;
36
import net.hackyourfuture.coursehub.data.UserAccountEntity;
7+
import net.hackyourfuture.coursehub.repository.InstructorRepository;
8+
import net.hackyourfuture.coursehub.repository.StudentRepository;
49
import net.hackyourfuture.coursehub.repository.UserAccountRepository;
10+
import net.hackyourfuture.coursehub.web.model.HttpErrorResponse;
511
import net.hackyourfuture.coursehub.web.model.LoginRequest;
6-
import net.hackyourfuture.coursehub.web.model.LoginResponse;
12+
import net.hackyourfuture.coursehub.web.model.LoginSuccessResponse;
13+
import org.springframework.http.HttpStatus;
14+
import org.springframework.http.ResponseEntity;
715
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
816
import org.springframework.security.authentication.AuthenticationManager;
917
import org.springframework.security.authentication.BadCredentialsException;
1018
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1119
import org.springframework.security.core.Authentication;
1220
import org.springframework.security.core.AuthenticationException;
21+
import org.springframework.security.core.context.SecurityContextHolder;
22+
import org.springframework.validation.annotation.Validated;
1323
import org.springframework.web.bind.annotation.PostMapping;
1424
import org.springframework.web.bind.annotation.RequestBody;
1525
import org.springframework.web.bind.annotation.RestController;
1626

27+
@Validated // will make sure that every request body annotated with @RequestBody is validated
1728
@RestController
1829
public class UserAuthenticationController {
1930
private final AuthenticationManager authenticationManager;
2031
private final UserAccountRepository userAccountRepository;
32+
private final StudentRepository studentRepository;
33+
private final InstructorRepository instructorRepository;
2134

2235
public UserAuthenticationController(
2336
AuthenticationManager authenticationManager,
24-
UserAccountRepository userAccountRepository) {
37+
UserAccountRepository userAccountRepository,
38+
StudentRepository studentRepository,
39+
InstructorRepository instructorRepository) {
2540
this.authenticationManager = authenticationManager;
2641
this.userAccountRepository = userAccountRepository;
42+
this.studentRepository = studentRepository;
43+
this.instructorRepository = instructorRepository;
2744
}
2845

2946
@PostMapping("/login")
30-
public LoginResponse login(@RequestBody LoginRequest request) {
47+
public ResponseEntity<Object> login(@RequestBody LoginRequest request, HttpServletRequest httpRequest) {
3148
try {
49+
// Authenticate the user with the provided credentials (email and password)
3250
Authentication authentication = authenticationManager.authenticate(
3351
new UsernamePasswordAuthenticationToken(request.emailAddress(), request.password()));
52+
// Save the authenticated user in the Spring security context
53+
SecurityContextHolder.getContext().setAuthentication(authentication);
54+
// Ensure a session is created for the authenticated user
55+
httpRequest.getSession(true);
56+
57+
// Retrieve the corresponding user data from the database to return
3458
UserAccountEntity user = userAccountRepository.findByEmailAddress(request.emailAddress());
3559
if (user == null) {
36-
return new LoginResponse(null, false, "No user found for provided email address");
60+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
61+
.body(new HttpErrorResponse("No user found for provided email address"));
62+
}
63+
64+
String firstName = null;
65+
String lastName = null;
66+
switch (user.role()) {
67+
case student -> {
68+
StudentEntity student = studentRepository.findById(user.userId());
69+
firstName = student.firstName();
70+
lastName = student.lastName();
71+
}
72+
case instructor -> {
73+
InstructorEntity instructor = instructorRepository.findById(user.userId());
74+
firstName = instructor.firstName();
75+
lastName = instructor.lastName();
76+
}
3777
}
38-
return new LoginResponse(user.userId(), authentication.isAuthenticated(), null);
78+
return ResponseEntity.ok(
79+
new LoginSuccessResponse(user.userId(), firstName, lastName, user.emailAddress(), user.role()));
3980
} catch (AuthenticationException e) {
4081
if (e instanceof BadCredentialsException) {
41-
return new LoginResponse(null, false, "Invalid credentials provided");
82+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
83+
.body(new HttpErrorResponse("Invalid credentials provided"));
4284
}
4385
if (e instanceof AuthenticationCredentialsNotFoundException) {
44-
return new LoginResponse(null, false, "No user found for provided email address");
86+
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
87+
.body(new HttpErrorResponse("No user found for provided email address"));
4588
}
46-
return new LoginResponse(null, false, "Something went wrong");
89+
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
90+
.body(new HttpErrorResponse("Something went wrong"));
4791
}
4892
}
49-
}
93+
}

src/main/java/net/hackyourfuture/coursehub/web/model/CourseDto.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.time.LocalDate;
44

55
public record CourseDto(
6+
Integer courseId,
67
String name,
78
String description,
89
String instructor,
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package net.hackyourfuture.coursehub.web.model;
2+
3+
public record HttpErrorResponse(String message) {}

src/main/java/net/hackyourfuture/coursehub/web/model/LoginResponse.java

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package net.hackyourfuture.coursehub.web.model;
2+
3+
import net.hackyourfuture.coursehub.data.Role;
4+
5+
public record LoginSuccessResponse(Integer userId, String firstName, String lastName, String emailAddress, Role role) {}

src/test/java/net/hackyourfuture/coursehub/service/CourseServiceIntegrationTest.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,34 @@ void setUp() {
6868

6969
@Test
7070
void shouldReturnCourses() {
71+
var allCourses = courseRepository.findAll();
72+
var course1 = allCourses.stream()
73+
.filter(c -> "Test Course 1".equals(c.name()))
74+
.findFirst()
75+
.orElseThrow();
76+
var course2 = allCourses.stream()
77+
.filter(c -> "Test Course 2".equals(c.name()))
78+
.findFirst()
79+
.orElseThrow();
80+
7181
var courses = courseService.getAllCourses();
7282

7383
assertThat(courses)
7484
.contains(new CourseDto(
85+
course1.courseId(),
7586
"Test Course 1",
7687
"Description 1",
7788
"Artur Havliukovskyi",
78-
LocalDate.now(),
79-
LocalDate.now().plusDays(30),
89+
course1.startDate(),
90+
course1.endDate(),
8091
30))
8192
.contains(new CourseDto(
93+
course2.courseId(),
8294
"Test Course 2",
8395
"Description 2",
8496
"Breus Blaauwendraad",
85-
LocalDate.now(),
86-
LocalDate.now().plusDays(60),
97+
course2.startDate(),
98+
course2.endDate(),
8799
50));
88100
}
89101
}

src/test/java/net/hackyourfuture/coursehub/service/CourseServiceTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,14 @@ void shouldReturnCoursesWithInstructorNames() {
6161

6262
assertThat(courses)
6363
.hasSize(2)
64-
.contains(new CourseDto(
64+
.contains(new CourseDto(1,
6565
"Testing course",
6666
"A course about testing",
6767
"Alice Smith",
6868
LocalDate.of(2026, Month.JANUARY, 15),
6969
LocalDate.of(2026, Month.MARCH, 1),
7070
30))
71-
.contains(new CourseDto(
71+
.contains(new CourseDto(2,
7272
"Spring course",
7373
"A course about using Spring Boot",
7474
"Bob Johnson",

0 commit comments

Comments
 (0)