update auth

- add tables and repos
- add dto
This commit is contained in:
Max W.
2025-01-19 19:31:43 +01:00
parent 38c619ae70
commit a8411b6e63
16 changed files with 339 additions and 15 deletions

View File

@ -0,0 +1,28 @@
package de.w665.biblenotes.db.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "bible_books")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class BibleBook {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "book_name_en", nullable = false)
private String bookNameEn;
@Column(name = "book_name_de", nullable = false)
private String bookNameDe;
@Column(name = "chapter_count", nullable = false)
private int chapterCount;
@Column(name = "is_new_testament", nullable = false)
private boolean isNewTestament;
}

View File

@ -0,0 +1,34 @@
package de.w665.biblenotes.db.entity;
import com.fasterxml.jackson.annotation.JsonInclude;
import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "bible_reading_plans")
@Getter
@Setter
@AllArgsConstructor
@ToString
@NoArgsConstructor
public class BibleReadingPlan {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "plan_name", nullable = false)
private String planName;
@Column(name = "start_date", nullable = false)
private LocalDateTime startDate;
@Column(name = "chapter_per_day", nullable = false)
private short chapterPerDay;
@JsonInclude(JsonInclude.Include.NON_NULL)
@ManyToOne
@JoinColumn(name = "creator_user_id", nullable = false)
private User creatorUser;
}

View File

@ -4,7 +4,6 @@ import jakarta.persistence.*;
import lombok.*;
import java.time.LocalDateTime;
import java.util.Date;
@Entity
@Table(name = "user_logins")

View File

@ -0,0 +1,31 @@
package de.w665.biblenotes.db.entity;
import jakarta.persistence.*;
import lombok.*;
@Entity
@Table(name = "user_statistics")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class UserStatistics {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
@ManyToOne
@JoinColumn(name = "current_book_id")
private BibleBook currentBook;
@Column(name = "current_chapter_id")
private Long currentChapterId;
@ManyToOne
@JoinColumn(name = "bible_reading_plan_id")
private BibleReadingPlan bibleReadingPlan;
}

View File

@ -0,0 +1,7 @@
package de.w665.biblenotes.db.repo;
import de.w665.biblenotes.db.entity.BibleBook;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BibleBookRepository extends JpaRepository<BibleBook, Long> {
}

View File

@ -0,0 +1,10 @@
package de.w665.biblenotes.db.repo;
import de.w665.biblenotes.db.entity.BibleReadingPlan;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface BibleReadingPlanRepository extends JpaRepository<BibleReadingPlan, Long> {
List<BibleReadingPlan> getBibleReadingPlanById(Long id);
}

View File

@ -0,0 +1,7 @@
package de.w665.biblenotes.db.repo;
import de.w665.biblenotes.db.entity.UserStatistics;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserStatisticsRepository extends JpaRepository<UserStatistics, Long> {
}

View File

@ -1,6 +1,6 @@
package de.w665.biblenotes.rest;
import de.w665.biblenotes.rest.ro.AuthenticationRequest;
import de.w665.biblenotes.rest.dto.AuthenticationRequest;
import de.w665.biblenotes.service.AuthenticationService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

View File

@ -1,4 +1,4 @@
package de.w665.biblenotes.rest.ro;
package de.w665.biblenotes.rest.dto;
import lombok.Getter;
import lombok.NoArgsConstructor;

View File

@ -0,0 +1,18 @@
package de.w665.biblenotes.rest.dto;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.time.LocalDateTime;
@Getter
@Setter
@ToString
public class BibleReadingPlanDTO {
private Long id;
private String planName;
private LocalDateTime startDate;
private short chapterPerDay;
private String creatorUserName;
}

View File

@ -0,0 +1,72 @@
package de.w665.biblenotes.rest.mappings;
import de.w665.biblenotes.db.entity.BibleReadingPlan;
import de.w665.biblenotes.db.entity.User;
import de.w665.biblenotes.db.repo.BibleReadingPlanRepository;
import de.w665.biblenotes.db.repo.UserRepository;
import de.w665.biblenotes.rest.SecureApiRestController;
import de.w665.biblenotes.rest.dto.BibleReadingPlanDTO;
import de.w665.biblenotes.rest.security.UserAuthenticationContext;
import jakarta.persistence.EntityManager;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
import java.util.Optional;
@RestController
public class BibleReadingPlanMapping extends SecureApiRestController {
private final EntityManager entityManager;
private final BibleReadingPlanRepository bibleReadingPlanRepository;
public BibleReadingPlanMapping(UserRepository userRepository, EntityManager entityManager, BibleReadingPlanRepository bibleReadingPlanRepository) {
this.entityManager = entityManager;
this.bibleReadingPlanRepository = bibleReadingPlanRepository;
}
@GetMapping("/bible-reading-plan")
public ResponseEntity<Object> getBibleReadingPlans(@RequestParam(required = true) Long id) {
Optional<BibleReadingPlan> brp = bibleReadingPlanRepository.findById(id);
if(brp.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
BibleReadingPlanDTO bibleReadingPlanDTO = new BibleReadingPlanDTO();
bibleReadingPlanDTO.setId(brp.get().getId());
bibleReadingPlanDTO.setPlanName(brp.get().getPlanName());
bibleReadingPlanDTO.setChapterPerDay(brp.get().getChapterPerDay());
bibleReadingPlanDTO.setStartDate(brp.get().getStartDate());
bibleReadingPlanDTO.setCreatorUserName(brp.get().getCreatorUser().getUsername());
return new ResponseEntity<>(bibleReadingPlanDTO, HttpStatus.OK);
}
@PostMapping("/bible-reading-plan")
public ResponseEntity<Object> createBibleReadingPlan(@RequestBody BibleReadingPlanDTO bibleReadingPlanDTO) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
// Construct entity
BibleReadingPlan plan = new BibleReadingPlan();
plan.setPlanName(bibleReadingPlanDTO.getPlanName());
plan.setChapterPerDay(bibleReadingPlanDTO.getChapterPerDay());
plan.setStartDate(bibleReadingPlanDTO.getStartDate());
// Set user as creator in db entity
User userProxy = entityManager.getReference(User.class, ((UserAuthenticationContext) auth.getPrincipal()).getUserId());
plan.setCreatorUser(userProxy);
BibleReadingPlan createdPlan = bibleReadingPlanRepository.save(plan);
// Construct response (we do this because otherwise the user object would be serialized (which would fail because it's a proxy + i addition we don't want to send the users credentials))
BibleReadingPlanDTO responseDTO = new BibleReadingPlanDTO();
responseDTO.setPlanName(createdPlan.getPlanName());
responseDTO.setChapterPerDay(createdPlan.getChapterPerDay());
responseDTO.setStartDate(createdPlan.getStartDate());
responseDTO.setCreatorUserName(((UserAuthenticationContext) auth.getPrincipal()).getUsername());
responseDTO.setId(createdPlan.getId());
return new ResponseEntity<>(responseDTO, HttpStatus.OK);
}
}

View File

@ -1,5 +1,6 @@
package de.w665.biblenotes.rest.security;
import de.w665.biblenotes.db.entity.Role;
import de.w665.biblenotes.service.AuthenticationService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
@ -60,12 +61,15 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
return;
}
String username = authenticationService.extractSubject(jwt);
// Extract the role from the JWT and set it to Spring AuthenticationContext for access control
// Extract the role and username from the JWT and set it to Spring AuthenticationContext for access control
String username = authenticationService.getClaimValue(jwt, "username", String.class);
String role = authenticationService.getClaimValue(jwt, "role", String.class);
int userId = authenticationService.getClaimValue(jwt, "userId", Integer.class);
UserAuthenticationContext uac = new UserAuthenticationContext(username, userId, Role.valueOf(role));
List<GrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority("ROLE_" + role));
JwtAuthenticationToken auth = new JwtAuthenticationToken(username, jwt, authorities);
JwtAuthenticationToken auth = new JwtAuthenticationToken(uac, jwt, authorities);
auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(auth);

View File

@ -1,5 +1,6 @@
package de.w665.biblenotes.rest.security;
import lombok.Getter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
@ -7,24 +8,24 @@ import java.util.Collection;
public class JwtAuthenticationToken extends AbstractAuthenticationToken {
private final String principal;
private final UserAuthenticationContext principal;
@Getter
private final String token;
public JwtAuthenticationToken(String principal, String token, Collection<? extends GrantedAuthority> authorities) {
public JwtAuthenticationToken(UserAuthenticationContext principal, String token, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.token = token;
super.setAuthenticated(true); // Set this to true only if authentication is verified
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return null;
return "Credentials are not stored.";
}
@Override
public Object getPrincipal() {
return null;
public UserAuthenticationContext getPrincipal() {
return this.principal;
}
}

View File

@ -0,0 +1,13 @@
package de.w665.biblenotes.rest.security;
import de.w665.biblenotes.db.entity.Role;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class UserAuthenticationContext {
private String username;
private int userId;
private Role role;
}

View File

@ -73,10 +73,11 @@ public class AuthenticationService {
Date expiryDate = new Date(nowMillis + expirationTime);
return Jwts.builder()
.subject("SharePulse Authentication Token")
.subject("BibleNotes Authentication Token")
.issuedAt(now)
.claim("role", user.getRole())
.claim("username", user.getUsername())
.claim("userId", user.getId())
.expiration(expiryDate)
.signWith(secretKey)
.compact();