diff --git a/book-my-show/backend/java/bms-monolith/.gitignore b/book-my-show/backend/java/bms-monolith/.gitignore
index 549e00a..ba4ef15 100644
--- a/book-my-show/backend/java/bms-monolith/.gitignore
+++ b/book-my-show/backend/java/bms-monolith/.gitignore
@@ -30,4 +30,4 @@ build/
!**/src/test/**/build/
### VS Code ###
-.vscode/
+.vscode
diff --git a/book-my-show/backend/java/bms-monolith/pom.xml b/book-my-show/backend/java/bms-monolith/pom.xml
index 76f9214..b1a72a3 100644
--- a/book-my-show/backend/java/bms-monolith/pom.xml
+++ b/book-my-show/backend/java/bms-monolith/pom.xml
@@ -54,6 +54,20 @@
org.springframework.boot
spring-boot-starter-validation
+
+ org.liquibase
+ liquibase-core
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+
org.projectlombok
@@ -70,6 +84,26 @@
jackson-datatype-jsr310
2.18.0
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.11.5
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.11.5
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
@@ -87,6 +121,14 @@
+
+ org.liquibase
+ liquibase-maven-plugin
+
+ src/main/resources/application.yaml
+ true
+
+
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSErrorMessageConstants.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSErrorMessageConstants.java
new file mode 100644
index 0000000..fa5912d
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSErrorMessageConstants.java
@@ -0,0 +1,10 @@
+package org.lbcc.bms.bms_monolith.common.constants;
+
+public final class BMSErrorMessageConstants {
+ private BMSErrorMessageConstants() {
+ }
+
+ public static final String UNEXPECTED_ERROR_MESSAGE = "An unexpected error occurred";
+ public static final String UNEXPECTED_ERROR_CODE = "UNEXPECTED_ERROR";
+ public static final String EVENT_SERVICE_ERROR = "EVENT_SERVICE_ERROR";
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSSuccessMessageConstants.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSSuccessMessageConstants.java
new file mode 100644
index 0000000..cfd9fc1
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/constants/BMSSuccessMessageConstants.java
@@ -0,0 +1,8 @@
+package org.lbcc.bms.bms_monolith.common.constants;
+
+public final class BMSSuccessMessageConstants {
+ private BMSSuccessMessageConstants() {
+ }
+
+ public static final String EVENT_SUCCESS_MESSAGE = "Events fetched successfully";
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java
index 83b1dd5..af1b843 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/BaseAuditingEntity.java
@@ -31,8 +31,8 @@ public abstract class BaseAuditingEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
- @Column(columnDefinition = "CHAR(36)")
- private UUID id;
+ @Column(columnDefinition = "CHAR(36)", updatable = false, nullable = false)
+ private String id;
@CreatedDate
@Column(updatable = false)
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Event.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Event.java
index 12dbaf9..20bb6b3 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Event.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Event.java
@@ -1,13 +1,13 @@
package org.lbcc.bms.bms_monolith.common.entity;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.CascadeType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.FetchType;
import jakarta.persistence.Column;
-import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -18,12 +18,12 @@
import java.util.List;
@Entity
+@Table(name = "events")
@Getter @Setter
@AllArgsConstructor @NoArgsConstructor
@SuperBuilder
public class Event extends BaseAuditingEntity {
- @Size(min=10, max=50, message = "Title must be between 10 and 50 characters.")
@Column(nullable = false, length = 50)
private String title;
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventShow.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventShow.java
index a8196e1..b1d2882 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventShow.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventShow.java
@@ -1,6 +1,7 @@
package org.lbcc.bms.bms_monolith.common.entity;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
@@ -13,7 +14,9 @@
import jakarta.persistence.FetchType;
import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
import lombok.Getter;
+import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import org.lbcc.bms.bms_monolith.common.enums.Genre;
@@ -21,13 +24,16 @@
import java.util.List;
@Entity
+@Table(name = "event_shows")
@Getter
@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
public class EventShow extends BaseAuditingEntity {
@ElementCollection(targetClass = Genre.class)
- @CollectionTable(name = "show_genre", joinColumns = @JoinColumn(name = "show_id"))
- @Column(name = "genre")
+ @CollectionTable(name = "show_genres", joinColumns = @JoinColumn(name = "show_id"))
+ @Column(name = "label")
@Enumerated(EnumType.STRING)
private List genres;
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventType.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventType.java
index e3b4af8..be81f6c 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventType.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/EventType.java
@@ -2,6 +2,7 @@
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -9,6 +10,7 @@
import lombok.experimental.SuperBuilder;
@Entity
+@Table(name = "event_types")
@Getter
@Setter
@AllArgsConstructor
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/RegisteredUser.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/RegisteredUser.java
new file mode 100644
index 0000000..2f16836
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/RegisteredUser.java
@@ -0,0 +1,48 @@
+package org.lbcc.bms.bms_monolith.common.entity;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@AllArgsConstructor
+@SuperBuilder
+@Data
+@Entity
+@EqualsAndHashCode(callSuper = true)
+@NoArgsConstructor
+@Table(name = "registered_users")
+public class RegisteredUser extends BaseAuditingEntity {
+
+ @Column(length = 20)
+ private String username;
+
+ @Column(length = 25)
+ private String email;
+
+ @Column(columnDefinition = "CHAR(10)")
+ private String phone;
+
+ @Column(length = 20)
+ @JsonIgnore
+ private String password;
+
+ @Builder.Default
+ @Column(columnDefinition = "TINYINT(1)")
+ private Boolean isActive = true;
+
+ @ManyToMany(fetch = FetchType.EAGER)
+ @JoinTable(
+ name = "user_roles",
+ joinColumns = {@JoinColumn(name = "user_id")},
+ inverseJoinColumns = {@JoinColumn(name = "role_id")})
+ @Builder.Default
+ private Set roles = new HashSet<>();
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Role.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Role.java
new file mode 100644
index 0000000..ba74d26
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Role.java
@@ -0,0 +1,24 @@
+package org.lbcc.bms.bms_monolith.common.entity;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import org.lbcc.bms.bms_monolith.common.enums.RoleType;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "roles")
+public class Role extends BaseAuditingEntity {
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ private RoleType label;
+
+ @Column(nullable = false, length = 50)
+ private String description;
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Seat.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Seat.java
index 0673e1d..536fb65 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Seat.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Seat.java
@@ -1,6 +1,12 @@
package org.lbcc.bms.bms_monolith.common.entity;
-import jakarta.persistence.*;
+import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Column;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -8,6 +14,7 @@
import org.lbcc.bms.bms_monolith.common.enums.OperationalStatus;
@Entity
+@Table(name = "seats")
@Getter
@Setter
@AllArgsConstructor
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatType.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatType.java
index 4a11cc2..50d26ff 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatType.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatType.java
@@ -2,6 +2,7 @@
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@@ -10,6 +11,7 @@
import java.math.BigDecimal;
@Entity
+@Table(name = "seat_types")
@Getter
@Setter
@AllArgsConstructor
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatTypeInShow.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatTypeInShow.java
index 45f7fb1..f421aca 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatTypeInShow.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/SeatTypeInShow.java
@@ -1,25 +1,31 @@
package org.lbcc.bms.bms_monolith.common.entity;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Column;
+import lombok.AllArgsConstructor;
import lombok.Getter;
+import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import java.math.BigDecimal;
@Entity
+@Table(name = "seat_type_in_shows")
@Getter
@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
public class SeatTypeInShow extends BaseAuditingEntity {
@ManyToOne
- @JoinColumn(name = "seat_type_id")
+ @JoinColumn(name = "seat_type_id", nullable = false)
private SeatType seatType;
@ManyToOne
- @JoinColumn(name = "show_id")
+ @JoinColumn(name = "show_id", nullable = false)
private EventShow show;
@Column(precision = 10, scale = 2)
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Vendor.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Vendor.java
index 5feef8b..94e32ff 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Vendor.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Vendor.java
@@ -1,37 +1,51 @@
package org.lbcc.bms.bms_monolith.common.entity;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import jakarta.persistence.Enumerated;
import jakarta.persistence.EnumType;
import jakarta.persistence.Column;
import jakarta.validation.constraints.Size;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.lbcc.bms.bms_monolith.common.enums.VendorStatus;
+import java.time.LocalDateTime;
+
@Entity
+@Table(name = "vendors")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Vendor extends BaseAuditingEntity {
+ @Column(nullable = false, length = 20)
private String name;
private String contactNumber;
+
+ @Column(nullable = false, length = 100)
private String email;
+
+ @Column(nullable = false)
private String address;
+
+ @Column(nullable = true, length = 100)
private String website;
@Enumerated(EnumType.STRING)
+ @Column(nullable = false, length = 20)
private VendorStatus status;
- @Column(nullable = true, length = 100)
- @Size(min = 10, max = 100, message = "Registration date must be between 10 and 100 characters.")
- private String registrationDate;
+ @Column(nullable = false)
+ private LocalDateTime registrationDate;
@Column(nullable = false, length = 255)
- @Size(min = 25, max = 255, message = "Logo URL must be between 25 and 255 characters.")
private String logoUrl;
}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Venue.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Venue.java
index 3e698c7..97d9bea 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Venue.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/entity/Venue.java
@@ -1,11 +1,13 @@
package org.lbcc.bms.bms_monolith.common.entity;
import jakarta.persistence.Entity;
+import jakarta.persistence.Table;
import jakarta.persistence.Enumerated;
import jakarta.persistence.EnumType;
import jakarta.persistence.CascadeType;
import jakarta.persistence.FetchType;
import jakarta.persistence.OneToMany;
+import jakarta.persistence.Column;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
@@ -19,12 +21,14 @@
import java.util.List;
@Entity
+@Table(name = "venues")
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class Venue extends BaseAuditingEntity {
+ @Column(nullable = false, unique = true)
private String name;
private String address;
private BigDecimal latitude;
@@ -38,7 +42,6 @@ public class Venue extends BaseAuditingEntity {
private OperationalStatus operationalStatus;
@OneToMany(mappedBy = "venue", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
- @NotNull(message = "Seats list cannot be null.")
- @Size(min = 1, max = 100, message = "Seats list must contain between 1 and 100 seats.")
+ @Column(nullable = false)
private List seats;
}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/enums/RoleType.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/enums/RoleType.java
new file mode 100644
index 0000000..0d64ed4
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/enums/RoleType.java
@@ -0,0 +1,8 @@
+package org.lbcc.bms.bms_monolith.common.enums;
+
+public enum RoleType {
+ GUEST,
+ VENDOR,
+ ADMIN,
+ USER,
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/exception/GlobalExceptionHandler.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/exception/GlobalExceptionHandler.java
index b0de11d..7080791 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/exception/GlobalExceptionHandler.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/common/exception/GlobalExceptionHandler.java
@@ -2,13 +2,20 @@
import org.lbcc.bms.bms_monolith.common.constants.BMSConstants;
import org.lbcc.bms.bms_monolith.common.response.ApiErrorResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception.RoleNotFoundException;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception.UserAlreadyExistsException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
+import java.util.ArrayList;
+import java.util.List;
+
@ControllerAdvice
public class GlobalExceptionHandler {
+
@ExceptionHandler(Exception.class)
public ResponseEntity handleGlobalException(Exception ex) {
return buildErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, BMSConstants.UNEXPECTED_ERROR_MESSAGE, BMSConstants.UNEXPECTED_ERROR_CODE);
@@ -21,4 +28,47 @@ public static ResponseEntity buildErrorResponse(HttpStatus sta
.build();
return ResponseEntity.status(status).body(errorResponse);
}
+
+ @ExceptionHandler(value = MethodArgumentNotValidException.class)
+ public ResponseEntity MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) {
+
+ List errorMessage = new ArrayList<>();
+
+ exception.getBindingResult().getFieldErrors().forEach(error -> {
+ errorMessage.add(error.getDefaultMessage());
+ });
+ return ResponseEntity
+ .badRequest()
+ .body(
+ ApiErrorResponse.builder()
+ // .isSuccess(false)
+ .message("Registration Failed: Please provide valid data.")
+ .code(errorMessage.toString())
+ .build()
+ );
+ }
+
+ @ExceptionHandler(value = UserAlreadyExistsException.class)
+ public ResponseEntity UserAlreadyExistsExceptionHandler(UserAlreadyExistsException exception) {
+ return ResponseEntity
+ .status(HttpStatus.CONFLICT)
+ .body(
+ ApiErrorResponse.builder()
+ // .isSuccess(false)
+ .message(exception.getMessage())
+ .build()
+ );
+ }
+
+ @ExceptionHandler(value = RoleNotFoundException.class)
+ public ResponseEntity RoleNotFoundExceptionHandler(RoleNotFoundException exception) {
+ return ResponseEntity
+ .status(HttpStatus.NOT_FOUND)
+ .body(
+ ApiErrorResponse.builder()
+ .message(exception.getMessage())
+ .build()
+ );
+ }
+
}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatInShowResponse.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatInShowResponse.java
index 6184e9a..0da96b3 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatInShowResponse.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatInShowResponse.java
@@ -8,12 +8,12 @@
@Getter
public class SeatInShowResponse {
- private UUID seatTypeInShowId;
+ private String seatTypeInShowId;
private String bookingStatus;
// Constructors, Getters, and Setters
- public SeatInShowResponse(UUID seatTypeInShowId, String bookingStatus) {
+ public SeatInShowResponse(String seatTypeInShowId, String bookingStatus) {
this.seatTypeInShowId = seatTypeInShowId;
this.bookingStatus = bookingStatus;
}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatTypeInShowResponse.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatTypeInShowResponse.java
index c5601d1..c9f9eff 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatTypeInShowResponse.java
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/eventservice/dto/SeatTypeInShowResponse.java
@@ -9,11 +9,11 @@
@Getter
public class SeatTypeInShowResponse {
- private UUID seatTypeId;
+ private String seatTypeId;
private String seatTypeName;
private BigDecimal price;
- public SeatTypeInShowResponse(UUID seatTypeId, String seatTypeName, BigDecimal price) {
+ public SeatTypeInShowResponse(String seatTypeId, String seatTypeName, BigDecimal price) {
this.seatTypeId = seatTypeId;
this.seatTypeName = seatTypeName;
this.price = price;
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/bootstrap/RoleDataSeeder.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/bootstrap/RoleDataSeeder.java
new file mode 100644
index 0000000..4c40e64
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/bootstrap/RoleDataSeeder.java
@@ -0,0 +1,42 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.bootstrap;
+
+import jakarta.transaction.Transactional;
+import org.lbcc.bms.bms_monolith.common.entity.Role;
+import org.lbcc.bms.bms_monolith.common.enums.RoleType;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository.RoleRepository;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Component;
+
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+@Component
+public class RoleDataSeeder {
+ private final RoleRepository roleRepository;
+
+ public RoleDataSeeder(RoleRepository roleRepository) {
+ this.roleRepository = roleRepository;
+ }
+
+ @EventListener
+ @Transactional
+ public void loadRoles(ContextRefreshedEvent event) {
+
+ List roles = Arrays.stream(RoleType.values()).toList();
+
+ for (RoleType roleType : roles) {
+ if (roleRepository.findByLabel(roleType) == null) {
+ Role role = new Role();
+ role.setId(UUID.randomUUID().toString());
+ role.setLabel(roleType);
+ role.setDescription(roleType.name());
+ role.setCreatedBy("system");
+ role.setCreatedDate(Instant.now());
+ roleRepository.save(role);
+ }
+ }
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/AuthEntryPointJwt.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/AuthEntryPointJwt.java
new file mode 100644
index 0000000..de9cd33
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/AuthEntryPointJwt.java
@@ -0,0 +1,39 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.MediaType;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.AuthenticationEntryPoint;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@Component
+public class AuthEntryPointJwt implements AuthenticationEntryPoint {
+
+ private static final Logger logger = LoggerFactory.getLogger(AuthEntryPointJwt.class);
+
+ @Override
+ public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
+ throws IOException {
+ logger.error("Unauthorized error: {}", authException.getMessage());
+
+ response.setContentType(MediaType.APPLICATION_JSON_VALUE);
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+
+ final Map body = new HashMap<>();
+ body.put("status", HttpServletResponse.SC_UNAUTHORIZED);
+ body.put("error", "Unauthorized");
+ body.put("message", authException.getMessage());
+ body.put("path", request.getServletPath());
+
+ final ObjectMapper mapper = new ObjectMapper();
+ mapper.writeValue(response.getOutputStream(), body);
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/JwtAuthenticationFilter.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/JwtAuthenticationFilter.java
new file mode 100644
index 0000000..3456582
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/JwtAuthenticationFilter.java
@@ -0,0 +1,85 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.config;
+
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.service.JwtService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.lang.NonNull;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+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.WebAuthenticationDetailsSource;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+
+import java.util.Optional;
+
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+ private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
+
+ private final JwtService jwtService;
+ private final UserDetailsService userDetailsService;
+ private final HandlerExceptionResolver handlerExceptionResolver;
+
+ public JwtAuthenticationFilter(
+ JwtService jwtService, UserDetailsService userDetailsService,
+ HandlerExceptionResolver handlerExceptionResolver
+ ) {
+ this.jwtService = jwtService;
+ this.userDetailsService = userDetailsService;
+ this.handlerExceptionResolver = handlerExceptionResolver;
+ }
+
+ @Override
+ protected void doFilterInternal(
+ @NonNull HttpServletRequest request,
+ @NonNull HttpServletResponse response,
+ @NonNull FilterChain filterChain
+ ) {
+ try {
+
+ final Optional jwt = parseJwt(request);
+ if (jwt.isEmpty() || !jwtService.validateJwtToken(jwt.get())) {
+ filterChain.doFilter(request, response);
+ return;
+ }
+
+ String username = jwtService.extractUsername(jwt.get());
+ Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+
+ if (username != null && authentication == null) {
+ UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
+
+ if (jwtService.isTokenValid(jwt.get(), userDetails)) {
+ UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
+
+ authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
+ SecurityContextHolder.getContext().setAuthentication(authToken);
+ }
+ }
+
+ filterChain.doFilter(request, response);
+ } catch (Exception exception) {
+ handlerExceptionResolver.resolveException(request, response, null, exception);
+ }
+
+ }
+
+ private Optional parseJwt(HttpServletRequest request) {
+ String headerAuth = request.getHeader("Authorization");
+
+ if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
+ return Optional.of(headerAuth.substring(7));
+ }
+ return Optional.empty();
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/SecurityConfiguration.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/SecurityConfiguration.java
new file mode 100644
index 0000000..6e34334
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/config/SecurityConfiguration.java
@@ -0,0 +1,108 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.AuthenticationProvider;
+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.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
+
+@Configuration(proxyBeanMethods = false)
+@EnableWebSecurity
+@EnableMethodSecurity
+public class SecurityConfiguration {
+
+ private final UserDetailsService userDetailsService;
+ private final AuthEntryPointJwt unauthorizedHandler;
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
+ //TODO: Load this from application properties
+ List publicApis = List.of("/auth/**");
+
+ public SecurityConfiguration(
+ UserDetailsService userDetailsService, AuthEntryPointJwt unauthorizedHandler,
+ JwtAuthenticationFilter jwtAuthenticationFilter
+ ) {
+ this.userDetailsService = userDetailsService;
+ this.unauthorizedHandler = unauthorizedHandler;
+ this.jwtAuthenticationFilter = jwtAuthenticationFilter;
+ }
+
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
+ return config.getAuthenticationManager();
+ }
+
+ @Bean
+ public AuthenticationProvider authenticationProvider() {
+ DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
+
+ daoAuthenticationProvider.setUserDetailsService(userDetailsService);
+ daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
+
+ return daoAuthenticationProvider;
+ }
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+
+ http.csrf(AbstractHttpConfigurer::disable)
+ .authorizeHttpRequests(authorize -> authorize
+ .requestMatchers(
+ publicApis.stream()
+ .map(AntPathRequestMatcher::new)
+ .toArray(RequestMatcher[]::new)
+ )
+ .permitAll()
+ .anyRequest()
+ .authenticated()
+ )
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
+ .authenticationProvider(authenticationProvider())
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+
+ @Bean
+ CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration configuration = new CorsConfiguration();
+
+ configuration.setAllowedOrigins(List.of("http://localhost:8005"));
+ configuration.setAllowedMethods(List.of("GET", "POST"));
+ configuration.setAllowedHeaders(List.of("Authorization", "Content-Type"));
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+
+ source.registerCorsConfiguration("/**", configuration);
+
+ return source;
+ }
+
+ @Bean
+ public WebSecurityCustomizer webSecurityCustomizer() {
+ return (web) -> web.ignoring().requestMatchers(publicApis.toArray(String[]::new));
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/controller/LoginController.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/controller/LoginController.java
new file mode 100644
index 0000000..da01e2c
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/controller/LoginController.java
@@ -0,0 +1,36 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.controller;
+
+import jakarta.validation.Valid;
+import org.lbcc.bms.bms_monolith.common.response.ApiResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.LogInRequest;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.LogInResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.service.LoginService;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/auth/login")
+public class LoginController {
+
+ private final LoginService loginService;
+
+ public LoginController(LoginService loginService) {
+ this.loginService = loginService;
+ }
+
+ @PostMapping
+ public ResponseEntity> login(
+ @RequestBody @Valid LogInRequest logInRequest
+ ) {
+ LogInResponse loginResponse = loginService.login(logInRequest);
+ ApiResponse response = ApiResponse.builder()
+ .success(true)
+ .message("Login successful!!")
+ .data(loginResponse)
+ .build();
+ return ResponseEntity.ok(response);
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/controller/RegisterController.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/controller/RegisterController.java
new file mode 100644
index 0000000..0a23476
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/controller/RegisterController.java
@@ -0,0 +1,37 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.controller;
+
+import jakarta.validation.Valid;
+import org.lbcc.bms.bms_monolith.common.response.ApiResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.SignUpRequest;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.SignUpResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.service.RegisterService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/auth/register")
+public class RegisterController {
+
+ private final RegisterService registerService;
+
+ public RegisterController(RegisterService registerService) {
+ this.registerService = registerService;
+ }
+
+ @PostMapping
+ public ResponseEntity> registerUser(
+ @RequestBody @Valid SignUpRequest signUpRequest
+ ) {
+ SignUpResponse signUpUser = registerService.signUpUser(signUpRequest);
+ ApiResponse response = ApiResponse.builder()
+ .success(true)
+ .message("User registered successfully!!")
+ .data(signUpUser)
+ .build();
+ return ResponseEntity.status(HttpStatus.CREATED).body(response);
+ }
+}
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/LogInRequest.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/LogInRequest.java
new file mode 100644
index 0000000..d3de372
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/LogInRequest.java
@@ -0,0 +1,8 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto;
+
+public record LogInRequest(
+ //TODO: Add validation
+ String username,
+ String password
+) {
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/LogInResponse.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/LogInResponse.java
new file mode 100644
index 0000000..b37c199
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/LogInResponse.java
@@ -0,0 +1,7 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto;
+
+public record LogInResponse(
+ String token,
+ long expiresIn
+) {
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/SignUpRequest.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/SignUpRequest.java
new file mode 100644
index 0000000..357297c
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/SignUpRequest.java
@@ -0,0 +1,33 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.Set;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class SignUpRequest {
+ @NotBlank(message = "Username is required!")
+ @Size(min = 3, message = "Username must have atleast 3 characters!")
+ @Size(max = 20, message = "Username can have have atmost 20 characters!")
+ private String username;
+
+ @Email(message = "Email is not in valid format!")
+ @NotBlank(message = "Email is required!")
+ private String email;
+
+ @NotBlank(message = "Password is required!")
+ @Size(min = 8, message = "Password must have atleast 8 characters!")
+ @Size(max = 20, message = "Password can have have atmost 20 characters!")
+ private String password;
+
+ private Set roles;
+
+
+}
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/SignUpResponse.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/SignUpResponse.java
new file mode 100644
index 0000000..d49a60d
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/dto/SignUpResponse.java
@@ -0,0 +1,6 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto;
+
+public record SignUpResponse(
+ String username
+) {
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/exeception/RoleNotFoundException.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/exeception/RoleNotFoundException.java
new file mode 100644
index 0000000..365350e
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/exeception/RoleNotFoundException.java
@@ -0,0 +1,7 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception;
+
+public class RoleNotFoundException extends RuntimeException {
+ public RoleNotFoundException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/exeception/UserAlreadyExistsException.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/exeception/UserAlreadyExistsException.java
new file mode 100644
index 0000000..7be1bd9
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/exeception/UserAlreadyExistsException.java
@@ -0,0 +1,7 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception;
+
+public class UserAlreadyExistsException extends RuntimeException {
+ public UserAlreadyExistsException(String message) {
+ super(message);
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/model/UserDetailsModel.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/model/UserDetailsModel.java
new file mode 100644
index 0000000..632d263
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/model/UserDetailsModel.java
@@ -0,0 +1,60 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.model;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.lbcc.bms.bms_monolith.common.entity.RegisteredUser;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.io.Serial;
+import java.util.Collection;
+import java.util.stream.Collectors;
+
+@AllArgsConstructor
+@Data
+public class UserDetailsModel implements UserDetails {
+
+ @Serial
+ private static final long serialVersionUID = -4335856020700036116L;
+ private RegisteredUser registeredUser;
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+
+ return registeredUser.getRoles().stream()
+ .map(role -> new SimpleGrantedAuthority(role.getLabel().name()))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public String getPassword() {
+ return registeredUser.getPassword();
+ }
+
+ @Override
+ public String getUsername() {
+ return registeredUser.getUsername();
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return registeredUser.getIsActive();
+ }
+}
+
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/repository/RegisteredUserRepository.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/repository/RegisteredUserRepository.java
new file mode 100644
index 0000000..405421e
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/repository/RegisteredUserRepository.java
@@ -0,0 +1,17 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository;
+
+import org.lbcc.bms.bms_monolith.common.entity.RegisteredUser;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+import java.util.Optional;
+
+@Repository
+public interface RegisteredUserRepository extends JpaRepository {
+
+ Optional findByUsername(String email);
+
+ Boolean existsByUsername(String username);
+
+ Boolean existsByEmail(String email);
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/repository/RoleRepository.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/repository/RoleRepository.java
new file mode 100644
index 0000000..76090fa
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/repository/RoleRepository.java
@@ -0,0 +1,11 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository;
+
+import org.lbcc.bms.bms_monolith.common.entity.Role;
+import org.lbcc.bms.bms_monolith.common.enums.RoleType;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface RoleRepository extends JpaRepository {
+ Role findByLabel(RoleType label);
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/DaoUserDetailsService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/DaoUserDetailsService.java
new file mode 100644
index 0000000..fc990b9
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/DaoUserDetailsService.java
@@ -0,0 +1,30 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.service;
+
+import org.lbcc.bms.bms_monolith.common.entity.RegisteredUser;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.model.UserDetailsModel;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository.RegisteredUserRepository;
+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;
+
+@Service
+public class DaoUserDetailsService implements UserDetailsService {
+
+ private final RegisteredUserRepository registeredUserRepository;
+
+ public DaoUserDetailsService(RegisteredUserRepository registeredUserRepository) {
+ this.registeredUserRepository = registeredUserRepository;
+ }
+
+ @Override
+ public UserDetails loadUserByUsername(String username) {
+ RegisteredUser registeredUser =
+ registeredUserRepository
+ .findByUsername(username)
+ .orElseThrow(
+ () -> new UsernameNotFoundException("User Not Found with username: " + username));
+
+ return new UserDetailsModel(registeredUser);
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/JwtService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/JwtService.java
new file mode 100644
index 0000000..cdaf6b2
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/JwtService.java
@@ -0,0 +1,117 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.service;
+
+import io.jsonwebtoken.*;
+import io.jsonwebtoken.io.Decoders;
+import io.jsonwebtoken.security.Keys;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.stereotype.Service;
+
+import java.security.Key;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+
+@Service
+public class JwtService {
+ private static final Logger logger = LoggerFactory.getLogger(JwtService.class);
+
+ private final String secretKey;
+ private final int expirationInMs;
+
+ public JwtService(
+ @Value("${app.auth.jwt.secret-key}") String secretKey,
+ @Value("${app.auth.jwt.expiration-in-ms}") int expirationInMs
+ ) {
+ this.secretKey = secretKey;
+ this.expirationInMs = expirationInMs;
+ }
+
+ public long getDefaultExpirationTime() {
+ return expirationInMs;
+ }
+
+ private Key getSignInKey() {
+ byte[] keyBytes = Decoders.BASE64.decode(secretKey);
+ return Keys.hmacShaKeyFor(keyBytes);
+ }
+
+ private Claims extractAllClaims(String token) {
+ return Jwts
+ .parserBuilder()
+ .setSigningKey(getSignInKey())
+ .build()
+ .parseClaimsJws(token)
+ .getBody();
+ }
+
+ public T extractClaim(String token, Function claimsResolver) {
+ final Claims claims = extractAllClaims(token);
+ return claimsResolver.apply(claims);
+ }
+
+ public String generateToken(UserDetails userDetails) {
+ return generateToken(new HashMap<>(), userDetails);
+ }
+
+ public String generateToken(Map extraClaims, UserDetails userDetails) {
+ return buildToken(extraClaims, userDetails, expirationInMs);
+ }
+
+ private String buildToken(
+ Map extraClaims,
+ UserDetails userDetails,
+ long expiration
+ ) {
+ return Jwts
+ .builder()
+ .setClaims(extraClaims)
+ .setSubject(userDetails.getUsername())
+ .setIssuedAt(new Date(System.currentTimeMillis()))
+ .setExpiration(new Date(System.currentTimeMillis() + expiration))
+ .signWith(getSignInKey(), SignatureAlgorithm.HS256)
+ .compact();
+ }
+
+
+ public String extractUsername(String token) {
+ return extractClaim(token, Claims::getSubject);
+ }
+
+
+ public boolean isTokenValid(String token, UserDetails userDetails) {
+ final String username = extractUsername(token);
+ return (username.equals(userDetails.getUsername())) && !isTokenExpired(token);
+ }
+
+ private boolean isTokenExpired(String token) {
+ return extractExpiration(token).before(new Date());
+ }
+
+
+ private Date extractExpiration(String token) {
+ return extractClaim(token, Claims::getExpiration);
+ }
+
+
+ public boolean validateJwtToken(String authToken) {
+ try {
+ Jwts.parserBuilder().setSigningKey(getSignInKey()).build().parse(authToken);
+ return true;
+ } catch (MalformedJwtException e) {
+ logger.error("Invalid JWT token: {}", e.getMessage());
+ } catch (ExpiredJwtException e) {
+ logger.error("JWT token is expired: {}", e.getMessage());
+ } catch (UnsupportedJwtException e) {
+ logger.error("JWT token is unsupported: {}", e.getMessage());
+ } catch (IllegalArgumentException e) {
+ logger.error("JWT claims string is empty: {}", e.getMessage());
+ }
+
+ return false;
+ }
+}
+
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/LoginService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/LoginService.java
new file mode 100644
index 0000000..29346a5
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/LoginService.java
@@ -0,0 +1,49 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.service;
+
+import org.lbcc.bms.bms_monolith.common.entity.RegisteredUser;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.LogInRequest;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.LogInResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.model.UserDetailsModel;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository.RegisteredUserRepository;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class LoginService {
+
+ private final JwtService jwtService;
+ private final AuthenticationManager authenticationManager;
+ private final RegisteredUserRepository registeredUserRepository;
+
+ public LoginService(
+ JwtService jwtService, AuthenticationManager authenticationManager,
+ RegisteredUserRepository registeredUserRepository
+ ) {
+ this.jwtService = jwtService;
+ this.authenticationManager = authenticationManager;
+ this.registeredUserRepository = registeredUserRepository;
+ }
+
+
+ public LogInResponse login(LogInRequest loginInRequest) {
+
+ authenticationManager.authenticate(
+ new UsernamePasswordAuthenticationToken(
+ loginInRequest.username(),
+ loginInRequest.password()
+ )
+ );
+
+ RegisteredUser authenticatedUser = registeredUserRepository
+ .findByUsername(loginInRequest.username())
+ .orElseThrow(() -> new UsernameNotFoundException("Username not found!!"));
+ String jwtToken = jwtService.generateToken(new UserDetailsModel(authenticatedUser));
+
+ return new LogInResponse(
+ jwtToken,
+ jwtService.getDefaultExpirationTime()
+ );
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RegisterService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RegisterService.java
new file mode 100644
index 0000000..601ed06
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RegisterService.java
@@ -0,0 +1,66 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.service;
+
+import org.lbcc.bms.bms_monolith.common.entity.RegisteredUser;
+import org.lbcc.bms.bms_monolith.common.entity.Role;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.SignUpRequest;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.dto.SignUpResponse;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception.RoleNotFoundException;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception.UserAlreadyExistsException;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Component
+public class RegisterService {
+
+ private final RegisteredUserService registeredUserService;
+ private final RoleFactoryService roleFactory;
+ private final PasswordEncoder passwordEncoder;
+
+ public RegisterService(
+ RegisteredUserService registeredUserService, RoleFactoryService roleFactory,
+ PasswordEncoder passwordEncoder
+ ) {
+ this.registeredUserService = registeredUserService;
+ this.roleFactory = roleFactory;
+ this.passwordEncoder = passwordEncoder;
+ }
+
+ public SignUpResponse signUpUser(SignUpRequest signUpRequest)
+ throws UserAlreadyExistsException, RoleNotFoundException {
+ if (registeredUserService.existsByEmail(signUpRequest.getEmail())) {
+ throw new UserAlreadyExistsException("Registration Failed: Provided email already exists. Try sign in or provide another email.");
+ }
+ if (registeredUserService.existsByUsername(signUpRequest.getUsername())) {
+ throw new UserAlreadyExistsException("Registration Failed: Provided username already exists. Try sign in or provide another username.");
+ }
+
+ RegisteredUser registeredUser = createUser(signUpRequest);
+ registeredUserService.save(registeredUser);
+ return new SignUpResponse(registeredUser.getUsername());
+ }
+
+ private RegisteredUser createUser(SignUpRequest signUpRequest) throws RoleNotFoundException {
+ return RegisteredUser.builder()
+ .email(signUpRequest.getEmail())
+ .username(signUpRequest.getUsername())
+ .password(passwordEncoder.encode(signUpRequest.getPassword()))
+ .roles(determineRoles(signUpRequest.getRoles()))
+ .build();
+ }
+
+ private Set determineRoles(Set strRoles) throws RoleNotFoundException {
+ Set roles = new HashSet<>();
+
+ if (strRoles == null) {
+ roles.add(roleFactory.getInstance("user"));
+ } else {
+ for (String role : strRoles) {
+ roles.add(roleFactory.getInstance(role.toLowerCase()));
+ }
+ }
+ return roles;
+ }
+}
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RegisteredUserService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RegisteredUserService.java
new file mode 100644
index 0000000..39d37b5
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RegisteredUserService.java
@@ -0,0 +1,27 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.service;
+
+import org.lbcc.bms.bms_monolith.common.entity.RegisteredUser;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository.RegisteredUserRepository;
+import org.springframework.stereotype.Service;
+
+@Service
+public class RegisteredUserService {
+
+ private final RegisteredUserRepository registeredUserRepository;
+
+ public RegisteredUserService(RegisteredUserRepository registeredUserRepository) {
+ this.registeredUserRepository = registeredUserRepository;
+ }
+
+ public boolean existsByUsername(String username) {
+ return registeredUserRepository.existsByUsername(username);
+ }
+
+ public boolean existsByEmail(String email) {
+ return registeredUserRepository.existsByEmail(email);
+ }
+
+ public void save(RegisteredUser registeredUser) {
+ registeredUserRepository.save(registeredUser);
+ }
+}
diff --git a/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RoleFactoryService.java b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RoleFactoryService.java
new file mode 100644
index 0000000..00eba9d
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/java/org/lbcc/bms/bms_monolith/security/uesrnamepassword/service/RoleFactoryService.java
@@ -0,0 +1,27 @@
+package org.lbcc.bms.bms_monolith.security.uesrnamepassword.service;
+
+import org.lbcc.bms.bms_monolith.common.entity.Role;
+import org.lbcc.bms.bms_monolith.common.enums.RoleType;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.exeception.RoleNotFoundException;
+import org.lbcc.bms.bms_monolith.security.uesrnamepassword.repository.RoleRepository;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RoleFactoryService {
+
+ private final RoleRepository roleRepository;
+
+ public RoleFactoryService(RoleRepository roleRepository) {
+ this.roleRepository = roleRepository;
+ }
+
+ public Role getInstance(String role) throws RoleNotFoundException {
+ return switch (role.toLowerCase()) {
+ case "admin" -> roleRepository.findByLabel(RoleType.ADMIN);
+ case "user" -> roleRepository.findByLabel(RoleType.USER);
+ case "vendor" -> roleRepository.findByLabel(RoleType.VENDOR);
+ case "guest" -> roleRepository.findByLabel(RoleType.GUEST);
+ default -> throw new RoleNotFoundException("No role found for " + role);
+ };
+ }
+}
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml b/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml
index a30b3ae..ea81d5d 100644
--- a/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml
+++ b/book-my-show/backend/java/bms-monolith/src/main/resources/application.yaml
@@ -1,4 +1,4 @@
-Spring:
+spring:
application:
name: BMS Monolith
datasource:
@@ -9,10 +9,18 @@ Spring:
jpa:
properties:
hibernate:
- dialect: org.hibernate.dialect.MySQLDialect
+ dialect: org.hibernate.dialect.MySQL8Dialect
hibernate:
- ddl-auto: update
+ ddl-auto: validate
show-sql: true
+ liquibase:
+ change-log: classpath:db/changelog/changelog-master.xml
+ enabled: true
server:
servlet:
- context-path: /api/v1
\ No newline at end of file
+ context-path: /api/v1
+app:
+ auth:
+ jwt:
+ secret-key: ${JWT_SECRET_KEY:ZmxhbWVyb2xsc3RvcHBlZGNyZWFtY29sbGVjdGZhbGxzb2xkaWVydG9uZWdpdmVua24=}
+ expiration-in-ms: ${JWT_EXPIRATION_IN_MS:50000}
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/changelog-master.xml b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/changelog-master.xml
new file mode 100644
index 0000000..ba0c725
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/changelog-master.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/add-auth-tables.xml b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/add-auth-tables.xml
new file mode 100644
index 0000000..c8be503
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/add-auth-tables.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/add-constraints.xml b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/add-constraints.xml
new file mode 100644
index 0000000..4db64bc
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/add-constraints.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/create-tables.xml b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/create-tables.xml
new file mode 100644
index 0000000..e580d92
--- /dev/null
+++ b/book-my-show/backend/java/bms-monolith/src/main/resources/db/changelog/v1.0/create-tables.xml
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+