From 4b7c5e32b76d231878639b23481785c067d4ae03 Mon Sep 17 00:00:00 2001 From: Stark Date: Wed, 16 Oct 2024 20:51:24 +0200 Subject: [PATCH 1/2] Code Improvements --- checkstyle.xml | 9 +- config.properties | 37 +- mvnw | 0 .../furizon/backend/BackendApplication.java | 10 +- .../java/net/furizon/backend/CmdRunner.java | 28 - .../backend/db/entities/pretix/Event.java | 117 ++- .../backend/db/entities/pretix/Order.java | 360 ++++--- .../backend/db/entities/pretix/Room.java | 27 +- .../backend/db/entities/pretix/RoomGuest.java | 17 +- .../db/entities/users/AuthenticationData.java | 19 +- .../backend/db/entities/users/Group.java | 30 +- .../backend/db/entities/users/User.java | 31 +- .../backend/db/entities/users/UserGroup.java | 33 +- .../db/entities/users/content/Fursuit.java | 14 +- .../db/entities/users/content/Media.java | 17 +- .../db/entities/users/content/MediaTags.java | 12 +- .../db/entities/users/content/Tag.java | 18 +- ...ntRepository.java => EventRepository.java} | 2 +- ...erRepository.java => OrderRepository.java} | 2 +- ...java => AuthenticationDataRepository.java} | 2 +- .../repositories/users/IUserRepository.java | 21 - .../db/repositories/users/UserRepository.java | 19 + .../security/SecurityConfiguration.java | 240 ++--- .../security/entities/UserSecurity.java | 10 +- .../{ICodeVerifier.java => CodeVerifier.java} | 2 +- .../managers/OTPAuthorizationManager.java | 25 +- .../backend/service/pretix/PretixService.java | 917 ++++++++++-------- .../backend/service/users/UserService.java | 161 +-- .../service/users/content/FursuitService.java | 2 - .../net/furizon/backend/utils/Download.java | 810 +++++++++------- .../furizon/backend/utils/SecurityUtil.java | 9 +- .../net/furizon/backend/utils/TextUtil.java | 8 +- .../backend/utils/ThrowableSupplier.java | 2 +- .../backend/utils/configs/BackendConfig.java | 22 - .../backend/utils/configs/PretixConfig.java | 63 +- .../utils/configs/PretixConnectionConfig.java | 3 - .../backend/utils/pretix/Constants.java | 38 +- .../backend/utils/pretix/ExtraDays.java | 11 +- .../backend/utils/pretix/OrderStatus.java | 28 +- .../backend/utils/pretix/QuestionType.java | 42 +- .../backend/utils/pretix/Sponsorship.java | 14 +- .../users/AuthenticationController.java | 11 +- .../web/entities/HttpErrorResponse.java | 12 +- .../entities/users/UserCreationRequest.java | 3 +- .../web/entities/users/UserResponse.java | 5 +- .../web/handlers/ExceptionHandler.java | 18 +- .../handlers/Oauth2LoginSuccessHandler.java | 29 + 47 files changed, 1862 insertions(+), 1448 deletions(-) mode change 100644 => 100755 mvnw delete mode 100644 src/main/java/net/furizon/backend/CmdRunner.java rename src/main/java/net/furizon/backend/db/repositories/pretix/{IEventRepository.java => EventRepository.java} (68%) rename src/main/java/net/furizon/backend/db/repositories/pretix/{IOrderRepository.java => OrderRepository.java} (85%) rename src/main/java/net/furizon/backend/db/repositories/users/{IAuthenticationDataRepository.java => AuthenticationDataRepository.java} (64%) delete mode 100644 src/main/java/net/furizon/backend/db/repositories/users/IUserRepository.java create mode 100644 src/main/java/net/furizon/backend/db/repositories/users/UserRepository.java rename src/main/java/net/furizon/backend/security/interfaces/{ICodeVerifier.java => CodeVerifier.java} (81%) delete mode 100644 src/main/java/net/furizon/backend/utils/configs/BackendConfig.java diff --git a/checkstyle.xml b/checkstyle.xml index 6d0fa0cd..ccb43224 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -17,7 +17,6 @@ --> - @@ -83,15 +82,9 @@ value="ANNOTATION_DEF, CLASS_DEF, CTOR_DEF, ENUM_CONSTANT_DEF, ENUM_DEF, INTERFACE_DEF, LAMBDA, LITERAL_CATCH, LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_IF, - LITERAL_SWITCH, LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF, + LITERAL_SYNCHRONIZED, LITERAL_TRY, LITERAL_WHILE, METHOD_DEF, OBJBLOCK, STATIC_INIT, RECORD_DEF, COMPACT_CTOR_DEF"/> - - - - - Move to Application yml +pretix.protocol=http +pretix.host-name=localhost +pretix.port=8000 +pretix.api-key=xxx +pretix.organizer=org +pretix.current-event=evt +pretix.run-healthcheck=true +pretix.max-connection-retries=3 +pretix.max-connections=10 +pretix.connection-timeout=10000 +pretix.max-propic-file-size-bytes=5242880 +pretix.max-propic-width=3000 +pretix.min-propic-width=3000 +pretix.max-propic-height=300 +pretix.min-propic-height=300 diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/src/main/java/net/furizon/backend/BackendApplication.java b/src/main/java/net/furizon/backend/BackendApplication.java index a8b7b439..5d7c21ca 100644 --- a/src/main/java/net/furizon/backend/BackendApplication.java +++ b/src/main/java/net/furizon/backend/BackendApplication.java @@ -1,15 +1,11 @@ package net.furizon.backend; -import net.furizon.backend.service.pretix.PretixService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class BackendApplication { - - public static void main(String[] args) { - SpringApplication.run(BackendApplication.class, args); - } - + public static void main(String[] args) { + SpringApplication.run(BackendApplication.class, args); + } } diff --git a/src/main/java/net/furizon/backend/CmdRunner.java b/src/main/java/net/furizon/backend/CmdRunner.java deleted file mode 100644 index 942113b8..00000000 --- a/src/main/java/net/furizon/backend/CmdRunner.java +++ /dev/null @@ -1,28 +0,0 @@ -package net.furizon.backend; - -import net.furizon.backend.service.pretix.PretixService; -import net.furizon.backend.service.users.UserService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; -import org.springframework.stereotype.Component; - -@Component -public class CmdRunner implements CommandLineRunner { - - @Autowired - private PretixService pretixService; - - @Autowired - private UserService userService; - - @Override - public void run(String... args) throws Exception { - pretixService.setupClient(); - pretixService.reloadEverything(); - try { - userService.register("woffo@woffo.ovh", "pisolino"); - } catch (Throwable e ){ - e.printStackTrace(System.out); - } - } -} diff --git a/src/main/java/net/furizon/backend/db/entities/pretix/Event.java b/src/main/java/net/furizon/backend/db/entities/pretix/Event.java index bb4bcbda..87a0b936 100644 --- a/src/main/java/net/furizon/backend/db/entities/pretix/Event.java +++ b/src/main/java/net/furizon/backend/db/entities/pretix/Event.java @@ -1,6 +1,14 @@ package net.furizon.backend.db.entities.pretix; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import jakarta.persistence.Temporal; +import jakarta.persistence.TemporalType; +import jakarta.persistence.Transient; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; @@ -13,68 +21,79 @@ import java.util.Map; import java.util.Set; +// TODO -> Replace on JOOQ + @Entity @Table(name = "events") @Getter @Setter public final class Event { + @Id + @Column(name = "event_slug") + @Setter(AccessLevel.NONE) + private String slug; - @Id - @Column(name = "event_slug") - @Setter(AccessLevel.NONE) - private String slug; - - @Column(name = "event_public_url") - private String publicUrl; + @Column(name = "event_public_url") + private String publicUrl; - @Transient - @Setter(AccessLevel.NONE) - @Getter(AccessLevel.NONE) - private Map eventNames; - private String eventNamesRaw;//map lang -> name + @Transient + @Setter(AccessLevel.NONE) + @Getter(AccessLevel.NONE) + private Map eventNames; + private String eventNamesRaw; //map lang -> name - @Column(name = "event_date_from") - @Temporal(TemporalType.TIMESTAMP) - private Date dateFrom; - @Column(name = "event_date_end") - @Temporal(TemporalType.TIMESTAMP) - private Date dateEnd; + @Column(name = "event_date_from") + @Temporal(TemporalType.TIMESTAMP) + private Date dateFrom; + @Column(name = "event_date_end") + @Temporal(TemporalType.TIMESTAMP) + private Date dateEnd; - @Column(name = "event_is_current") - private boolean isCurrentEvent; + @Column(name = "event_is_current") + private boolean isCurrentEvent; - @OneToMany(mappedBy = "orderEvent", fetch = FetchType.LAZY) - private Set orders; + @OneToMany(mappedBy = "orderEvent", fetch = FetchType.LAZY) + private Set orders; - public Event() {} + public Event() { + } - public Event(String organizer, String eventCode, String publicUrl, Map eventName, String dateFrom, String dateEnd){ - slug = getSlug(organizer, eventCode); - this.publicUrl = TextUtil.leadingSlash(publicUrl) + eventCode; - this.eventNames = eventName; - this.eventNamesRaw = new JSONObject(eventName).toString(); - this.dateFrom = Date.from(ZonedDateTime.parse(dateFrom).toInstant()); - this.dateEnd = Date.from(ZonedDateTime.parse(dateEnd).toInstant()); - } + public Event( + String organizer, + String eventCode, + String publicUrl, + Map eventName, + String dateFrom, + String dateEnd + ) { + slug = getSlug(organizer, eventCode); + this.publicUrl = TextUtil.leadingSlash(publicUrl) + eventCode; + this.eventNames = eventName; + this.eventNamesRaw = new JSONObject(eventName).toString(); + this.dateFrom = Date.from(ZonedDateTime.parse(dateFrom).toInstant()); + this.dateEnd = Date.from(ZonedDateTime.parse(dateEnd).toInstant()); + } - public String getEventName(String lang){ - if(eventNames == null){ - eventNames = new HashMap<>(); - JSONObject obj = new JSONObject(eventNamesRaw); - for(String s : obj.keySet()) eventNames.put(s, obj.getString(s)); - } - return eventNames.get(lang); - } + public String getEventName(String lang) { + if (eventNames == null) { + eventNames = new HashMap<>(); + JSONObject obj = new JSONObject(eventNamesRaw); + for (String s : obj.keySet()) { + eventNames.put(s, obj.getString(s)); + } + } + return eventNames.get(lang); + } - public void setSlug (String organizer, String event) { - this.slug = getSlug(organizer, event); - } + public void setSlug(String organizer, String event) { + this.slug = getSlug(organizer, event); + } - public void setSlug (String slug){ - this.slug = slug; - } + public void setSlug(String slug) { + this.slug = slug; + } - public static String getSlug(String organizer, String event){ - return organizer + "/" + event; - } + public static String getSlug(String organizer, String event) { + return organizer + "/" + event; + } } diff --git a/src/main/java/net/furizon/backend/db/entities/pretix/Order.java b/src/main/java/net/furizon/backend/db/entities/pretix/Order.java index 40466b0f..3a7e7366 100644 --- a/src/main/java/net/furizon/backend/db/entities/pretix/Order.java +++ b/src/main/java/net/furizon/backend/db/entities/pretix/Order.java @@ -1,12 +1,22 @@ package net.furizon.backend.db.entities.pretix; -import jakarta.persistence.*; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; import net.furizon.backend.db.entities.users.User; import net.furizon.backend.service.pretix.PretixService; -import net.furizon.backend.utils.pretix.*; +import net.furizon.backend.utils.pretix.Constants; +import net.furizon.backend.utils.pretix.ExtraDays; +import net.furizon.backend.utils.pretix.OrderStatus; +import net.furizon.backend.utils.pretix.QuestionType; +import net.furizon.backend.utils.pretix.Sponsorship; import org.apache.http.entity.ContentType; import org.json.JSONArray; import org.json.JSONObject; @@ -14,178 +24,224 @@ import java.time.LocalDate; import java.time.LocalTime; import java.time.ZonedDateTime; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeoutException; +// TODO -> Replace on JOOQ + @Entity @Table(name = "orders") @Getter public class Order { + @Id + private String code; - @Id - private String code; - - private OrderStatus orderStatus; + private OrderStatus orderStatus; @Getter(AccessLevel.NONE) - private long dailyDays = 0L; //bitmask of days + private long dailyDays = 0L; //bitmask of days - private Sponsorship sponsorship = Sponsorship.NONE; + private Sponsorship sponsorship = Sponsorship.NONE; - private ExtraDays extraDays = ExtraDays.NONE; + private ExtraDays extraDays = ExtraDays.NONE; - private int roomCapacity = 0; // 0 = has no room + private int roomCapacity = 0; // 0 = has no room - private String hotelLocation = "ITALY"; + private String hotelLocation = "ITALY"; - private String pretixOrderSecret = "GABIBBO"; + private String pretixOrderSecret = "GABIBBO"; - private boolean hasMembership = false; + private boolean hasMembership = false; - private int answersMainPositionId = -1; + private int answersMainPositionId = -1; @Getter(AccessLevel.NONE) - private String answers; + private String answers; - @ManyToOne - @JoinColumn(name = "user_id") - private User orderOwner; + @ManyToOne + @JoinColumn(name = "user_id") + private User orderOwner; - @ManyToOne - @JoinColumn(name = "event_id") - private Event orderEvent; + @ManyToOne + @JoinColumn(name = "event_id") + private Event orderEvent; - @OneToOne(mappedBy = "roomOrder") - @Setter - private Room orderRoom; + @OneToOne(mappedBy = "roomOrder") + @Setter + private Room orderRoom; - @Transient + @Transient @Getter(AccessLevel.NONE) - private Map answersData = null; - private void loadAnswers(PretixService ps) { - JSONArray jsonArray = new JSONArray(answers); - Map answersData = new HashMap(); - for(int i = 0; i < jsonArray.length(); i++){ - JSONObject obj = jsonArray.getJSONObject(i); - int questionId = obj.getInt("question"); - String answerIdentifier = ps.translateQuestionId(questionId); - String value = obj.getString("answer"); - Object o = switch(ps.translateQuestionType(questionId)){ - case NUMBER -> Float.parseFloat(value); - case STRING_ONE_LINE -> value; - case STRING_MULTI_LINE -> value; - case BOOLEAN -> value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes"); - case LIST_SINGLE_CHOICE -> value; - case LIST_MULTIPLE_CHOICE -> value; - case FILE -> Constants.QUESTIONS_FILE_KEEP; - case DATE -> LocalDate.parse(value); - case TIME -> LocalTime.parse(value); - case DATE_TIME -> ZonedDateTime.parse(value); - case COUNTRY_CODE -> value; - case PHONE_NUMBER -> value; - }; - answersData.put(answerIdentifier, o); - } - this.answersData = answersData; - } - private void saveAnswers(PretixService ps) { - if(answersData == null) loadAnswers(ps); - JSONArray jsonArray = new JSONArray(); - for(String key : answersData.keySet()){ - Object o = answersData.get(key); - String out = switch(ps.translateQuestionType(key)){ - case QuestionType.NUMBER -> String.valueOf(o); - case STRING_ONE_LINE -> (String) o; - case STRING_MULTI_LINE -> (String) o; - case BOOLEAN -> ((boolean) o) ? "true" : "false"; - case LIST_SINGLE_CHOICE -> (String) o; - case LIST_MULTIPLE_CHOICE -> (String) o; - case FILE -> (String) o; - case DATE -> o.toString(); - case TIME -> o.toString(); - case DATE_TIME -> o.toString(); - case COUNTRY_CODE -> (String) o; - case PHONE_NUMBER -> (String) o; - }; - out = out.strip(); - if(!out.isEmpty()) - jsonArray.put(out); - } - answers = jsonArray.toString(); - } - - public Object getAnswerValue(String answer, PretixService ps){ - if(answersData == null) loadAnswers(ps); - return answersData.get(answer); - - } - public void setAnswerFile(String answer, ContentType mimeType, String fileName, byte[] bytes, PretixService ps) throws TimeoutException { - String newAns = ps.uploadFile(mimeType, fileName, bytes); - this.setAnswerValue(answer, newAns, ps); - } - public void setAnswerValue(String answer, Object value, PretixService ps){ - if(answersData == null) loadAnswers(ps); - answersData.put(answer, value); - saveAnswers(ps); - } - public void removeAnswer(String answer, PretixService ps){ - if(answersData == null) loadAnswers(ps); - answersData.remove(answer); - saveAnswers(ps); - } - public String getAnswersRaw(){ - return answers; - } - public void resetFileUploadAnswers(PretixService ps){ //After uploading a file, we need to change it's value to "file:keep" - for (String key : answersData.keySet()) { - if(ps.translateQuestionType(key) == QuestionType.FILE) - answersData.put(key, Constants.QUESTIONS_FILE_KEEP); - } - saveAnswers(ps); - } - - public boolean isDaily(){ - return dailyDays != 0L; - } - - public boolean hasDay(int day){ - return (dailyDays & (1L << day)) != 0L; - } - - @Transient + private Map answersData = null; + + private void loadAnswers(PretixService ps) { + JSONArray jsonArray = new JSONArray(answers); + Map answersData = new HashMap(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject obj = jsonArray.getJSONObject(i); + int questionId = obj.getInt("question"); + String answerIdentifier = ps.translateQuestionId(questionId); + String value = obj.getString("answer"); + Object o = switch (ps.translateQuestionType(questionId)) { + case NUMBER -> Float.parseFloat(value); + case STRING_ONE_LINE -> value; + case STRING_MULTI_LINE -> value; + case BOOLEAN -> value.equalsIgnoreCase("true") || value.equalsIgnoreCase("yes"); + case LIST_SINGLE_CHOICE -> value; + case LIST_MULTIPLE_CHOICE -> value; + case FILE -> Constants.QUESTIONS_FILE_KEEP; + case DATE -> LocalDate.parse(value); + case TIME -> LocalTime.parse(value); + case DATE_TIME -> ZonedDateTime.parse(value); + case COUNTRY_CODE -> value; + case PHONE_NUMBER -> value; + }; + answersData.put(answerIdentifier, o); + } + this.answersData = answersData; + } + + private void saveAnswers(PretixService ps) { + if (answersData == null) { + loadAnswers(ps); + } + JSONArray jsonArray = new JSONArray(); + for (String key : answersData.keySet()) { + Object o = answersData.get(key); + String out = switch (ps.translateQuestionType(key)) { + case QuestionType.NUMBER -> String.valueOf(o); + case STRING_ONE_LINE -> (String) o; + case STRING_MULTI_LINE -> (String) o; + case BOOLEAN -> ((boolean) o) ? "true" : "false"; + case LIST_SINGLE_CHOICE -> (String) o; + case LIST_MULTIPLE_CHOICE -> (String) o; + case FILE -> (String) o; + case DATE -> o.toString(); + case TIME -> o.toString(); + case DATE_TIME -> o.toString(); + case COUNTRY_CODE -> (String) o; + case PHONE_NUMBER -> (String) o; + }; + out = out.strip(); + if (!out.isEmpty()) { + jsonArray.put(out); + } + } + answers = jsonArray.toString(); + } + + public Object getAnswerValue(String answer, PretixService ps) { + if (answersData == null) { + loadAnswers(ps); + } + return answersData.get(answer); + + } + + public void setAnswerFile( + String answer, + ContentType mimeType, + String fileName, + byte[] bytes, + PretixService ps + ) throws TimeoutException { + String newAns = ps.uploadFile(mimeType, fileName, bytes); + this.setAnswerValue(answer, newAns, ps); + } + + public void setAnswerValue(String answer, Object value, PretixService ps) { + if (answersData == null) { + loadAnswers(ps); + } + answersData.put(answer, value); + saveAnswers(ps); + } + + public void removeAnswer(String answer, PretixService ps) { + if (answersData == null) { + loadAnswers(ps); + } + answersData.remove(answer); + saveAnswers(ps); + } + + public String getAnswersRaw() { + return answers; + } + + public void resetFileUploadAnswers(PretixService ps) { + //After uploading a file, we need to change it's value to "file:keep" + for (String key : answersData.keySet()) { + if (ps.translateQuestionType(key) == QuestionType.FILE) { + answersData.put(key, Constants.QUESTIONS_FILE_KEEP); + } + } + saveAnswers(ps); + } + + public boolean isDaily() { + return dailyDays != 0L; + } + + public boolean hasDay(int day) { + return (dailyDays & (1L << day)) != 0L; + } + + @Transient @Getter(AccessLevel.NONE) - private Set dailyDaysSet = null; - public Set getDays(){ - if(dailyDaysSet == null){ - Set s = new HashSet<>(); - for(int i = 0; i < 64; i++) - if(hasDay(i)) - s.add(i); - dailyDaysSet = s; - } - return dailyDaysSet; - } - - public boolean ownsRoom(){ - return roomCapacity > 0; - } - - public void update(String code, OrderStatus status, String pretixOrderSecret, int positionId, Set days, Sponsorship sponsorship, ExtraDays extraDays, int roomCapacity, String hotelLocation, boolean hasMembership, User orderOwner, Event orderEvent, JSONArray answersJson){ - this.code = code; - this.orderStatus = status; - this.extraDays = extraDays; - this.sponsorship = sponsorship; - this.roomCapacity = roomCapacity; - this.hasMembership = hasMembership; - this.hotelLocation = hotelLocation; - this.pretixOrderSecret = pretixOrderSecret; - this.answersMainPositionId = positionId; - this.answers = answersJson.toString(); - this.answersData = null; - this.dailyDaysSet = null; - - this.dailyDays = 0L; - for(Integer day : days) - dailyDays |= 1L << day; - } + private Set dailyDaysSet = null; + + public Set getDays() { + if (dailyDaysSet == null) { + Set s = new HashSet<>(); + for (int i = 0; i < 64; i++) { + if (hasDay(i)) { + s.add(i); + } + } + dailyDaysSet = s; + } + return dailyDaysSet; + } + + public boolean ownsRoom() { + return roomCapacity > 0; + } + + public void update( + String code, + OrderStatus status, + String pretixOrderSecret, + int positionId, + Set days, + Sponsorship sponsorship, + ExtraDays extraDays, + int roomCapacity, + String hotelLocation, + boolean hasMembership, + User orderOwner, + Event orderEvent, + JSONArray answersJson + ) { + this.code = code; + this.orderStatus = status; + this.extraDays = extraDays; + this.sponsorship = sponsorship; + this.roomCapacity = roomCapacity; + this.hasMembership = hasMembership; + this.hotelLocation = hotelLocation; + this.pretixOrderSecret = pretixOrderSecret; + this.answersMainPositionId = positionId; + this.answers = answersJson.toString(); + this.answersData = null; + this.dailyDaysSet = null; + + this.dailyDays = 0L; + for (Integer day : days) { + dailyDays |= 1L << day; + } + } } diff --git a/src/main/java/net/furizon/backend/db/entities/pretix/Room.java b/src/main/java/net/furizon/backend/db/entities/pretix/Room.java index 326a3749..3cf3f7ab 100644 --- a/src/main/java/net/furizon/backend/db/entities/pretix/Room.java +++ b/src/main/java/net/furizon/backend/db/entities/pretix/Room.java @@ -1,6 +1,14 @@ package net.furizon.backend.db.entities.pretix; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; @@ -9,26 +17,29 @@ @Entity @Table(name = "rooms") public class Room { - - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Getter @Column(name = "room_id") + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Getter + @Column(name = "room_id") private long id; @Column(name = "room_name") - @Getter @Setter + @Getter + @Setter private String name; @Column(name = "room_confirmed") - @Getter @Setter + @Getter + @Setter private Boolean isConfirmed; @OneToOne @JoinColumn(name = "order_id") - @Getter @Setter + @Getter + @Setter private Order roomOrder; @OneToMany(mappedBy = "room") @Getter private List roomGuests; - } diff --git a/src/main/java/net/furizon/backend/db/entities/pretix/RoomGuest.java b/src/main/java/net/furizon/backend/db/entities/pretix/RoomGuest.java index ce19a1b4..cde85b57 100644 --- a/src/main/java/net/furizon/backend/db/entities/pretix/RoomGuest.java +++ b/src/main/java/net/furizon/backend/db/entities/pretix/RoomGuest.java @@ -1,6 +1,13 @@ package net.furizon.backend.db.entities.pretix; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; import net.furizon.backend.db.entities.users.User; @@ -8,7 +15,6 @@ @Entity @Table(name = "room_guests") public class RoomGuest { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Getter @@ -17,12 +23,13 @@ public class RoomGuest { @ManyToOne @JoinColumn(name = "user_id") - @Getter @Setter + @Getter + @Setter private User guest; @ManyToOne @JoinColumn(name = "room_id") - @Getter @Setter + @Getter + @Setter private Room room; - } diff --git a/src/main/java/net/furizon/backend/db/entities/users/AuthenticationData.java b/src/main/java/net/furizon/backend/db/entities/users/AuthenticationData.java index 6c8084ff..18e55253 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/AuthenticationData.java +++ b/src/main/java/net/furizon/backend/db/entities/users/AuthenticationData.java @@ -1,26 +1,33 @@ package net.furizon.backend.db.entities.users; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Data; -import lombok.Getter; import lombok.Setter; +// TODO -> Replace on JOOQ @Entity @Table(name = "authentications") @Data public class AuthenticationData { - - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) @Setter(AccessLevel.NONE) - @Column(name="authentication_id", nullable = false) + @Column(name = "authentication_id", nullable = false) private long id; @OneToOne @JoinColumn(name = "user_id") private User authenticationOwner; - @Column(name="authentication_password", nullable = false) + @Column(name = "authentication_password", nullable = false) private String passwordHash; @Column(name = "authentication_email", unique = true, nullable = false) diff --git a/src/main/java/net/furizon/backend/db/entities/users/Group.java b/src/main/java/net/furizon/backend/db/entities/users/Group.java index 6cd80a81..adf6f58a 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/Group.java +++ b/src/main/java/net/furizon/backend/db/entities/users/Group.java @@ -1,25 +1,33 @@ package net.furizon.backend.db.entities.users; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; import java.util.List; +// TODO -> Replace on JOOQ + @Entity @Table(name = "groups") @Getter public class Group { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "group_id", nullable = false) + private long id; - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name="group_id", nullable = false) - private long id; - - @Setter - @Column(name = "group_name", nullable = false) - private String name; - - @OneToMany(mappedBy = "group", fetch = FetchType.LAZY) - private List userGroupAssociations; + @Setter + @Column(name = "group_name", nullable = false) + private String name; + @OneToMany(mappedBy = "group", fetch = FetchType.LAZY) + private List userGroupAssociations; } diff --git a/src/main/java/net/furizon/backend/db/entities/users/User.java b/src/main/java/net/furizon/backend/db/entities/users/User.java index 8d0bae8b..76abcbcb 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/User.java +++ b/src/main/java/net/furizon/backend/db/entities/users/User.java @@ -1,25 +1,32 @@ package net.furizon.backend.db.entities.users; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import net.furizon.backend.db.entities.pretix.Order; -import net.furizon.backend.db.entities.pretix.Room; import net.furizon.backend.db.entities.pretix.RoomGuest; import net.furizon.backend.db.entities.users.content.Fursuit; import net.furizon.backend.db.entities.users.content.Media; -import net.furizon.backend.service.users.UserService; import java.util.List; import java.util.Set; +// TODO -> Replace on JOOQ + /** * The user class represent a past, present or future attendee at the convention, * it may be the author of published images, owner of a room or a roomie, * owns one or more membership cards, either expired or not. * May be linked to any orders placed in pretix. - *

+ *

* TODO: handle nickname and fursona name problem */ @Entity @@ -27,22 +34,26 @@ @NoArgsConstructor @Getter public final class User { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name="user_id", nullable = false) + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "user_id", nullable = false) private long id; - @Column(name="user_secret", nullable = false) + @Column(name = "user_secret", nullable = false) private String secret; - @Setter @Column(name = "user_first_name") + @Setter + @Column(name = "user_first_name") // TODO -> nullable? private String firstName; - @Setter @Column(name = "user_last_name") + @Setter + @Column(name = "user_last_name") // TODO -> nullable? private String lastName; - @Setter @Column(name = "user_locale") + @Setter + @Column(name = "user_locale") // TODO -> nullable? private String locale = "en"; diff --git a/src/main/java/net/furizon/backend/db/entities/users/UserGroup.java b/src/main/java/net/furizon/backend/db/entities/users/UserGroup.java index cc9d5fc5..3f0bf2b4 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/UserGroup.java +++ b/src/main/java/net/furizon/backend/db/entities/users/UserGroup.java @@ -1,9 +1,16 @@ package net.furizon.backend.db.entities.users; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import lombok.Getter; -import java.io.Serializable; +// TODO -> Replace on JOOQ /** * Associates users to groups @@ -12,17 +19,17 @@ @Entity @Table(name = "user_group") public class UserGroup { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Getter + @Column(name = "user_group_id", nullable = false) + private long id; - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Getter - @Column(name="user_group_id", nullable = false) - private long id; + @ManyToOne + @JoinColumn(name = "user_id") + private User user; - @ManyToOne - @JoinColumn(name = "user_id") - private User user; - - @ManyToOne - @JoinColumn(name = "group_id") - private Group group; + @ManyToOne + @JoinColumn(name = "group_id") + private Group group; } diff --git a/src/main/java/net/furizon/backend/db/entities/users/content/Fursuit.java b/src/main/java/net/furizon/backend/db/entities/users/content/Fursuit.java index d2cf00c4..f3450ddf 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/content/Fursuit.java +++ b/src/main/java/net/furizon/backend/db/entities/users/content/Fursuit.java @@ -1,12 +1,22 @@ package net.furizon.backend.db.entities.users.content; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.AccessLevel; import lombok.Data; import lombok.Getter; import lombok.Setter; import net.furizon.backend.db.entities.users.User; +// TODO -> Replace on JOOQ + @Entity @Table(name = "fursuits") @Data @@ -14,7 +24,7 @@ public class Fursuit { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Getter - @Column(name="fursuit_id", nullable = false) + @Column(name = "fursuit_id", nullable = false) @Setter(AccessLevel.NONE) private long id; diff --git a/src/main/java/net/furizon/backend/db/entities/users/content/Media.java b/src/main/java/net/furizon/backend/db/entities/users/content/Media.java index 739ffdc7..309b4a4c 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/content/Media.java +++ b/src/main/java/net/furizon/backend/db/entities/users/content/Media.java @@ -1,19 +1,30 @@ package net.furizon.backend.db.entities.users.content; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.Table; import lombok.Getter; import net.furizon.backend.db.entities.users.User; import java.util.List; +// TODO -> Replace on JOOQ + @Entity @Table(name = "media") @Getter public class Media { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) - @Column(name="media_id", nullable = false) + @Column(name = "media_id", nullable = false) private long id; @ManyToOne diff --git a/src/main/java/net/furizon/backend/db/entities/users/content/MediaTags.java b/src/main/java/net/furizon/backend/db/entities/users/content/MediaTags.java index 05070cd4..8cfbd8ea 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/content/MediaTags.java +++ b/src/main/java/net/furizon/backend/db/entities/users/content/MediaTags.java @@ -1,15 +1,23 @@ package net.furizon.backend.db.entities.users.content; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; import lombok.Getter; +// TODO -> Replace on JOOQ @Entity @Table(name = "media_tags") public class MediaTags { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Getter - @Column(name="media_tag_id", nullable = false) + @Column(name = "media_tag_id", nullable = false) private long id; @ManyToOne diff --git a/src/main/java/net/furizon/backend/db/entities/users/content/Tag.java b/src/main/java/net/furizon/backend/db/entities/users/content/Tag.java index c20345ed..25fcc4e3 100644 --- a/src/main/java/net/furizon/backend/db/entities/users/content/Tag.java +++ b/src/main/java/net/furizon/backend/db/entities/users/content/Tag.java @@ -1,27 +1,35 @@ package net.furizon.backend.db.entities.users.content; -import jakarta.persistence.*; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; import lombok.Getter; import lombok.Setter; import java.util.List; +// TODO -> Replace on JOOQ + @Entity @Table(name = "tags") public class Tag { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Getter - @Column(name="tag_id", nullable = false) + @Column(name = "tag_id", nullable = false) private long id; @Column(name = "tag_code", unique = true) - @Getter @Setter + @Getter + @Setter private String tagCode; @OneToMany(mappedBy = "tag", fetch = FetchType.LAZY) @Getter private List mediaTags; - } diff --git a/src/main/java/net/furizon/backend/db/repositories/pretix/IEventRepository.java b/src/main/java/net/furizon/backend/db/repositories/pretix/EventRepository.java similarity index 68% rename from src/main/java/net/furizon/backend/db/repositories/pretix/IEventRepository.java rename to src/main/java/net/furizon/backend/db/repositories/pretix/EventRepository.java index 8a6671a7..06929c1c 100644 --- a/src/main/java/net/furizon/backend/db/repositories/pretix/IEventRepository.java +++ b/src/main/java/net/furizon/backend/db/repositories/pretix/EventRepository.java @@ -3,5 +3,5 @@ import net.furizon.backend.db.entities.pretix.Event; import org.springframework.data.repository.ListCrudRepository; -public interface IEventRepository extends ListCrudRepository { +public interface EventRepository extends ListCrudRepository { } diff --git a/src/main/java/net/furizon/backend/db/repositories/pretix/IOrderRepository.java b/src/main/java/net/furizon/backend/db/repositories/pretix/OrderRepository.java similarity index 85% rename from src/main/java/net/furizon/backend/db/repositories/pretix/IOrderRepository.java rename to src/main/java/net/furizon/backend/db/repositories/pretix/OrderRepository.java index 46b93688..5a3cb679 100644 --- a/src/main/java/net/furizon/backend/db/repositories/pretix/IOrderRepository.java +++ b/src/main/java/net/furizon/backend/db/repositories/pretix/OrderRepository.java @@ -6,7 +6,7 @@ import java.util.Optional; -public interface IOrderRepository extends ListCrudRepository { +public interface OrderRepository extends ListCrudRepository { @Query(value = "SELECT O FROM Order O INNER JOIN Event E on O.orderEvent = E WHERE O.code = ?1 AND E.slug = ?2") Optional findByCodeAndEvent(String orderCode, String eventSlug); } diff --git a/src/main/java/net/furizon/backend/db/repositories/users/IAuthenticationDataRepository.java b/src/main/java/net/furizon/backend/db/repositories/users/AuthenticationDataRepository.java similarity index 64% rename from src/main/java/net/furizon/backend/db/repositories/users/IAuthenticationDataRepository.java rename to src/main/java/net/furizon/backend/db/repositories/users/AuthenticationDataRepository.java index fe7bff57..25fe4aab 100644 --- a/src/main/java/net/furizon/backend/db/repositories/users/IAuthenticationDataRepository.java +++ b/src/main/java/net/furizon/backend/db/repositories/users/AuthenticationDataRepository.java @@ -3,6 +3,6 @@ import net.furizon.backend.db.entities.users.AuthenticationData; import org.springframework.data.repository.ListCrudRepository; -public interface IAuthenticationDataRepository extends ListCrudRepository { +public interface AuthenticationDataRepository extends ListCrudRepository { } diff --git a/src/main/java/net/furizon/backend/db/repositories/users/IUserRepository.java b/src/main/java/net/furizon/backend/db/repositories/users/IUserRepository.java deleted file mode 100644 index 2df2bdac..00000000 --- a/src/main/java/net/furizon/backend/db/repositories/users/IUserRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package net.furizon.backend.db.repositories.users; - -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.ListCrudRepository; -import net.furizon.backend.db.entities.users.User; - -import java.util.List; -import java.util.Optional; - -public interface IUserRepository extends ListCrudRepository { - - @Query(value = "SELECT U FROM User U WHERE U.firstName LIKE '%?1%' OR U.lastName LIKE '%?2%'") - List findByName(String firstName, String lastName); - - @Query(value = "SELECT U FROM User U WHERE U.authentication.email = ?1") - Optional findByEmail(String email); - - @Query(value = "SELECT U FROM User U WHERE U.secret = ?1") - Optional findBySecret(String secret); - -} diff --git a/src/main/java/net/furizon/backend/db/repositories/users/UserRepository.java b/src/main/java/net/furizon/backend/db/repositories/users/UserRepository.java new file mode 100644 index 00000000..bc0b15d5 --- /dev/null +++ b/src/main/java/net/furizon/backend/db/repositories/users/UserRepository.java @@ -0,0 +1,19 @@ +package net.furizon.backend.db.repositories.users; + +import net.furizon.backend.db.entities.users.User; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.ListCrudRepository; + +import java.util.List; +import java.util.Optional; + +public interface UserRepository extends ListCrudRepository { + @Query(value = "SELECT U FROM User U WHERE U.firstName LIKE '%?1%' OR U.lastName LIKE '%?2%'") + List findByName(String firstName, String lastName); + + @Query(value = "SELECT U FROM User U WHERE U.authentication.email = ?1") + Optional findByEmail(String email); + + @Query(value = "SELECT U FROM User U WHERE U.secret = ?1") + Optional findBySecret(String secret); +} diff --git a/src/main/java/net/furizon/backend/security/SecurityConfiguration.java b/src/main/java/net/furizon/backend/security/SecurityConfiguration.java index 1483d99a..19b170c5 100644 --- a/src/main/java/net/furizon/backend/security/SecurityConfiguration.java +++ b/src/main/java/net/furizon/backend/security/SecurityConfiguration.java @@ -23,132 +23,142 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.logout.LogoutFilter; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; -import org.springframework.security.web.csrf.*; +import org.springframework.security.web.csrf.CookieCsrfTokenRepository; +import org.springframework.security.web.csrf.CsrfToken; +import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler; +import org.springframework.security.web.csrf.CsrfTokenRequestHandler; +import org.springframework.security.web.csrf.XorCsrfTokenRequestAttributeHandler; import org.springframework.util.StringUtils; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.filter.OncePerRequestFilter; -import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; - import java.io.IOException; import java.util.List; import java.util.function.Supplier; +import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; + @Configuration @EnableMethodSecurity @RequiredArgsConstructor public class SecurityConfiguration { - - private final UserDetailsService userDetailsService; - private final Oauth2LoginSuccessHandler oauth2LoginSuccessHandler; - - - @Bean - public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationSuccessHandler primarySuccessHandler) throws Exception { - // Map the allowed endpoints - http.authorizeHttpRequests(customizer -> { - customizer - .requestMatchers(antMatcher(HttpMethod.POST, "/api/v1/authentication/login")).permitAll() - .anyRequest().authenticated(); - }); - - http.oauth2Login(customizer -> { - customizer.successHandler(oauth2LoginSuccessHandler); - }); - - // Show unauthorized error - http.exceptionHandling(customizer -> { - customizer.authenticationEntryPoint( - (request, response, authException) -> { - response.sendError(401, "Unauthorized"); - }); - }); - - // Add the credentials verification filter - http.addFilterBefore(new UsernamePasswordAuthenticationFilter(), LogoutFilter.class); - http.userDetailsService(userDetailsService); - - http.csrf(csrf -> { - csrf - .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) - .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()); - }).addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); - - http.cors(customizer -> { - customizer.configurationSource(corsConfigurationSource()); - }); - - return http.build(); - } - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - - private CorsConfigurationSource corsConfigurationSource() { - return new CorsConfigurationSource() { - @Override - public CorsConfiguration getCorsConfiguration(@NotNull HttpServletRequest request) { - CorsConfiguration config = new CorsConfiguration(); - config.setAllowedOrigins(List.of("http://localhost")); - config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); - config.setAllowedHeaders(List.of("*")); - config.setAllowCredentials(true); - return config; - } - }; - } - - @Bean - public AuthenticationManager authenticationManager() { - DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); - daoAuthenticationProvider.setUserDetailsService(userDetailsService); - daoAuthenticationProvider.setPasswordEncoder(passwordEncoder()); - return new ProviderManager(daoAuthenticationProvider); - } - - final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler { - private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler(); - - @Override - public void handle(HttpServletRequest request, HttpServletResponse response, Supplier csrfToken) { - /* - * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of - * the CsrfToken when it is rendered in the response body. - */ - this.delegate.handle(request, response, csrfToken); - } - - @Override - public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { - /* - * If the request contains a request header, use CsrfTokenRequestAttributeHandler - * to resolve the CsrfToken. This applies when a single-page application includes - * the header value automatically, which was obtained via a cookie containing the - * raw CsrfToken. - */ - if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) { - return super.resolveCsrfTokenValue(request, csrfToken); - } - /* - * In all other cases (e.g. if the request contains a request parameter), use - * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies - * when a server-side rendered form includes the _csrf request parameter as a - * hidden input. - */ - return this.delegate.resolveCsrfTokenValue(request, csrfToken); - } - } - - final class CsrfCookieFilter extends OncePerRequestFilter { - @Override - protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf"); - // Render the token value to a cookie by causing the deferred token to be loaded - csrfToken.getToken(); - filterChain.doFilter(request, response); - } - } + private final UserDetailsService userDetailsService; + + private final Oauth2LoginSuccessHandler oauth2LoginSuccessHandler; + + @Bean + public SecurityFilterChain filterChain( + HttpSecurity http, + AuthenticationSuccessHandler primarySuccessHandler + ) throws Exception { + // Map the allowed endpoints + http.authorizeHttpRequests(customizer -> { + customizer + .requestMatchers(antMatcher(HttpMethod.POST, "/api/v1/authentication/login")).permitAll() + .anyRequest().authenticated(); + }); + + http.oauth2Login(customizer -> { + customizer.successHandler(oauth2LoginSuccessHandler); + }); + + // Show unauthorized error + http.exceptionHandling(customizer -> { + customizer.authenticationEntryPoint( + (request, response, authException) -> { + response.sendError(401, "Unauthorized"); + }); + }); + + // Add the credentials verification filter + http.addFilterBefore(new UsernamePasswordAuthenticationFilter(), LogoutFilter.class); + http.userDetailsService(userDetailsService); + + http.csrf(csrf -> { + csrf + .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) + .csrfTokenRequestHandler(new SpaCsrfTokenRequestHandler()); + }).addFilterAfter(new CsrfCookieFilter(), BasicAuthenticationFilter.class); + + http.cors(customizer -> { + customizer.configurationSource(corsConfigurationSource()); + }); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + private CorsConfigurationSource corsConfigurationSource() { + return new CorsConfigurationSource() { + @Override + public CorsConfiguration getCorsConfiguration(@NotNull HttpServletRequest request) { + CorsConfiguration config = new CorsConfiguration(); + config.setAllowedOrigins(List.of("http://localhost")); + config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")); + config.setAllowedHeaders(List.of("*")); + config.setAllowCredentials(true); + return config; + } + }; + } + + @Bean + public AuthenticationManager authenticationManager() { + DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); + daoAuthenticationProvider.setUserDetailsService(userDetailsService); + daoAuthenticationProvider.setPasswordEncoder(passwordEncoder()); + return new ProviderManager(daoAuthenticationProvider); + } + + final class SpaCsrfTokenRequestHandler extends CsrfTokenRequestAttributeHandler { + private final CsrfTokenRequestHandler delegate = new XorCsrfTokenRequestAttributeHandler(); + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, Supplier csrfToken) { + /* + * Always use XorCsrfTokenRequestAttributeHandler to provide BREACH protection of + * the CsrfToken when it is rendered in the response body. + */ + this.delegate.handle(request, response, csrfToken); + } + + @Override + public String resolveCsrfTokenValue(HttpServletRequest request, CsrfToken csrfToken) { + /* + * If the request contains a request header, use CsrfTokenRequestAttributeHandler + * to resolve the CsrfToken. This applies when a single-page application includes + * the header value automatically, which was obtained via a cookie containing the + * raw CsrfToken. + */ + if (StringUtils.hasText(request.getHeader(csrfToken.getHeaderName()))) { + return super.resolveCsrfTokenValue(request, csrfToken); + } + /* + * In all other cases (e.g. if the request contains a request parameter), use + * XorCsrfTokenRequestAttributeHandler to resolve the CsrfToken. This applies + * when a server-side rendered form includes the _csrf request parameter as a + * hidden input. + */ + return this.delegate.resolveCsrfTokenValue(request, csrfToken); + } + } + + final class CsrfCookieFilter extends OncePerRequestFilter { + @Override + protected void doFilterInternal( + HttpServletRequest request, + @NotNull HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { + CsrfToken csrfToken = (CsrfToken) request.getAttribute("_csrf"); + // Render the token value to a cookie by causing the deferred token to be loaded + csrfToken.getToken(); + filterChain.doFilter(request, response); + } + } } diff --git a/src/main/java/net/furizon/backend/security/entities/UserSecurity.java b/src/main/java/net/furizon/backend/security/entities/UserSecurity.java index 96417718..35382466 100644 --- a/src/main/java/net/furizon/backend/security/entities/UserSecurity.java +++ b/src/main/java/net/furizon/backend/security/entities/UserSecurity.java @@ -8,14 +8,14 @@ import java.util.Collection; import java.util.List; +@Getter public class UserSecurity implements UserDetails { - - @Getter private User user; - public UserSecurity() {} + public UserSecurity() { + } - public UserSecurity (User from) { + public UserSecurity(User from) { this.user = from; } @@ -54,7 +54,7 @@ public boolean isEnabled() { return !this.user.getAuthentication().isLoginDisabled(); } - public boolean is2faEnabled () { + public boolean is2faEnabled() { return this.user.getAuthentication().is2faEnabled(); } } diff --git a/src/main/java/net/furizon/backend/security/interfaces/ICodeVerifier.java b/src/main/java/net/furizon/backend/security/interfaces/CodeVerifier.java similarity index 81% rename from src/main/java/net/furizon/backend/security/interfaces/ICodeVerifier.java rename to src/main/java/net/furizon/backend/security/interfaces/CodeVerifier.java index 175a89f4..281395fa 100644 --- a/src/main/java/net/furizon/backend/security/interfaces/ICodeVerifier.java +++ b/src/main/java/net/furizon/backend/security/interfaces/CodeVerifier.java @@ -2,6 +2,6 @@ import net.furizon.backend.db.entities.users.User; -public interface ICodeVerifier { +public interface CodeVerifier { boolean verify(User user, String code); } diff --git a/src/main/java/net/furizon/backend/security/managers/OTPAuthorizationManager.java b/src/main/java/net/furizon/backend/security/managers/OTPAuthorizationManager.java index 0f7006d6..00f8e93b 100644 --- a/src/main/java/net/furizon/backend/security/managers/OTPAuthorizationManager.java +++ b/src/main/java/net/furizon/backend/security/managers/OTPAuthorizationManager.java @@ -1,17 +1,12 @@ package net.furizon.backend.security.managers; -import org.springframework.security.authorization.AuthorizationDecision; -import org.springframework.security.authorization.AuthorizationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.web.access.intercept.RequestAuthorizationContext; - -import java.util.function.Supplier; - -public class OTPAuthorizationManager implements AuthorizationManager { - - @Override - public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { - return new AuthorizationDecision(authentication.get() instanceof OTPAuthentication); - } - -} +// TODO -> + +//public class OTPAuthorizationManager implements AuthorizationManager { +// +// @Override +// public AuthorizationDecision check(Supplier authentication, RequestAuthorizationContext object) { +// return new AuthorizationDecision(authentication.get() instanceof OTPAuthentication); +// } +// +//} diff --git a/src/main/java/net/furizon/backend/service/pretix/PretixService.java b/src/main/java/net/furizon/backend/service/pretix/PretixService.java index 356c80fb..ab1b3e85 100644 --- a/src/main/java/net/furizon/backend/service/pretix/PretixService.java +++ b/src/main/java/net/furizon/backend/service/pretix/PretixService.java @@ -1,32 +1,40 @@ package net.furizon.backend.service.pretix; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import net.furizon.backend.db.entities.pretix.Event; import net.furizon.backend.db.entities.pretix.Order; import net.furizon.backend.db.entities.users.User; -import net.furizon.backend.db.repositories.pretix.IEventRepository; -import net.furizon.backend.db.repositories.pretix.IOrderRepository; -import net.furizon.backend.db.repositories.users.IUserRepository; -import net.furizon.backend.utils.TextUtil; -import net.furizon.backend.utils.pretix.*; +import net.furizon.backend.db.repositories.pretix.EventRepository; +import net.furizon.backend.db.repositories.pretix.OrderRepository; +import net.furizon.backend.db.repositories.users.UserRepository; import net.furizon.backend.utils.Download; +import net.furizon.backend.utils.TextUtil; import net.furizon.backend.utils.ThrowableSupplier; import net.furizon.backend.utils.configs.PretixConfig; +import net.furizon.backend.utils.pretix.Constants; +import net.furizon.backend.utils.pretix.ExtraDays; +import net.furizon.backend.utils.pretix.OrderStatus; +import net.furizon.backend.utils.pretix.QuestionType; +import net.furizon.backend.utils.pretix.Sponsorship; import org.apache.http.entity.ContentType; import org.apache.logging.log4j.util.TriConsumer; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - import org.springframework.data.util.Pair; - -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.util.*; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.TimeoutException; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -36,377 +44,524 @@ */ @Service @Slf4j +@RequiredArgsConstructor public class PretixService { - private final IUserRepository userRepository; - private final IEventRepository eventRepository; - private final IOrderRepository orderRepository; - private PretixConfig pretixConfig; - - @Autowired - private PretixService (IUserRepository userRepository, IEventRepository eventRepository, IOrderRepository orderRepository, PretixConfig pretixConfig) { - this.userRepository = userRepository; - this.eventRepository = eventRepository; - this.orderRepository = orderRepository; - this.pretixConfig = pretixConfig; - } - - private PretixIdsMap pretixIdsCache = null; - private class PretixIdsMap { - - public Set ticketIds = new HashSet<>(); - public Map dailyIds = new HashMap<>(); //map id -> day idx - - public Set membershipCardIds = new HashSet<>(); - - public Set sponsorshipItemIds = new HashSet<>(); - public Map sponsorshipVariationIds = new HashMap<>(); - - public Map extraDaysIds = new HashMap<>(); - - public Set roomItemIds = new HashSet<>(); - public Map> roomVariationIds = new HashMap<>(); //map id -> (capacity, hotelName) - - public Map questionTypeIds = new HashMap<>(); - public Map questionIdentifiers = new HashMap<>(); - public Map questionIdentifiersToId = new HashMap<>(); - - public int questionSecret = -1; - - } - - public boolean reloadEverything(){ - try { - reloadEvents(); - reloadOrderData(); - return true; - } catch(TimeoutException te) { - return false; - } - } - - public boolean reloadOrderData(){ - try { - PretixIdsMap cache = new PretixIdsMap(); - reloadProducts(cache); - reloadQuestions(cache); - pretixIdsCache = cache; - reloadOrders(); - return true; - } catch(TimeoutException te) { - return false; - } - } - - private synchronized void reloadOrders() throws TimeoutException { - getAllPages("orders", pretixConfig.getEventUrl(), this::parseOrderAndUpdateDB); - } - - private synchronized void reloadQuestions(PretixIdsMap pretixIdsCache) throws TimeoutException { - getAllPages("questions", pretixConfig.getEventUrl(), (item) -> { - int id = item.getInt("id"); - String identifier = item.getString("identifier"); - pretixIdsCache.questionTypeIds.put(id, QuestionType.fromCode(item.getString("type"))); - pretixIdsCache.questionIdentifiers.put(id, identifier); - pretixIdsCache.questionIdentifiersToId.put(identifier, id); - - if(item.getString("identifier").equals(Constants.QUESTIONS_ACCOUNT_SECRET)) - pretixIdsCache.questionSecret = id; - }); - } - private synchronized void reloadProducts(PretixIdsMap pretixIdsCache) throws TimeoutException { - TriConsumer> searchVariations = (item, prefix, fnc) -> { - JSONArray variations = item.getJSONArray("variations"); - for(int i = 0; i < variations.length(); i++){ - JSONObject variation = variations.getJSONObject(i); - String identifierVariation = variation.getJSONObject("meta_data").getString(Constants.METADATA_IDENTIFIER_ITEM); - int variationId = variation.getInt("id"); - - if(identifierVariation.startsWith(prefix)) - fnc.accept(variationId, identifierVariation.substring(prefix.length())); - } - }; - - getAllPages("items", pretixConfig.getEventUrl(), (item) -> { - String identifier = item.getJSONObject("meta_data").getString(Constants.METADATA_IDENTIFIER_ITEM); - int itemId = item.getInt("id"); - - if(identifier.startsWith(Constants.METADATA_EXTRA_DAYS_TAG_PREFIX)){ - String s = identifier.substring(Constants.METADATA_EXTRA_DAYS_TAG_PREFIX.length()); - ExtraDays ed = ExtraDays.valueOf(s); - pretixIdsCache.extraDaysIds.put(itemId, ed); - - - } else if(identifier.startsWith(Constants.METADATA_EVENT_TICKET_DAILY_TAG_PREFIX)) { - String s = identifier.substring(Constants.METADATA_EVENT_TICKET_DAILY_TAG_PREFIX.length()); - int day = Integer.parseInt(s); - pretixIdsCache.dailyIds.put(itemId, day); - - } else switch(identifier){ - case Constants.METADATA_EVENT_TICKET: { - pretixIdsCache.ticketIds.add(itemId); - break; - } - case Constants.METADATA_MEMBERSHIP_CARD: { - pretixIdsCache.membershipCardIds.add(itemId); - break; - } - case Constants.METADATA_SPONSORSHIP: { - pretixIdsCache.sponsorshipItemIds.add(itemId); - searchVariations.accept(item, Constants.METADATA_SPONSORSHIP_VARIATIONS_TAG_PREFIX, (variationId, s) -> { - Sponsorship ss = Sponsorship.valueOf(s); - pretixIdsCache.sponsorshipVariationIds.put(variationId, ss); - }); - break; - } - case Constants.METADATA_ROOM: { - pretixIdsCache.roomItemIds.add(itemId); - searchVariations.accept(item, Constants.METADATA_ROOM_TYPE_TAG_PREFIX, (variationId, s) -> { - String[] sp = s.split("_"); - String hotelName = sp[0]; - int capacity = Integer.parseInt(sp[1]); - pretixIdsCache.roomVariationIds.put(variationId, Pair.of(capacity, hotelName)); - }); - break; - } - } - }); - } - - public void fetchOrder(String code, String secret) throws TimeoutException { - Download.Response res = doGet("orders" + code.replaceAll("[^A-Za-z0-9]+", ""), pretixConfig.getEventUrl(), Constants.STATUS_CODES_WITH_404, null); - if(res.getStatusCode() == 404) throw new RuntimeException("Order not found"); - JSONObject orderData = res.getResponseJson(); - if(!orderData.getString("secret").equals(secret)) throw new RuntimeException("Order not found"); //Same exception to not leak matched order code - parseOrderAndUpdateDB(orderData); - } - private void parseOrderAndUpdateDB(JSONObject orderData){ - PretixIdsMap pCache = pretixIdsCache; - boolean hasTicket = false; //If no ticket is found, we don't store the order at all - - String code = orderData.getString("code"); - String secret = orderData.getString("secret"); - OrderStatus status = OrderStatus.fromCode(orderData.getString("status")); - - Set days = new HashSet<>(); - Sponsorship sponsorship = Sponsorship.NONE; - ExtraDays extraDays = ExtraDays.NONE; - int answersMainPositionId = 0; - String hotelLocation = null; - boolean membership = false; - JSONArray answers = null; - String userSecret = null; - int roomCapacity = 0; - - JSONArray positions = orderData.getJSONArray("positions"); - if(positions.length() == 0) status = OrderStatus.CANCELED; - for(int i = 0; i < positions.length(); i++) { - JSONObject position = positions.getJSONObject(i); - int item = position.getInt("item"); - - if (pCache.ticketIds.contains(item)) { - hasTicket = true; - answersMainPositionId = position.getInt("id"); - answers = position.getJSONArray("answers"); - for (int j = 0; j < answers.length(); j++) { - JSONObject answer = answers.getJSONObject(j); - int qId = answer.getInt("question"); - if (translateQuestionType(qId) == QuestionType.FILE) - answer.put("answer", Constants.QUESTIONS_FILE_KEEP); - if (qId == pCache.questionSecret) - userSecret = answer.getString("answer"); - } - } else if (pCache.dailyIds.containsKey(item)) { - days.add(pCache.dailyIds.get(item)); - } else if (pCache.membershipCardIds.contains(item)) { - membership = true; - } else - - if(pCache.sponsorshipItemIds.contains(item)) { - Sponsorship s = pCache.sponsorshipVariationIds.get(position.getInt("variation")); - if(s.ordinal() > sponsorship.ordinal()) sponsorship = s; //keep the best sponsorship - } else - - if(pCache.extraDaysIds.containsKey(item)) { - ExtraDays d = pCache.extraDaysIds.get(item); - if(extraDays != ExtraDays.BOTH) { - if (extraDays != d && extraDays != ExtraDays.NONE) { - extraDays = ExtraDays.BOTH; - } else extraDays = d; - } - } else - - if(pCache.roomItemIds.contains(item)) { - Pair room = pCache.roomVariationIds.get(position.getInt("variation")); - roomCapacity = room.getFirst(); - hotelLocation = room.getSecond(); - } - } - - // Fetch Order by code - Order order = orderRepository.findByCodeAndEvent(code, pretixConfig.getCurrentEventObj().getSlug()).orElse(null); - if(hasTicket && (status == OrderStatus.PENDING || status == OrderStatus.PAID)) { - // fetch user from db by userSecret - User usr = null; - if (!TextUtil.isEmpty(userSecret)) - usr = userRepository.findBySecret(userSecret).orElse(null); - - if (order == null) //order not found - order = new Order(); - order.update(code, status, secret, answersMainPositionId, days, sponsorship, extraDays, roomCapacity, hotelLocation, membership, usr, pretixConfig.getCurrentEventObj(), answers); - orderRepository.save(order); - } else { - if(order != null) - orderRepository.delete(order); - } - } - - //TODO: Organizers and events can change slug and we have no other way to uniquely identify an event. Eventually: Create "something" which can move the events and related orders to a new one - private List> reloadOrganizers() throws TimeoutException { - List> organizers = new LinkedList<>(); - getAllPages("organizers/", pretixConfig.getBaseUrl(), (res) -> organizers.add(Pair.of(res.getString("slug"), res.getString("public_url")))); - return organizers; - } - - public void reloadEvents() throws TimeoutException { - List> organizers = reloadOrganizers(); - - String currentEvent = pretixConfig.getCurrentEvent(); - String currentOrg = pretixConfig.getOrganizer(); - for(Pair organizerPair : organizers){ - String organizer = organizerPair.getFirst(); - getAllPages(TextUtil.url("organizers", organizer, "events"), pretixConfig.getBaseUrl(), (res) -> { - - Map names = new HashMap<>(); - JSONObject obj = res.getJSONObject("name"); - for(String s : obj.keySet()) names.put(s, obj.getString(s)); - - String eventCode = res.getString("slug"); - Event evt = eventRepository.findById(Event.getSlug(organizer, eventCode)).orElse(null); - if (evt == null) { - evt = new Event( - organizer, - res.getString("slug"), - organizerPair.getSecond(), - names, - res.getString("date_from"), - res.getString("date_to") - ); - evt.setCurrentEvent(evt.getSlug().equals(Event.getSlug(currentOrg, currentEvent))); - evt = eventRepository.save(evt); - } - if (evt != null && evt.isCurrentEvent()) { - pretixConfig.setCurrentEventObj(evt); - } - }); - } - } - - - public String uploadFile(ContentType mimeType, String fileName, byte[] data) throws TimeoutException { - Map headers = new HashMap<>(); - headers.put("Content-Type", mimeType.toString()); - headers.put("Content-Disposition", "attachment; filename=\"" + fileName + "\""); - return doPost("upload", data, pretixConfig.getBaseUrl(), Constants.STATUS_CODES_FILE_UPLOAD, headers).getResponseJson().getString("id"); - } - - - public synchronized String translateQuestionId(int answerId){ - return pretixIdsCache.questionIdentifiers.get(answerId); - } - public synchronized QuestionType translateQuestionType(int answerId){ - return pretixIdsCache.questionTypeIds.get(answerId); - } - public synchronized QuestionType translateQuestionType(String questionIdentifier){ - return pretixIdsCache.questionTypeIds.get(pretixIdsCache.questionIdentifiersToId.get(questionIdentifier)); - } - - //Push orders to pretix - public synchronized void submitAnswersToPretix(Order order) throws TimeoutException { - JSONObject payload = new JSONObject(); - JSONArray ans = order.getOrderStatus() == OrderStatus.CANCELED ? new JSONArray() : new JSONArray(order.getAnswersRaw()); - payload.put("answers", ans); - - Download.Response res = doPatch("orderpositions/" + order.getAnswersMainPositionId(), payload, pretixConfig.getEventUrl(), null, null); - - if(res.getStatusCode() != 200){ - try { - JSONObject d = res.getResponseJson(); - if (d.has("answers")) { - JSONArray errs = d.getJSONArray("answers"); - for(int i = 0; i < errs.length() && i < ans.length(); i++) - log.error("[ANSWERS SENDING] ERROR ON '" + ans.getString(0) + "': " + errs.getString(i)); - } else log.error("[ANSWERS SENDING] GENERIC ERROR. Response: " + res.toString()); - } catch(JSONException e) { - throw new RuntimeException("There has been an error while updating this answers."); - } - } - - order.resetFileUploadAnswers(this); - } - - private void getAllPages(String url, String baseUrl, Consumer elementFnc) throws TimeoutException { - int pages = 0; - while(true){ - pages += 1; - - Download.Response res = doGet(TextUtil.leadingSlash(url) + "?page=" + pages, baseUrl, Constants.STATUS_CODES_WITH_404, null); - if(res.getStatusCode() == 404) break; - - JSONObject response = res.getResponseJson(); - JSONArray objs = response.getJSONArray("results"); - for(int i = 0; i < objs.length(); i++) - elementFnc.accept(objs.getJSONObject(i)); - - if(response.get("next") == null) break; - } - } - - private Download httpClient; - - public void setupClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { - httpClient = new Download(pretixConfig.getConnectionTimeout(), pretixConfig.getConnectionHeaders(), null, pretixConfig.getMaxConnections(), false); - } - - private Download.Response doGet(String url, String baseUrl, int[] expectedStatusCodes, Map headers) throws TimeoutException { - return doRequest(url, () -> httpClient.get(TextUtil.leadingSlash(baseUrl) + url).addHeaders(headers).go(), null, expectedStatusCodes, "GETing"); - } - - private Download.Response doPost(String url, Object content, String baseUrl, int[] expectedStatusCodes, Map headers) throws TimeoutException { - return doRequest(url, () -> httpClient.post(TextUtil.leadingSlash(baseUrl) + url).addHeaders(headers).setBody(content).go(), null, expectedStatusCodes, "POSTing"); - } - - private Download.Response doPatch(String url, JSONObject json, String baseUrl, int[] expectedStatusCodes, Map headers) throws TimeoutException { - return doRequest(url, () -> httpClient.patch(TextUtil.leadingSlash(baseUrl) + url).addHeaders(headers).setJson(json).go(), null, expectedStatusCodes, "PATCHing"); - } - - private Download.Response doRequest(String url, ThrowableSupplier doReq, Runnable metricsFunc, int[] expectedStatusCodes, String opLogStr) throws TimeoutException { - List allowedStates = Arrays.stream(expectedStatusCodes).boxed().toList(); - Download.Response res = null; - int maxRetries = pretixConfig.getMaxConnectionRetries(); - for(int i = 0; i < maxRetries; i++){ - try { - //metricsFunc.run(); TODO - - Download.Response r = doReq.get(); - - int statusCode = r.getStatusCode(); - if(allowedStates != null && !allowedStates.contains(statusCode)){ - //incPretixErrors(); TODO - log.warn("[PRETIX] Got an unexpected status code ({}) while {} '{}'. Allowed status codes: {}", statusCode, opLogStr, url, allowedStates); - continue; - } - res = r; - - } catch (Exception e) { - //incPretixErrors(); TODO - log.warn("[PRETIX] An error ({}) occurred while {} '{}':\n{}", i, opLogStr, url, e.getMessage()); - } - if (res != null) break; - } - if(res == null){ - log.error("[PRETIX] Reached PRETIX_REQUESTS_MAX ({}) while {} '{}'. Aborting", maxRetries, opLogStr, url); - throw new TimeoutException("PRETIX_REQUESTS_MAX reached while " + opLogStr + " to pretix."); - } - return res; - } + private final UserRepository userRepository; + + private final EventRepository eventRepository; + + private final OrderRepository orderRepository; + + private final PretixConfig pretixConfig; + + private PretixIdsMap pretixIdsCache = null; + + // Let's do a separated service for cache + private class PretixIdsMap { + + public Set ticketIds = new HashSet<>(); + public Map dailyIds = new HashMap<>(); //map id -> day idx + + public Set membershipCardIds = new HashSet<>(); + + public Set sponsorshipItemIds = new HashSet<>(); + public Map sponsorshipVariationIds = new HashMap<>(); + + public Map extraDaysIds = new HashMap<>(); + + public Set roomItemIds = new HashSet<>(); + public Map> roomVariationIds = new HashMap<>(); //map id -> (capacity, hotelName) + + public Map questionTypeIds = new HashMap<>(); + public Map questionIdentifiers = new HashMap<>(); + public Map questionIdentifiersToId = new HashMap<>(); + + public int questionSecret = -1; + + } + + public boolean reloadEverything() { + try { + reloadEvents(); + reloadOrderData(); + return true; + } catch (TimeoutException te) { + return false; + } + } + + public boolean reloadOrderData() { + try { + PretixIdsMap cache = new PretixIdsMap(); + reloadProducts(cache); + reloadQuestions(cache); + pretixIdsCache = cache; + reloadOrders(); + return true; + } catch (TimeoutException te) { + return false; + } + } + + private synchronized void reloadOrders() throws TimeoutException { + getAllPages("orders", pretixConfig.getEventUrl(), this::parseOrderAndUpdateDatabase); + } + + private synchronized void reloadQuestions(PretixIdsMap pretixIdsCache) throws TimeoutException { + getAllPages("questions", pretixConfig.getEventUrl(), (item) -> { + int id = item.getInt("id"); + String identifier = item.getString("identifier"); + pretixIdsCache.questionTypeIds.put(id, QuestionType.get(item.getString("type"))); + pretixIdsCache.questionIdentifiers.put(id, identifier); + pretixIdsCache.questionIdentifiersToId.put(identifier, id); + + if (item.getString("identifier").equals(Constants.QUESTIONS_ACCOUNT_SECRET)) { + pretixIdsCache.questionSecret = id; + } + }); + } + + private synchronized void reloadProducts(PretixIdsMap pretixIdsCache) throws TimeoutException { + TriConsumer> searchVariations = (item, prefix, fnc) -> { + JSONArray variations = item.getJSONArray("variations"); + for (int i = 0; i < variations.length(); i++) { + JSONObject variation = variations.getJSONObject(i); + String identifierVariation = variation.getJSONObject("meta_data") + .getString(Constants.METADATA_IDENTIFIER_ITEM); + int variationId = variation.getInt("id"); + + if (identifierVariation.startsWith(prefix)) { + fnc.accept(variationId, identifierVariation.substring(prefix.length())); + } + } + }; + + getAllPages("items", pretixConfig.getEventUrl(), (item) -> { + String identifier = item.getJSONObject("meta_data").getString(Constants.METADATA_IDENTIFIER_ITEM); + int itemId = item.getInt("id"); + + if (identifier.startsWith(Constants.METADATA_EXTRA_DAYS_TAG_PREFIX)) { + String s = identifier.substring(Constants.METADATA_EXTRA_DAYS_TAG_PREFIX.length()); + ExtraDays ed = ExtraDays.valueOf(s); + pretixIdsCache.extraDaysIds.put(itemId, ed); + + + } else if (identifier.startsWith(Constants.METADATA_EVENT_TICKET_DAILY_TAG_PREFIX)) { + String s = identifier.substring(Constants.METADATA_EVENT_TICKET_DAILY_TAG_PREFIX.length()); + int day = Integer.parseInt(s); + pretixIdsCache.dailyIds.put(itemId, day); + + } else { + switch (identifier) { + case Constants.METADATA_EVENT_TICKET: { + pretixIdsCache.ticketIds.add(itemId); + break; + } + case Constants.METADATA_MEMBERSHIP_CARD: { + pretixIdsCache.membershipCardIds.add(itemId); + break; + } + case Constants.METADATA_SPONSORSHIP: { + pretixIdsCache.sponsorshipItemIds.add(itemId); + searchVariations.accept( + item, + Constants.METADATA_SPONSORSHIP_VARIATIONS_TAG_PREFIX, + (variationId, s) -> { + Sponsorship ss = Sponsorship.valueOf(s); + pretixIdsCache.sponsorshipVariationIds.put(variationId, ss); + } + ); + break; + } + case Constants.METADATA_ROOM: { + pretixIdsCache.roomItemIds.add(itemId); + searchVariations.accept( + item, + Constants.METADATA_ROOM_TYPE_TAG_PREFIX, + (variationId, s) -> { + String[] sp = s.split("_"); + String hotelName = sp[0]; + int capacity = Integer.parseInt(sp[1]); + pretixIdsCache.roomVariationIds.put(variationId, Pair.of(capacity, hotelName)); + } + ); + break; + } + default: + // TODO -> Log? + break; + } + } + }); + } + + public void fetchOrder(String code, String secret) throws TimeoutException { + Download.Response res = doGet( + "orders" + code.replaceAll("[^A-Za-z0-9]+", ""), + pretixConfig.getEventUrl(), + Constants.STATUS_CODES_WITH_404, + null + ); + if (res.getStatusCode() == 404) { + throw new RuntimeException("Order not found"); + } + JSONObject orderData = res.getResponseJson(); + if (!orderData.getString("secret").equals(secret)) { + throw new RuntimeException("Order not found"); //Same exception to not leak matched order code + } + parseOrderAndUpdateDatabase(orderData); + } + + private void parseOrderAndUpdateDatabase(JSONObject orderData) { + PretixIdsMap cache = pretixIdsCache; + boolean hasTicket = false; //If no ticket is found, we don't store the order at all + + String code = orderData.getString("code"); + String secret = orderData.getString("secret"); + OrderStatus status = OrderStatus.get(orderData.getString("status")); + + Set days = new HashSet<>(); + Sponsorship sponsorship = Sponsorship.NONE; + ExtraDays extraDays = ExtraDays.NONE; + int answersMainPositionId = 0; + String hotelLocation = null; + boolean membership = false; + JSONArray answers = null; + String userSecret = null; + int roomCapacity = 0; + + JSONArray positions = orderData.getJSONArray("positions"); + if (positions.isEmpty()) { + status = OrderStatus.CANCELED; + } + for (int i = 0; i < positions.length(); i++) { + JSONObject position = positions.getJSONObject(i); + int item = position.getInt("item"); + + if (cache.ticketIds.contains(item)) { + hasTicket = true; + answersMainPositionId = position.getInt("id"); + answers = position.getJSONArray("answers"); + for (int j = 0; j < answers.length(); j++) { + JSONObject answer = answers.getJSONObject(j); + int questionId = answer.getInt("question"); + if (translateQuestionType(questionId) == QuestionType.FILE) { + answer.put("answer", Constants.QUESTIONS_FILE_KEEP); + } + if (questionId == cache.questionSecret) { + userSecret = answer.getString("answer"); + } + } + } else if (cache.dailyIds.containsKey(item)) { + days.add(cache.dailyIds.get(item)); + } else if (cache.membershipCardIds.contains(item)) { + membership = true; + } else if (cache.sponsorshipItemIds.contains(item)) { + Sponsorship s = cache.sponsorshipVariationIds.get(position.getInt("variation")); + if (s.ordinal() > sponsorship.ordinal()) { + sponsorship = s; //keep the best sponsorship + } + } else if (cache.extraDaysIds.containsKey(item)) { + ExtraDays d = cache.extraDaysIds.get(item); + if (extraDays != ExtraDays.BOTH) { + if (extraDays != d && extraDays != ExtraDays.NONE) { + extraDays = ExtraDays.BOTH; + } else { + extraDays = d; + } + } + } else if (cache.roomItemIds.contains(item)) { + Pair room = cache.roomVariationIds.get(position.getInt("variation")); + roomCapacity = room.getFirst(); + hotelLocation = room.getSecond(); + } + } + + // Fetch Order by code + Order order = orderRepository.findByCodeAndEvent( + code, + pretixConfig.getCurrentEventObj().getSlug() + ).orElse(null); + if (hasTicket && (status == OrderStatus.PENDING || status == OrderStatus.PAID)) { + // fetch user from db by userSecret + User usr = null; + if (!TextUtil.isEmpty(userSecret)) { + usr = userRepository.findBySecret(userSecret).orElse(null); + } + + if (order == null) { + order = new Order(); //order not found + } + + order.update( + code, + status, + secret, + answersMainPositionId, + days, + sponsorship, + extraDays, + roomCapacity, + hotelLocation, + membership, + usr, + pretixConfig.getCurrentEventObj(), + answers + ); + orderRepository.save(order); + } else { + if (order != null) { + orderRepository.delete(order); + } + } + } + + //TODO: Organizers and events can change slug and we have no other way to uniquely identify an event. + // Eventually: Create "something" which can move the events and related orders to a new one + private List> reloadOrganizers() throws TimeoutException { + List> organizers = new LinkedList<>(); + getAllPages( + "organizers/", + pretixConfig.getBaseUrl(), + (res) -> organizers.add( + Pair.of( + res.getString("slug"), + res.getString("public_url") + ) + ) + ); + return organizers; + } + + public void reloadEvents() throws TimeoutException { + List> organizers = reloadOrganizers(); + + String currentEvent = pretixConfig.getCurrentEvent(); + String currentOrg = pretixConfig.getOrganizer(); + for (Pair organizerPair : organizers) { + String organizer = organizerPair.getFirst(); + getAllPages(TextUtil.url("organizers", organizer, "events"), pretixConfig.getBaseUrl(), (res) -> { + + Map names = new HashMap<>(); + JSONObject obj = res.getJSONObject("name"); + for (String s : obj.keySet()) { + names.put(s, obj.getString(s)); + } + + String eventCode = res.getString("slug"); + Event evt = eventRepository.findById(Event.getSlug(organizer, eventCode)).orElse(null); + if (evt == null) { + evt = new Event( + organizer, + res.getString("slug"), + organizerPair.getSecond(), + names, + res.getString("date_from"), + res.getString("date_to") + ); + evt.setCurrentEvent(evt.getSlug().equals(Event.getSlug(currentOrg, currentEvent))); + evt = eventRepository.save(evt); + } + if (evt != null && evt.isCurrentEvent()) { + pretixConfig.setCurrentEventObj(evt); + } + }); + } + } + + + public String uploadFile(ContentType mimeType, String fileName, byte[] data) throws TimeoutException { + Map headers = new HashMap<>(); + headers.put("Content-Type", mimeType.toString()); + headers.put("Content-Disposition", "attachment; filename=\"" + fileName + "\""); + return doPost( + "upload", + data, + pretixConfig.getBaseUrl(), + Constants.STATUS_CODES_FILE_UPLOAD, + headers + ).getResponseJson().getString("id"); + } + + // Bad synchronized blocks, better to avoid it + public synchronized String translateQuestionId(int answerId) { + return pretixIdsCache.questionIdentifiers.get(answerId); + } + + // Bad synchronized blocks, better to avoid it + public synchronized QuestionType translateQuestionType(int answerId) { + return pretixIdsCache.questionTypeIds.get(answerId); + } + + // Bad synchronized blocks, better to avoid it + public synchronized QuestionType translateQuestionType(String questionIdentifier) { + return pretixIdsCache.questionTypeIds.get(pretixIdsCache.questionIdentifiersToId.get(questionIdentifier)); + } + + //Push orders to pretix + public synchronized void submitAnswersToPretix(Order order) throws TimeoutException { + JSONObject payload = new JSONObject(); + JSONArray ans = order.getOrderStatus() == OrderStatus.CANCELED + ? new JSONArray() + : new JSONArray(order.getAnswersRaw()); + payload.put("answers", ans); + + Download.Response res = doPatch( + "orderpositions/" + order.getAnswersMainPositionId(), + payload, + pretixConfig.getEventUrl(), + null, + null + ); + + if (res.getStatusCode() != 200) { + try { + JSONObject d = res.getResponseJson(); + if (d.has("answers")) { + JSONArray errs = d.getJSONArray("answers"); + for (int i = 0; i < errs.length() && i < ans.length(); i++) { + log.error("[ANSWERS SENDING] ERROR ON '" + ans.getString(0) + "': " + errs.getString(i)); + } + } else { + log.error("[ANSWERS SENDING] GENERIC ERROR. Response: " + res.toString()); + } + } catch (JSONException e) { + throw new RuntimeException("There has been an error while updating this answers."); + } + } + + order.resetFileUploadAnswers(this); + } + + private void getAllPages(String url, String baseUrl, Consumer elementFnc) throws TimeoutException { + int pages = 0; + while (true) { + pages += 1; + Download.Response res = doGet( + TextUtil.leadingSlash(url) + "?page=" + pages, + baseUrl, + Constants.STATUS_CODES_WITH_404, + null + ); + if (res.getStatusCode() == 404) { + break; + } + + // TODO -> Better replace iton jackson + JSONObject response = res.getResponseJson(); + JSONArray objs = response.getJSONArray("results"); + for (int i = 0; i < objs.length(); i++) { + elementFnc.accept(objs.getJSONObject(i)); + } + + if (response.get("next") == null) { + break; + } + } + } + + // Let's find a best alternative for a custom client :3 + private Download httpClient; + + public void setupClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException { + httpClient = new Download( + pretixConfig.getConnectionTimeout(), + pretixConfig.getConnectionHeaders(), + null, + pretixConfig.getMaxConnections(), + false + ); + } + + private Download.Response doGet( + String url, + String baseUrl, + int[] expectedStatusCodes, + Map headers + ) throws TimeoutException { + return doRequest( + url, + () -> httpClient.get(TextUtil.leadingSlash(baseUrl) + url).addHeaders(headers).go(), + null, + expectedStatusCodes, + "GETing" + ); + } + + private Download.Response doPost( + String url, + Object content, + String baseUrl, + int[] expectedStatusCodes, + Map headers + ) throws TimeoutException { + return doRequest( + url, + () -> httpClient.post(TextUtil.leadingSlash(baseUrl) + url).addHeaders(headers).setBody(content).go(), + null, + expectedStatusCodes, + "POSTing" + ); + } + + private Download.Response doPatch( + String url, + JSONObject json, + String baseUrl, + int[] expectedStatusCodes, + Map headers + ) throws TimeoutException { + return doRequest( + url, + () -> httpClient.patch(TextUtil.leadingSlash(baseUrl) + url).addHeaders(headers).setJson(json).go(), + null, + expectedStatusCodes, + "PATCHing" + ); + } + + private Download.Response doRequest( + String url, + ThrowableSupplier doReq, + Runnable metricsFunc, + int[] expectedStatusCodes, + String opLogStr + ) throws TimeoutException { + List allowedStates = Arrays.stream(expectedStatusCodes).boxed().toList(); + Download.Response res = null; + int maxRetries = pretixConfig.getMaxConnectionRetries(); + for (int i = 0; i < maxRetries; i++) { + try { + //metricsFunc.run(); TODO + + Download.Response r = doReq.get(); + + int statusCode = r.getStatusCode(); + if (allowedStates != null && !allowedStates.contains(statusCode)) { + //incPretixErrors(); TODO + log.warn( + "[PRETIX] Got an unexpected status code ({}) while {} '{}'. Allowed status codes: {}", + statusCode, + opLogStr, + url, + allowedStates + ); + continue; + } + res = r; + + } catch (Exception e) { + //incPretixErrors(); TODO + log.warn("[PRETIX] An error ({}) occurred while {} '{}':\n{}", i, opLogStr, url, e.getMessage()); + } + if (res != null) { + break; + } + } + if (res == null) { + log.error("[PRETIX] Reached PRETIX_REQUESTS_MAX ({}) while {} '{}'. Aborting", maxRetries, opLogStr, url); + throw new TimeoutException("PRETIX_REQUESTS_MAX reached while " + opLogStr + " to pretix."); + } + return res; + } } diff --git a/src/main/java/net/furizon/backend/service/users/UserService.java b/src/main/java/net/furizon/backend/service/users/UserService.java index 99b9d0ba..e9041b96 100644 --- a/src/main/java/net/furizon/backend/service/users/UserService.java +++ b/src/main/java/net/furizon/backend/service/users/UserService.java @@ -5,24 +5,19 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.furizon.backend.db.entities.users.User; import net.furizon.backend.db.entities.users.AuthenticationData; -import net.furizon.backend.db.repositories.users.IAuthenticationDataRepository; -import net.furizon.backend.db.repositories.users.IUserRepository; +import net.furizon.backend.db.entities.users.User; +import net.furizon.backend.db.repositories.users.AuthenticationDataRepository; +import net.furizon.backend.db.repositories.users.UserRepository; import net.furizon.backend.security.entities.UserSecurity; -import net.furizon.backend.utils.SecurityUtil; import net.furizon.backend.utils.TextUtil; import net.furizon.backend.web.entities.users.UserLoginRequest; -import net.furizon.backend.web.entities.users.UserResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import org.jetbrains.annotations.NotNull; import org.springframework.dao.DuplicateKeyException; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolderStrategy; @@ -35,8 +30,6 @@ import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.stereotype.Service; -import java.util.Collection; -import java.util.List; import java.util.Optional; import java.util.UUID; @@ -44,74 +37,96 @@ @Slf4j @RequiredArgsConstructor public class UserService implements UserDetailsService { - private final IUserRepository userRepository; - private final AuthenticationManager authenticationManager; - private SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository(); - private SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); - private final IAuthenticationDataRepository authenticationDataRepository; - private final PasswordEncoder passwordEncoder; + private final UserRepository userRepository; + private final AuthenticationManager authenticationManager; + private final AuthenticationDataRepository authenticationDataRepository; + private final PasswordEncoder passwordEncoder; + private final SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository(); + private final SecurityContextLogoutHandler logoutHandler = new SecurityContextLogoutHandler(); - @Override - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - Optional usr = userRepository.findByEmail(username); - if (usr.isEmpty()) throw new IllegalArgumentException("User not found"); - return new UserSecurity(usr.get()); - } + // TODO -> Should not be here + // read/write package tho, will implement it with JOOQ + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + return new UserSecurity( + userRepository.findByEmail(username) + .orElseThrow(() -> new IllegalArgumentException("User not found")) + ); + } - /** - * Sets the cookie for the user if the username and password are correct - */ - public void login(HttpServletRequest request, HttpServletResponse response, UserLoginRequest body) throws AuthenticationException { - UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated(body.getEmail(), body.getPassword()); - Authentication authentication = authenticationManager.authenticate(token); - SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy(); - SecurityContext context = securityContextHolderStrategy.createEmptyContext(); - context.setAuthentication(authentication); - securityContextHolderStrategy.setContext(context); - securityContextRepository.saveContext(context, request, response); - } + /** + * Sets the cookie for the user if the username and password are correct + */ + // TODO ->This thing should not be in Service, + //it suppose to be on authentication filter chain + // by login method we should create session/token and operate with it + public void login( + HttpServletRequest request, + HttpServletResponse response, + UserLoginRequest body + ) throws AuthenticationException { + UsernamePasswordAuthenticationToken token = UsernamePasswordAuthenticationToken.unauthenticated( + body.getEmail(), + body.getPassword() + ); + Authentication authentication = authenticationManager.authenticate(token); + SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy(); + SecurityContext context = securityContextHolderStrategy.createEmptyContext(); + context.setAuthentication(authentication); + securityContextHolderStrategy.setContext(context); + securityContextRepository.saveContext(context, request, response); + } - @Transactional - public UserResponse getSession(HttpServletRequest request) { - User user = SecurityUtil.getAuthenticatedUser(); - return new UserResponse(user); - } + // it it works? + //@Transactional + //public UserResponse getSession(HttpServletRequest request) { + //User user = SecurityUtil.getAuthenticatedUser(); + //return new UserResponse(user); + // } - public void logout(HttpServletRequest request, HttpServletResponse response) { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - this.logoutHandler.logout(request, response, authentication); - } + public void logout(HttpServletRequest request, HttpServletResponse response) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + this.logoutHandler.logout(request, response, authentication); + } - @Transactional - public User register (String username, String password) { - Optional usr = userRepository.findByEmail(username); - if (usr.isPresent()) throw new IllegalArgumentException("The user already exists"); - User toRegister = new User(this.createUniqueSecret()); - toRegister = userRepository.save(toRegister); + @Transactional + public User register(String username, String password) { + Optional usr = userRepository.findByEmail(username); + if (usr.isPresent()) { + throw new IllegalArgumentException("The user already exists"); + } + User toRegister = new User(this.createUniqueSecret()); + toRegister = userRepository.save(toRegister); - AuthenticationData auth = new AuthenticationData(); - auth.setEmail(username); - auth.setPasswordHash(passwordEncoder.encode(password)); - auth.setAuthenticationOwner(toRegister); - auth = authenticationDataRepository.save(auth); - toRegister = userRepository.findById(toRegister.getId()).orElse(null); - return toRegister; - } + AuthenticationData auth = new AuthenticationData(); + auth.setEmail(username); + auth.setPasswordHash(passwordEncoder.encode(password)); + auth.setAuthenticationOwner(toRegister); + auth = authenticationDataRepository.save(auth); + toRegister = userRepository.findById(toRegister.getId()).orElse(null); + return toRegister; + } - public static final int MAX_USER_SECRET_GENERATION_TRIES = 5; + public static final int MAX_USER_SECRET_GENERATION_TRIES = 5; - public String createUniqueSecret(){ - String toReturn = null; - for (int t = 0; t < MAX_USER_SECRET_GENERATION_TRIES; t++) { - toReturn = UUID.randomUUID().toString(); - if (userRepository.findBySecret(toReturn).isEmpty()){ - break; - } - toReturn = null; - } - if (TextUtil.isEmpty(toReturn)) { - throw new DuplicateKeyException("Failed to generate secret after " + MAX_USER_SECRET_GENERATION_TRIES + " times."); - } - return toReturn; - } + // TODO -> Better use Database unique index instead of this implementation + // In worst case we will call database 5 times + // And this code looks pretty wierd c: + @NotNull + public String createUniqueSecret() { + String toReturn = null; + for (int t = 0; t < MAX_USER_SECRET_GENERATION_TRIES; t++) { + toReturn = UUID.randomUUID().toString(); + if (userRepository.findBySecret(toReturn).isEmpty()) { + break; + } + toReturn = null; + } + if (TextUtil.isEmpty(toReturn)) { + throw new DuplicateKeyException( + "Failed to generate secret after " + MAX_USER_SECRET_GENERATION_TRIES + " times." + ); + } + return toReturn; + } } diff --git a/src/main/java/net/furizon/backend/service/users/content/FursuitService.java b/src/main/java/net/furizon/backend/service/users/content/FursuitService.java index daeb029a..50a1f9c3 100644 --- a/src/main/java/net/furizon/backend/service/users/content/FursuitService.java +++ b/src/main/java/net/furizon/backend/service/users/content/FursuitService.java @@ -1,7 +1,5 @@ package net.furizon.backend.service.users.content; -import net.furizon.backend.db.entities.users.content.Fursuit; - public class FursuitService { } diff --git a/src/main/java/net/furizon/backend/utils/Download.java b/src/main/java/net/furizon/backend/utils/Download.java index d75e148f..c7e0f97b 100644 --- a/src/main/java/net/furizon/backend/utils/Download.java +++ b/src/main/java/net/furizon/backend/utils/Download.java @@ -1,22 +1,6 @@ package net.furizon.backend.utils; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.KeyManagementException; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.concurrent.TimeUnit; - -import javax.net.ssl.HostnameVerifier; - -import java.util.Objects; - +import lombok.ToString; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; @@ -31,7 +15,11 @@ import org.apache.http.client.config.RequestConfig; import org.apache.http.client.config.RequestConfig.Builder; import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.*; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPatch; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; @@ -53,13 +41,28 @@ import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicNameValuePair; import org.apache.http.protocol.BasicHttpContext; -import org.apache.http.ssl.SSLContextBuilder; -import org.apache.http.ssl.SSLContexts; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpCoreContext; +import org.apache.http.ssl.SSLContextBuilder; +import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; import org.json.JSONObject; +import javax.net.ssl.HostnameVerifier; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + /* * GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -738,356 +741,433 @@ You should also get your employer (if you work as a programmer) or school, */ +// If it is an HttpClient class, why it calls Download? +// Anyway, better replace it public class Download { - - public static final int DEFAULT_MAX_CONNECTIONS = 20; - public static final int DEFAULT_TIMEOUT = 4000; - private CookieStore cookieStore = new BasicCookieStore(); - private CredentialsProvider provider = new BasicCredentialsProvider(); - private PoolingHttpClientConnectionManager connManager; - private RequestConfig defaultParams; - private HttpHost defaultProxy; - private int defaultTimeout; - private HttpClient client; - - public Download() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { - this(DEFAULT_TIMEOUT, null, null, DEFAULT_MAX_CONNECTIONS, true); - } - public Download(int defaultTimeout, Map defaultHeaders, HttpHost defaultProxy, int maxConnections, boolean checkSSL) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { - Builder requestConfigBuilder = RequestConfig.custom() - .setConnectionRequestTimeout(defaultTimeout) - .setConnectTimeout(defaultTimeout) - .setSocketTimeout(defaultTimeout) - .setRedirectsEnabled(true); - this.defaultTimeout = defaultTimeout; - if(defaultProxy != null) { - //App.LOGGER.config("Starting download client with a proxy " + proxy); - requestConfigBuilder.setProxy(defaultProxy); - this.defaultProxy = defaultProxy; - } - defaultParams = requestConfigBuilder.build(); - - HttpClientBuilder httpClientBuilder = HttpClients.custom() - .setDefaultCredentialsProvider(provider) - .setDefaultRequestConfig(defaultParams) - .setDefaultCookieStore(cookieStore); - if(defaultHeaders != null) { - List
hs = new LinkedList
(); - for(Entry e : defaultHeaders.entrySet()) - hs.add(new BasicHeader(e.getKey(), e.getValue())); - httpClientBuilder.setDefaultHeaders(hs); - } - SSLConnectionSocketFactory scsf = null; - if(!checkSSL) { - //System.out.println("Starting download client without checking ssl certificates"); - scsf = new SSLConnectionSocketFactory(SSLContexts.custom().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build(), NoopHostnameVerifier.INSTANCE); - httpClientBuilder.setSSLSocketFactory(scsf); - httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); - httpClientBuilder.setSSLContext(new SSLContextBuilder().loadTrustMaterial(null, TrustAllStrategy.INSTANCE).build()); - } else { - HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault()); - scsf = new SSLConnectionSocketFactory(SSLContexts.createDefault(), hostnameVerifier); - } - connManager = new PoolingHttpClientConnectionManager( - RegistryBuilder.create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", scsf).build(), - null, - null, - null, //DNS to null I suppose? - -1l, - TimeUnit.MILLISECONDS); - connManager.setDefaultMaxPerRoute(maxConnections); - connManager.setMaxTotal(maxConnections); - httpClientBuilder.setConnectionManager(connManager); - - client = httpClientBuilder.build(); - } - - public enum RequestMethod {GET, POST, PATCH} - - public static class Request { - private RequestMethod method = RequestMethod.GET; - private boolean redirectsDisabled; - private Map headers = new LinkedHashMap(); - private List bodyParams = new ArrayList(); - private Download originalDwn; - private HttpEntity body; - private HttpHost proxy; - private int timeout; - private String url; - - public Request(String url) { - this.url = url; - } - public Request addHeaders(Map headers) { - if(headers != null) - this.headers.putAll(headers); - return this; - } - public Request setHeader(String name, String value) { - headers.put(name, value); - return this; - } - public Request setBodyParam(String name, String value) { - bodyParams.add(new BasicNameValuePair(name, value)); - return this; - } - public Request setBody(Object body) throws UnsupportedEncodingException { - switch (body) { - case String s -> setBodyString(s); - case byte[] bytes -> setBodyRaw(bytes); - case JSONObject jsonObject -> setJson(jsonObject); - case null, default -> throw new RuntimeException("Unsupported object type"); - } - return this; - } - public Request setPostBody(Object body, ContentType contentType){ - switch (body) { - case String s -> setBodyString(s, contentType); - case byte[] bytes -> setBodyRaw(bytes, contentType); - case JSONObject jsonObject -> setJson(jsonObject, contentType); - case null, default -> throw new RuntimeException("Unsupported object type"); - } - return this; - } - public Request setBodyString(String body) throws UnsupportedEncodingException { - this.body = new StringEntity(body); - return this; - } - public Request setBodyString(String body, ContentType contentType) { - this.body = new StringEntity(body, contentType); - headers.put(HttpHeaders.CONTENT_TYPE, contentType.toString()); - return this; - } - public Request setBodyRaw(byte[] body) { - this.body = new ByteArrayEntity(body, ContentType.APPLICATION_OCTET_STREAM); - //headers.put("Content-type", contentType.toString()); - return this; - } - public Request setBodyRaw(byte[] body, ContentType contentType) { - this.body = new ByteArrayEntity(body, contentType); - //headers.put("Content-type", contentType.toString()); - return this; - } - public Request setJson(JSONObject json) { - body = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); - return this; - } - public Request setJson(JSONObject json, ContentType contentType) { - body = new StringEntity(json.toString(), contentType); - return this; - } - public Request setTimeout(int timeout) { - this.timeout = timeout; - return this; - } - public Request setProxy(HttpHost proxy) { - this.proxy = proxy; - return this; - } - public Request setGet() { - method = RequestMethod.GET; - return this; - } - public Request setPost() { - method = RequestMethod.POST; - return this; - } - public Request setPatch(){ - method = RequestMethod.PATCH; - return this; - } - public Request setMethod(RequestMethod method){ - this.method = method; - return this; - } - public Request disableRedirects() { - redirectsDisabled = true; - return this; - } - public Request enableRedirects() { - redirectsDisabled = false; - return this; - } - - private Request setOriginalDwn(Download dwn) { - timeout = dwn.defaultTimeout; - originalDwn = dwn; - return this; - } - public Response go() throws IOException { - return switch(method){ - case GET -> originalDwn.get(this); - case POST -> originalDwn.post(this); - case PATCH -> originalDwn.patch(this); - }; - } - - private void setHeaders(HttpRequestBase request) { - for(Entry e : headers.entrySet()) { - request.setHeader(e.getKey(), e.getValue()); - } - } - private HttpEntity getEntity() throws UnsupportedEncodingException { - if(body != null) - return body; - return new UrlEncodedFormEntity(bodyParams, "UTF-8"); - } - } - public static class Response { - private int respCode; - private ArrayList
respHeaders = new ArrayList
(); - private byte[] response; - private String respStr; - private String landingUrl; - private JSONObject respJson; - - @Override - public String toString() { - return "Response [respCode=" + respCode + ", respHeaders=" + respHeaders + ", landingUrl=" + landingUrl + "]"; - } - - private Response(HttpResponse response, HttpEntity responseEntity) throws IOException { - respCode = response.getStatusLine().getStatusCode(); - setResponseBody(responseEntity); - setHeaders(response.getAllHeaders()); - } - - public int getStatusCode() { - return respCode; - } - public String getHeader(String name) { - return getHeader(name, 0); - } - public String getHeader(String name, int headerNo) { - int found = 0; - for(Header h : respHeaders) - if(h.getName().equalsIgnoreCase(name)) - if(found++ == headerNo) - return h.getValue(); - return null; - } - public String getLandingUrl() { - return landingUrl; - } - public byte[] getResponseRaw() { - return response; - } - public String getResponse() { - if(respStr == null) - respStr = new String(response); - return respStr; - } - public JSONObject getResponseJson() { - if(respJson == null) - respJson = new JSONObject(getResponse()); - return respJson; - } - - private void setHeaders(Header[] headers) { - for(Header h : headers) - respHeaders.add(h); - } - private void setResponseBody(HttpEntity resp) throws IOException { - response = EntityUtils.toByteArray(resp); - EntityUtils.consume(resp); - } - } - - private Response doRequest(Request req, HttpRequestBase httpReq) throws IOException { - boolean updateTimeout = req.timeout != defaultTimeout; - boolean updateProxy = req.proxy != null && !Objects.equals(req.proxy, defaultProxy); - if(updateTimeout || updateProxy || req.redirectsDisabled) { - Builder requestConfigBuilder = RequestConfig.custom(); - if(updateTimeout) - requestConfigBuilder.setConnectionRequestTimeout(req.timeout).setConnectTimeout(req.timeout).setSocketTimeout(req.timeout); - if(updateProxy) - requestConfigBuilder.setProxy(req.proxy); - if(req.redirectsDisabled) - requestConfigBuilder.setRedirectsEnabled(false); - httpReq.setConfig(requestConfigBuilder.build()); - } - req.setHeaders(httpReq); - HttpContext context = new BasicHttpContext(); + public static final int DEFAULT_MAX_CONNECTIONS = 20; + public static final int DEFAULT_TIMEOUT = 4000; + private CookieStore cookieStore = new BasicCookieStore(); + private CredentialsProvider provider = new BasicCredentialsProvider(); + private PoolingHttpClientConnectionManager connManager; + private RequestConfig defaultParams; + private HttpHost defaultProxy; + private int defaultTimeout; + private HttpClient client; + + public Download() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { + this(DEFAULT_TIMEOUT, null, null, DEFAULT_MAX_CONNECTIONS, true); + } + + public Download( + int defaultTimeout, + Map + defaultHeaders, + HttpHost defaultProxy, + int maxConnections, + boolean checkSsl + ) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { + Builder requestConfigBuilder = RequestConfig.custom() + .setConnectionRequestTimeout(defaultTimeout) + .setConnectTimeout(defaultTimeout) + .setSocketTimeout(defaultTimeout) + .setRedirectsEnabled(true); + this.defaultTimeout = defaultTimeout; + if (defaultProxy != null) { + //App.LOGGER.config("Starting download client with a proxy " + proxy); + requestConfigBuilder.setProxy(defaultProxy); + this.defaultProxy = defaultProxy; + } + defaultParams = requestConfigBuilder.build(); + + HttpClientBuilder httpClientBuilder = HttpClients.custom() + .setDefaultCredentialsProvider(provider) + .setDefaultRequestConfig(defaultParams) + .setDefaultCookieStore(cookieStore); + if (defaultHeaders != null) { + List
hs = new LinkedList
(); + for (Entry e : defaultHeaders.entrySet()) { + hs.add(new BasicHeader(e.getKey(), e.getValue())); + } + httpClientBuilder.setDefaultHeaders(hs); + } + SSLConnectionSocketFactory scsf = null; + if (!checkSsl) { + //System.out.println("Starting download client without checking ssl certificates"); + scsf = new SSLConnectionSocketFactory( + SSLContexts.custom() + .loadTrustMaterial(null, TrustAllStrategy.INSTANCE) + .build(), + NoopHostnameVerifier.INSTANCE + ); + httpClientBuilder.setSSLSocketFactory(scsf); + httpClientBuilder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); + httpClientBuilder.setSSLContext( + new SSLContextBuilder() + .loadTrustMaterial(null, TrustAllStrategy.INSTANCE) + .build() + ); + } else { + HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(PublicSuffixMatcherLoader.getDefault()); + scsf = new SSLConnectionSocketFactory(SSLContexts.createDefault(), hostnameVerifier); + } + connManager = new PoolingHttpClientConnectionManager( + RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", scsf) + .build(), + null, + null, + null, //DNS to null I suppose? + -1L, + TimeUnit.MILLISECONDS + ); + connManager.setDefaultMaxPerRoute(maxConnections); + connManager.setMaxTotal(maxConnections); + httpClientBuilder.setConnectionManager(connManager); + + client = httpClientBuilder.build(); + } + + public enum RequestMethod { + GET, + POST, + PATCH + } + + public static class Request { + private RequestMethod method = RequestMethod.GET; + private boolean redirectsDisabled; + private Map headers = new LinkedHashMap(); + private List bodyParams = new ArrayList(); + private Download originalDwn; + private HttpEntity body; + private HttpHost proxy; + private int timeout; + private String url; + + public Request(String url) { + this.url = url; + } + + public Request addHeaders(Map headers) { + if (headers != null) { + this.headers.putAll(headers); + } + return this; + } + + public Request setHeader(String name, String value) { + headers.put(name, value); + return this; + } + + public Request setBodyParam(String name, String value) { + bodyParams.add(new BasicNameValuePair(name, value)); + return this; + } + + public Request setBody(Object body) throws UnsupportedEncodingException { + switch (body) { + case String s -> setBodyString(s); + case byte[] bytes -> setBodyRaw(bytes); + case JSONObject jsonObject -> setJson(jsonObject); + case null, default -> throw new RuntimeException("Unsupported object type"); + } + return this; + } + + public Request setPostBody(Object body, ContentType contentType) { + switch (body) { + case String s -> setBodyString(s, contentType); + case byte[] bytes -> setBodyRaw(bytes, contentType); + case JSONObject jsonObject -> setJson(jsonObject, contentType); + case null, default -> throw new RuntimeException("Unsupported object type"); + } + return this; + } + + public Request setBodyString(String body) throws UnsupportedEncodingException { + this.body = new StringEntity(body); + return this; + } + + public Request setBodyString(String body, ContentType contentType) { + this.body = new StringEntity(body, contentType); + headers.put(HttpHeaders.CONTENT_TYPE, contentType.toString()); + return this; + } + + public Request setBodyRaw(byte[] body) { + this.body = new ByteArrayEntity(body, ContentType.APPLICATION_OCTET_STREAM); + //headers.put("Content-type", contentType.toString()); + return this; + } + + public Request setBodyRaw(byte[] body, ContentType contentType) { + this.body = new ByteArrayEntity(body, contentType); + //headers.put("Content-type", contentType.toString()); + return this; + } + + public Request setJson(JSONObject json) { + body = new StringEntity(json.toString(), ContentType.APPLICATION_JSON); + return this; + } + + public Request setJson(JSONObject json, ContentType contentType) { + body = new StringEntity(json.toString(), contentType); + return this; + } + + public Request setTimeout(int timeout) { + this.timeout = timeout; + return this; + } + + public Request setProxy(HttpHost proxy) { + this.proxy = proxy; + return this; + } + + public Request setGet() { + method = RequestMethod.GET; + return this; + } + + public Request setPost() { + method = RequestMethod.POST; + return this; + } + + public Request setPatch() { + method = RequestMethod.PATCH; + return this; + } + + public Request setMethod(RequestMethod method) { + this.method = method; + return this; + } + + public Request disableRedirects() { + redirectsDisabled = true; + return this; + } + + public Request enableRedirects() { + redirectsDisabled = false; + return this; + } + + private Request setOriginalDwn(Download dwn) { + timeout = dwn.defaultTimeout; + originalDwn = dwn; + return this; + } + + public Response go() throws IOException { + return switch (method) { + case GET -> originalDwn.get(this); + case POST -> originalDwn.post(this); + case PATCH -> originalDwn.patch(this); + }; + } + + private void setHeaders(HttpRequestBase request) { + for (Entry e : headers.entrySet()) { + request.setHeader(e.getKey(), e.getValue()); + } + } + + private HttpEntity getEntity() throws UnsupportedEncodingException { + if (body != null) { + return body; + } + return new UrlEncodedFormEntity(bodyParams, "UTF-8"); + } + } + + @ToString + public static class Response { + private int respCode; + private ArrayList
respHeaders = new ArrayList
(); + private byte[] response; + private String respStr; + private String landingUrl; + private JSONObject respJson; + + private Response(HttpResponse response, HttpEntity responseEntity) throws IOException { + respCode = response.getStatusLine().getStatusCode(); + setResponseBody(responseEntity); + setHeaders(response.getAllHeaders()); + } + + public int getStatusCode() { + return respCode; + } + + public String getHeader(String name) { + return getHeader(name, 0); + } + + public String getHeader(String name, int headerNo) { + int found = 0; + for (Header h : respHeaders) { + if (h.getName().equalsIgnoreCase(name)) { + if (found++ == headerNo) { + return h.getValue(); + } + } + } + return null; + } + + public String getLandingUrl() { + return landingUrl; + } + + public byte[] getResponseRaw() { + return response; + } + + public String getResponse() { + if (respStr == null) { + respStr = new String(response); + } + return respStr; + } + + public JSONObject getResponseJson() { + if (respJson == null) { + respJson = new JSONObject(getResponse()); + } + return respJson; + } + + private void setHeaders(Header[] headers) { + for (Header h : headers) { + respHeaders.add(h); + } + } + + private void setResponseBody(HttpEntity resp) throws IOException { + response = EntityUtils.toByteArray(resp); + EntityUtils.consume(resp); + } + } + + private Response doRequest(Request req, HttpRequestBase httpReq) throws IOException { + boolean updateTimeout = req.timeout != defaultTimeout; + boolean updateProxy = req.proxy != null && !Objects.equals(req.proxy, defaultProxy); + if (updateTimeout || updateProxy || req.redirectsDisabled) { + Builder requestConfigBuilder = RequestConfig.custom(); + if (updateTimeout) { + requestConfigBuilder.setConnectionRequestTimeout(req.timeout) + .setConnectTimeout(req.timeout) + .setSocketTimeout(req.timeout); + } + if (updateProxy) { + requestConfigBuilder.setProxy(req.proxy); + } + if (req.redirectsDisabled) { + requestConfigBuilder.setRedirectsEnabled(false); + } + httpReq.setConfig(requestConfigBuilder.build()); + } + req.setHeaders(httpReq); + HttpContext context = new BasicHttpContext(); HttpResponse response = client.execute(httpReq, context); HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(HttpCoreContext.HTTP_REQUEST); HttpHost currentHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); - String currentUrl = (currentReq.getURI().isAbsolute()) ? currentReq.getURI().toString() : (currentHost.toURI() + currentReq.getURI()); - HttpEntity entity = response.getEntity(); - if (entity != null) { - Response resp = new Response(response, entity); - resp.landingUrl = currentUrl; - httpReq.releaseConnection(); - return resp; - } - return null; - } - public Request get(String url) { - return new Request(url).setGet().setOriginalDwn(this); - } - public Request post(String url) { - return new Request(url).setPost().setOriginalDwn(this); - } - public Request patch(String url) { - return new Request(url).setPatch().setOriginalDwn(this); - } - - public Response get(Request req) throws IOException { - HttpGet httpGet = new HttpGet(req.url); - return doRequest(req, httpGet); - } - public Response post(Request req) throws IOException { - HttpPost httpPost = new HttpPost(req.url); - httpPost.setEntity(req.getEntity()); - return doRequest(req, httpPost); - } - public Response patch(Request req) throws IOException { - HttpPatch httpPatch = new HttpPatch(req.url); - httpPatch.setEntity(req.getEntity()); - return doRequest(req, httpPatch); - } - - public Download setLogin(String username, String password) { + String currentUrl = (currentReq.getURI().isAbsolute()) + ? currentReq.getURI().toString() + : (currentHost.toURI() + currentReq.getURI()); + HttpEntity entity = response.getEntity(); + if (entity != null) { + Response resp = new Response(response, entity); + resp.landingUrl = currentUrl; + httpReq.releaseConnection(); + return resp; + } + return null; + } + + public Request get(String url) { + return new Request(url).setGet().setOriginalDwn(this); + } + + public Response get(Request req) throws IOException { + HttpGet httpGet = new HttpGet(req.url); + return doRequest(req, httpGet); + } + + public Request post(String url) { + return new Request(url).setPost().setOriginalDwn(this); + } + + public Response post(Request req) throws IOException { + HttpPost httpPost = new HttpPost(req.url); + httpPost.setEntity(req.getEntity()); + return doRequest(req, httpPost); + } + + public Request patch(String url) { + return new Request(url).setPatch().setOriginalDwn(this); + } + + public Response patch(Request req) throws IOException { + HttpPatch httpPatch = new HttpPatch(req.url); + httpPatch.setEntity(req.getEntity()); + return doRequest(req, httpPatch); + } + + public Download setLogin(String username, String password) { provider.setCredentials( - AuthScope.ANY, - new UsernamePasswordCredentials(username, password) + AuthScope.ANY, + new UsernamePasswordCredentials(username, password) ); return this; - } - - public Download setCookie(String key, String value, String domain, String path) { - BasicClientCookie cookie = new BasicClientCookie(key, value); - cookie.setPath(path); - cookie.setDomain(domain); - cookieStore.addCookie(cookie); - return this; - } - public String getCookie(String name) { - return getCookieObj(name, 0).getValue(); - } - public Cookie getCookieObj(String name, int cookieNo) { - List cookies = cookieStore.getCookies(); - int found = 0; - for(Cookie c : cookies) - if(c.getName().equalsIgnoreCase(name)) - if(found++ == cookieNo) - return c; - return null; - } - public List getCookies(){ - return new ArrayList(cookieStore.getCookies()); - } - - @SuppressWarnings("unused") - private static void copyrightTM(){ - System.out.println("Download engine made by https://stranck.ovh (c)\n" - + "\n" - + "Need help? Contact me!\n" - + "Telegram channel: https://t.me/Stranck\n" - + "Telegram user: https://t.me/LucaStranck\n" - + "YouTube channel: https://www.youtube.com/channel/UCmMWUz0QZ7WhIBx-1Dz-IGg\n" - + "Twitter: https://twitter.com/LStranck https://twitter.com/stranckV2\n" - + "Instagram: https://www.instagram.com/stranck/\n" - + "Github: https://github.com/stranck\n" - + "Pastebin: https://pastebin.com/u/Stranck\n"); - } + } + + public Download setCookie(String key, String value, String domain, String path) { + BasicClientCookie cookie = new BasicClientCookie(key, value); + cookie.setPath(path); + cookie.setDomain(domain); + cookieStore.addCookie(cookie); + return this; + } + + public String getCookie(String name) { + return getCookieObj(name, 0).getValue(); + } + + public Cookie getCookieObj(String name, int cookieNo) { + List cookies = cookieStore.getCookies(); + int found = 0; + for (Cookie c : cookies) { + if (c.getName().equalsIgnoreCase(name)) { + if (found++ == cookieNo) { + return c; + } + } + } + return null; + } + + public List getCookies() { + return new ArrayList(cookieStore.getCookies()); + } + + @SuppressWarnings("unused") + private static void copyright() { + System.out.println("Download engine made by https://stranck.ovh (c)\n" + + "\n" + + "Need help? Contact me!\n" + + "Telegram channel: https://t.me/Stranck\n" + + "Telegram user: https://t.me/LucaStranck\n" + + "YouTube channel: https://www.youtube.com/channel/UCmMWUz0QZ7WhIBx-1Dz-IGg\n" + + "Twitter: https://twitter.com/LStranck https://twitter.com/stranckV2\n" + + "Instagram: https://www.instagram.com/stranck/\n" + + "Github: https://github.com/stranck\n" + + "Pastebin: https://pastebin.com/u/Stranck\n"); + } } \ No newline at end of file diff --git a/src/main/java/net/furizon/backend/utils/SecurityUtil.java b/src/main/java/net/furizon/backend/utils/SecurityUtil.java index 0ea4fb52..01739000 100644 --- a/src/main/java/net/furizon/backend/utils/SecurityUtil.java +++ b/src/main/java/net/furizon/backend/utils/SecurityUtil.java @@ -11,17 +11,20 @@ @Slf4j public class SecurityUtil { - private static final SecurityContextRepository securityContextRepository = new HttpSessionSecurityContextRepository(); + private static final SecurityContextRepository securityContextRepository = + new HttpSessionSecurityContextRepository(); /** * Get the authenticated user from the SecurityContextHolder - * @throws net.furizon.backend.web.entities.exceptions.ApiException if the user is not found in the SecurityContextHolder + * + * @throws net.furizon.backend.web.entities.exceptions.ApiException + * if the user is not found in the SecurityContextHolder */ public static User getAuthenticatedUser() { Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if (principal instanceof User user) { return user; - }else { + } else { log.error("User requested but not found in SecurityContextHolder"); throw ApiException.builder().status(401).message("Authentication required").build(); } diff --git a/src/main/java/net/furizon/backend/utils/TextUtil.java b/src/main/java/net/furizon/backend/utils/TextUtil.java index b202b356..f267f269 100644 --- a/src/main/java/net/furizon/backend/utils/TextUtil.java +++ b/src/main/java/net/furizon/backend/utils/TextUtil.java @@ -3,17 +3,15 @@ import java.util.Arrays; public class TextUtil { - - public static String leadingSlash (String input) { + public static String leadingSlash(String input) { return Character.valueOf(input.charAt(input.length() - 1)).equals('/') ? input : input + "/"; } - public static String url(String ...strings) { + public static String url(String... strings) { return String.join("/", Arrays.asList(strings)); } public static boolean isEmpty(String value) { - return value == null || value.trim().length() == 0; + return value == null || value.trim().isEmpty(); } - } diff --git a/src/main/java/net/furizon/backend/utils/ThrowableSupplier.java b/src/main/java/net/furizon/backend/utils/ThrowableSupplier.java index 84b0f925..56b198bf 100644 --- a/src/main/java/net/furizon/backend/utils/ThrowableSupplier.java +++ b/src/main/java/net/furizon/backend/utils/ThrowableSupplier.java @@ -2,5 +2,5 @@ @FunctionalInterface public interface ThrowableSupplier { - T get() throws E; + T get() throws E; } diff --git a/src/main/java/net/furizon/backend/utils/configs/BackendConfig.java b/src/main/java/net/furizon/backend/utils/configs/BackendConfig.java deleted file mode 100644 index 183cc753..00000000 --- a/src/main/java/net/furizon/backend/utils/configs/BackendConfig.java +++ /dev/null @@ -1,22 +0,0 @@ -package net.furizon.backend.utils.configs; - -import lombok.Data; -import lombok.Getter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.PropertySource; -import org.springframework.stereotype.Component; - -@Data -@Component -@PropertySource(value = "file:config.properties") -@ConfigurationProperties(prefix = "backend") -public class BackendConfig { - @Getter - private int port; - - @Getter - private String host; - - @Getter - private String secret; -} diff --git a/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java b/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java index a4024e5f..57370afe 100644 --- a/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java +++ b/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java @@ -2,6 +2,7 @@ import lombok.Data; import lombok.Getter; +import lombok.NoArgsConstructor; import lombok.Setter; import net.furizon.backend.db.entities.pretix.Event; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -15,12 +16,8 @@ @Component @PropertySource(value = "file:config.properties") @ConfigurationProperties(prefix = "pretix") +@NoArgsConstructor public class PretixConfig { - - public PretixConfig () { - - } - @Getter private String protocol; @@ -33,10 +30,12 @@ public PretixConfig () { @Getter private String apiKey; + @Setter @Getter private String organizer; - @Getter @Setter + @Getter + @Setter private boolean runHealthcheck; @Getter @@ -44,30 +43,38 @@ public PretixConfig () { /* Connection */ - @Getter @Setter + @Getter + @Setter private int maxConnectionRetries; - @Getter @Setter + @Getter + @Setter private int maxConnections; - @Getter @Setter + @Getter + @Setter private int connectionTimeout; /* Profile pic */ - @Getter @Setter + @Getter + @Setter private long maxPropicFileSizeBytes; - @Getter @Setter + @Getter + @Setter private long maxPropicWidth; - @Getter @Setter + @Getter + @Setter private long minPropicWidth; - @Getter @Setter + @Getter + @Setter private long maxPropicHeight; - @Getter @Setter + @Getter + @Setter private long minPropicHeight; /****************** @@ -85,16 +92,22 @@ public PretixConfig () { public void setHostName(String hostName) { this.hostName = hostName; - if(connectionHeaders == null) getConnectionHeaders(); //This will create them + if (connectionHeaders == null) { + getConnectionHeaders(); //This will create them + } connectionHeaders.put("Host", hostName); } + public void setApiKey(String apiKey) { this.apiKey = apiKey; - if(connectionHeaders == null) getConnectionHeaders(); //This will create them + if (connectionHeaders == null) { + getConnectionHeaders(); //This will create them + } connectionHeaders.put("Authorization", "Token " + apiKey); } + public Map getConnectionHeaders() { - if(connectionHeaders == null) { + if (connectionHeaders == null) { connectionHeaders = new HashMap<>(); connectionHeaders.put("Host", hostName); connectionHeaders.put("Authorization", "Token " + apiKey); @@ -104,32 +117,30 @@ public Map getConnectionHeaders() { public void setEndpointUrl(String endpointUrl) { //TODO: Check if setters are automatically called when data is loaded from db - if(!endpointUrl.endsWith("/")) endpointUrl += "/"; + if (!endpointUrl.endsWith("/")) { + endpointUrl += "/"; + } baseUrl = endpointUrl + "api/v1/"; } - public void setOrganizer(String organizer) { - this.organizer = organizer; - } - public void setCurrentEvent(String currentEvent) { this.currentEvent = currentEvent; this.currentEventObj = null; } + public Event getCurrentEventObj() { - if(currentEventObj == null){ + if (currentEventObj == null) { //TODO: Load from db } return currentEventObj; } - public String getEventUrl(){ + public String getEventUrl() { return getBaseUrl() + "organizers/" + organizer + "/events/" + currentEvent; } - public String getBaseUrl() { + public String getBaseUrl() { baseUrl = getProtocol() + "://" + getHostName() + ":" + getPort() + "/api/v1/"; return baseUrl; } - } diff --git a/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java b/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java index 58d4fdb8..a9202db3 100644 --- a/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java +++ b/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java @@ -1,8 +1,6 @@ package net.furizon.backend.utils.configs; import lombok.Data; -import lombok.Getter; -import lombok.Setter; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @@ -14,5 +12,4 @@ public class PretixConnectionConfig { - } diff --git a/src/main/java/net/furizon/backend/utils/pretix/Constants.java b/src/main/java/net/furizon/backend/utils/pretix/Constants.java index 82e3dddd..adf187b2 100644 --- a/src/main/java/net/furizon/backend/utils/pretix/Constants.java +++ b/src/main/java/net/furizon/backend/utils/pretix/Constants.java @@ -1,33 +1,33 @@ package net.furizon.backend.utils.pretix; public class Constants { - public static final String METADATA_IDENTIFIER_ITEM = "item_name"; - //public static final String METADATA_CATEGORY_IDENTIFIER = "category_name"; + public static final String METADATA_IDENTIFIER_ITEM = "item_name"; + //public static final String METADATA_CATEGORY_IDENTIFIER = "category_name"; - //public static final String CATEGORY_TICKETS = "tickets"; - //public static final String CATEGORY_MEMBERSHIPS = "memberships"; - //public static final String CATEGORY_SPONSORSHIPS = "sponsorships"; + //public static final String CATEGORY_TICKETS = "tickets"; + //public static final String CATEGORY_MEMBERSHIPS = "memberships"; + //public static final String CATEGORY_SPONSORSHIPS = "sponsorships"; - public static final String METADATA_EVENT_TICKET = "ticket"; - public static final String METADATA_EVENT_TICKET_DAILY_TAG_PREFIX = "ticket_daily_"; + public static final String METADATA_EVENT_TICKET = "ticket"; + public static final String METADATA_EVENT_TICKET_DAILY_TAG_PREFIX = "ticket_daily_"; - public static final String METADATA_MEMBERSHIP_CARD = "membership_card"; + public static final String METADATA_MEMBERSHIP_CARD = "membership_card"; - public static final String METADATA_SPONSORSHIP = "sponsorship"; - public static final String METADATA_SPONSORSHIP_VARIATIONS_TAG_PREFIX = "sponsorship_type_"; + public static final String METADATA_SPONSORSHIP = "sponsorship"; + public static final String METADATA_SPONSORSHIP_VARIATIONS_TAG_PREFIX = "sponsorship_type_"; - public static final String METADATA_EXTRA_DAYS_TAG_PREFIX = "extra_days_"; + public static final String METADATA_EXTRA_DAYS_TAG_PREFIX = "extra_days_"; - public static final String METADATA_ROOM = "room"; - public static final String METADATA_ROOM_TYPE_TAG_PREFIX = "room_type_"; + public static final String METADATA_ROOM = "room"; + public static final String METADATA_ROOM_TYPE_TAG_PREFIX = "room_type_"; - public static final String QUESTIONS_ACCOUNT_SECRET = "account_secret"; + public static final String QUESTIONS_ACCOUNT_SECRET = "account_secret"; - public static final String QUESTIONS_FILE_KEEP = "file:keep"; + public static final String QUESTIONS_FILE_KEEP = "file:keep"; - // Why? you have Spring Http Codes Class :) - public static final int[] STATUS_CODES_WITH_404 = new int[] { 200, 404 }; - public static final int[] STATUS_CODES_ONLY_200 = new int[] { 200 }; - public static final int[] STATUS_CODES_FILE_UPLOAD = new int[] { 201 }; + // Why? you have Spring Http Codes Class :) + public static final int[] STATUS_CODES_WITH_404 = new int[]{200, 404}; + public static final int[] STATUS_CODES_ONLY_200 = new int[]{200}; + public static final int[] STATUS_CODES_FILE_UPLOAD = new int[]{201}; } diff --git a/src/main/java/net/furizon/backend/utils/pretix/ExtraDays.java b/src/main/java/net/furizon/backend/utils/pretix/ExtraDays.java index 806924e8..e415d77d 100644 --- a/src/main/java/net/furizon/backend/utils/pretix/ExtraDays.java +++ b/src/main/java/net/furizon/backend/utils/pretix/ExtraDays.java @@ -1,9 +1,12 @@ package net.furizon.backend.utils.pretix; public enum ExtraDays { - NONE, EARLY, LATE, BOTH; + NONE, + EARLY, + LATE, + BOTH; - public static Sponsorship getFromOrdinal(int ordinal) { - return Sponsorship.values()[ordinal]; - } + public static Sponsorship get(int ordinal) { + return Sponsorship.values()[ordinal]; + } } diff --git a/src/main/java/net/furizon/backend/utils/pretix/OrderStatus.java b/src/main/java/net/furizon/backend/utils/pretix/OrderStatus.java index 7dff5f49..54cb85a4 100644 --- a/src/main/java/net/furizon/backend/utils/pretix/OrderStatus.java +++ b/src/main/java/net/furizon/backend/utils/pretix/OrderStatus.java @@ -3,24 +3,26 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.HashMap; +import java.util.Arrays; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; @RequiredArgsConstructor public enum OrderStatus { - CANCELED("c"), PENDING("n"), PAID("p"), EXPIRED("e"); + CANCELED("c"), + PENDING("n"), + PAID("p"), + EXPIRED("e"); - @Getter - private final String code; + @Getter + private final String code; - private static final Map MAP = new HashMap<>(); + private static final Map MAP = Arrays + .stream(OrderStatus.values()) + .collect(Collectors.toMap(OrderStatus::getCode, Function.identity())); - static { - for (OrderStatus type : OrderStatus.values()) - MAP.put(type.code, type); - } - - public static OrderStatus fromCode(String code) { - return MAP.get(code); - } + public static OrderStatus get(String code) { + return MAP.get(code); + } } diff --git a/src/main/java/net/furizon/backend/utils/pretix/QuestionType.java b/src/main/java/net/furizon/backend/utils/pretix/QuestionType.java index c9af4c8b..5100023f 100644 --- a/src/main/java/net/furizon/backend/utils/pretix/QuestionType.java +++ b/src/main/java/net/furizon/backend/utils/pretix/QuestionType.java @@ -3,25 +3,35 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; -import java.util.HashMap; +import java.util.Arrays; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; @RequiredArgsConstructor public enum QuestionType { - NUMBER("N"), STRING_ONE_LINE("S"), STRING_MULTI_LINE("T"), BOOLEAN("B"), LIST_SINGLE_CHOICE("C"), LIST_MULTIPLE_CHOICE("M"), FILE("F"), DATE("D"), TIME("H"), DATE_TIME("W"), COUNTRY_CODE("CC"), PHONE_NUMBER("TEL"); - - @Getter - private final String code; - - private static final Map MAP = new HashMap<>(); - - static { - for (QuestionType type : QuestionType.values()) - MAP.put(type.code, type); - } - - public static QuestionType fromCode(String code) { - return MAP.get(code); - } + NUMBER("N"), + STRING_ONE_LINE("S"), + STRING_MULTI_LINE("T"), + BOOLEAN("B"), + LIST_SINGLE_CHOICE("C"), + LIST_MULTIPLE_CHOICE("M"), + FILE("F"), + DATE("D"), + TIME("H"), + DATE_TIME("W"), + COUNTRY_CODE("CC"), + PHONE_NUMBER("TEL"); + + @Getter + private final String code; + + private static final Map MAP = Arrays + .stream(QuestionType.values()) + .collect(Collectors.toMap(QuestionType::getCode, Function.identity())); + + public static QuestionType get(String code) { + return MAP.get(code); + } } diff --git a/src/main/java/net/furizon/backend/utils/pretix/Sponsorship.java b/src/main/java/net/furizon/backend/utils/pretix/Sponsorship.java index f4bcbdfc..7c3e7e9b 100644 --- a/src/main/java/net/furizon/backend/utils/pretix/Sponsorship.java +++ b/src/main/java/net/furizon/backend/utils/pretix/Sponsorship.java @@ -3,14 +3,16 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; +@Getter @RequiredArgsConstructor public enum Sponsorship { - NONE(""), SPONSOR(""), SUPER_SPONSOR(""); //These MUST be in "importance" order + NONE(""), + SPONSOR(""), + SUPER_SPONSOR(""); //These MUST be in "importance" order - @Getter - private final String color; //TODO for frontend + private final String color; //TODO for frontend - public static Sponsorship getFromOrdinal(int ordinal){ - return Sponsorship.values()[ordinal]; - } + public static Sponsorship get(int ordinal) { + return Sponsorship.values()[ordinal]; + } } diff --git a/src/main/java/net/furizon/backend/web/controllers/users/AuthenticationController.java b/src/main/java/net/furizon/backend/web/controllers/users/AuthenticationController.java index 7cbe0be7..4900f485 100644 --- a/src/main/java/net/furizon/backend/web/controllers/users/AuthenticationController.java +++ b/src/main/java/net/furizon/backend/web/controllers/users/AuthenticationController.java @@ -18,13 +18,16 @@ @RequestMapping("/authentication") @Slf4j public class AuthenticationController { - private final UserService userService; @PostMapping("/login") - public ResponseEntity login (HttpServletRequest request, HttpServletResponse response, @Valid @RequestBody UserLoginRequest body) { + public ResponseEntity login( + HttpServletRequest request, + HttpServletResponse response, + @Valid @RequestBody UserLoginRequest body + ) { userService.login(request, response, body); - + // TODO -> Implement + return ResponseEntity.noContent().build(); } - } diff --git a/src/main/java/net/furizon/backend/web/entities/HttpErrorResponse.java b/src/main/java/net/furizon/backend/web/entities/HttpErrorResponse.java index e4a8d7dc..1d1becdc 100644 --- a/src/main/java/net/furizon/backend/web/entities/HttpErrorResponse.java +++ b/src/main/java/net/furizon/backend/web/entities/HttpErrorResponse.java @@ -12,10 +12,13 @@ public class HttpErrorResponse { private Map errors; private List generalErrors; - public static HttpErrorResponse of(String message, int status, Map errors, List generalErrors) { - HttpErrorResponse toReturn = new HttpErrorResponse(); - toReturn.message = message; - toReturn.status = status; + public static HttpErrorResponse of( + String message, + int status, + Map errors, + List generalErrors + ) { + HttpErrorResponse toReturn = of(message, status); toReturn.errors = errors; toReturn.generalErrors = generalErrors; return toReturn; @@ -27,5 +30,4 @@ public static HttpErrorResponse of(String message, int status) { toReturn.status = status; return toReturn; } - } diff --git a/src/main/java/net/furizon/backend/web/entities/users/UserCreationRequest.java b/src/main/java/net/furizon/backend/web/entities/users/UserCreationRequest.java index 57c73fda..ef3e6cff 100644 --- a/src/main/java/net/furizon/backend/web/entities/users/UserCreationRequest.java +++ b/src/main/java/net/furizon/backend/web/entities/users/UserCreationRequest.java @@ -11,9 +11,8 @@ public class UserCreationRequest { private String email; @NotNull - @Length(min=8) + @Length(min = 8) private String password; private String passwordConfirmation; - } diff --git a/src/main/java/net/furizon/backend/web/entities/users/UserResponse.java b/src/main/java/net/furizon/backend/web/entities/users/UserResponse.java index c3a4bf20..55ef7344 100644 --- a/src/main/java/net/furizon/backend/web/entities/users/UserResponse.java +++ b/src/main/java/net/furizon/backend/web/entities/users/UserResponse.java @@ -2,6 +2,9 @@ import lombok.Data; +import java.util.ArrayList; +import java.util.List; + @Data public class UserResponse { private Long id; @@ -9,6 +12,6 @@ public class UserResponse { private String lastName; private String email; private String profileImageUrl; - private List connectedAccounts = new ArrayList<>(); + // private List connectedAccounts = new ArrayList<>(); private List authorities = new ArrayList<>(); } diff --git a/src/main/java/net/furizon/backend/web/handlers/ExceptionHandler.java b/src/main/java/net/furizon/backend/web/handlers/ExceptionHandler.java index e9ed5362..c6b6e9b0 100644 --- a/src/main/java/net/furizon/backend/web/handlers/ExceptionHandler.java +++ b/src/main/java/net/furizon/backend/web/handlers/ExceptionHandler.java @@ -1,7 +1,7 @@ package net.furizon.backend.web.handlers; +import lombok.extern.slf4j.Slf4j; import net.furizon.backend.web.entities.HttpErrorResponse; -import org.slf4j.Logger; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; @@ -16,16 +16,20 @@ import java.util.List; import java.util.Map; +// TODO -> Better implement it another way +@Slf4j public class ExceptionHandler extends ResponseEntityExceptionHandler { - - private static final Logger log = org.slf4j.LoggerFactory.getLogger(ExceptionHandler.class); - @Override - protected ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { + protected ResponseEntity handleMethodArgumentNotValid( + MethodArgumentNotValidException ex, + HttpHeaders headers, + HttpStatusCode status, + WebRequest request + ) { Map errors = new HashMap(); List generalErrors = new ArrayList(); - ex.getBindingResult().getAllErrors().forEach((error)-> { - if (error instanceof FieldError ferr){ + ex.getBindingResult().getAllErrors().forEach((error) -> { + if (error instanceof FieldError ferr) { String fieldName = ferr.getField(); String errorMessage = ferr.getDefaultMessage(); errors.put(fieldName, errorMessage); diff --git a/src/main/java/net/furizon/backend/web/handlers/Oauth2LoginSuccessHandler.java b/src/main/java/net/furizon/backend/web/handlers/Oauth2LoginSuccessHandler.java index 2459cedf..ad9494a3 100644 --- a/src/main/java/net/furizon/backend/web/handlers/Oauth2LoginSuccessHandler.java +++ b/src/main/java/net/furizon/backend/web/handlers/Oauth2LoginSuccessHandler.java @@ -1,6 +1,35 @@ package net.furizon.backend.web.handlers; +// TODO -> + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import java.io.IOException; + +@Configuration public class Oauth2LoginSuccessHandler implements AuthenticationSuccessHandler { + @Override + public void onAuthenticationSuccess( + HttpServletRequest request, + HttpServletResponse response, + FilterChain chain, + Authentication authentication + ) throws IOException, ServletException { + AuthenticationSuccessHandler.super.onAuthenticationSuccess(request, response, chain, authentication); + } + + @Override + public void onAuthenticationSuccess( + HttpServletRequest request, + HttpServletResponse response, + Authentication authentication + ) throws IOException, ServletException { + // TODO -> + } } From 7f566f6749cf736ded4f45f06c4206ca881c0e84 Mon Sep 17 00:00:00 2001 From: Stark Date: Wed, 16 Oct 2024 20:59:48 +0200 Subject: [PATCH 2/2] Refactor Configs --- .env.example | 4 +++ config.properties | 16 --------- .../backend/utils/configs/PretixConfig.java | 2 -- .../utils/configs/PretixConnectionConfig.java | 2 -- src/main/resources/application.properties | 7 ---- src/main/resources/application.yml | 36 +++++++++++++++++++ 6 files changed, 40 insertions(+), 27 deletions(-) create mode 100644 .env.example delete mode 100644 config.properties delete mode 100644 src/main/resources/application.properties create mode 100644 src/main/resources/application.yml diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..46ca945a --- /dev/null +++ b/.env.example @@ -0,0 +1,4 @@ +BACKEND_SERVER_PORT=8080 +BACKEND_DATASOURCE_URL=jdbc:postgresql://localhost:5433/postgres +BACKEND_DATASOURCE_USERNAME=postgres +BACKEND_DATASOURCE_PASSWORD=postgres diff --git a/config.properties b/config.properties deleted file mode 100644 index 54af9f6b..00000000 --- a/config.properties +++ /dev/null @@ -1,16 +0,0 @@ -# TODO -> Move to Application yml -pretix.protocol=http -pretix.host-name=localhost -pretix.port=8000 -pretix.api-key=xxx -pretix.organizer=org -pretix.current-event=evt -pretix.run-healthcheck=true -pretix.max-connection-retries=3 -pretix.max-connections=10 -pretix.connection-timeout=10000 -pretix.max-propic-file-size-bytes=5242880 -pretix.max-propic-width=3000 -pretix.min-propic-width=3000 -pretix.max-propic-height=300 -pretix.min-propic-height=300 diff --git a/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java b/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java index 57370afe..d5623c67 100644 --- a/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java +++ b/src/main/java/net/furizon/backend/utils/configs/PretixConfig.java @@ -6,7 +6,6 @@ import lombok.Setter; import net.furizon.backend.db.entities.pretix.Event; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -14,7 +13,6 @@ @Data @Component -@PropertySource(value = "file:config.properties") @ConfigurationProperties(prefix = "pretix") @NoArgsConstructor public class PretixConfig { diff --git a/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java b/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java index a9202db3..ee40913b 100644 --- a/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java +++ b/src/main/java/net/furizon/backend/utils/configs/PretixConnectionConfig.java @@ -2,12 +2,10 @@ import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Data @Component -@PropertySource(value = "file:config.properties") @ConfigurationProperties(prefix = "pretix.connection") public class PretixConnectionConfig { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 818ade9b..00000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -spring.application.name=backend -spring.datasource.url=jdbc:postgresql://localhost:5432/furizon -spring.datasource.username=user -spring.datasource.password=pass -spring.jpa.hibernate.ddl-auto=update -spring.datasource.driver-class-name=org.postgresql.Driver -spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 00000000..a59c5908 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,36 @@ +server: + port: ${BACKEND_SERVER_PORT:8080} + +spring: + application: + name: fz-backend + datasource: + url: ${BACKEND_DATASOURCE_URL:jdbc:postgresql://localhost:5432/furizon} + username: ${BACKEND_DATASOURCE_USERNAME:user} + password: ${BACKEND_DATASOURCE_PASSWORD:pass} + driver-class-name: org.postgresql.Driver + # TODO -> Remove it after Jooq migration + jpa: + hibernate: + ddl-auto: update + properties: + hibernate: + dialect: org.hibernate.dialect.PostgreSQLDialect + +# TODO -> Env variables +pretix: + protocol: http + host-name: localhost + port: 8000 + api-key: xxx + organizer: org + current-event: evt + run-healthcheck: true + max-connection-retries: 3 + max-connections: 10 + connection-timeout: 10000 + max-propic-file-size-bytes: 5242880 + max-propic-width: 3000 + min-propic-width: 3000 + max-propic-height: 300 + min-propic-height: 300