Implemented primitive rest validation.

A better implementation to be done later.
This commit is contained in:
Rohan Sircar 2019-10-24 12:12:42 +05:30
parent 97091123b9
commit d3ac95e8f4
16 changed files with 171 additions and 34 deletions

View File

@ -7,6 +7,8 @@ import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.util.List;
import javax.validation.Valid;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.ChatMessage;
@ -34,7 +36,7 @@ public class AdminRESTController {
@PostMapping(value = "/post/re-encrypt", consumes = { "application/json" })
@ResponseBody
public ResponseEntity<ReencryptionDTO> reencryptMessages(@RequestBody List<ReencryptionDTO> reencryptionDTOs,
public ResponseEntity<ReencryptionDTO> reencryptMessages(@RequestBody @Valid List<ReencryptionDTO> reencryptionDTOs,
Principal principal) {
if (reencryptionDTOs.size() > 0) {
chatService.reencryptMessages(reencryptionDTOs);

View File

@ -6,16 +6,23 @@ import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.validation.Valid;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.MessageCipher;
import org.ros.chatto.error.ErrorModel;
import org.ros.chatto.error.ErrorResponse;
import org.ros.chatto.service.ChatService;
import org.ros.chatto.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@ -23,6 +30,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
@RestController
@ -36,15 +44,53 @@ public class ChatMessageController {
@PostMapping(value = "/post/message", consumes = { "application/json" })
@ResponseBody
public ResponseEntity<ChatMessageDTO> newMessage(@RequestBody ChatMessageDTO chatMessageDTO, Principal principal) {
public ResponseEntity<?> newMessage(@RequestBody @Valid ChatMessageDTO chatMessageDTO,
Principal principal) {
// if (bindingResult.hasErrors()) {
//
//// return new ResponseEntity<List<FieldError>>(bindingResult.getFieldErrors(),HttpStatus.BAD_REQUEST);
// return new ResponseEntity<ErrorResponse>(handleException(bindingResult), HttpStatus.BAD_REQUEST);
// }
MessageCipherDTO messageCipher = chatMessageDTO.getMessageCipher();
String fromUser = principal.getName();
String toUser = chatMessageDTO.getToUser();
System.out.println("Message cipher = " + messageCipher);
chatService.saveNewMessage(fromUser, toUser, messageCipher);
return new ResponseEntity<ChatMessageDTO>(HttpStatus.OK);
chatMessageDTO = chatService.saveNewMessage(fromUser, toUser, messageCipher);
HttpHeaders responseHeader = new HttpHeaders();
return new ResponseEntity<ChatMessageDTO>(chatMessageDTO, responseHeader, HttpStatus.CREATED);
}
/**
* Method that check against {@code @Valid} Objects passed to controller endpoints
*
* @param exception
* @return a {@code ErrorResponse}
* @see com.aroussi.util.validation.ErrorResponse
*/
@ExceptionHandler(value=MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleException(MethodArgumentNotValidException exception) {
List<ErrorModel> errorMessages = exception.getBindingResult().getFieldErrors().stream()
.map(err -> new ErrorModel(err.getField(), err.getRejectedValue(), err.getDefaultMessage()))
.distinct()
.collect(Collectors.toList());
return ErrorResponse.builder().errorMessage(errorMessages).build();
}
@ExceptionHandler(value=MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleException(BindingResult bindingResult) {
List<ErrorModel> errorMessages = bindingResult.getFieldErrors().stream()
.map(err -> new ErrorModel(err.getField(), err.getRejectedValue(), err.getDefaultMessage()))
.distinct()
.collect(Collectors.toList());
return ErrorResponse.builder().errorMessage(errorMessages).build();
}
@GetMapping(value = "/get/messages/{userName}")
@ResponseBody
public List<ChatMessageDTO> sendAllMessages(@PathVariable String userName, Principal principal) {

View File

@ -25,8 +25,9 @@ public class RegistrationController {
}
@PostMapping("/perform_registration")
public String performRegistration(Model model,
@ModelAttribute("userRegistrationDTO") @Valid UserRegistrationDTO userRegistrationDTO, BindingResult bindingResult) {
public String performRegistration(
@ModelAttribute("userRegistrationDTO") @Valid UserRegistrationDTO userRegistrationDTO,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
System.out.println("Input has errors!");
return "registration";

View File

@ -2,13 +2,21 @@ package org.ros.chatto.dto;
import java.util.Date;
import org.ros.chatto.model.MessageCipher;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import lombok.Data;
@Data
public class ChatMessageDTO {
private String toUser, fromUser;
@NotBlank(message = "Username should not be blank")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "Username must be alphanumeric")
@Size(max=15)
private String toUser;
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "Username must be alphanumeric")
@Size(max=15)
private String fromUser;
private MessageCipherDTO messageCipher;
private Date messageTime;
}

View File

@ -1,5 +1,11 @@
package org.ros.chatto.dto;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
@ -8,18 +14,39 @@ import lombok.Setter;
@Getter
@Setter
public class MessageCipherDTO {
@Pattern(regexp = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$") // regex for base64
@NotBlank
private String iv;
@Max(1)
@NotBlank
private int v;
@Max(1_000_000)
@Min(1_000)
@NotBlank
@JsonProperty("iter")
private int iterations;
@Max(256)
@Min(128)
@JsonProperty("ks")
private int keySize;
@Max(256)
@Min(128)
@JsonProperty("ts")
private int tagSize;
@Pattern(regexp = "^[A-Za-z0-9]+$") // alphabetic
@NotBlank
private String mode;
@Pattern(regexp = "^[A-Za-z0-9]+$")
private String adata;
@Pattern(regexp = "^[A-Za-z0-9]+$")
@NotBlank
private String cipher;
@Pattern(regexp = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$")
@NotBlank
private String salt;
@JsonProperty("ct")
@Pattern(regexp = "^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$")
@NotBlank
@Size(max = 2000, min = 1)
private String cipherText;
}

View File

@ -2,13 +2,22 @@ package org.ros.chatto.dto;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import org.ros.chatto.model.MessageCipher;
import lombok.Data;
@Data
public class ReencryptionDTO {
@NotBlank(message = "Username should not be blank")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "Username must be alphanumeric")
@Size(max=15)
private String toUser, fromUser;
@NotBlank
@Size(max=600)
private MessageCipher messageCipher;
private Date messageTime;
}

View File

@ -11,9 +11,9 @@ public class UserRegistrationDTO {
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "Username must be alphanumeric")
private String userName;
@Transient
@Size(min = 4, max = 75, message = "Username must be between 4 and 75 characters")
@Size(min = 4, max = 75, message = "Password must be between 4 and 75 characters")
@NotBlank(message = "Password should not be blank")
@Pattern(regexp = "^.*(?=.{6,})(?=.*d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$", message = "Invalid password format")
// @Pattern(regexp = "^.*(?=.{6,})(?=.*d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$", message = "Invalid password format")
private String password;
public String getUserName() {

View File

@ -0,0 +1,16 @@
package org.ros.chatto.error;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErrorModel{
private String fieldName;
private Object rejectedValue;
private String messageError;
}

View File

@ -0,0 +1,17 @@
package org.ros.chatto.error;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ErrorResponse {
private List<ErrorModel> errorMessage;
}

View File

@ -33,7 +33,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
public class ChatUser {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
// @SequenceGenerator(name="user_generator", sequenceName = "user_seq", allocationSize=50)
// @SequenceGenerator(name="user_generator", sequenceName = "user_seq", allocationSize=50) //mysql does not support sequence id generator
@Column(name = "user_id")
private int userID;
@Column(name = "name")

View File

@ -3,6 +3,8 @@ package org.ros.chatto.service;
import java.util.Date;
import java.util.List;
import javax.validation.Valid;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.dto.ReencryptionDTO;
@ -10,7 +12,7 @@ import org.ros.chatto.model.ChatMessage;
import org.springframework.data.domain.PageRequest;
public interface ChatService {
public void saveNewMessage(String fromUser, String toUser, MessageCipherDTO messageCipherDTO);
public @Valid ChatMessageDTO saveNewMessage(String fromUser, String toUser, MessageCipherDTO messageCipherDTO);
public List<ChatMessageDTO> getAllMessages(String fromUser, String toUser);

View File

@ -1,5 +1,6 @@
package org.ros.chatto.service;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
@ -34,18 +35,26 @@ public class ChatServiceImpl implements ChatService {
@Autowired
MyConversionService myConversionService;
public void saveNewMessage(String fromUserName, String toUserName, MessageCipherDTO messageCipherDTO) {
@Transactional
public ChatMessageDTO saveNewMessage(String fromUserName, String toUserName, MessageCipherDTO messageCipherDTO) {
MessageCipher messageCipher = myConversionService.convertToMessageCipher(messageCipherDTO);
ChatUser fromUser = userRepository.findByUserName(fromUserName);
ChatUser toUser = userRepository.findByUserName(toUserName);
// if(fromUser ==null || toUser == null)
// {
// System.out.println("User is null");
// throw new SQLException();
// }
ChatMessage chatMessage = new ChatMessage();
messageCipher = messageCipherRepository.save(messageCipher);
chatMessage.setMessageCipher(messageCipher);
chatMessage.setFromUser(fromUser);
chatMessage.setToUser(toUser);
chatMessageRepository.save(chatMessage);
chatMessage = chatMessageRepository.save(chatMessage);
return myConversionService.convertToChatMessageDTO(chatMessage);
}

View File

@ -2,6 +2,8 @@ package org.ros.chatto.service;
import java.util.List;
import javax.transaction.Transactional;
import org.ros.chatto.dto.UserRegistrationDTO;
import org.ros.chatto.model.ChatUser;
import org.ros.chatto.model.Role;
@ -31,6 +33,7 @@ public class UserServiceImpl implements UserService{
UserRepositoryCustom userRepositoryCustom;
@Override
@Transactional
public void saveChatUser(ChatUser user) {
// TODO Auto-generated method stub
ChatUser changedUser = userRepository.save(user);
@ -41,6 +44,7 @@ public class UserServiceImpl implements UserService{
}
@Override
@Transactional
public void registerUser(UserRegistrationDTO userRegistrationDTO) {
// TODO Auto-generated method stub
ChatUser user = new ChatUser();

View File

@ -62,7 +62,6 @@ function handleChangePassphraseForm() {
messageCiphers.push(messageCipherNewObj);
// let messageCipherJson = JSON.stringify(messageCipherNewObj);
let fromUser = sessionStorage.getItem('username');
let chatMessageDTO = {
"toUser": user,
"fromUser": username,

View File

@ -16,11 +16,11 @@
<link th:href="@{/css/master.css}" href="../static/css/master.css" rel="stylesheet">
<link th:href="@{/css/colors.css}" href="../static/css/colors.css" rel="stylesheet">
</link>
<script th:inline="javascript">
var hostAddress = window.location.host;
/* var hostAddress2 = [[#{test.bindAddress}]]; */
console.log("hostname" + window.location.host);
// console.log("hostname" + window.location.host);
</script>
<meta charset="UTF-8">

View File

@ -17,7 +17,7 @@
<div class="row">
<div class="col-sm py-5">
<!-- <h4 class="display-4 text-center py-2">Chat</h4> -->
<div class="card text-white bg-primary mb-3 text-center card-form rounded mx-auto" id="login-card">
<div class="card text-white bg-primary mb-3 card-form rounded mx-auto" id="login-card">
<div class="card-body rounded">
<!-- <h4 class="card-title">Chat</h4> -->
@ -36,22 +36,19 @@
</div> -->
<div class="card-text">
<h2 class="card-title">Register</h2>
<h2 class="card-title text-center">Register</h2>
<form action="#" th:action="@{/perform_registration}" th:object=${userRegistrationDTO} method="POST">
<div class="form-group">
<label>Enter username: </label>
<div th:classappend="${#fields.hasErrors('userName')} ? 'input-icon right' : ''">
<i th:if="${#fields.hasErrors('userName')}" class="fa fa-exclamation tooltips" data-original-title="please enter a valid username" data-container="body"></i>
<input class="form-control" th:field="*{userName}" type="text" name="username" id="username" required>
<span th:if="${#fields.hasErrors('userName')}" class="help-block" th:errors="*{userName}"></span>
</div>
<input th:classappend="${#fields.hasErrors('userName')} ? 'is-invalid' : ''" class="form-control" th:field="*{userName}" type="text" name="username" required>
<small class="form-text">Username must be alphanumeric</small>
<span th:if="${#fields.hasErrors('userName')}" class="help-block text-danger" th:errors="*{userName}"></span>
</div>
<div class="form-group">
<label>Enter password: </label>
<i th:if="${#fields.hasErrors('password')}" class="fa fa-exclamation tooltips" data-original-title="please enter a valid first name" data-container="body"></i>
<input class="form-control" th:field="*{password}" type="password" name="password" id="password" required>
<span th:if="${#fields.hasErrors('password')}" class="help-block" th:errors="*{password}"></span>
<input th:classappend="${#fields.hasErrors('password')} ? 'is-invalid' : ''" class="form-control" th:field="*{password}" type="password" name="password" id="password" required>
<span th:if="${#fields.hasErrors('password')}" class="help-block text-danger" th:errors="*{password}"></span>
</div>
<div class="form-group">
<label for="password-repeat">Repeat password: </label>