mirror of
https://github.com/usatiuk/y.git
synced 2025-10-28 18:37:47 +01:00
admin stuff
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import "./ProfileCard.scss";
|
import "./ProfileCard.scss";
|
||||||
import { Form, Link, useNavigation } from "react-router-dom";
|
import { Form, Link, useNavigation } from "react-router-dom";
|
||||||
|
import { useHomeContext } from "./HomeContext";
|
||||||
|
|
||||||
export function ProfileCard({
|
export function ProfileCard({
|
||||||
username,
|
username,
|
||||||
@@ -7,13 +8,17 @@ export function ProfileCard({
|
|||||||
uuid,
|
uuid,
|
||||||
actions,
|
actions,
|
||||||
alreadyFollowing,
|
alreadyFollowing,
|
||||||
|
isAdmin,
|
||||||
}: {
|
}: {
|
||||||
username: string;
|
username: string;
|
||||||
fullName: string;
|
fullName: string;
|
||||||
uuid: string;
|
uuid: string;
|
||||||
actions: boolean;
|
actions: boolean;
|
||||||
alreadyFollowing: boolean;
|
alreadyFollowing: boolean;
|
||||||
|
isAdmin: boolean;
|
||||||
}) {
|
}) {
|
||||||
|
const homeContext = useHomeContext();
|
||||||
|
|
||||||
const navigation = useNavigation();
|
const navigation = useNavigation();
|
||||||
const busy = navigation.state === "submitting";
|
const busy = navigation.state === "submitting";
|
||||||
|
|
||||||
@@ -54,6 +59,32 @@ export function ProfileCard({
|
|||||||
</button>
|
</button>
|
||||||
</Form>
|
</Form>
|
||||||
))}
|
))}
|
||||||
|
{homeContext.user.isAdmin &&
|
||||||
|
(isAdmin ? (
|
||||||
|
<Form method={"put"}>
|
||||||
|
<input hidden={true} value={uuid} name={"uuid"} />
|
||||||
|
<button
|
||||||
|
type={"submit"}
|
||||||
|
name={"intent"}
|
||||||
|
value={"unadmin"}
|
||||||
|
disabled={busy}
|
||||||
|
>
|
||||||
|
unadmin
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
) : (
|
||||||
|
<Form method={"put"}>
|
||||||
|
<input hidden={true} value={uuid} name={"uuid"} />
|
||||||
|
<button
|
||||||
|
type={"submit"}
|
||||||
|
name={"intent"}
|
||||||
|
value={"admin"}
|
||||||
|
disabled={busy}
|
||||||
|
>
|
||||||
|
make admin
|
||||||
|
</button>
|
||||||
|
</Form>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export function UserList() {
|
|||||||
uuid={u.uuid}
|
uuid={u.uuid}
|
||||||
key={u.uuid}
|
key={u.uuid}
|
||||||
actions={homeContext.user.uuid != u.uuid}
|
actions={homeContext.user.uuid != u.uuid}
|
||||||
|
isAdmin={u.isAdmin}
|
||||||
alreadyFollowing={following.some(
|
alreadyFollowing={following.some(
|
||||||
(f) => f.uuid == u.uuid,
|
(f) => f.uuid == u.uuid,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
addAdmin,
|
||||||
addFollower,
|
addFollower,
|
||||||
deleteSelf,
|
deleteSelf,
|
||||||
|
removeAdmin,
|
||||||
removeFollower,
|
removeFollower,
|
||||||
signup,
|
signup,
|
||||||
updateSelf,
|
updateSelf,
|
||||||
@@ -101,6 +103,10 @@ export async function userListAction({ request }: ActionFunctionArgs) {
|
|||||||
return await addFollower(formData.get("uuid")!.toString());
|
return await addFollower(formData.get("uuid")!.toString());
|
||||||
} else if (intent == "unfollow") {
|
} else if (intent == "unfollow") {
|
||||||
return await removeFollower(formData.get("uuid")!.toString());
|
return await removeFollower(formData.get("uuid")!.toString());
|
||||||
|
} else if (intent == "unadmin") {
|
||||||
|
return await removeAdmin(formData.get("uuid")!.toString());
|
||||||
|
} else if (intent == "admin") {
|
||||||
|
return await addAdmin(formData.get("uuid")!.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,3 +73,11 @@ export async function removeFollower(uuid: string): Promise<TNoContentToResp> {
|
|||||||
NoContentToResp,
|
NoContentToResp,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function addAdmin(uuid: string): Promise<TNoContentToResp> {
|
||||||
|
return fetchJSONAuth("/person/admins/" + uuid, "PUT", NoContentToResp);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeAdmin(uuid: string): Promise<TNoContentToResp> {
|
||||||
|
return fetchJSONAuth("/person/admins/" + uuid, "DELETE", NoContentToResp);
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ export const PersonTo = z.object({
|
|||||||
uuid: z.string(),
|
uuid: z.string(),
|
||||||
username: z.string(),
|
username: z.string(),
|
||||||
fullName: z.string(),
|
fullName: z.string(),
|
||||||
|
isAdmin: z.boolean(),
|
||||||
});
|
});
|
||||||
export type TPersonTo = z.infer<typeof PersonTo>;
|
export type TPersonTo = z.infer<typeof PersonTo>;
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ import com.usatiuk.tjv.y.server.service.ChatService;
|
|||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -36,16 +36,16 @@ public class ChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public ChatTo create(Principal principal, @RequestBody ChatCreateTo chatCreateTo) {
|
public ChatTo create(Authentication authentication, @RequestBody ChatCreateTo chatCreateTo) {
|
||||||
var chat = new Chat();
|
var chat = new Chat();
|
||||||
|
|
||||||
if (Arrays.stream(chatCreateTo.memberUuids()).noneMatch(n -> Objects.equals(n, principal.getName())))
|
if (Arrays.stream(chatCreateTo.memberUuids()).noneMatch(n -> Objects.equals(n, authentication.getName())))
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Creator of chat must be its member");
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Creator of chat must be its member");
|
||||||
|
|
||||||
if (chatCreateTo.memberUuids().length <= 1)
|
if (chatCreateTo.memberUuids().length <= 1)
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Chat must have members other than its creator");
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Chat must have members other than its creator");
|
||||||
|
|
||||||
chat.setCreator(entityManager.getReference(Person.class, principal.getName()));
|
chat.setCreator(entityManager.getReference(Person.class, authentication.getName()));
|
||||||
chat.setMembers(Arrays.stream(chatCreateTo.memberUuids()).map(
|
chat.setMembers(Arrays.stream(chatCreateTo.memberUuids()).map(
|
||||||
p -> entityManager.getReference(Person.class, p)
|
p -> entityManager.getReference(Person.class, p)
|
||||||
).toList());
|
).toList());
|
||||||
@@ -56,9 +56,9 @@ public class ChatController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/by-id/{id}")
|
@GetMapping(path = "/by-id/{id}")
|
||||||
public ChatTo get(Principal principal, @PathVariable Long id) {
|
public ChatTo get(Authentication authentication, @PathVariable Long id) {
|
||||||
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||||
var userRef = entityManager.getReference(Person.class, principal.getName());
|
var userRef = entityManager.getReference(Person.class, authentication.getName());
|
||||||
if (!chat.getMembers().contains(userRef))
|
if (!chat.getMembers().contains(userRef))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||||
return chatMapper.makeDto(chat);
|
return chatMapper.makeDto(chat);
|
||||||
@@ -66,20 +66,20 @@ public class ChatController {
|
|||||||
|
|
||||||
@DeleteMapping(path = "/by-id/{id}")
|
@DeleteMapping(path = "/by-id/{id}")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void delete(Principal principal, @PathVariable Long id) {
|
public void delete(Authentication authentication, @PathVariable Long id) {
|
||||||
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||||
if (!Objects.equals(chat.getCreator().getUuid(), principal.getName()))
|
if (!Objects.equals(chat.getCreator().getUuid(), authentication.getName()))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't creator of the chat");
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't creator of the chat");
|
||||||
chatService.deleteById(id);
|
chatService.deleteById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PatchMapping(path = "/by-id/{id}")
|
@PatchMapping(path = "/by-id/{id}")
|
||||||
public ChatTo update(Principal principal, @PathVariable Long id, @RequestBody ChatCreateTo chatCreateTo) {
|
public ChatTo update(Authentication authentication, @PathVariable Long id, @RequestBody ChatCreateTo chatCreateTo) {
|
||||||
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||||
if (!Objects.equals(chat.getCreator().getUuid(), principal.getName()))
|
if (!Objects.equals(chat.getCreator().getUuid(), authentication.getName()))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't creator of the chat");
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't creator of the chat");
|
||||||
|
|
||||||
if (Arrays.stream(chatCreateTo.memberUuids()).noneMatch(n -> Objects.equals(n, principal.getName())))
|
if (Arrays.stream(chatCreateTo.memberUuids()).noneMatch(n -> Objects.equals(n, authentication.getName())))
|
||||||
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Creator of chat must be its member");
|
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Creator of chat must be its member");
|
||||||
|
|
||||||
if (chatCreateTo.memberUuids().length <= 1)
|
if (chatCreateTo.memberUuids().length <= 1)
|
||||||
@@ -96,14 +96,14 @@ public class ChatController {
|
|||||||
|
|
||||||
|
|
||||||
@GetMapping(path = "/my")
|
@GetMapping(path = "/my")
|
||||||
public Stream<ChatTo> getMy(Principal principal) {
|
public Stream<ChatTo> getMy(Authentication authentication) {
|
||||||
return chatService.readByMember(principal.getName()).stream().map(chatMapper::makeDto);
|
return chatService.readByMember(authentication.getName()).stream().map(chatMapper::makeDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/by-id/{id}/members")
|
@GetMapping(path = "/by-id/{id}/members")
|
||||||
public Stream<PersonTo> getMembers(Principal principal, @PathVariable Long id) {
|
public Stream<PersonTo> getMembers(Authentication authentication, @PathVariable Long id) {
|
||||||
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
var chat = chatService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||||
var userRef = entityManager.getReference(Person.class, principal.getName());
|
var userRef = entityManager.getReference(Person.class, authentication.getName());
|
||||||
if (!chat.getMembers().contains(userRef))
|
if (!chat.getMembers().contains(userRef))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||||
return chat.getMembers().stream().map(personMapper::makeDto);
|
return chat.getMembers().stream().map(personMapper::makeDto);
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ import com.usatiuk.tjv.y.server.service.MessageService;
|
|||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -33,9 +33,9 @@ public class MessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/by-chat/{chatTd}")
|
@GetMapping(path = "/by-chat/{chatTd}")
|
||||||
public Stream<MessageTo> get(Principal principal, @PathVariable Long chatTd) {
|
public Stream<MessageTo> get(Authentication authentication, @PathVariable Long chatTd) {
|
||||||
var chat = chatService.readById(chatTd).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
var chat = chatService.readById(chatTd).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||||
var userRef = entityManager.getReference(Person.class, principal.getName());
|
var userRef = entityManager.getReference(Person.class, authentication.getName());
|
||||||
if (!chat.getMembers().contains(userRef))
|
if (!chat.getMembers().contains(userRef))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||||
|
|
||||||
@@ -43,9 +43,9 @@ public class MessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(path = "/by-chat/{chatId}")
|
@PostMapping(path = "/by-chat/{chatId}")
|
||||||
public MessageTo post(Principal principal, @PathVariable Long chatId, @RequestBody MessageCreateTo messageCreateTo) {
|
public MessageTo post(Authentication authentication, @PathVariable Long chatId, @RequestBody MessageCreateTo messageCreateTo) {
|
||||||
var chat = chatService.readById(chatId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
var chat = chatService.readById(chatId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||||
var userRef = entityManager.getReference(Person.class, principal.getName());
|
var userRef = entityManager.getReference(Person.class, authentication.getName());
|
||||||
if (!chat.getMembers().contains(userRef))
|
if (!chat.getMembers().contains(userRef))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||||
|
|
||||||
@@ -55,9 +55,9 @@ public class MessageController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PatchMapping(path = "/by-id/{id}")
|
@PatchMapping(path = "/by-id/{id}")
|
||||||
public MessageTo update(Principal principal, @PathVariable long id, @RequestBody MessageCreateTo messageCreateTo) {
|
public MessageTo update(Authentication authentication, @PathVariable long id, @RequestBody MessageCreateTo messageCreateTo) {
|
||||||
var message = messageService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
var message = messageService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
if (!Objects.equals(message.getAuthor().getUuid(), principal.getName()))
|
if (!Objects.equals(message.getAuthor().getUuid(), authentication.getName()))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
message.setContents(messageCreateTo.contents());
|
message.setContents(messageCreateTo.contents());
|
||||||
messageService.update(message);
|
messageService.update(message);
|
||||||
@@ -66,10 +66,10 @@ public class MessageController {
|
|||||||
|
|
||||||
@DeleteMapping(path = "/by-id/{id}")
|
@DeleteMapping(path = "/by-id/{id}")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void delete(Principal principal, @PathVariable long id) {
|
public void delete(Authentication authentication, @PathVariable long id) {
|
||||||
var read = messageService.readById(id);
|
var read = messageService.readById(id);
|
||||||
if (read.isEmpty()) return;
|
if (read.isEmpty()) return;
|
||||||
if (!Objects.equals(read.get().getAuthor().getId(), principal.getName())) {
|
if (!Objects.equals(read.get().getAuthor().getId(), authentication.getName())) {
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
messageService.deleteById(id);
|
messageService.deleteById(id);
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
|||||||
import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.stream.StreamSupport;
|
import java.util.stream.StreamSupport;
|
||||||
@@ -67,8 +67,8 @@ public class PersonController {
|
|||||||
|
|
||||||
|
|
||||||
@GetMapping(path = "/self")
|
@GetMapping(path = "/self")
|
||||||
public PersonTo getSelf(Principal principal) throws UserNotFoundException {
|
public PersonTo getSelf(Authentication authentication) throws UserNotFoundException {
|
||||||
Optional<Person> found = personService.readById(principal.getName());
|
Optional<Person> found = personService.readById(authentication.getName());
|
||||||
|
|
||||||
if (found.isEmpty()) throw new UserNotFoundException();
|
if (found.isEmpty()) throw new UserNotFoundException();
|
||||||
|
|
||||||
@@ -76,8 +76,8 @@ public class PersonController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PatchMapping(path = "/self")
|
@PatchMapping(path = "/self")
|
||||||
public PersonTo update(Principal principal, @RequestBody PersonSignupTo personSignupTo) {
|
public PersonTo update(Authentication authentication, @RequestBody PersonSignupTo personSignupTo) {
|
||||||
var person = personService.readById(principal.getName()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
var person = personService.readById(authentication.getName()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
person.setUsername(personSignupTo.username())
|
person.setUsername(personSignupTo.username())
|
||||||
.setFullName(personSignupTo.fullName());
|
.setFullName(personSignupTo.fullName());
|
||||||
if (!personSignupTo.password().isEmpty()) person.setPassword(passwordEncoder.encode(personSignupTo.password()));
|
if (!personSignupTo.password().isEmpty()) person.setPassword(passwordEncoder.encode(personSignupTo.password()));
|
||||||
@@ -87,13 +87,13 @@ public class PersonController {
|
|||||||
|
|
||||||
@DeleteMapping(path = "/self")
|
@DeleteMapping(path = "/self")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void delete(Principal principal) {
|
public void delete(Authentication authentication) {
|
||||||
var person = personService.readById(principal.getName()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
var person = personService.readById(authentication.getName()).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
for (Chat c : person.getChats()) {
|
for (Chat c : person.getChats()) {
|
||||||
c.getMembers().remove(person);
|
c.getMembers().remove(person);
|
||||||
chatService.update(c);
|
chatService.update(c);
|
||||||
}
|
}
|
||||||
personService.deleteById(principal.getName());
|
personService.deleteById(authentication.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
@@ -102,25 +102,43 @@ public class PersonController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/followers")
|
@GetMapping(path = "/followers")
|
||||||
public Stream<PersonTo> getFollowers(Principal principal) throws UserNotFoundException {
|
public Stream<PersonTo> getFollowers(Authentication authentication) throws UserNotFoundException {
|
||||||
return personService.getFollowers(principal.getName()).stream().map(personMapper::makeDto);
|
return personService.getFollowers(authentication.getName()).stream().map(personMapper::makeDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/following")
|
@GetMapping(path = "/following")
|
||||||
public Stream<PersonTo> getFollowing(Principal principal) throws UserNotFoundException {
|
public Stream<PersonTo> getFollowing(Authentication authentication) throws UserNotFoundException {
|
||||||
return personService.getFollowing(principal.getName()).stream().map(personMapper::makeDto);
|
return personService.getFollowing(authentication.getName()).stream().map(personMapper::makeDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping(path = "/admins")
|
||||||
|
public Stream<PersonTo> getAdmins() {
|
||||||
|
return personService.getAdmins().stream().map(personMapper::makeDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping(path = "/admins/{uuid}")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public void addAdmin(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||||
|
personService.addAdmin(authentication, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteMapping(path = "/admins/{uuid}")
|
||||||
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
|
public void deleteAdmin(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||||
|
personService.removeAdmin(authentication, uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@PutMapping(path = "/following/{uuid}")
|
@PutMapping(path = "/following/{uuid}")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void addFollowing(Principal principal, @PathVariable String uuid) throws UserNotFoundException {
|
public void addFollowing(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||||
personService.addFollower(principal.getName(), uuid);
|
personService.addFollower(authentication.getName(), uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@DeleteMapping(path = "/following/{uuid}")
|
@DeleteMapping(path = "/following/{uuid}")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void deleteFollowing(Principal principal, @PathVariable String uuid) throws UserNotFoundException {
|
public void deleteFollowing(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||||
personService.removeFollower(principal.getName(), uuid);
|
personService.removeFollower(authentication.getName(), uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import com.usatiuk.tjv.y.server.service.PostService;
|
|||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.server.ResponseStatusException;
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.security.Principal;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@@ -31,9 +31,9 @@ public class PostController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public PostTo createPost(Principal principal, @RequestBody PostCreateTo postCreateTo) {
|
public PostTo createPost(Authentication authentication, @RequestBody PostCreateTo postCreateTo) {
|
||||||
Post post = new Post();
|
Post post = new Post();
|
||||||
post.setAuthor(entityManager.getReference(Person.class, principal.getName()));
|
post.setAuthor(entityManager.getReference(Person.class, authentication.getName()));
|
||||||
post.setText(postCreateTo.text());
|
post.setText(postCreateTo.text());
|
||||||
return postMapper.makeDto(postService.create(post));
|
return postMapper.makeDto(postService.create(post));
|
||||||
}
|
}
|
||||||
@@ -55,8 +55,8 @@ public class PostController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/by-following")
|
@GetMapping(path = "/by-following")
|
||||||
public Stream<PostTo> readAllByFollowees(Principal principal) {
|
public Stream<PostTo> readAllByFollowees(Authentication authentication) {
|
||||||
return postService.readByPersonFollowees(principal.getName()).stream().map(postMapper::makeDto);
|
return postService.readByPersonFollowees(authentication.getName()).stream().map(postMapper::makeDto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/{id}")
|
@GetMapping(path = "/{id}")
|
||||||
@@ -67,9 +67,9 @@ public class PostController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@PatchMapping(path = "/{id}")
|
@PatchMapping(path = "/{id}")
|
||||||
public PostTo update(Principal principal, @PathVariable long id, @RequestBody PostCreateTo postCreateTo) {
|
public PostTo update(Authentication authentication, @PathVariable long id, @RequestBody PostCreateTo postCreateTo) {
|
||||||
var post = postService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
var post = postService.readById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||||
if (!Objects.equals(post.getAuthor().getUuid(), principal.getName()))
|
if (!Objects.equals(post.getAuthor().getUuid(), authentication.getName()))
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
post.setText(postCreateTo.text());
|
post.setText(postCreateTo.text());
|
||||||
postService.update(post);
|
postService.update(post);
|
||||||
@@ -78,10 +78,10 @@ public class PostController {
|
|||||||
|
|
||||||
@DeleteMapping(path = "/{id}")
|
@DeleteMapping(path = "/{id}")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
public void delete(Principal principal, @PathVariable long id) {
|
public void delete(Authentication authentication, @PathVariable long id) {
|
||||||
var read = postService.readById(id);
|
var read = postService.readById(id);
|
||||||
if (read.isEmpty()) return;
|
if (read.isEmpty()) return;
|
||||||
if (!Objects.equals(read.get().getAuthor().getId(), principal.getName())) {
|
if (!Objects.equals(read.get().getAuthor().getId(), authentication.getName())) {
|
||||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||||
}
|
}
|
||||||
postService.deleteById(id);
|
postService.deleteById(id);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package com.usatiuk.tjv.y.server.dto;
|
package com.usatiuk.tjv.y.server.dto;
|
||||||
|
|
||||||
public record PersonTo(String uuid, String username, String fullName) {
|
public record PersonTo(String uuid, String username, String fullName, boolean isAdmin) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ import org.springframework.stereotype.Component;
|
|||||||
@Component
|
@Component
|
||||||
public class PersonMapper {
|
public class PersonMapper {
|
||||||
public PersonTo makeDto(Person person) {
|
public PersonTo makeDto(Person person) {
|
||||||
return new PersonTo(person.getUuid(), person.getUsername(), person.getFullName());
|
return new PersonTo(person.getUuid(), person.getUsername(), person.getFullName(), person.isAdmin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ public class Person implements EntityWithId<String> {
|
|||||||
@OneToMany(mappedBy = "author", orphanRemoval = true)
|
@OneToMany(mappedBy = "author", orphanRemoval = true)
|
||||||
private Collection<Message> messages = new ArrayList<>();
|
private Collection<Message> messages = new ArrayList<>();
|
||||||
|
|
||||||
|
private boolean admin;
|
||||||
|
|
||||||
@ManyToMany
|
@ManyToMany
|
||||||
@JoinTable(name = "person_follows",
|
@JoinTable(name = "person_follows",
|
||||||
joinColumns = @JoinColumn(name = "follower"),
|
joinColumns = @JoinColumn(name = "follower"),
|
||||||
|
|||||||
@@ -3,10 +3,13 @@ package com.usatiuk.tjv.y.server.repository;
|
|||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
import com.usatiuk.tjv.y.server.entity.Person;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public interface PersonRepository extends CrudRepository<Person, String> {
|
public interface PersonRepository extends CrudRepository<Person, String> {
|
||||||
Optional<Person> findByUsername(String username);
|
Optional<Person> findByUsername(String username);
|
||||||
|
|
||||||
boolean existsByUsername(String username);
|
boolean existsByUsername(String username);
|
||||||
|
|
||||||
|
Collection<Person> findByAdminIsTrue();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
|
|||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -24,8 +24,9 @@ public class JwtUserDetailsService implements UserDetailsService {
|
|||||||
public UserDetails loadUserByUsername(String uuid) {
|
public UserDetails loadUserByUsername(String uuid) {
|
||||||
Optional<Person> person = personService.readById(uuid);
|
Optional<Person> person = personService.readById(uuid);
|
||||||
if (!person.isPresent()) throw new UsernameNotFoundException("User with UUID " + uuid + " not found");
|
if (!person.isPresent()) throw new UsernameNotFoundException("User with UUID " + uuid + " not found");
|
||||||
List<SimpleGrantedAuthority> roles =
|
ArrayList<SimpleGrantedAuthority> roles =
|
||||||
Collections.singletonList(new SimpleGrantedAuthority(UserRoles.ROLE_USER.name()));
|
new ArrayList<>(List.of(new SimpleGrantedAuthority(UserRoles.ROLE_USER.name())));
|
||||||
|
if (person.get().isAdmin()) roles.add(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name()));
|
||||||
return new JwtUser(uuid, person.get().getPassword(), roles);
|
return new JwtUser(uuid, person.get().getPassword(), roles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.usatiuk.tjv.y.server.service;
|
|||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
import com.usatiuk.tjv.y.server.entity.Person;
|
||||||
import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
||||||
import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -15,10 +16,13 @@ public interface PersonService extends CrudService<Person, String> {
|
|||||||
Optional<Person> readByUsername(String username);
|
Optional<Person> readByUsername(String username);
|
||||||
|
|
||||||
Collection<Person> getFollowers(String uuid) throws UserNotFoundException;
|
Collection<Person> getFollowers(String uuid) throws UserNotFoundException;
|
||||||
|
|
||||||
Collection<Person> getFollowing(String uuid) throws UserNotFoundException;
|
Collection<Person> getFollowing(String uuid) throws UserNotFoundException;
|
||||||
|
|
||||||
void addFollower(String follower, String followee) throws UserNotFoundException;
|
void addFollower(String follower, String followee) throws UserNotFoundException;
|
||||||
|
|
||||||
void removeFollower(String follower, String followee) throws UserNotFoundException;
|
void removeFollower(String follower, String followee) throws UserNotFoundException;
|
||||||
|
|
||||||
|
Collection<Person> getAdmins();
|
||||||
|
void addAdmin(Authentication caller, String uuid) throws UserNotFoundException;
|
||||||
|
void removeAdmin(Authentication caller, String uuid) throws UserNotFoundException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,17 @@ package com.usatiuk.tjv.y.server.service;
|
|||||||
|
|
||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
import com.usatiuk.tjv.y.server.entity.Person;
|
||||||
import com.usatiuk.tjv.y.server.repository.PersonRepository;
|
import com.usatiuk.tjv.y.server.repository.PersonRepository;
|
||||||
|
import com.usatiuk.tjv.y.server.security.UserRoles;
|
||||||
import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
import com.usatiuk.tjv.y.server.service.exceptions.UserAlreadyExistsException;
|
||||||
import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
import com.usatiuk.tjv.y.server.service.exceptions.UserNotFoundException;
|
||||||
import jakarta.persistence.EntityManager;
|
import jakarta.persistence.EntityManager;
|
||||||
import org.springframework.data.repository.CrudRepository;
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -36,6 +41,9 @@ public class PersonServiceImpl extends CrudServiceImpl<Person, String> implement
|
|||||||
throw new UserAlreadyExistsException();
|
throw new UserAlreadyExistsException();
|
||||||
|
|
||||||
person.setPassword(passwordEncoder.encode(person.getPassword()));
|
person.setPassword(passwordEncoder.encode(person.getPassword()));
|
||||||
|
|
||||||
|
if (personRepository.findByAdminIsTrue().isEmpty()) person.setAdmin(true);
|
||||||
|
|
||||||
return create(person);
|
return create(person);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,4 +84,32 @@ public class PersonServiceImpl extends CrudServiceImpl<Person, String> implement
|
|||||||
person.getFollowing().remove(entityManager.getReference(Person.class, followee));
|
person.getFollowing().remove(entityManager.getReference(Person.class, followee));
|
||||||
personRepository.save(person);
|
personRepository.save(person);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAdmin(Authentication caller, String uuid) throws UserNotFoundException {
|
||||||
|
if (!caller.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||||
|
|
||||||
|
var person = personRepository.findById(uuid).orElseThrow(UserNotFoundException::new);
|
||||||
|
person.setAdmin(true);
|
||||||
|
personRepository.save(person);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAdmin(Authentication caller, String uuid) throws UserNotFoundException {
|
||||||
|
if (!caller.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||||
|
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||||
|
|
||||||
|
var person = personRepository.findById(uuid).orElseThrow(UserNotFoundException::new);
|
||||||
|
// TODO
|
||||||
|
if (personRepository.findByAdminIsTrue().size() == 1) return;
|
||||||
|
|
||||||
|
person.setAdmin(false);
|
||||||
|
personRepository.save(person);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Person> getAdmins() {
|
||||||
|
return personRepository.findByAdminIsTrue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user