mirror of
https://github.com/usatiuk/y.git
synced 2025-10-28 10:37:47 +01:00
use spring security more
This commit is contained in:
@@ -6,6 +6,7 @@ import jakarta.validation.ConstraintViolationException;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
@@ -33,6 +34,13 @@ public class ApiExceptionHandler extends ResponseEntityExceptionHandler {
|
||||
new HttpHeaders(), HttpStatus.UNAUTHORIZED, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler(AccessDeniedException.class)
|
||||
protected ResponseEntity<Object> handleAccessDeniedException(AccessDeniedException ex, WebRequest request) {
|
||||
return handleExceptionInternal(ex,
|
||||
new ErrorTo(List.of(ex.getMessage()), HttpStatus.FORBIDDEN.value()),
|
||||
new HttpHeaders(), HttpStatus.FORBIDDEN, request);
|
||||
}
|
||||
|
||||
@ExceptionHandler(UsernameNotFoundException.class)
|
||||
protected ResponseEntity<Object> handleUsernameNotFoundException(UsernameNotFoundException ex, WebRequest request) {
|
||||
return handleExceptionInternal(ex,
|
||||
|
||||
@@ -26,29 +26,29 @@ public class ChatController {
|
||||
}
|
||||
|
||||
@GetMapping(path = "/by-id/{id}")
|
||||
public ChatTo get(Authentication authentication, @PathVariable Long id) {
|
||||
return chatService.getById(authentication, id);
|
||||
public ChatTo get(@PathVariable Long id) {
|
||||
return chatService.getById(id);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/by-id/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void delete(Authentication authentication, @PathVariable Long id) {
|
||||
chatService.deleteById(authentication, id);
|
||||
public void delete(@PathVariable Long id) {
|
||||
chatService.deleteById(id);
|
||||
}
|
||||
|
||||
@PatchMapping(path = "/by-id/{id}")
|
||||
public ChatTo update(Authentication authentication, @PathVariable Long id, @RequestBody ChatCreateTo chatCreateTo) {
|
||||
return chatService.update(authentication, id, chatCreateTo);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping(path = "/my")
|
||||
public Collection<ChatTo> getMy(Authentication authentication) {
|
||||
return chatService.getMy(authentication);
|
||||
}
|
||||
|
||||
@GetMapping(path = "/by-id/{id}/members")
|
||||
public Collection<PersonTo> getMembers(Authentication authentication, @PathVariable Long id) {
|
||||
return chatService.getMembers(authentication, id);
|
||||
public Collection<PersonTo> getMembers(@PathVariable Long id) {
|
||||
return chatService.getMembers(id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class MessageController {
|
||||
|
||||
@GetMapping(path = "/by-chat/{chatTd}")
|
||||
public Collection<MessageTo> get(Authentication authentication, @PathVariable Long chatTd) {
|
||||
return messageService.getByChat(authentication, chatTd);
|
||||
return messageService.getByChat(chatTd);
|
||||
}
|
||||
|
||||
@PostMapping(path = "/by-chat/{chatId}")
|
||||
@@ -30,19 +30,19 @@ public class MessageController {
|
||||
}
|
||||
|
||||
@PatchMapping(path = "/by-id/{id}")
|
||||
public MessageTo update(Authentication authentication, @PathVariable long id, @RequestBody MessageCreateTo messageCreateTo) {
|
||||
return messageService.update(authentication, id, messageCreateTo);
|
||||
public MessageTo update(@PathVariable long id, @RequestBody MessageCreateTo messageCreateTo) {
|
||||
return messageService.update(id, messageCreateTo);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/by-id/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void delete(Authentication authentication, @PathVariable long id) {
|
||||
messageService.delete(authentication, id);
|
||||
public void delete(@PathVariable long id) {
|
||||
messageService.delete(id);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Collection<MessageTo> getAll(Authentication authentication) {
|
||||
return messageService.readAll(authentication);
|
||||
public Collection<MessageTo> getAll() {
|
||||
return messageService.readAll();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -54,8 +54,8 @@ public class PersonController {
|
||||
|
||||
@DeleteMapping(path = "/by-uuid/{uuid}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteByUuid(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||
personService.deleteByUuid(authentication, uuid);
|
||||
public void deleteByUuid(@PathVariable String uuid) throws UserNotFoundException {
|
||||
personService.deleteByUuid(uuid);
|
||||
}
|
||||
|
||||
|
||||
@@ -81,14 +81,14 @@ public class PersonController {
|
||||
|
||||
@PutMapping(path = "/admins/{uuid}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void addAdmin(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||
personService.addAdmin(authentication, uuid);
|
||||
public void addAdmin(@PathVariable String uuid) throws UserNotFoundException {
|
||||
personService.addAdmin(uuid);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/admins/{uuid}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void deleteAdmin(Authentication authentication, @PathVariable String uuid) throws UserNotFoundException {
|
||||
personService.removeAdmin(authentication, uuid);
|
||||
public void deleteAdmin(@PathVariable String uuid) throws UserNotFoundException {
|
||||
personService.removeAdmin(uuid);
|
||||
}
|
||||
|
||||
@PutMapping(path = "/following/{uuid}")
|
||||
|
||||
@@ -47,19 +47,19 @@ public class PostController {
|
||||
}
|
||||
|
||||
@PatchMapping(path = "/{id}")
|
||||
public PostTo update(Authentication authentication, @PathVariable long id, @RequestBody PostCreateTo postCreateTo) {
|
||||
return postService.updatePost(authentication, id, postCreateTo);
|
||||
public PostTo update(@PathVariable long id, @RequestBody PostCreateTo postCreateTo) {
|
||||
return postService.updatePost(id, postCreateTo);
|
||||
}
|
||||
|
||||
@DeleteMapping(path = "/{id}")
|
||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||
public void delete(Authentication authentication, @PathVariable long id) {
|
||||
postService.deletePost(authentication, id);
|
||||
public void delete(@PathVariable long id) {
|
||||
postService.deletePost(id);
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public Collection<PostTo> getAll(Authentication authentication) {
|
||||
return postService.readAll(authentication);
|
||||
public Collection<PostTo> getAll() {
|
||||
return postService.readAll();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.util.Collection;
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class Chat implements EntityWithId<Long> {
|
||||
public class Chat {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
package com.usatiuk.tjv.y.server.entity;
|
||||
|
||||
public interface EntityWithId<ID> {
|
||||
ID getId();
|
||||
}
|
||||
@@ -17,7 +17,7 @@ import java.time.Instant;
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class Message implements EntityWithId<Long> {
|
||||
public class Message {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.Collection;
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class Person implements EntityWithId<String> {
|
||||
public class Person {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.UUID)
|
||||
private String uuid;
|
||||
@@ -57,8 +57,4 @@ public class Person implements EntityWithId<String> {
|
||||
@ManyToMany(mappedBy = "members")
|
||||
private Collection<Chat> chats;
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return uuid;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import java.time.Instant;
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class Post implements EntityWithId<Long> {
|
||||
public class Post {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
@@ -31,9 +31,4 @@ public class Post implements EntityWithId<Long> {
|
||||
|
||||
@CreationTimestamp
|
||||
private Instant createdAt;
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
@@ -31,6 +32,7 @@ import java.util.List;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
|
||||
public class WebSecurityConfig {
|
||||
private final JwtRequestFilter jwtRequestFilter;
|
||||
|
||||
|
||||
@@ -3,20 +3,27 @@ package com.usatiuk.tjv.y.server.service;
|
||||
import com.usatiuk.tjv.y.server.dto.ChatCreateTo;
|
||||
import com.usatiuk.tjv.y.server.dto.ChatTo;
|
||||
import com.usatiuk.tjv.y.server.dto.PersonTo;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ChatService {
|
||||
ChatTo create(Authentication authentication, ChatCreateTo chatCreateTo);
|
||||
|
||||
@PreAuthorize("@chatService.isCreatorOf(authentication.principal.username, #id)")
|
||||
ChatTo update(Authentication authentication, Long id, ChatCreateTo chatCreateTo);
|
||||
|
||||
Collection<ChatTo> getMy(Authentication authentication);
|
||||
|
||||
ChatTo getById(Authentication authentication, Long id);
|
||||
void deleteById(Authentication authentication, Long id);
|
||||
@PreAuthorize("@chatService.isMemberOf(authentication.principal.username, #id)")
|
||||
ChatTo getById(Long id);
|
||||
@PreAuthorize("@chatService.isMemberOf(authentication.principal.username, #id)")
|
||||
void deleteById(Long id);
|
||||
|
||||
Collection<PersonTo> getMembers(Authentication authentication, Long id);
|
||||
@PreAuthorize("@chatService.isMemberOf(authentication.principal.username, #id)")
|
||||
Collection<PersonTo> getMembers(Long id);
|
||||
|
||||
boolean isMemberOf(String personUuid, Long chatId);
|
||||
boolean isCreatorOf(String personUuid, Long chatId);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
@Service
|
||||
@Service("chatService")
|
||||
public class ChatServiceImpl implements ChatService {
|
||||
|
||||
private final ChatRepository chatRepository;
|
||||
@@ -57,8 +57,6 @@ public class ChatServiceImpl implements ChatService {
|
||||
@Override
|
||||
public ChatTo update(Authentication authentication, Long id, ChatCreateTo chatCreateTo) {
|
||||
var chat = chatRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
if (!Objects.equals(chat.getCreator().getUuid(), authentication.getName()))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't creator of the chat");
|
||||
|
||||
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");
|
||||
@@ -81,29 +79,20 @@ public class ChatServiceImpl implements ChatService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatTo getById(Authentication authentication, Long id) {
|
||||
public ChatTo getById(Long id) {
|
||||
var chat = chatRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
var userRef = entityManager.getReference(Person.class, authentication.getName());
|
||||
if (!chat.getMembers().contains(userRef))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||
|
||||
return chatMapper.makeDto(chat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteById(Authentication authentication, Long id) {
|
||||
public void deleteById(Long id) {
|
||||
var chat = chatRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
if (!Objects.equals(chat.getCreator().getUuid(), authentication.getName()))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't creator of the chat");
|
||||
chatRepository.delete(chat);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PersonTo> getMembers(Authentication authentication, Long id) {
|
||||
public Collection<PersonTo> getMembers(Long id) {
|
||||
var chat = chatRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
var userRef = entityManager.getReference(Person.class, authentication.getName());
|
||||
if (!chat.getMembers().contains(userRef))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||
return chat.getMembers().stream().map(personMapper::makeDto).toList();
|
||||
}
|
||||
|
||||
@@ -113,4 +102,10 @@ public class ChatServiceImpl implements ChatService {
|
||||
var userRef = entityManager.getReference(Person.class, personUuid);
|
||||
return chat.getMembers().contains(userRef);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCreatorOf(String personUuid, Long chatId) {
|
||||
var chat = chatRepository.findById(chatId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
return Objects.equals(chat.getCreator().getUuid(), personUuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,16 +2,25 @@ package com.usatiuk.tjv.y.server.service;
|
||||
|
||||
import com.usatiuk.tjv.y.server.dto.MessageCreateTo;
|
||||
import com.usatiuk.tjv.y.server.dto.MessageTo;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface MessageService {
|
||||
Collection<MessageTo> getByChat(Authentication authentication, Long chatId);
|
||||
@PreAuthorize("@chatService.isMemberOf(authentication.principal.username, #chatId)")
|
||||
Collection<MessageTo> getByChat(Long chatId);
|
||||
@PreAuthorize("@chatService.isMemberOf(authentication.principal.username, #chatId)")
|
||||
MessageTo addToChat(Authentication authentication, Long chatId, MessageCreateTo messageCreateTo);
|
||||
|
||||
MessageTo update(Authentication authentication, Long id, MessageCreateTo msg);
|
||||
void delete(Authentication authentication, Long id);
|
||||
@PreAuthorize("@messageService.isAuthorOf(authentication.principal.username, #id)")
|
||||
MessageTo update(Long id, MessageCreateTo msg);
|
||||
@PreAuthorize("@messageService.isAuthorOf(authentication.principal.username, #id)")
|
||||
void delete(Long id);
|
||||
|
||||
Collection<MessageTo> readAll(Authentication authentication);
|
||||
@Secured({"ROLE_ADMIN"})
|
||||
Collection<MessageTo> readAll();
|
||||
|
||||
boolean isAuthorOf(String userUuid, Long messageId);
|
||||
}
|
||||
|
||||
@@ -7,11 +7,9 @@ import com.usatiuk.tjv.y.server.entity.Chat;
|
||||
import com.usatiuk.tjv.y.server.entity.Message;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
import com.usatiuk.tjv.y.server.repository.MessageRepository;
|
||||
import com.usatiuk.tjv.y.server.security.UserRoles;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@@ -19,34 +17,26 @@ import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@Service
|
||||
@Service("messageService")
|
||||
public class MessageServiceImpl implements MessageService {
|
||||
|
||||
private final MessageRepository messageRepository;
|
||||
private final MessageMapper messageMapper;
|
||||
private final ChatService chatService;
|
||||
private final EntityManager entityManager;
|
||||
|
||||
public MessageServiceImpl(MessageRepository messageRepository, MessageMapper messageMapper, ChatService chatService, EntityManager entityManager) {
|
||||
public MessageServiceImpl(MessageRepository messageRepository, MessageMapper messageMapper, EntityManager entityManager) {
|
||||
this.messageRepository = messageRepository;
|
||||
this.messageMapper = messageMapper;
|
||||
this.chatService = chatService;
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageTo> getByChat(Authentication authentication, Long chatId) {
|
||||
if (!chatService.isMemberOf(authentication.getName(), chatId))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||
|
||||
public Collection<MessageTo> getByChat(Long chatId) {
|
||||
return messageRepository.findByChat_Id(chatId).stream().map(messageMapper::makeDto).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageTo addToChat(Authentication authentication, Long chatId, MessageCreateTo messageCreateTo) {
|
||||
if (!chatService.isMemberOf(authentication.getName(), chatId))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "User isn't member of the chat");
|
||||
|
||||
Message message = new Message().setChat(entityManager.getReference(Chat.class, chatId))
|
||||
.setAuthor(entityManager.getReference(Person.class, authentication.getName()))
|
||||
.setContents(messageCreateTo.contents());
|
||||
@@ -56,30 +46,28 @@ public class MessageServiceImpl implements MessageService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageTo update(Authentication authentication, Long id, MessageCreateTo msg) {
|
||||
public MessageTo update(Long id, MessageCreateTo msg) {
|
||||
var message = messageRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
if (!Objects.equals(message.getAuthor().getUuid(), authentication.getName()))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||
message.setContents(msg.contents());
|
||||
messageRepository.save(message);
|
||||
return messageMapper.makeDto(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(Authentication authentication, Long id) {
|
||||
public void delete(Long id) {
|
||||
var read = messageRepository.findById(id);
|
||||
if (read.isEmpty()) return;
|
||||
if (!Objects.equals(read.get().getAuthor().getId(), authentication.getName())) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
messageRepository.delete(read.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<MessageTo> readAll(Authentication authentication) {
|
||||
if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
public Collection<MessageTo> readAll() {
|
||||
return StreamSupport.stream(messageRepository.findAll().spliterator(), false).map(messageMapper::makeDto).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorOf(String userUuid, Long messageId) {
|
||||
var msg = messageRepository.findById(messageId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
return Objects.equals(msg.getAuthor().getUuid(), userUuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.usatiuk.tjv.y.server.service;
|
||||
import com.usatiuk.tjv.y.server.dto.PersonCreateTo;
|
||||
import com.usatiuk.tjv.y.server.dto.PersonTo;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Collection;
|
||||
@@ -20,7 +21,8 @@ public interface PersonService {
|
||||
PersonTo update(Authentication authentication, PersonCreateTo person);
|
||||
|
||||
void deleteSelf(Authentication authentication);
|
||||
void deleteByUuid(Authentication authentication, String uuid);
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN') or authentication.principal.username == #uuid")
|
||||
void deleteByUuid(String uuid);
|
||||
|
||||
Collection<PersonTo> readAll();
|
||||
|
||||
@@ -31,7 +33,9 @@ public interface PersonService {
|
||||
void removeFollower(Authentication authentication, String followee);
|
||||
|
||||
Collection<PersonTo> getAdmins();
|
||||
void addAdmin(Authentication caller, String uuid);
|
||||
void removeAdmin(Authentication caller, String uuid);
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN') ")
|
||||
void addAdmin(String uuid);
|
||||
@PreAuthorize("hasRole('ROLE_ADMIN') ")
|
||||
void removeAdmin(String uuid);
|
||||
|
||||
}
|
||||
|
||||
@@ -6,19 +6,17 @@ import com.usatiuk.tjv.y.server.dto.converters.PersonMapper;
|
||||
import com.usatiuk.tjv.y.server.entity.Chat;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
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.UserNotFoundException;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.transaction.Transactional;
|
||||
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.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@@ -89,32 +87,25 @@ public class PersonServiceImpl implements PersonService {
|
||||
return personMapper.makeDto(found);
|
||||
}
|
||||
|
||||
private void deleteByUuid(String uuid) {
|
||||
@Transactional
|
||||
public void deleteByUuid(String uuid) {
|
||||
var person = personRepository.findById(uuid).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
for (Chat c : person.getChats()) {
|
||||
c.getMembers().remove(person);
|
||||
}
|
||||
|
||||
for (Person p : person.getFollowers()) {
|
||||
p.getFollowing().remove(person);
|
||||
personRepository.save(p);
|
||||
}
|
||||
|
||||
personRepository.delete(person);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteSelf(Authentication authentication) {
|
||||
deleteByUuid(authentication.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteByUuid(Authentication authentication, String uuid) {
|
||||
if ((!Objects.equals(authentication.getName(), uuid)) &&
|
||||
!authentication.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
deleteByUuid(uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PersonTo> readAll() {
|
||||
@@ -148,24 +139,15 @@ public class PersonServiceImpl implements PersonService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdmin(Authentication caller, String uuid) {
|
||||
if (!caller.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
public void addAdmin(String uuid) {
|
||||
var person = personRepository.findById(uuid).orElseThrow(UserNotFoundException::new);
|
||||
person.setAdmin(true);
|
||||
personRepository.save(person);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAdmin(Authentication caller, String uuid) {
|
||||
if (!caller.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
public void removeAdmin(String uuid) {
|
||||
var person = personRepository.findById(uuid).orElseThrow(UserNotFoundException::new);
|
||||
// TODO
|
||||
if (personRepository.findByAdminIsTrue().size() == 1) return;
|
||||
|
||||
person.setAdmin(false);
|
||||
personRepository.save(person);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,18 @@ package com.usatiuk.tjv.y.server.service;
|
||||
|
||||
import com.usatiuk.tjv.y.server.dto.PostCreateTo;
|
||||
import com.usatiuk.tjv.y.server.dto.PostTo;
|
||||
import org.springframework.security.access.annotation.Secured;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface PostService {
|
||||
PostTo createPost(Authentication authentication, PostCreateTo postCreateTo);
|
||||
PostTo updatePost(Authentication authentication, Long id, PostCreateTo postCreateTo);
|
||||
void deletePost(Authentication authentication, Long id);
|
||||
@PreAuthorize("@postService.isAuthorOf(authentication.principal.username, #id)")
|
||||
PostTo updatePost(Long id, PostCreateTo postCreateTo);
|
||||
@PreAuthorize("@postService.isAuthorOf(authentication.principal.username, #id)")
|
||||
void deletePost(Long id);
|
||||
|
||||
PostTo readById(Long id);
|
||||
|
||||
@@ -18,5 +22,8 @@ public interface PostService {
|
||||
|
||||
Collection<PostTo> readByPersonFollowees(Authentication authentication);
|
||||
|
||||
Collection<PostTo> readAll(Authentication authentication);
|
||||
@Secured({"ROLE_ADMIN"})
|
||||
Collection<PostTo> readAll();
|
||||
|
||||
boolean isAuthorOf(String userUuid, Long postId);
|
||||
}
|
||||
|
||||
@@ -6,11 +6,9 @@ import com.usatiuk.tjv.y.server.dto.converters.PostMapper;
|
||||
import com.usatiuk.tjv.y.server.entity.Person;
|
||||
import com.usatiuk.tjv.y.server.entity.Post;
|
||||
import com.usatiuk.tjv.y.server.repository.PostRepository;
|
||||
import com.usatiuk.tjv.y.server.security.UserRoles;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
@@ -18,7 +16,7 @@ import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
@Service
|
||||
@Service("postService")
|
||||
public class PostServiceImpl implements PostService {
|
||||
private final PostRepository postRepository;
|
||||
private final PostMapper postMapper;
|
||||
@@ -60,30 +58,28 @@ public class PostServiceImpl implements PostService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PostTo updatePost(Authentication authentication, Long id, PostCreateTo postCreateTo) {
|
||||
public PostTo updatePost(Long id, PostCreateTo postCreateTo) {
|
||||
var post = postRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
|
||||
if (!Objects.equals(post.getAuthor().getUuid(), authentication.getName()))
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||
post.setText(postCreateTo.text());
|
||||
postRepository.save(post);
|
||||
return postMapper.makeDto(post);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deletePost(Authentication authentication, Long id) {
|
||||
public void deletePost(Long id) {
|
||||
var read = postRepository.findById(id);
|
||||
if (read.isEmpty()) return;
|
||||
if (!Objects.equals(read.get().getAuthor().getId(), authentication.getName())) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
postRepository.delete(read.get());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PostTo> readAll(Authentication authentication) {
|
||||
if (!authentication.getAuthorities().contains(new SimpleGrantedAuthority(UserRoles.ROLE_ADMIN.name())))
|
||||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
|
||||
|
||||
public Collection<PostTo> readAll() {
|
||||
return StreamSupport.stream(postRepository.findAll().spliterator(), false).map(postMapper::makeDto).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAuthorOf(String userUuid, Long postId) {
|
||||
var p = postRepository.findById(postId).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Chat not found"));
|
||||
return Objects.equals(p.getAuthor().getUuid(), userUuid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ public abstract class DemoDataDbTest {
|
||||
new Person()
|
||||
.setUsername("person1")
|
||||
.setFullName("Person 1")
|
||||
.setPassword(passwordEncoder.encode(person1Password)));
|
||||
.setPassword(passwordEncoder.encode(person1Password)).setAdmin(true));
|
||||
person1Auth = new TokenResponseTo(jwtTokenService.generateToken(person1.getUuid()));
|
||||
person2 = personRepository.save(
|
||||
new Person()
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.usatiuk.tjv.y.server.controller;
|
||||
|
||||
import com.usatiuk.tjv.y.server.dto.ErrorTo;
|
||||
import com.usatiuk.tjv.y.server.dto.MessageTo;
|
||||
import com.usatiuk.tjv.y.server.repository.MessageRepository;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
public class MessageControllerTest extends DemoDataDbTest {
|
||||
|
||||
@Autowired
|
||||
MessageRepository messageRepository;
|
||||
|
||||
@Test
|
||||
void shouldGetMessagesIfAdmin() {
|
||||
var response = restTemplate.exchange(addr + "/message", HttpMethod.GET,
|
||||
new HttpEntity<>(createAuthHeaders(person1Auth)),
|
||||
MessageTo[].class);
|
||||
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals(HttpStatus.OK, response.getStatusCode());
|
||||
|
||||
var toResponse = response.getBody();
|
||||
Assertions.assertNotNull(toResponse);
|
||||
|
||||
Assertions.assertEquals(toResponse.length, messageRepository.findAll().spliterator().estimateSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotGetMessagesIfNotAdmin() {
|
||||
var response = restTemplate.exchange(addr + "/message", HttpMethod.GET,
|
||||
new HttpEntity<>(createAuthHeaders(person2Auth)),
|
||||
ErrorTo.class);
|
||||
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals(HttpStatus.FORBIDDEN, response.getStatusCode());
|
||||
|
||||
var toResponse = response.getBody();
|
||||
Assertions.assertNotNull(toResponse);
|
||||
Assertions.assertEquals(toResponse.code(), HttpStatus.FORBIDDEN.value());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -68,6 +68,17 @@ public class PersonControllerTest extends DemoDataDbTest {
|
||||
Assertions.assertEquals(personToResponse.fullName(), person1.getFullName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldDeleteSelf() {
|
||||
var response = restTemplate.exchange(addr + "/person/self",
|
||||
HttpMethod.DELETE, new HttpEntity<>(createAuthHeaders(person2Auth)), Object.class);
|
||||
|
||||
Assertions.assertNotNull(response);
|
||||
Assertions.assertEquals(HttpStatus.NO_CONTENT, response.getStatusCode());
|
||||
|
||||
Assertions.assertFalse(personRepository.existsById(person2.getUuid()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldGetFollowers() {
|
||||
var response = restTemplate.exchange(addr + "/person/followers",
|
||||
|
||||
Reference in New Issue
Block a user