diff --git a/data/hotel.sqlite b/data/hotel.sqlite index 9c93398..40c6252 100644 Binary files a/data/hotel.sqlite and b/data/hotel.sqlite differ diff --git a/src/main/java/com/mallardromain/hotel/config/AppConfig.java b/src/main/java/com/mallardromain/hotel/config/AppConfig.java new file mode 100644 index 0000000..84f667e --- /dev/null +++ b/src/main/java/com/mallardromain/hotel/config/AppConfig.java @@ -0,0 +1,16 @@ +package com.mallardromain.hotel.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class AppConfig { + + // By putting this here, UserService can grab it WITHOUT triggering SecurityConfig! + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/src/main/java/com/mallardromain/hotel/config/DatabaseSeeder.java b/src/main/java/com/mallardromain/hotel/config/DatabaseSeeder.java index a0ec5c1..735c6bc 100644 --- a/src/main/java/com/mallardromain/hotel/config/DatabaseSeeder.java +++ b/src/main/java/com/mallardromain/hotel/config/DatabaseSeeder.java @@ -18,9 +18,12 @@ public class DatabaseSeeder { if (userRepository.count() == 0) { User testUser1 = new User("test1", passwordEncoder.encode("password1")); User testUser2 = new User("test2", passwordEncoder.encode("password2")); - + User adminUser1 = new User("admin1", passwordEncoder.encode("password1")); + adminUser1.setRole("ADMIN"); + userRepository.save(testUser1); userRepository.save(testUser2); + userRepository.save(adminUser1); } }; } diff --git a/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java b/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java index 73a264a..ee09083 100644 --- a/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java +++ b/src/main/java/com/mallardromain/hotel/config/SecurityConfig.java @@ -6,6 +6,7 @@ import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.ProviderManager; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.userdetails.UserDetailsService; @@ -17,7 +18,9 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic import com.mallardromain.hotel.security.JWTFilter; @Configuration +@EnableMethodSecurity public class SecurityConfig { + private final JWTFilter jwtFilter; public SecurityConfig(JWTFilter jwtFilter) { @@ -46,12 +49,6 @@ public class SecurityConfig { return http.build(); } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - @Bean public AuthenticationManager authenticationManager( AuthenticationConfiguration config, diff --git a/src/main/java/com/mallardromain/hotel/controller/RoomController.java b/src/main/java/com/mallardromain/hotel/controller/RoomController.java index 9350a76..1cc2631 100644 --- a/src/main/java/com/mallardromain/hotel/controller/RoomController.java +++ b/src/main/java/com/mallardromain/hotel/controller/RoomController.java @@ -2,6 +2,10 @@ package com.mallardromain.hotel.controller; import java.util.List; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import com.mallardromain.hotel.model.Room; @@ -19,27 +23,57 @@ public class RoomController { } @GetMapping - public List getRooms() { - return service.findAll(); + public ResponseEntity getRooms() { + try{ + List roomList = service.findAll(); + return ResponseEntity.ok(roomList); + } catch(RuntimeException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } + } - @PostMapping - public Room createRoom(@RequestBody Room room){ - return service.createRoom(room); + @PostMapping("/create") + @PreAuthorize("hasRole('ADMIN')") + public ResponseEntity createRoom(@RequestBody Room room){ + try{ + Room createdRoom = service.createRoom(room); + return ResponseEntity.ok(createdRoom); + } catch(RuntimeException e){ + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } } - @PutMapping("/{id}/clean") - public Room setRoomClean( - @PathVariable Integer id - ){ - return service.setRoomClean(id); + public ResponseEntity setRoomClean(@PathVariable Integer id){ + try{ + Room updatedRoom = service.setRoomClean(id); + return ResponseEntity.ok(updatedRoom); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } } @PutMapping("/{id}/dirty") - public Room setRoomDirty( - @PathVariable Integer id - ){ - return service.setRoomDirty(id); + public ResponseEntity setRoomDirty(@PathVariable Integer id) { + try { + Room updatedRoom = service.setRoomDirty(id); + return ResponseEntity.ok(updatedRoom); + } catch (RuntimeException e){ + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } + } + + @DeleteMapping("/delete/{id}") + @PreAuthorize("hasRole('ADMIN')") // Keep the boss door locked! + public ResponseEntity deleteRoom(@PathVariable Integer id) { + try{ + service.deleteRoom(id); + return ResponseEntity.ok("Room successfully deleted!"); + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); + } + + } } diff --git a/src/main/java/com/mallardromain/hotel/model/Room.java b/src/main/java/com/mallardromain/hotel/model/Room.java index 3ccd823..d3662d5 100644 --- a/src/main/java/com/mallardromain/hotel/model/Room.java +++ b/src/main/java/com/mallardromain/hotel/model/Room.java @@ -19,7 +19,7 @@ public class Room { } - public Room(Integer id, String number, String status){ + public Room(String number, String status){ //this.id = id; id is/should be generated by the DB this.number = number; this.status = status; diff --git a/src/main/java/com/mallardromain/hotel/model/User.java b/src/main/java/com/mallardromain/hotel/model/User.java index da36a7e..2612f3f 100644 --- a/src/main/java/com/mallardromain/hotel/model/User.java +++ b/src/main/java/com/mallardromain/hotel/model/User.java @@ -14,6 +14,9 @@ public class User { private String password; + @Column(nullable = false) + private String role = "USER"; + public User(){ } @@ -46,4 +49,13 @@ public class User { public void setPassword(String password){ this.password = password; } + + public String getRole(){ + return role; + } + + public void setRole(String role){ + this.role = role; + } + } diff --git a/src/main/java/com/mallardromain/hotel/security/JWTFilter.java b/src/main/java/com/mallardromain/hotel/security/JWTFilter.java index 76ff351..2cd5872 100644 --- a/src/main/java/com/mallardromain/hotel/security/JWTFilter.java +++ b/src/main/java/com/mallardromain/hotel/security/JWTFilter.java @@ -5,6 +5,8 @@ import java.util.Collections; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; @@ -23,9 +25,11 @@ import jakarta.servlet.http.HttpServletResponse; public class JWTFilter extends OncePerRequestFilter { private final JWTService jwtService; + private final UserDetailsService userDetailsService; - public JWTFilter(JWTService jwtService){ + public JWTFilter(JWTService jwtService, UserDetailsService userDetailsService){ this.jwtService = jwtService; + this.userDetailsService = userDetailsService; } @Override @@ -35,14 +39,9 @@ public class JWTFilter extends OncePerRequestFilter { FilterChain filterChain ) throws ServletException, IOException { - String authHeader = request - .getHeader("Authorization"); - - if ( authHeader == null - || - !authHeader.startsWith("Bearer ") - ) { + String authHeader = request.getHeader("Authorization"); + if ( authHeader == null || !authHeader.startsWith("Bearer ") ) { filterChain.doFilter(request, response); return; } @@ -51,23 +50,18 @@ public class JWTFilter extends OncePerRequestFilter { if (jwtService.isTockenValid(token)){ - String username = jwtService - .extractUsername(token); + String username = jwtService.extractUsername(token); - UsernamePasswordAuthenticationToken auth = - new UsernamePasswordAuthenticationToken( - username, + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( + userDetails, null, - Collections.emptyList() - ); - - auth.setDetails( - new WebAuthenticationDetailsSource() - .buildDetails(request) + userDetails.getAuthorities() ); - SecurityContextHolder.getContext() - .setAuthentication(auth); + auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request) ); + SecurityContextHolder.getContext().setAuthentication(auth); } filterChain.doFilter(request, response); diff --git a/src/main/java/com/mallardromain/hotel/service/RoomService.java b/src/main/java/com/mallardromain/hotel/service/RoomService.java index d4b13ea..d5b13bd 100644 --- a/src/main/java/com/mallardromain/hotel/service/RoomService.java +++ b/src/main/java/com/mallardromain/hotel/service/RoomService.java @@ -22,14 +22,7 @@ public class RoomService { return repo.findAll(); } - public Room createRoomTest() { - - Room room = new Room( - 666, - "test", - "clean" - ); - + public Room createRoomTest(Room room) { return repo.save(room); } @@ -47,7 +40,7 @@ public class RoomService { return repo.save(room); } - public Room setRoomDirty(Integer id){ + public Room setRoomDirty(Integer id){ Room room = repo.findById(id) .orElseThrow(() -> new RuntimeException("Room not found")); @@ -57,4 +50,11 @@ public class RoomService { return repo.save(room); } + public void deleteRoom(Integer id) { + if (!repo.existsById(id)) { + throw new RuntimeException("Room with ID " + id + " not found"); + } + repo.deleteById(id); + } + } \ No newline at end of file diff --git a/src/main/java/com/mallardromain/hotel/service/UserService.java b/src/main/java/com/mallardromain/hotel/service/UserService.java index 8b7b153..9c6ea49 100644 --- a/src/main/java/com/mallardromain/hotel/service/UserService.java +++ b/src/main/java/com/mallardromain/hotel/service/UserService.java @@ -40,7 +40,7 @@ public class UserService implements UserDetailsService { .map(user -> org.springframework.security.core.userdetails.User .withUsername(user.getUsername()) .password(user.getPassword()) - .authorities("USER") // Default role + .authorities("ROLE_" + user.getRole().toUpperCase()) // Default role .build() ) .orElseThrow(() -> new UsernameNotFoundException("User not found in DB"));