From a678cf34168fb437c04b1cdbdaaa0bfcc108a7a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20Ha=C3=9Fel?= Date: Tue, 5 Nov 2024 12:07:06 +0100 Subject: [PATCH] HUGE REFACTOR: Using immutable Uuid classes + ArgumentResolvers in Controllers --- .../angular/src/app/api/User/user.service.ts | 8 +- src/main/angular/src/app/api/common/Page.ts | 8 +- .../angular/src/app/api/common/api.service.ts | 4 + .../{NumbersPrivateDto.ts => Numbers.ts} | 6 +- .../app/api/tools/Numbers/numbers.service.ts | 23 ++-- .../pages/group/group/group.component.html | 101 ++++++++++++------ .../app/pages/group/group/group.component.ts | 36 +++++-- .../pages/tools/numbers/numbers.component.ts | 4 +- .../de/ph87/tools/UserArgumentResolver.java | 40 ------- .../ph87/tools/common/uuid/AbstractUuid.java | 43 ++++++++ .../common/uuid/AbstractUuidDeserializer.java | 20 ++++ .../tools/common/uuid/UuidSerializer.java | 20 ++++ src/main/java/de/ph87/tools/group/Group.java | 35 +++--- .../group/{Access.java => GroupAccess.java} | 2 +- .../ph87/tools/group/GroupAccessService.java | 16 +-- .../group/GroupChangePasswordInbound.java | 19 ---- .../tools/group/GroupChangeTitleRequest.java | 19 ---- .../de/ph87/tools/group/GroupController.java | 26 +++-- .../java/de/ph87/tools/group/GroupDto.java | 6 +- .../de/ph87/tools/group/GroupJoinRequest.java | 19 ---- .../de/ph87/tools/group/GroupService.java | 32 +++--- .../requests/GroupChangePasswordRequest.java | 23 ++++ .../requests/GroupChangeTitleRequest.java | 23 ++++ .../group/requests/GroupJoinRequest.java | 23 ++++ .../ph87/tools/group/uuid/GroupAbstract.java | 30 ++++++ .../de/ph87/tools/group/uuid/GroupUuid.java | 16 +++ .../group/uuid/GroupUuidArgumentResolver.java | 33 ++++++ .../group/uuid/GroupUuidDeserializer.java | 18 ++++ .../de/ph87/tools/tools/numbers/Numbers.java | 39 ++++--- .../tools/tools/numbers/NumbersAccess.java | 20 ++++ .../tools/numbers/NumbersController.java | 26 +++-- ...NumbersPrivateDto.java => NumbersDto.java} | 11 +- .../tools/numbers/NumbersRepository.java | 7 ++ .../tools/tools/numbers/NumbersService.java | 57 +++++----- .../tools/numbers/uuid/NumbersAbstract.java | 20 ++++ .../tools/tools/numbers/uuid/NumbersUuid.java | 16 +++ .../uuid/NumbersUuidArgumentResolver.java | 33 ++++++ src/main/java/de/ph87/tools/user/User.java | 42 ++++++-- .../de/ph87/tools/user/UserCommonDto.java | 29 ----- .../de/ph87/tools/user/UserController.java | 17 +-- .../de/ph87/tools/user/UserPrivateDto.java | 20 ++-- .../de/ph87/tools/user/UserPublicDto.java | 10 +- .../java/de/ph87/tools/user/UserService.java | 50 +++++---- .../de/ph87/tools/user/reference/IUser.java | 31 ------ .../UserLoginRequest.java} | 4 +- .../ph87/tools/user/uuid/UserPrivateUuid.java | 16 +++ .../uuid/UserPrivateUuidArgumentResolver.java | 44 ++++++++ .../tools/user/uuid/UserPublicAbstract.java | 20 ++++ .../ph87/tools/user/uuid/UserPublicUuid.java | 16 +++ .../uuid/UserPublicUuidArgumentResolver.java | 33 ++++++ .../java/de/ph87/tools/web/WebConfig.java | 10 +- 51 files changed, 839 insertions(+), 385 deletions(-) rename src/main/angular/src/app/api/tools/Numbers/{NumbersPrivateDto.ts => Numbers.ts} (78%) delete mode 100644 src/main/java/de/ph87/tools/UserArgumentResolver.java create mode 100644 src/main/java/de/ph87/tools/common/uuid/AbstractUuid.java create mode 100644 src/main/java/de/ph87/tools/common/uuid/AbstractUuidDeserializer.java create mode 100644 src/main/java/de/ph87/tools/common/uuid/UuidSerializer.java rename src/main/java/de/ph87/tools/group/{Access.java => GroupAccess.java} (84%) delete mode 100644 src/main/java/de/ph87/tools/group/GroupChangePasswordInbound.java delete mode 100644 src/main/java/de/ph87/tools/group/GroupChangeTitleRequest.java delete mode 100644 src/main/java/de/ph87/tools/group/GroupJoinRequest.java create mode 100644 src/main/java/de/ph87/tools/group/requests/GroupChangePasswordRequest.java create mode 100644 src/main/java/de/ph87/tools/group/requests/GroupChangeTitleRequest.java create mode 100644 src/main/java/de/ph87/tools/group/requests/GroupJoinRequest.java create mode 100644 src/main/java/de/ph87/tools/group/uuid/GroupAbstract.java create mode 100644 src/main/java/de/ph87/tools/group/uuid/GroupUuid.java create mode 100644 src/main/java/de/ph87/tools/group/uuid/GroupUuidArgumentResolver.java create mode 100644 src/main/java/de/ph87/tools/group/uuid/GroupUuidDeserializer.java create mode 100644 src/main/java/de/ph87/tools/tools/numbers/NumbersAccess.java rename src/main/java/de/ph87/tools/tools/numbers/{NumbersPrivateDto.java => NumbersDto.java} (50%) create mode 100644 src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersAbstract.java create mode 100644 src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuid.java create mode 100644 src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuidArgumentResolver.java delete mode 100644 src/main/java/de/ph87/tools/user/UserCommonDto.java delete mode 100644 src/main/java/de/ph87/tools/user/reference/IUser.java rename src/main/java/de/ph87/tools/user/{LoginRequest.java => requests/UserLoginRequest.java} (73%) create mode 100644 src/main/java/de/ph87/tools/user/uuid/UserPrivateUuid.java create mode 100644 src/main/java/de/ph87/tools/user/uuid/UserPrivateUuidArgumentResolver.java create mode 100644 src/main/java/de/ph87/tools/user/uuid/UserPublicAbstract.java create mode 100644 src/main/java/de/ph87/tools/user/uuid/UserPublicUuid.java create mode 100644 src/main/java/de/ph87/tools/user/uuid/UserPublicUuidArgumentResolver.java diff --git a/src/main/angular/src/app/api/User/user.service.ts b/src/main/angular/src/app/api/User/user.service.ts index 134f995..dcd79c0 100644 --- a/src/main/angular/src/app/api/User/user.service.ts +++ b/src/main/angular/src/app/api/User/user.service.ts @@ -7,15 +7,17 @@ import {EventType, Router} from "@angular/router"; import {BehaviorSubject, filter, Subject, Subscription} from "rxjs"; import {Group} from "../group/Group"; import {StompService} from "@stomp/ng2-stompjs"; -import {NumbersPrivateDto} from "../tools/Numbers/NumbersPrivateDto"; +import {Numbers} from "../tools/Numbers/Numbers"; function userPushMessageFromJson(json: any): object { const type = json['_type_']; switch (type) { - case 'NumbersPrivateDto': - return NumbersPrivateDto.fromJson(json['payload']); + case 'NumbersDto': + return Numbers.fromJson(json['payload']); case 'UserPrivateDto': return UserPrivate.fromJson(json['payload']); + case 'GroupDto': + return Group.fromJson(json['payload']); } throw new Error("Not implemented UserPushMessage._type_ = " + type); } diff --git a/src/main/angular/src/app/api/common/Page.ts b/src/main/angular/src/app/api/common/Page.ts index a90508f..51d1745 100644 --- a/src/main/angular/src/app/api/common/Page.ts +++ b/src/main/angular/src/app/api/common/Page.ts @@ -17,10 +17,10 @@ export class Page { static fromJson(fromJson: FromJson): FromJson> { return (json: any) => new Page( - validateNumber(json.page.size), - validateNumber(json.page.number), - validateNumber(json.page.totalPages), - validateNumber(json.page.totalElements), + validateNumber(json.size), + validateNumber(json.number), + validateNumber(json.totalPages), + validateNumber(json.totalElements), validateList(json.content, fromJson), ); } diff --git a/src/main/angular/src/app/api/common/api.service.ts b/src/main/angular/src/app/api/common/api.service.ts index 92e8cb7..4bfe8c6 100644 --- a/src/main/angular/src/app/api/common/api.service.ts +++ b/src/main/angular/src/app/api/common/api.service.ts @@ -52,6 +52,10 @@ export class ApiService { return this.http.get(getApiUrl('http', path), {withCredentials: true}).pipe(map(fromJson)).subscribe(next); } + getPage(path: any[], fromJson: FromJson, next: Next> | undefined = undefined): Subscription { + return this.http.get(getApiUrl('http', path), {withCredentials: true}).pipe(map(Page.fromJson(fromJson))).subscribe(next); + } + getList(path: any[], fromJson: FromJson, next: Next | undefined = undefined): Subscription { return this.http.get(getApiUrl('http', path), {withCredentials: true}).pipe(map(list => list.map(fromJson))).subscribe(next); } diff --git a/src/main/angular/src/app/api/tools/Numbers/NumbersPrivateDto.ts b/src/main/angular/src/app/api/tools/Numbers/Numbers.ts similarity index 78% rename from src/main/angular/src/app/api/tools/Numbers/NumbersPrivateDto.ts rename to src/main/angular/src/app/api/tools/Numbers/Numbers.ts index d2d2100..6b16336 100644 --- a/src/main/angular/src/app/api/tools/Numbers/NumbersPrivateDto.ts +++ b/src/main/angular/src/app/api/tools/Numbers/Numbers.ts @@ -1,7 +1,7 @@ import {Group} from "../../group/Group"; import {validateDate, validateNumber, validateString} from "../../common/validators"; -export class NumbersPrivateDto { +export class Numbers { constructor( readonly uuid: string, @@ -12,8 +12,8 @@ export class NumbersPrivateDto { // - } - static fromJson(json: any): NumbersPrivateDto { - return new NumbersPrivateDto( + static fromJson(json: any): Numbers { + return new Numbers( validateString(json['uuid']), Group.fromJson(json['group']), validateDate(json['date']), diff --git a/src/main/angular/src/app/api/tools/Numbers/numbers.service.ts b/src/main/angular/src/app/api/tools/Numbers/numbers.service.ts index 358f0d2..9d97fc6 100644 --- a/src/main/angular/src/app/api/tools/Numbers/numbers.service.ts +++ b/src/main/angular/src/app/api/tools/Numbers/numbers.service.ts @@ -3,8 +3,9 @@ import {ApiService} from "../../common/api.service"; import {Router} from "@angular/router"; import {UserService} from "../../User/user.service"; import {Next} from "../../common/types"; -import {NumbersPrivateDto} from "./NumbersPrivateDto"; +import {Numbers} from "./Numbers"; import {validateBoolean, validateString} from "../../common/validators"; +import {Page} from "../../common/Page"; @Injectable({ providedIn: 'root' @@ -16,25 +17,29 @@ export class NumbersService { protected readonly router: Router, protected readonly userService: UserService ) { - this.userService.subscribePush(m => m instanceof NumbersPrivateDto, message => { + this.userService.subscribePush(m => m instanceof Numbers, message => { this.router.navigate(['Numbers', message.uuid]) }); } - create(groupUuid: string) { + create(groupUuid: string): void { this.api.getNone(['Numbers', 'create', groupUuid]); } - byUuid(uuid: string, next: Next) { - this.api.postSingle(['Numbers', 'byUuid'], uuid, NumbersPrivateDto.fromJson, next); + page(groupUuid: string, page: number, pageSize: number, next: Next>): void { + this.api.getPage(['Numbers', 'page', groupUuid, page, pageSize], Numbers.fromJson, next); } - canAccess(uuid: string, next: Next) { - this.api.postSingle(['Numbers', 'canAccess'], uuid, validateBoolean, next); + byUuid(numbersUuid: string, next: Next): void { + this.api.postSingle(['Numbers', 'byUuid'], numbersUuid, Numbers.fromJson, next); } - getGroupUuid(uuid: string | null, next: Next) { - this.api.postSingle(['Numbers', 'getGroupUuid'], uuid, validateString, next); + canAccess(numbersUuid: string, next: Next): void { + this.api.postSingle(['Numbers', 'canAccess'], numbersUuid, validateBoolean, next); + } + + getGroupUuid(numbersUuid: string | null, next: Next): void { + this.api.postSingle(['Numbers', 'getGroupUuid'], numbersUuid, validateString, next); } } diff --git a/src/main/angular/src/app/pages/group/group/group.component.html b/src/main/angular/src/app/pages/group/group/group.component.html index 6d69d97..fb311ca 100644 --- a/src/main/angular/src/app/pages/group/group/group.component.html +++ b/src/main/angular/src/app/pages/group/group/group.component.html @@ -1,34 +1,73 @@ - -

Gruppe

- - - - - - - - - - - - - - - - - -
Erstellt{{ group.value.created | date:'yyyy-MM-dd HH:mm' }}
Titel - - -
Passwort - - -
Teilnehmer -
{{ user.name }}
-
+
- +
+
+
+ Passwort +
+
+
+ +
+
- + - +
+
+
+ Gruppe +
+
+ + + + + + + + + + + + + + + + + +
Erstellt{{ group.value.created | date:'yyyy-MM-dd HH:mm' }}
Titel + + +
Passwort + + +
Teilnehmer +
{{ user.name }}
+
+
+
+
+ +
+
+
+ Nummern +
+
+
+ + + + + +
{{ numbers.date | relative:now }}{{ numbers.number }}
+
+ +
+
+
+ +
+ +
diff --git a/src/main/angular/src/app/pages/group/group/group.component.ts b/src/main/angular/src/app/pages/group/group/group.component.ts index 2e8cdfd..9402e4f 100644 --- a/src/main/angular/src/app/pages/group/group/group.component.ts +++ b/src/main/angular/src/app/pages/group/group/group.component.ts @@ -1,4 +1,4 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, OnDestroy, OnInit} from '@angular/core'; import {DatePipe, NgForOf, NgIf} from "@angular/common"; import {FormsModule, ReactiveFormsModule} from "@angular/forms"; import {TextComponent} from "../../../shared/text/text.component"; @@ -9,6 +9,10 @@ import {Group} from "../../../api/group/Group"; import {Subscribed} from "../../../api/Subscribed"; import {NumbersService} from "../../../api/tools/Numbers/numbers.service"; import {PasswordComponent} from "../../../shared/password/password.component"; +import {Numbers} from "../../../api/tools/Numbers/Numbers"; +import {Page} from "../../../api/common/Page"; +import {RelativePipe} from "../../../shared/relative.pipe"; +import {Subscription, timer} from "rxjs"; @Component({ selector: 'app-group', @@ -20,19 +24,26 @@ import {PasswordComponent} from "../../../shared/password/password.component"; ReactiveFormsModule, TextComponent, FormsModule, - PasswordComponent + PasswordComponent, + RelativePipe ], templateUrl: './group.component.html', styleUrl: './group.component.less' }) -export class GroupComponent implements OnInit { +export class GroupComponent implements OnInit, OnDestroy { protected readonly group: Subscribed; + protected numbersList: Page = Page.EMPTY; + protected granted: boolean | null = null; protected uuid: string | null = null; + protected now: Date = new Date(); + + private timer?: Subscription; + constructor( protected readonly router: Router, protected readonly activatedRoute: ActivatedRoute, @@ -44,21 +55,30 @@ export class GroupComponent implements OnInit { } ngOnInit(): void { + this.timer = timer(1000, 1000).subscribe(() => this.now = new Date()); this.activatedRoute.params.subscribe(params => { - const uuid = params['uuid']; - this.uuid = uuid; + const groupUuid = params['uuid']; + this.uuid = groupUuid; this.granted = null; - if (uuid) { - this.groupService.canAccess(uuid, granted => { + if (groupUuid) { + this.groupService.canAccess(groupUuid, granted => { this.granted = granted; if (granted) { - this.groupService.get(uuid, group => this.group.value = group); + this.groupService.get(groupUuid, group => this.group.value = group); + this.numbersService.page(groupUuid, 0, 10, numbersList => this.numbersList = numbersList); } }); } }); } + ngOnDestroy(): void { + if (this.timer) { + this.timer.unsubscribe(); + this.timer = undefined; + } + } + protected changeTitle(group: Group, title: string) { this.groupService.changeTitle(group, title, group => this.group.value = group); } diff --git a/src/main/angular/src/app/pages/tools/numbers/numbers.component.ts b/src/main/angular/src/app/pages/tools/numbers/numbers.component.ts index 769efb4..9f5c89b 100644 --- a/src/main/angular/src/app/pages/tools/numbers/numbers.component.ts +++ b/src/main/angular/src/app/pages/tools/numbers/numbers.component.ts @@ -1,5 +1,5 @@ import {Component, OnDestroy, OnInit} from '@angular/core'; -import {NumbersPrivateDto} from "../../../api/tools/Numbers/NumbersPrivateDto"; +import {Numbers} from "../../../api/tools/Numbers/Numbers"; import {ActivatedRoute} from "@angular/router"; import {NumbersService} from "../../../api/tools/Numbers/numbers.service"; import {NgIf} from "@angular/common"; @@ -24,7 +24,7 @@ import {Subscription, timer} from "rxjs"; }) export class NumbersComponent implements OnInit, OnDestroy { - protected numbers: NumbersPrivateDto | null = null; + protected numbers: Numbers | null = null; protected now: Date = new Date(); diff --git a/src/main/java/de/ph87/tools/UserArgumentResolver.java b/src/main/java/de/ph87/tools/UserArgumentResolver.java deleted file mode 100644 index 8979476..0000000 --- a/src/main/java/de/ph87/tools/UserArgumentResolver.java +++ /dev/null @@ -1,40 +0,0 @@ -package de.ph87.tools; - -import de.ph87.tools.user.User; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import lombok.NonNull; -import org.springframework.core.MethodParameter; -import org.springframework.stereotype.Component; -import org.springframework.web.bind.support.WebDataBinderFactory; -import org.springframework.web.context.request.NativeWebRequest; -import org.springframework.web.method.support.HandlerMethodArgumentResolver; -import org.springframework.web.method.support.ModelAndViewContainer; - -import java.util.Arrays; - -@Component -public class UserArgumentResolver implements HandlerMethodArgumentResolver { - - public static final String USER_UUID_COOKIE_NAME = "PatrixToolsUserUuid"; - - @Override - public boolean supportsParameter(@NonNull final MethodParameter parameter) { - return parameter.getParameterType() == User.class; - } - - @Override - public User resolveArgument(@NonNull final MethodParameter parameter, final ModelAndViewContainer mavContainer, @NonNull final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { - if (!(webRequest instanceof final HttpServletRequest request)) { - return null; - } - - final String uuid = Arrays.stream(request.getCookies()).filter(cookie -> USER_UUID_COOKIE_NAME.equalsIgnoreCase(cookie.getName())).findFirst().map(Cookie::getValue).orElse(null); - if (uuid == null) { - return null; - } - - return null; - } - -} \ No newline at end of file diff --git a/src/main/java/de/ph87/tools/common/uuid/AbstractUuid.java b/src/main/java/de/ph87/tools/common/uuid/AbstractUuid.java new file mode 100644 index 0000000..6e1c7f1 --- /dev/null +++ b/src/main/java/de/ph87/tools/common/uuid/AbstractUuid.java @@ -0,0 +1,43 @@ +package de.ph87.tools.common.uuid; + +import lombok.Getter; +import lombok.NonNull; + +import java.util.UUID; + +@Getter +public abstract class AbstractUuid { + + public final String uuid; + + @SuppressWarnings("unused") // used by lombok @NoArgsConstructor + protected AbstractUuid() { + this(UUID.randomUUID().toString()); + } + + protected AbstractUuid(@NonNull final String uuid) { + if (uuid.length() != 36) { + throw new RuntimeException(); + } + this.uuid = uuid; + } + + @Override + public String toString() { + return uuid; + } + + @Override + public int hashCode() { + return uuid.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof final AbstractUuid casted) { + return uuid.equals(casted.uuid); + } + return false; + } + +} diff --git a/src/main/java/de/ph87/tools/common/uuid/AbstractUuidDeserializer.java b/src/main/java/de/ph87/tools/common/uuid/AbstractUuidDeserializer.java new file mode 100644 index 0000000..c826fd8 --- /dev/null +++ b/src/main/java/de/ph87/tools/common/uuid/AbstractUuidDeserializer.java @@ -0,0 +1,20 @@ +package de.ph87.tools.common.uuid; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import lombok.NonNull; + +import java.io.IOException; + +public abstract class AbstractUuidDeserializer extends JsonDeserializer { + + @Override + public AbstractUuid deserialize(final JsonParser jsonParser, final DeserializationContext deserializationContext) throws IOException { + return create(jsonParser.readValueAs(String.class)); + } + + @NonNull + protected abstract AbstractUuid create(@NonNull final String s); + +} diff --git a/src/main/java/de/ph87/tools/common/uuid/UuidSerializer.java b/src/main/java/de/ph87/tools/common/uuid/UuidSerializer.java new file mode 100644 index 0000000..a4276c8 --- /dev/null +++ b/src/main/java/de/ph87/tools/common/uuid/UuidSerializer.java @@ -0,0 +1,20 @@ +package de.ph87.tools.common.uuid; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +public class UuidSerializer extends JsonSerializer { + + @Override + public void serialize(final AbstractUuid t, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider) throws IOException { + if (t == null) { + jsonGenerator.writeNull(); + } else { + jsonGenerator.writeString(t.uuid); + } + } + +} diff --git a/src/main/java/de/ph87/tools/group/Group.java b/src/main/java/de/ph87/tools/group/Group.java index 552026d..ab508d1 100644 --- a/src/main/java/de/ph87/tools/group/Group.java +++ b/src/main/java/de/ph87/tools/group/Group.java @@ -1,5 +1,7 @@ package de.ph87.tools.group; +import de.ph87.tools.group.uuid.GroupAbstract; +import de.ph87.tools.group.uuid.GroupUuid; import de.ph87.tools.user.User; import de.ph87.tools.web.IWebSocketMessage; import jakarta.persistence.*; @@ -16,13 +18,24 @@ import java.util.UUID; @ToString @NoArgsConstructor @Table(name = "`group`") -public class Group implements IWebSocketMessage { +public class Group extends GroupAbstract implements IWebSocketMessage { @Id @NonNull - @Column(nullable = false) + @Getter(AccessLevel.NONE) private String uuid = UUID.randomUUID().toString(); + @Transient + private GroupUuid _uuid; + + @NonNull + public GroupUuid getUuid() { + if (_uuid == null) { + _uuid = new GroupUuid(uuid); + } + return _uuid; + } + @NonNull @ManyToOne(optional = false) private User owner; @@ -68,22 +81,4 @@ public class Group implements IWebSocketMessage { return List.of("Number", uuid); } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public boolean isOwnedBy(@NonNull final User user) { - return owner.equals(user); - } - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof final Group group)) { - return false; - } - return group.uuid.equals(this.uuid); - } - - @Override - public int hashCode() { - return uuid.hashCode(); - } - } diff --git a/src/main/java/de/ph87/tools/group/Access.java b/src/main/java/de/ph87/tools/group/GroupAccess.java similarity index 84% rename from src/main/java/de/ph87/tools/group/Access.java rename to src/main/java/de/ph87/tools/group/GroupAccess.java index 20aeacd..d5bd1ec 100644 --- a/src/main/java/de/ph87/tools/group/Access.java +++ b/src/main/java/de/ph87/tools/group/GroupAccess.java @@ -4,7 +4,7 @@ import de.ph87.tools.user.User; import lombok.Data; @Data -public class Access { +public class GroupAccess { public final User user; diff --git a/src/main/java/de/ph87/tools/group/GroupAccessService.java b/src/main/java/de/ph87/tools/group/GroupAccessService.java index dbadd11..43ad36e 100644 --- a/src/main/java/de/ph87/tools/group/GroupAccessService.java +++ b/src/main/java/de/ph87/tools/group/GroupAccessService.java @@ -1,7 +1,9 @@ package de.ph87.tools.group; +import de.ph87.tools.group.uuid.GroupUuid; import de.ph87.tools.user.User; import de.ph87.tools.user.UserService; +import de.ph87.tools.user.uuid.UserPrivateUuid; import jakarta.annotation.Nullable; import lombok.NonNull; import lombok.RequiredArgsConstructor; @@ -21,7 +23,7 @@ public class GroupAccessService { private final UserService userService; - public boolean canAccess(@NonNull final String groupUuid, @Nullable final String userPrivateUuid) { + public boolean canAccess(@NonNull final GroupUuid groupUuid, @Nullable final UserPrivateUuid userPrivateUuid) { final User user = userService.accessOrNull(userPrivateUuid); if (user == null) { return false; @@ -31,14 +33,14 @@ public class GroupAccessService { } @NonNull - public Access access(@NonNull final String groupUuid, @NonNull final String userPrivateUuid) { + public GroupAccess access(@NonNull final UserPrivateUuid userPrivateUuid, @NonNull final GroupUuid groupUuid) { final User user = userService.access(userPrivateUuid); final Group group = getByUuidOrThrow(groupUuid); - return new Access(user, group); + return new GroupAccess(user, group); } - public Access accessAsOwner(@NonNull final String userPrivateUuid, @NonNull final String groupUuid) { - final Access access = access(groupUuid, userPrivateUuid); + public GroupAccess accessAsOwner(@NonNull final UserPrivateUuid userPrivateUuid, @NonNull final GroupUuid groupUuid) { + final GroupAccess access = access(userPrivateUuid, groupUuid); if (!access.group.isOwnedBy(access.user)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } @@ -46,8 +48,8 @@ public class GroupAccessService { } @NonNull - private Group getByUuidOrThrow(@NonNull final String groupUuid) { - return groupRepository.findById(groupUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + private Group getByUuidOrThrow(@NonNull final GroupUuid groupUuid) { + return groupRepository.findByUuid(groupUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); } } diff --git a/src/main/java/de/ph87/tools/group/GroupChangePasswordInbound.java b/src/main/java/de/ph87/tools/group/GroupChangePasswordInbound.java deleted file mode 100644 index 9214e77..0000000 --- a/src/main/java/de/ph87/tools/group/GroupChangePasswordInbound.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.ph87.tools.group; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -@Getter -@ToString -@AllArgsConstructor -public class GroupChangePasswordInbound { - - @NonNull - public final String uuid; - - @NonNull - public final String password; - -} diff --git a/src/main/java/de/ph87/tools/group/GroupChangeTitleRequest.java b/src/main/java/de/ph87/tools/group/GroupChangeTitleRequest.java deleted file mode 100644 index 55ad441..0000000 --- a/src/main/java/de/ph87/tools/group/GroupChangeTitleRequest.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.ph87.tools.group; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -@Getter -@ToString -@AllArgsConstructor -public class GroupChangeTitleRequest { - - @NonNull - public final String uuid; - - @NonNull - public final String title; - -} diff --git a/src/main/java/de/ph87/tools/group/GroupController.java b/src/main/java/de/ph87/tools/group/GroupController.java index a2d2700..c42013b 100644 --- a/src/main/java/de/ph87/tools/group/GroupController.java +++ b/src/main/java/de/ph87/tools/group/GroupController.java @@ -1,5 +1,11 @@ package de.ph87.tools.group; +import de.ph87.tools.group.requests.GroupChangePasswordRequest; +import de.ph87.tools.group.requests.GroupChangeTitleRequest; +import de.ph87.tools.group.requests.GroupJoinRequest; +import de.ph87.tools.group.uuid.GroupUuid; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import de.ph87.tools.user.uuid.UserPublicUuid; import jakarta.annotation.Nullable; import jakarta.servlet.http.HttpServletResponse; import lombok.NonNull; @@ -8,8 +14,6 @@ import org.springframework.web.bind.annotation.*; import java.util.Set; -import static de.ph87.tools.UserArgumentResolver.USER_UUID_COOKIE_NAME; - @CrossOrigin @RestController @RequiredArgsConstructor @@ -21,49 +25,49 @@ public class GroupController { private final GroupAccessService groupAccessService; @GetMapping("create") - public GroupDto create(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String privateUuid, @NonNull final HttpServletResponse response) { + public GroupDto create(@Nullable final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) { return groupService.create(privateUuid, response); } @PostMapping("canAccess") - public boolean canAccess(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String privateUuid, @NonNull @RequestBody final String groupUuid) { + public boolean canAccess(@Nullable final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) { return groupAccessService.canAccess(groupUuid, privateUuid); } @PostMapping("get") - public GroupDto get(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @NonNull @RequestBody final String groupUuid) { + public GroupDto get(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) { return groupService.get(privateUuid, groupUuid); } @NonNull @GetMapping("findAllJoined") - public Set findAllJoined(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid) { + public Set findAllJoined(@NonNull final UserPrivateUuid userUuid) { return groupService.findAllJoined(userUuid); } @NonNull @PostMapping("findAllCommon") - public Set findAllCommon(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull @RequestBody final String targetUuid) { + public Set findAllCommon(@NonNull final UserPrivateUuid userUuid, @NonNull final UserPublicUuid targetUuid) { return groupService.findAllCommon(userUuid, targetUuid); } @PostMapping("join") - public GroupDto join(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String privateUuid, @NonNull @RequestBody final GroupJoinRequest request, @NonNull final HttpServletResponse response) { + public GroupDto join(@Nullable final UserPrivateUuid privateUuid, @NonNull @RequestBody final GroupJoinRequest request, @NonNull final HttpServletResponse response) { return groupService.join(privateUuid, request, response); } @PostMapping("changeTitle") - public GroupDto changeTitle(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @NonNull @RequestBody final GroupChangeTitleRequest request) { + public GroupDto changeTitle(@NonNull final UserPrivateUuid privateUuid, @NonNull @RequestBody final GroupChangeTitleRequest request) { return groupService.changeTitle(privateUuid, request); } @PostMapping("changePassword") - public GroupDto changePassword(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @NonNull @RequestBody final GroupChangePasswordInbound request) { + public GroupDto changePassword(@NonNull final UserPrivateUuid privateUuid, @NonNull @RequestBody final GroupChangePasswordRequest request) { return groupService.changePassword(privateUuid, request); } @PostMapping("leave") - public void leave(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @NonNull @RequestBody final String groupUuid) { + public void leave(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) { groupService.leave(privateUuid, groupUuid); } diff --git a/src/main/java/de/ph87/tools/group/GroupDto.java b/src/main/java/de/ph87/tools/group/GroupDto.java index e8b5336..07e6788 100644 --- a/src/main/java/de/ph87/tools/group/GroupDto.java +++ b/src/main/java/de/ph87/tools/group/GroupDto.java @@ -1,5 +1,8 @@ package de.ph87.tools.group; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import de.ph87.tools.common.uuid.UuidSerializer; +import de.ph87.tools.group.uuid.GroupUuid; import de.ph87.tools.user.UserPublicDto; import lombok.Getter; import lombok.NonNull; @@ -13,7 +16,8 @@ import java.util.Set; public class GroupDto { @NonNull - public final String uuid; + @JsonSerialize(using = UuidSerializer.class) + public final GroupUuid uuid; @NonNull public final String title; diff --git a/src/main/java/de/ph87/tools/group/GroupJoinRequest.java b/src/main/java/de/ph87/tools/group/GroupJoinRequest.java deleted file mode 100644 index 1654c29..0000000 --- a/src/main/java/de/ph87/tools/group/GroupJoinRequest.java +++ /dev/null @@ -1,19 +0,0 @@ -package de.ph87.tools.group; - -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -@Getter -@ToString -@AllArgsConstructor -public class GroupJoinRequest { - - @NonNull - public final String uuid; - - @NonNull - public final String password; - -} diff --git a/src/main/java/de/ph87/tools/group/GroupService.java b/src/main/java/de/ph87/tools/group/GroupService.java index 9105736..cfbf00a 100644 --- a/src/main/java/de/ph87/tools/group/GroupService.java +++ b/src/main/java/de/ph87/tools/group/GroupService.java @@ -1,9 +1,15 @@ package de.ph87.tools.group; +import de.ph87.tools.group.requests.GroupChangePasswordRequest; +import de.ph87.tools.group.requests.GroupChangeTitleRequest; +import de.ph87.tools.group.requests.GroupJoinRequest; +import de.ph87.tools.group.uuid.GroupUuid; import de.ph87.tools.user.User; import de.ph87.tools.user.UserPublicDto; import de.ph87.tools.user.UserService; import de.ph87.tools.user.push.UserPushService; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import de.ph87.tools.user.uuid.UserPublicUuid; import jakarta.annotation.Nullable; import jakarta.servlet.http.HttpServletResponse; import lombok.NonNull; @@ -32,20 +38,20 @@ public class GroupService { private final UserService userService; @NonNull - public GroupDto create(@Nullable final String privateUuid, @NonNull final HttpServletResponse response) { + public GroupDto create(@Nullable final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) { final User user = userService.getUserByPrivateUuidOrElseCreate(privateUuid, response); final Group group = createUnchecked(user); return doJoinUnchecked(group, user); } @NonNull - public GroupDto get(@NonNull final String privateUuid, @NonNull final String groupUuid) { - final Access ug = groupAccessService.access(groupUuid, privateUuid); + public GroupDto get(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) { + final GroupAccess ug = groupAccessService.access(privateUuid, groupUuid); return toDto(ug.group); } @NonNull - public GroupDto join(@Nullable final String privateUuid, @NonNull final GroupJoinRequest request, @NonNull final HttpServletResponse response) { + public GroupDto join(@Nullable final UserPrivateUuid privateUuid, @NonNull final GroupJoinRequest request, @NonNull final HttpServletResponse response) { final User user = userService.getUserByPrivateUuidOrElseCreate(privateUuid, response); final Group group = getByGroupByGroupUuid(request.uuid); if (!group.getPassword().equals(request.password)) { @@ -56,13 +62,13 @@ public class GroupService { } @NonNull - private Group getByGroupByGroupUuid(@NonNull final String groupUuid) { - return groupRepository.findByUuid(groupUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + private Group getByGroupByGroupUuid(@NonNull final GroupUuid groupUuid) { + return groupRepository.findByUuid(groupUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); } @NonNull - public GroupDto changeTitle(@NonNull final String privateUuid, @NonNull final GroupChangeTitleRequest request) { - final Access ug = groupAccessService.access(request.uuid, privateUuid); + public GroupDto changeTitle(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupChangeTitleRequest request) { + final GroupAccess ug = groupAccessService.access(privateUuid, request.uuid); if (!ug.group.isOwnedBy(ug.user)) { throw new ResponseStatusException(HttpStatus.FORBIDDEN); } @@ -71,13 +77,13 @@ public class GroupService { } @NonNull - public GroupDto changePassword(@NonNull final String privateUuid, @NonNull final GroupChangePasswordInbound request) { - final Access access = groupAccessService.accessAsOwner(privateUuid, request.uuid); + public GroupDto changePassword(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupChangePasswordRequest request) { + final GroupAccess access = groupAccessService.accessAsOwner(privateUuid, request.uuid); access.group.setPassword(request.password); return publish(access.group); } - public void leave(@NonNull final String privateUuid, @NonNull final String groupUuid) { + public void leave(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid) { final User user = userService.access(privateUuid); final Group group = getByGroupByGroupUuid(groupUuid); doLeaveUnchecked(group, user); @@ -135,13 +141,13 @@ public class GroupService { } @NonNull - public Set findAllJoined(@NonNull final String privateUuid) { + public Set findAllJoined(@NonNull final UserPrivateUuid privateUuid) { final User principal = userService.access(privateUuid); return groupRepository.findAllByUsersContains(principal).stream().map(this::toDto).collect(Collectors.toSet()); } @NonNull - public Set findAllCommon(@NonNull final String privateUuid, @NonNull final String targetUuid) { + public Set findAllCommon(@NonNull final UserPrivateUuid privateUuid, @NonNull final UserPublicUuid targetUuid) { final User principal = userService.access(privateUuid); final User target = userService.getByPublicUuid(targetUuid); return findAllCommonGroups(principal, target); diff --git a/src/main/java/de/ph87/tools/group/requests/GroupChangePasswordRequest.java b/src/main/java/de/ph87/tools/group/requests/GroupChangePasswordRequest.java new file mode 100644 index 0000000..5cbe557 --- /dev/null +++ b/src/main/java/de/ph87/tools/group/requests/GroupChangePasswordRequest.java @@ -0,0 +1,23 @@ +package de.ph87.tools.group.requests; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import de.ph87.tools.group.uuid.GroupUuid; +import de.ph87.tools.group.uuid.GroupUuidDeserializer; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +@AllArgsConstructor +public class GroupChangePasswordRequest { + + @NonNull + @JsonDeserialize(using = GroupUuidDeserializer.class) + public final GroupUuid uuid; + + @NonNull + public final String password; + +} diff --git a/src/main/java/de/ph87/tools/group/requests/GroupChangeTitleRequest.java b/src/main/java/de/ph87/tools/group/requests/GroupChangeTitleRequest.java new file mode 100644 index 0000000..e34e074 --- /dev/null +++ b/src/main/java/de/ph87/tools/group/requests/GroupChangeTitleRequest.java @@ -0,0 +1,23 @@ +package de.ph87.tools.group.requests; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import de.ph87.tools.group.uuid.GroupUuid; +import de.ph87.tools.group.uuid.GroupUuidDeserializer; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +@AllArgsConstructor +public class GroupChangeTitleRequest { + + @NonNull + @JsonDeserialize(using = GroupUuidDeserializer.class) + public final GroupUuid uuid; + + @NonNull + public final String title; + +} diff --git a/src/main/java/de/ph87/tools/group/requests/GroupJoinRequest.java b/src/main/java/de/ph87/tools/group/requests/GroupJoinRequest.java new file mode 100644 index 0000000..fac2df4 --- /dev/null +++ b/src/main/java/de/ph87/tools/group/requests/GroupJoinRequest.java @@ -0,0 +1,23 @@ +package de.ph87.tools.group.requests; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import de.ph87.tools.group.uuid.GroupUuid; +import de.ph87.tools.group.uuid.GroupUuidDeserializer; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NonNull; +import lombok.ToString; + +@Getter +@ToString +@AllArgsConstructor +public class GroupJoinRequest { + + @NonNull + @JsonDeserialize(using = GroupUuidDeserializer.class) + public final GroupUuid uuid; + + @NonNull + public final String password; + +} diff --git a/src/main/java/de/ph87/tools/group/uuid/GroupAbstract.java b/src/main/java/de/ph87/tools/group/uuid/GroupAbstract.java new file mode 100644 index 0000000..a5679a5 --- /dev/null +++ b/src/main/java/de/ph87/tools/group/uuid/GroupAbstract.java @@ -0,0 +1,30 @@ +package de.ph87.tools.group.uuid; + +import de.ph87.tools.user.uuid.UserPublicAbstract; +import lombok.NonNull; + +public abstract class GroupAbstract { + + public abstract GroupUuid getUuid(); + + public abstract UserPublicAbstract getOwner(); + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean isOwnedBy(@NonNull final UserPublicAbstract user) { + return getOwner().equals(user); + } + + @Override + public boolean equals(final Object obj) { + if (obj instanceof final GroupAbstract casted) { + return casted.getUuid().equals(this.getUuid()); + } + return false; + } + + @Override + public int hashCode() { + return getUuid().hashCode(); + } + +} diff --git a/src/main/java/de/ph87/tools/group/uuid/GroupUuid.java b/src/main/java/de/ph87/tools/group/uuid/GroupUuid.java new file mode 100644 index 0000000..da400fc --- /dev/null +++ b/src/main/java/de/ph87/tools/group/uuid/GroupUuid.java @@ -0,0 +1,16 @@ +package de.ph87.tools.group.uuid; + +import de.ph87.tools.common.uuid.AbstractUuid; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Getter +@NoArgsConstructor +public class GroupUuid extends AbstractUuid { + + public GroupUuid(@NonNull final String uuid) { + super(uuid); + } + +} diff --git a/src/main/java/de/ph87/tools/group/uuid/GroupUuidArgumentResolver.java b/src/main/java/de/ph87/tools/group/uuid/GroupUuidArgumentResolver.java new file mode 100644 index 0000000..7c616c5 --- /dev/null +++ b/src/main/java/de/ph87/tools/group/uuid/GroupUuidArgumentResolver.java @@ -0,0 +1,33 @@ +package de.ph87.tools.group.uuid; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.io.IOException; + +@Component +public class GroupUuidArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(@NonNull final MethodParameter parameter) { + return parameter.getParameterType() == GroupUuid.class; + } + + @Override + @SneakyThrows(IOException.class) + public GroupUuid resolveArgument(@NonNull final MethodParameter parameter, final ModelAndViewContainer mavContainer, @NonNull final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { + if (!(webRequest.getNativeRequest() instanceof final HttpServletRequest request)) { + throw new RuntimeException(); + } + final String uuid = new String(request.getInputStream().readAllBytes()); + return new GroupUuid(uuid); + } + +} \ No newline at end of file diff --git a/src/main/java/de/ph87/tools/group/uuid/GroupUuidDeserializer.java b/src/main/java/de/ph87/tools/group/uuid/GroupUuidDeserializer.java new file mode 100644 index 0000000..62a1409 --- /dev/null +++ b/src/main/java/de/ph87/tools/group/uuid/GroupUuidDeserializer.java @@ -0,0 +1,18 @@ +package de.ph87.tools.group.uuid; + +import de.ph87.tools.common.uuid.AbstractUuidDeserializer; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Getter +@NoArgsConstructor +public class GroupUuidDeserializer extends AbstractUuidDeserializer { + + @NonNull + @Override + protected GroupUuid create(@NonNull final String s) { + return new GroupUuid(s); + } + +} diff --git a/src/main/java/de/ph87/tools/tools/numbers/Numbers.java b/src/main/java/de/ph87/tools/tools/numbers/Numbers.java index 2016c2e..e48c742 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/Numbers.java +++ b/src/main/java/de/ph87/tools/tools/numbers/Numbers.java @@ -1,13 +1,12 @@ package de.ph87.tools.tools.numbers; import de.ph87.tools.group.Group; +import de.ph87.tools.tools.numbers.uuid.NumbersAbstract; +import de.ph87.tools.tools.numbers.uuid.NumbersUuid; import de.ph87.tools.user.User; import de.ph87.tools.user.reference.UserReference; import jakarta.persistence.*; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.NonNull; -import lombok.ToString; +import lombok.*; import java.time.ZonedDateTime; import java.util.List; @@ -18,13 +17,24 @@ import java.util.UUID; @ToString @NoArgsConstructor @Table(name = "`numbers`") -public class Numbers { +public class Numbers extends NumbersAbstract { @Id @NonNull - @Column(nullable = false) + @Getter(AccessLevel.NONE) private String uuid = UUID.randomUUID().toString(); + @Transient + private NumbersUuid _uuid; + + @NonNull + public NumbersUuid getUuid() { + if (_uuid == null) { + _uuid = new NumbersUuid(uuid); + } + return _uuid; + } + @NonNull @ManyToOne(optional = false) private Group group; @@ -42,31 +52,18 @@ public class Numbers { this.users = users; } - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof final Numbers numbers)) { - return false; - } - return numbers.uuid.equals(this.uuid); - } - - @Override - public int hashCode() { - return uuid.hashCode(); - } - public int getNumberForUser(@NonNull final User user) { for (int userReferenceIndex = 0; userReferenceIndex < users.size(); userReferenceIndex++) { final UserReference userReference = users.get(userReferenceIndex); if (userReference.getUser() != null && userReference.getUser().equals(user)) { - return userReferenceIndex; + return userReferenceIndex + 1; } } throw new RuntimeException(); } public boolean containsUser(@NonNull final User user) { - return users.stream().anyMatch(u -> user.equalsIUser(u.getUser())); + return users.stream().anyMatch(u -> user.equals(u.getUser())); } } diff --git a/src/main/java/de/ph87/tools/tools/numbers/NumbersAccess.java b/src/main/java/de/ph87/tools/tools/numbers/NumbersAccess.java new file mode 100644 index 0000000..a11e424 --- /dev/null +++ b/src/main/java/de/ph87/tools/tools/numbers/NumbersAccess.java @@ -0,0 +1,20 @@ +package de.ph87.tools.tools.numbers; + +import de.ph87.tools.user.User; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +class NumbersAccess { + + public final Numbers numbers; + + public final User user; + + public NumbersAccess(final Numbers numbers, final User user) { + this.numbers = numbers; + this.user = user; + } + +} diff --git a/src/main/java/de/ph87/tools/tools/numbers/NumbersController.java b/src/main/java/de/ph87/tools/tools/numbers/NumbersController.java index ba4d666..b928b1a 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/NumbersController.java +++ b/src/main/java/de/ph87/tools/tools/numbers/NumbersController.java @@ -1,12 +1,14 @@ package de.ph87.tools.tools.numbers; +import de.ph87.tools.group.uuid.GroupUuid; +import de.ph87.tools.tools.numbers.uuid.NumbersUuid; +import de.ph87.tools.user.uuid.UserPrivateUuid; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; import org.springframework.web.bind.annotation.*; -import static de.ph87.tools.UserArgumentResolver.USER_UUID_COOKIE_NAME; - @Slf4j @CrossOrigin @RestController @@ -17,25 +19,31 @@ public class NumbersController { private final NumbersService numbersService; @GetMapping("create/{groupUuid}") - public void create(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @PathVariable final String groupUuid) { + public void create(@NonNull final UserPrivateUuid privateUuid, @PathVariable final GroupUuid groupUuid) { numbersService.create(privateUuid, groupUuid); } + @NonNull + @GetMapping("page/{groupUuid}/{page}/{pageSize}") + public Page page(@NonNull final UserPrivateUuid privateUuid, @PathVariable final GroupUuid groupUuid, @PathVariable final int page, @PathVariable final int pageSize) { + return numbersService.page(privateUuid, groupUuid, page, pageSize); + } + @PostMapping("canAccess") - public boolean canAccess(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @RequestBody final String groupUuid) { - return numbersService.canAccess(privateUuid, groupUuid); + public boolean canAccess(@NonNull final UserPrivateUuid privateUuid, final NumbersUuid numbersUuid) { + return numbersService.canAccess(privateUuid, numbersUuid); } @NonNull @PostMapping("getGroupUuid") - public String getGroupUuid(@RequestBody final String groupUuid) { - return numbersService.getGroupUuid(groupUuid); + public GroupUuid getGroupUuid(final NumbersUuid numbersUuid) { + return numbersService.getGroupUuid(numbersUuid); } @NonNull @PostMapping("byUuid") - public NumbersPrivateDto byUuid(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String privateUuid, @RequestBody final String groupUuid) { - return numbersService.dtoByUuid(privateUuid, groupUuid); + public NumbersDto byUuid(@NonNull final UserPrivateUuid privateUuid, final NumbersUuid numbersUuid) { + return numbersService.dtoByUuid(privateUuid, numbersUuid); } } diff --git a/src/main/java/de/ph87/tools/tools/numbers/NumbersPrivateDto.java b/src/main/java/de/ph87/tools/tools/numbers/NumbersDto.java similarity index 50% rename from src/main/java/de/ph87/tools/tools/numbers/NumbersPrivateDto.java rename to src/main/java/de/ph87/tools/tools/numbers/NumbersDto.java index 0ef6843..5dcd20d 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/NumbersPrivateDto.java +++ b/src/main/java/de/ph87/tools/tools/numbers/NumbersDto.java @@ -1,6 +1,10 @@ package de.ph87.tools.tools.numbers; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import de.ph87.tools.common.uuid.UuidSerializer; import de.ph87.tools.group.GroupDto; +import de.ph87.tools.tools.numbers.uuid.NumbersAbstract; +import de.ph87.tools.tools.numbers.uuid.NumbersUuid; import lombok.Getter; import lombok.NonNull; import lombok.ToString; @@ -9,10 +13,11 @@ import java.time.ZonedDateTime; @Getter @ToString(callSuper = true) -public class NumbersPrivateDto { +public class NumbersDto extends NumbersAbstract { @NonNull - private final String uuid; + @JsonSerialize(using = UuidSerializer.class) + private final NumbersUuid uuid; @NonNull private final GroupDto group; @@ -22,7 +27,7 @@ public class NumbersPrivateDto { private final int number; - public NumbersPrivateDto(@NonNull final Numbers numbers, @NonNull final GroupDto group, final int number) { + public NumbersDto(@NonNull final Numbers numbers, @NonNull final GroupDto group, final int number) { this.uuid = numbers.getUuid(); this.date = numbers.getDate(); this.group = group; diff --git a/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java b/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java index 6cccb43..35204da 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java +++ b/src/main/java/de/ph87/tools/tools/numbers/NumbersRepository.java @@ -1,7 +1,14 @@ package de.ph87.tools.tools.numbers; +import de.ph87.tools.group.Group; +import lombok.NonNull; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.repository.ListCrudRepository; public interface NumbersRepository extends ListCrudRepository { + @NonNull + Page findAllByGroup(@NonNull Group group, @NonNull Pageable pageable); + } diff --git a/src/main/java/de/ph87/tools/tools/numbers/NumbersService.java b/src/main/java/de/ph87/tools/tools/numbers/NumbersService.java index 41155a0..17197cf 100644 --- a/src/main/java/de/ph87/tools/tools/numbers/NumbersService.java +++ b/src/main/java/de/ph87/tools/tools/numbers/NumbersService.java @@ -1,18 +1,23 @@ package de.ph87.tools.tools.numbers; +import de.ph87.tools.group.GroupAccess; import de.ph87.tools.group.GroupAccessService; import de.ph87.tools.group.GroupDto; import de.ph87.tools.group.GroupService; +import de.ph87.tools.group.uuid.GroupUuid; +import de.ph87.tools.tools.numbers.uuid.NumbersUuid; import de.ph87.tools.user.User; import de.ph87.tools.user.UserService; import de.ph87.tools.user.push.UserPushService; import de.ph87.tools.user.reference.UserReference; import de.ph87.tools.user.reference.UserReferenceService; -import lombok.Getter; +import de.ph87.tools.user.uuid.UserPrivateUuid; import lombok.NonNull; import lombok.RequiredArgsConstructor; -import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -41,8 +46,8 @@ public class NumbersService { private final UserService userService; - public void create(@NonNull final String userPrivateUuid, @NonNull final String groupUuid) { - final de.ph87.tools.group.Access access = groupAccessService.accessAsOwner(userPrivateUuid, groupUuid); + public void create(@NonNull final UserPrivateUuid userPrivateUuid, @NonNull final GroupUuid groupUuid) { + final GroupAccess access = groupAccessService.accessAsOwner(userPrivateUuid, groupUuid); final List users = access.getGroup().getUsers().stream().map(userReferenceService::create).collect(Collectors.toList()); Collections.shuffle(users); final Numbers numbers = numbersRepository.save(new Numbers(access.getGroup(), users)); @@ -58,59 +63,51 @@ public class NumbersService { } private void publish(@NonNull final Numbers numbers, @NonNull final User user) { - final NumbersPrivateDto dto = toPrivateDto(numbers, user); + final NumbersDto dto = toDto(numbers, user); log.debug("Sending event: {}", dto); userPushService.push(user, dto); } @NonNull - public NumbersPrivateDto toPrivateDto(@NonNull final Numbers numbers, @NonNull final User user) { + public NumbersDto toDto(@NonNull final Numbers numbers, @NonNull final User user) { final GroupDto group = groupService.toDto(numbers.getGroup()); final int number = numbers.getNumberForUser(user); - return new NumbersPrivateDto(numbers, group, number); + return new NumbersDto(numbers, group, number); } - public boolean canAccess(@NonNull final String privateUuid, @NonNull final String numbersUuid) { + public boolean canAccess(@NonNull final UserPrivateUuid privateUuid, @NonNull final NumbersUuid numbersUuid) { final User user = userService.access(privateUuid); - final Numbers numbers = numbersRepository.findById(numbersUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + final Numbers numbers = numbersRepository.findById(numbersUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); return numbers.containsUser(user); } @NonNull - public String getGroupUuid(@NonNull final String numbersUuid) { - final Numbers numbers = numbersRepository.findById(numbersUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + public GroupUuid getGroupUuid(@NonNull final NumbersUuid numbersUuid) { + final Numbers numbers = numbersRepository.findById(numbersUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); return numbers.getGroup().getUuid(); } @NonNull - public NumbersPrivateDto dtoByUuid(@NonNull final String privateUuid, @NonNull final String numbersUuid) { - final Access access = access(privateUuid, numbersUuid); - return toPrivateDto(access.numbers, access.user); + public NumbersDto dtoByUuid(@NonNull final UserPrivateUuid privateUuid, @NonNull final NumbersUuid numbersUuid) { + final NumbersAccess access = access(privateUuid, numbersUuid); + return toDto(access.numbers, access.user); } @NonNull - private Access access(@NonNull final String privateUuid, @NonNull final String numbersUuid) { + private NumbersAccess access(@NonNull final UserPrivateUuid privateUuid, @NonNull final NumbersUuid numbersUuid) { final User user = userService.access(privateUuid); - final Numbers numbers = numbersRepository.findById(numbersUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + final Numbers numbers = numbersRepository.findById(numbersUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); if (!numbers.containsUser(user)) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST); } - return new Access(numbers, user); + return new NumbersAccess(numbers, user); } - @Getter - @ToString - private static class Access { - - public final Numbers numbers; - - public final User user; - - public Access(final Numbers numbers, final User user) { - this.numbers = numbers; - this.user = user; - } - + @NonNull + public Page page(@NonNull final UserPrivateUuid privateUuid, @NonNull final GroupUuid groupUuid, final int page, final int pageSize) { + final GroupAccess access = groupAccessService.access(privateUuid, groupUuid); + final PageRequest pageable = PageRequest.of(page, pageSize, Sort.by(new Sort.Order(Sort.Direction.DESC, "date"))); + return numbersRepository.findAllByGroup(access.group, pageable).map(numbers -> toDto(numbers, access.user)); } } diff --git a/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersAbstract.java b/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersAbstract.java new file mode 100644 index 0000000..5cc3133 --- /dev/null +++ b/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersAbstract.java @@ -0,0 +1,20 @@ +package de.ph87.tools.tools.numbers.uuid; + +public abstract class NumbersAbstract { + + public abstract NumbersUuid getUuid(); + + @Override + public boolean equals(final Object obj) { + if (obj instanceof final NumbersAbstract casted) { + return casted.getUuid().equals(this.getUuid()); + } + return false; + } + + @Override + public int hashCode() { + return getUuid().hashCode(); + } + +} diff --git a/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuid.java b/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuid.java new file mode 100644 index 0000000..6fdf832 --- /dev/null +++ b/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuid.java @@ -0,0 +1,16 @@ +package de.ph87.tools.tools.numbers.uuid; + +import de.ph87.tools.common.uuid.AbstractUuid; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Getter +@NoArgsConstructor +public class NumbersUuid extends AbstractUuid { + + public NumbersUuid(@NonNull final String uuid) { + super(uuid); + } + +} diff --git a/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuidArgumentResolver.java b/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuidArgumentResolver.java new file mode 100644 index 0000000..9b3d23b --- /dev/null +++ b/src/main/java/de/ph87/tools/tools/numbers/uuid/NumbersUuidArgumentResolver.java @@ -0,0 +1,33 @@ +package de.ph87.tools.tools.numbers.uuid; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.io.IOException; + +@Component +public class NumbersUuidArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(@NonNull final MethodParameter parameter) { + return parameter.getParameterType() == NumbersUuid.class; + } + + @Override + @SneakyThrows(IOException.class) + public NumbersUuid resolveArgument(@NonNull final MethodParameter parameter, final ModelAndViewContainer mavContainer, @NonNull final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { + if (!(webRequest.getNativeRequest() instanceof final HttpServletRequest request)) { + throw new RuntimeException(); + } + final String uuid = new String(request.getInputStream().readAllBytes()); + return new NumbersUuid(uuid); + } + +} \ No newline at end of file diff --git a/src/main/java/de/ph87/tools/user/User.java b/src/main/java/de/ph87/tools/user/User.java index 4b0d678..ad95b72 100644 --- a/src/main/java/de/ph87/tools/user/User.java +++ b/src/main/java/de/ph87/tools/user/User.java @@ -1,10 +1,9 @@ package de.ph87.tools.user; -import de.ph87.tools.user.reference.IUser; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import de.ph87.tools.user.uuid.UserPublicAbstract; +import de.ph87.tools.user.uuid.UserPublicUuid; +import jakarta.persistence.*; import lombok.*; import org.springframework.security.crypto.password.PasswordEncoder; @@ -16,16 +15,39 @@ import java.util.UUID; @ToString @NoArgsConstructor @Table(name = "`user`") -public class User extends IUser { +public class User extends UserPublicAbstract { @Id @NonNull - @Column(nullable = false) - private String privateUuid = UUID.randomUUID().toString(); + @Getter(AccessLevel.NONE) + private String publicUuid = UUID.randomUUID().toString(); + + @Transient + private UserPublicUuid _publicUuid; @NonNull - @Column(nullable = false) - private String publicUuid = UUID.randomUUID().toString(); + public UserPublicUuid getPublicUuid() { + if (_publicUuid == null) { + _publicUuid = new UserPublicUuid(publicUuid); + } + return _publicUuid; + } + + @NonNull + @Getter(AccessLevel.NONE) + @Column(nullable = false, unique = true, updatable = false) + private String privateUuid = UUID.randomUUID().toString(); + + @Transient + private UserPrivateUuid _privateUuid; + + @NonNull + public UserPrivateUuid getPrivateUuid() { + if (_privateUuid == null) { + _privateUuid = new UserPrivateUuid(privateUuid); + } + return _privateUuid; + } @NonNull @Column(nullable = false) diff --git a/src/main/java/de/ph87/tools/user/UserCommonDto.java b/src/main/java/de/ph87/tools/user/UserCommonDto.java deleted file mode 100644 index 7285b6f..0000000 --- a/src/main/java/de/ph87/tools/user/UserCommonDto.java +++ /dev/null @@ -1,29 +0,0 @@ -package de.ph87.tools.user; - -import de.ph87.tools.group.GroupDto; -import lombok.Getter; -import lombok.NonNull; -import lombok.ToString; - -import java.util.Set; - -@Getter -@ToString -public class UserCommonDto { - - @NonNull - public final String publicUuid; - - @NonNull - public final String name; - - @NonNull - public final Set commonGroups; - - public UserCommonDto(@NonNull final User target, @NonNull final Set commonGroups) { - this.publicUuid = target.getPublicUuid(); - this.name = target.getName(); - this.commonGroups = commonGroups; - } - -} diff --git a/src/main/java/de/ph87/tools/user/UserController.java b/src/main/java/de/ph87/tools/user/UserController.java index b7daef4..36da9f9 100644 --- a/src/main/java/de/ph87/tools/user/UserController.java +++ b/src/main/java/de/ph87/tools/user/UserController.java @@ -1,5 +1,8 @@ package de.ph87.tools.user; +import de.ph87.tools.user.requests.UserLoginRequest; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import de.ph87.tools.user.uuid.UserPublicUuid; import jakarta.annotation.Nullable; import jakarta.servlet.http.HttpServletResponse; import lombok.NonNull; @@ -7,8 +10,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; -import static de.ph87.tools.UserArgumentResolver.USER_UUID_COOKIE_NAME; - @Slf4j @CrossOrigin @RestController @@ -20,36 +21,36 @@ public class UserController { @Nullable @GetMapping("whoAmI") - public UserPrivateDto whoAmI(@CookieValue(name = USER_UUID_COOKIE_NAME, required = false) @Nullable final String userUuid, @NonNull final HttpServletResponse response) { + public UserPrivateDto whoAmI(@Nullable final UserPrivateUuid userUuid, @NonNull final HttpServletResponse response) { return userService.whoAmI(userUuid, response); } @NonNull @PostMapping("getByPublicUuid") - public UserPublicDto getByPublicUuid(@NonNull @RequestBody final String publicUuid) { + public UserPublicDto getByPublicUuid(@NonNull final UserPublicUuid publicUuid) { return userService.getDtoByPublicUuid(publicUuid); } @Nullable @GetMapping("login") - public UserPrivateDto login(@NonNull @RequestBody final LoginRequest loginRequest, @NonNull final HttpServletResponse response) { + public UserPrivateDto login(@NonNull @RequestBody final UserLoginRequest loginRequest, @NonNull final HttpServletResponse response) { return userService.login(loginRequest, response); } @NonNull @PostMapping("changeName") - public UserPrivateDto changeName(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull @RequestBody final String name) { + public UserPrivateDto changeName(@NonNull final UserPrivateUuid userUuid, @NonNull @RequestBody final String name) { return userService.changeName(userUuid, name); } @NonNull @PostMapping("changePassword") - public UserPrivateDto changePassword(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull @RequestBody final String password) { + public UserPrivateDto changePassword(@NonNull final UserPrivateUuid userUuid, @NonNull @RequestBody final String password) { return userService.changePassword(userUuid, password); } @GetMapping("delete") - public void delete(@CookieValue(name = USER_UUID_COOKIE_NAME) @NonNull final String userUuid, @NonNull final HttpServletResponse response) { + public void delete(@NonNull final UserPrivateUuid userUuid, @NonNull final HttpServletResponse response) { userService.delete(userUuid, response); } diff --git a/src/main/java/de/ph87/tools/user/UserPrivateDto.java b/src/main/java/de/ph87/tools/user/UserPrivateDto.java index cf3c6a4..e9e1c11 100644 --- a/src/main/java/de/ph87/tools/user/UserPrivateDto.java +++ b/src/main/java/de/ph87/tools/user/UserPrivateDto.java @@ -1,6 +1,10 @@ package de.ph87.tools.user; -import de.ph87.tools.user.reference.IUser; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import de.ph87.tools.common.uuid.UuidSerializer; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import de.ph87.tools.user.uuid.UserPublicAbstract; +import de.ph87.tools.user.uuid.UserPublicUuid; import jakarta.annotation.Nullable; import lombok.Getter; import lombok.NonNull; @@ -10,20 +14,22 @@ import java.time.ZonedDateTime; @Getter @ToString -public class UserPrivateDto extends IUser { +public class UserPrivateDto extends UserPublicAbstract { @NonNull private final String pushType = "UserPrivate"; @NonNull - public final String publicUuid; + @JsonSerialize(using = UuidSerializer.class) + public final UserPublicUuid publicUuid; + + @NonNull + @JsonSerialize(using = UuidSerializer.class) + public final UserPrivateUuid privateUuid; @NonNull public final String name; - @NonNull - public final String privateUuid; - @NonNull public final ZonedDateTime created; @@ -31,8 +37,8 @@ public class UserPrivateDto extends IUser { public UserPrivateDto(@NonNull final User user) { this.publicUuid = user.getPublicUuid(); - this.name = user.getName(); this.privateUuid = user.getPrivateUuid(); + this.name = user.getName(); this.created = user.getCreated(); this.password = !user.getPassword().isEmpty(); } diff --git a/src/main/java/de/ph87/tools/user/UserPublicDto.java b/src/main/java/de/ph87/tools/user/UserPublicDto.java index 604cae8..83d5b58 100644 --- a/src/main/java/de/ph87/tools/user/UserPublicDto.java +++ b/src/main/java/de/ph87/tools/user/UserPublicDto.java @@ -1,16 +1,20 @@ package de.ph87.tools.user; -import de.ph87.tools.user.reference.IUser; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import de.ph87.tools.common.uuid.UuidSerializer; +import de.ph87.tools.user.uuid.UserPublicAbstract; +import de.ph87.tools.user.uuid.UserPublicUuid; import lombok.Getter; import lombok.NonNull; import lombok.ToString; @Getter @ToString -public class UserPublicDto extends IUser { +public class UserPublicDto extends UserPublicAbstract { @NonNull - public final String publicUuid; + @JsonSerialize(using = UuidSerializer.class) + public final UserPublicUuid publicUuid; @NonNull public final String name; diff --git a/src/main/java/de/ph87/tools/user/UserService.java b/src/main/java/de/ph87/tools/user/UserService.java index 17a1c1a..a249735 100644 --- a/src/main/java/de/ph87/tools/user/UserService.java +++ b/src/main/java/de/ph87/tools/user/UserService.java @@ -1,5 +1,9 @@ package de.ph87.tools.user; +import de.ph87.tools.common.uuid.AbstractUuid; +import de.ph87.tools.user.requests.UserLoginRequest; +import de.ph87.tools.user.uuid.UserPrivateUuid; +import de.ph87.tools.user.uuid.UserPublicUuid; import jakarta.annotation.Nullable; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; @@ -18,7 +22,7 @@ import java.util.Random; import java.util.function.Consumer; import java.util.regex.Pattern; -import static de.ph87.tools.UserArgumentResolver.USER_UUID_COOKIE_NAME; +import static de.ph87.tools.user.uuid.UserPrivateUuidArgumentResolver.USER_UUID_COOKIE_NAME; @Slf4j @Service @@ -42,7 +46,7 @@ public class UserService { private final PasswordEncoder passwordEncoder; @NonNull - public UserPrivateDto login(@NonNull final LoginRequest loginRequest, @NonNull final HttpServletResponse response) { + public UserPrivateDto login(@NonNull final UserLoginRequest loginRequest, @NonNull final HttpServletResponse response) { final User user = userRepository.findByName(loginRequest.name).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); if (passwordEncoder.matches(loginRequest.password, user.getPassword())) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST); @@ -53,24 +57,30 @@ public class UserService { } @NonNull - public User getUserByPrivateUuidOrElseCreate(@Nullable final String privateUuid, @NonNull final HttpServletResponse response) { - final User user = Optional.ofNullable(privateUuid).map(userRepository::findByPrivateUuid).filter(Optional::isPresent).map(Optional::get).orElseGet(this::createUnchecked); + public User getUserByPrivateUuidOrElseCreate(@Nullable final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) { + final User user = Optional + .ofNullable(privateUuid) + .map(AbstractUuid::getUuid) + .map(userRepository::findByPrivateUuid) + .filter(Optional::isPresent) + .map(Optional::get) + .orElseGet(this::createUnchecked); writeUserUuidCookie(response, user); return user; } @Nullable - public UserPrivateDto whoAmI(@Nullable final String privateUuid, final @NonNull HttpServletResponse response) { - if (privateUuid == null || privateUuid.isEmpty()) { + public UserPrivateDto whoAmI(@Nullable final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) { + if (privateUuid == null) { return null; } - final User user = userRepository.findByPrivateUuid(privateUuid).orElse(null); + final User user = userRepository.findByPrivateUuid(privateUuid.uuid).orElse(null); writeUserUuidCookie(response, user); return UserPrivateDto.orNull(user); } @NonNull - public UserPrivateDto changeName(@NonNull final String privateUuid, @NonNull final String name) { + public UserPrivateDto changeName(@NonNull final UserPrivateUuid privateUuid, @NonNull final String name) { return modify(privateUuid, user -> { if (user.getName().equals(name)) { return; @@ -94,7 +104,7 @@ public class UserService { } @NonNull - public UserPrivateDto changePassword(@NonNull final String privateUuid, @NonNull final String password) { + public UserPrivateDto changePassword(@NonNull final UserPrivateUuid privateUuid, @NonNull final String password) { return modify(privateUuid, user -> { if (password.length() < PASSWORD_MIN_LENGTH) { log.warn("Cannot change User password: too short: length={}/{}, user={}", password.length(), PASSWORD_MIN_LENGTH, user); @@ -109,8 +119,8 @@ public class UserService { }); } - public void delete(@NonNull final String privateUuid, final @NonNull HttpServletResponse response) { - final User user = userRepository.findByPrivateUuid(privateUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + public void delete(@NonNull final UserPrivateUuid privateUuid, @NonNull final HttpServletResponse response) { + final User user = userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); deleteUnchecked(response, user); } @@ -136,7 +146,7 @@ public class UserService { } @NonNull - private UserPrivateDto modify(@NonNull final String privateUuid, @NonNull Consumer modifier) { + private UserPrivateDto modify(@NonNull final UserPrivateUuid privateUuid, @NonNull Consumer modifier) { final User user = access(privateUuid); modifier.accept(user); return new UserPrivateDto(user); @@ -151,8 +161,8 @@ public class UserService { /* ACCESS --------------------------------------------------------------------------------------- */ @Nullable - public User accessOrNull(@Nullable final String userPrivateUuid) { - if (userPrivateUuid == null || userPrivateUuid.isEmpty()) { + public User accessOrNull(@Nullable final UserPrivateUuid userPrivateUuid) { + if (userPrivateUuid == null) { return null; } return access(userPrivateUuid); @@ -161,18 +171,18 @@ public class UserService { /* GETTERS & FINDERS ---------------------------------------------------------------------------- */ @NonNull - public UserPublicDto getDtoByPublicUuid(@NonNull final String publicUuid) { + public UserPublicDto getDtoByPublicUuid(@NonNull final UserPublicUuid publicUuid) { return new UserPublicDto(getByPublicUuid(publicUuid)); } @NonNull - public User access(@NonNull final String privateUuid) { - return userRepository.findByPrivateUuid(privateUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + public User access(@NonNull final UserPrivateUuid privateUuid) { + return userRepository.findByPrivateUuid(privateUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); } @NonNull - public User getByPublicUuid(@NonNull final String publicUuid) { - return userRepository.findByPublicUuid(publicUuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); + public User getByPublicUuid(@NonNull final UserPublicUuid publicUuid) { + return userRepository.findByPublicUuid(publicUuid.uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.BAD_REQUEST)); } /* COOKIES -------------------------------------------------------------------------------------- */ @@ -180,7 +190,7 @@ public class UserService { private static void writeUserUuidCookie(@NonNull final HttpServletResponse response, @Nullable final User user) { final Cookie cookie = new Cookie(USER_UUID_COOKIE_NAME, ""); if (user != null) { - cookie.setValue(user.getPrivateUuid()); + cookie.setValue(user.getPrivateUuid().uuid); } cookie.setMaxAge(PASSWORD_MIN_LENGTH * 365 * 24 * 60 * 60); cookie.setPath("/"); diff --git a/src/main/java/de/ph87/tools/user/reference/IUser.java b/src/main/java/de/ph87/tools/user/reference/IUser.java deleted file mode 100644 index 2fc3e4b..0000000 --- a/src/main/java/de/ph87/tools/user/reference/IUser.java +++ /dev/null @@ -1,31 +0,0 @@ -package de.ph87.tools.user.reference; - -import jakarta.annotation.Nullable; -import lombok.NonNull; - -public abstract class IUser { - - @NonNull - public abstract String getPublicUuid(); - - @Override - public boolean equals(final Object obj) { - if (!(obj instanceof final IUser user)) { - return false; - } - return equalsIUser(user); - } - - public boolean equalsIUser(@Nullable final IUser user) { - if (user == null) { - return false; - } - return user.getPublicUuid().equals(this.getPublicUuid()); - } - - @Override - public int hashCode() { - return getPublicUuid().hashCode(); - } - -} diff --git a/src/main/java/de/ph87/tools/user/LoginRequest.java b/src/main/java/de/ph87/tools/user/requests/UserLoginRequest.java similarity index 73% rename from src/main/java/de/ph87/tools/user/LoginRequest.java rename to src/main/java/de/ph87/tools/user/requests/UserLoginRequest.java index 4e0cea9..3cedd33 100644 --- a/src/main/java/de/ph87/tools/user/LoginRequest.java +++ b/src/main/java/de/ph87/tools/user/requests/UserLoginRequest.java @@ -1,4 +1,4 @@ -package de.ph87.tools.user; +package de.ph87.tools.user.requests; import lombok.Getter; import lombok.RequiredArgsConstructor; @@ -7,7 +7,7 @@ import lombok.ToString; @Getter @ToString @RequiredArgsConstructor -public class LoginRequest { +public class UserLoginRequest { public final String name; diff --git a/src/main/java/de/ph87/tools/user/uuid/UserPrivateUuid.java b/src/main/java/de/ph87/tools/user/uuid/UserPrivateUuid.java new file mode 100644 index 0000000..a070fb8 --- /dev/null +++ b/src/main/java/de/ph87/tools/user/uuid/UserPrivateUuid.java @@ -0,0 +1,16 @@ +package de.ph87.tools.user.uuid; + +import de.ph87.tools.common.uuid.AbstractUuid; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Getter +@NoArgsConstructor +public class UserPrivateUuid extends AbstractUuid { + + public UserPrivateUuid(@NonNull final String uuid) { + super(uuid); + } + +} diff --git a/src/main/java/de/ph87/tools/user/uuid/UserPrivateUuidArgumentResolver.java b/src/main/java/de/ph87/tools/user/uuid/UserPrivateUuidArgumentResolver.java new file mode 100644 index 0000000..0b4f042 --- /dev/null +++ b/src/main/java/de/ph87/tools/user/uuid/UserPrivateUuidArgumentResolver.java @@ -0,0 +1,44 @@ +package de.ph87.tools.user.uuid; + +import jakarta.annotation.Nullable; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import lombok.NonNull; +import org.springframework.core.MethodParameter; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import org.springframework.web.server.ResponseStatusException; + +import java.util.Arrays; + +@Component +public class UserPrivateUuidArgumentResolver implements HandlerMethodArgumentResolver { + + public static final String USER_UUID_COOKIE_NAME = "PatrixToolsUserUuid"; + + @Override + public boolean supportsParameter(@NonNull final MethodParameter parameter) { + return parameter.getParameterType() == UserPrivateUuid.class; + } + + @Override + public UserPrivateUuid resolveArgument(@NonNull final MethodParameter parameter, final ModelAndViewContainer mavContainer, @NonNull final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { + final boolean nullable = Arrays.stream(parameter.getParameterAnnotations()).anyMatch(annotation -> annotation.annotationType().equals(Nullable.class)); + if (!(webRequest.getNativeRequest() instanceof final HttpServletRequest request)) { + throw new RuntimeException(); + } + final String uuid = Arrays.stream(request.getCookies()).filter(cookie -> USER_UUID_COOKIE_NAME.equalsIgnoreCase(cookie.getName())).findFirst().map(Cookie::getValue).orElse(null); + if (uuid == null || uuid.length() != 36) { + if (!nullable) { + throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); + } + return null; + } + return new UserPrivateUuid(uuid); + } + +} \ No newline at end of file diff --git a/src/main/java/de/ph87/tools/user/uuid/UserPublicAbstract.java b/src/main/java/de/ph87/tools/user/uuid/UserPublicAbstract.java new file mode 100644 index 0000000..e65efb1 --- /dev/null +++ b/src/main/java/de/ph87/tools/user/uuid/UserPublicAbstract.java @@ -0,0 +1,20 @@ +package de.ph87.tools.user.uuid; + +public abstract class UserPublicAbstract { + + public abstract UserPublicUuid getPublicUuid(); + + @Override + public boolean equals(final Object obj) { + if (obj instanceof final UserPublicAbstract casted) { + return casted.getPublicUuid().equals(this.getPublicUuid()); + } + return false; + } + + @Override + public int hashCode() { + return getPublicUuid().hashCode(); + } + +} diff --git a/src/main/java/de/ph87/tools/user/uuid/UserPublicUuid.java b/src/main/java/de/ph87/tools/user/uuid/UserPublicUuid.java new file mode 100644 index 0000000..a6d5afd --- /dev/null +++ b/src/main/java/de/ph87/tools/user/uuid/UserPublicUuid.java @@ -0,0 +1,16 @@ +package de.ph87.tools.user.uuid; + +import de.ph87.tools.common.uuid.AbstractUuid; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; + +@Getter +@NoArgsConstructor +public class UserPublicUuid extends AbstractUuid { + + public UserPublicUuid(@NonNull final String uuid) { + super(uuid); + } + +} diff --git a/src/main/java/de/ph87/tools/user/uuid/UserPublicUuidArgumentResolver.java b/src/main/java/de/ph87/tools/user/uuid/UserPublicUuidArgumentResolver.java new file mode 100644 index 0000000..6fde996 --- /dev/null +++ b/src/main/java/de/ph87/tools/user/uuid/UserPublicUuidArgumentResolver.java @@ -0,0 +1,33 @@ +package de.ph87.tools.user.uuid; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.NonNull; +import lombok.SneakyThrows; +import org.springframework.core.MethodParameter; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; + +import java.io.IOException; + +@Component +public class UserPublicUuidArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(@NonNull final MethodParameter parameter) { + return parameter.getParameterType() == UserPublicUuid.class; + } + + @Override + @SneakyThrows(IOException.class) + public UserPublicUuid resolveArgument(@NonNull final MethodParameter parameter, final ModelAndViewContainer mavContainer, @NonNull final NativeWebRequest webRequest, final WebDataBinderFactory binderFactory) { + if (!(webRequest.getNativeRequest() instanceof final HttpServletRequest request)) { + throw new RuntimeException(); + } + final String uuid = new String(request.getInputStream().readAllBytes()); + return new UserPublicUuid(uuid); + } + +} \ No newline at end of file diff --git a/src/main/java/de/ph87/tools/web/WebConfig.java b/src/main/java/de/ph87/tools/web/WebConfig.java index 2f818d4..9c86b25 100644 --- a/src/main/java/de/ph87/tools/web/WebConfig.java +++ b/src/main/java/de/ph87/tools/web/WebConfig.java @@ -1,6 +1,9 @@ package de.ph87.tools.web; -import de.ph87.tools.UserArgumentResolver; +import de.ph87.tools.group.uuid.GroupUuidArgumentResolver; +import de.ph87.tools.tools.numbers.uuid.NumbersUuidArgumentResolver; +import de.ph87.tools.user.uuid.UserPrivateUuidArgumentResolver; +import de.ph87.tools.user.uuid.UserPublicUuidArgumentResolver; import lombok.NonNull; import lombok.RequiredArgsConstructor; import org.springframework.context.annotation.Configuration; @@ -21,7 +24,10 @@ public class WebConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List argumentResolvers) { - argumentResolvers.add(new UserArgumentResolver()); + argumentResolvers.add(new UserPrivateUuidArgumentResolver()); + argumentResolvers.add(new UserPublicUuidArgumentResolver()); + argumentResolvers.add(new GroupUuidArgumentResolver()); + argumentResolvers.add(new NumbersUuidArgumentResolver()); } @Override