Architecture improvements and validation

Improved system architecture and added sample regex validation to
registration
This commit is contained in:
Rohan Sircar 2019-10-22 17:42:18 +05:30
parent 4607ae5e17
commit acbacc8fa8
31 changed files with 962 additions and 322 deletions

View File

@ -100,18 +100,20 @@
<artifactId>cache-api</artifactId>
</dependency>
<!-- <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId>
<version>2.2.11</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId> <version>2.2.11</version> </dependency> -->
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 -->
<!-- https://mvnrepository.com/artifact/javax.xml.bind/jaxb-api -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.11</version>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
<version>2.2.11</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
@ -124,6 +126,7 @@
<!-- <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId>
<version>1.1.0</version> </dependency> -->
</dependencies>
<build>

View File

@ -31,7 +31,7 @@ public final class RESTAuthenticationEntryPoint
response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 - " + authEx.getMessage());
writer.println("HTTP ApplicationStatus 401 - " + authEx.getMessage());
}
@Override

View File

@ -1,14 +1,25 @@
package org.ros.chatto.controller;
import java.security.Principal;
import org.ros.chatto.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/admin")
public class AdminController {
@Autowired
private UserService userService;
@RequestMapping
public String viewManageUsers() {
return "/admin/home";
public ModelAndView viewManageUsers(Principal principal) {
ModelAndView modelAndView = new ModelAndView("/admin/home");
modelAndView.addObject("user", new String());
modelAndView.addObject("userNames", userService.getAllRegularUsers());
return modelAndView;
}
}

View File

@ -0,0 +1,87 @@
package org.ros.chatto.controller;
import java.security.Principal;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.Date;
import java.util.List;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.ChatMessage;
import org.ros.chatto.service.ChatService;
import org.ros.chatto.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/admin")
public class AdminRESTController {
@Autowired
private ChatService chatService;
@Autowired
private UserService userService;
@PostMapping(value = "/post/re-encrypt", consumes = { "application/json" })
@ResponseBody
public ResponseEntity<ReencryptionDTO> reencryptMessages(@RequestBody List<ReencryptionDTO> reencryptionDTOs,
Principal principal) {
if (reencryptionDTOs.size() > 0) {
chatService.reencryptMessages(reencryptionDTOs);
}
return new ResponseEntity<ReencryptionDTO>(HttpStatus.OK);
}
@GetMapping(value = "/get/messages/{userName}")
@ResponseBody
public List<ReencryptionDTO> sendAllMessages(@PathVariable String userName, Principal principal) {
List<ReencryptionDTO> reencryptionDTOs = chatService.getAllMessagesForReencryption(principal.getName(), userName);
return reencryptionDTOs;
}
@GetMapping(value = "/get/messages/{userName}/{lastMessageTime}")
@ResponseBody
public List<ChatMessageDTO> sendNewMessages(@PathVariable String userName, @PathVariable String lastMessageTime,
Principal principal) {
System.out.println("Last message time = " + lastMessageTime);
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// offset (hh:mm - "+00:00" when it's zero)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
// offset (hhmm - "+0000" when it's zero)
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
// offset (hh - "Z" when it's zero)
.optionalStart().appendOffset("+HH", "Z").optionalEnd()
// create formatter
.toFormatter();
Date date = Date.from(OffsetDateTime.parse(lastMessageTime, formatter).toInstant());
List<ChatMessageDTO> chatMessageDTOs = chatService.getNewMessages(principal.getName(), userName, date);
return chatMessageDTOs;
}
@GetMapping("/get/users")
public List<String> getAllOtherUsers(Principal principal) {
return userService.findAllOtherUsers(principal.getName());
}
}
//public ResponseEntity<List<ChatMessage>> getMessages(@PathVariable String userName, Principal principal) {
////List<ChatMessage> chatMessages = chatMessageRepository.getAllMessages(principal.getName(), userName);
//
//// return posts.stream()
//// .map(post -> convertToDto(post))
//// .collect(Collectors.toList());
//return new ResponseEntity<List<ChatMessage>>(chatMessages, HttpStatus.OK);
//}

View File

@ -8,6 +8,8 @@ import java.util.Date;
import java.util.List;
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.service.ChatService;
import org.ros.chatto.service.UserService;
@ -19,6 +21,7 @@ import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
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.RestController;
@ -34,7 +37,7 @@ public class ChatMessageController {
@PostMapping(value = "/post/message", consumes = { "application/json" })
@ResponseBody
public ResponseEntity<ChatMessageDTO> newMessage(@RequestBody ChatMessageDTO chatMessageDTO, Principal principal) {
MessageCipher messageCipher = chatMessageDTO.getMessageCipher();
MessageCipherDTO messageCipher = chatMessageDTO.getMessageCipher();
String fromUser = principal.getName();
String toUser = chatMessageDTO.getToUser();
System.out.println("Message cipher = " + messageCipher);
@ -48,9 +51,19 @@ public class ChatMessageController {
List<ChatMessageDTO> chatMessageDTOs = chatService.getAllMessages(principal.getName(), userName);
return chatMessageDTOs;
}
@GetMapping(value = "/get/messages/{userName}", params = { "page", "size" })
@ResponseBody
public List<ChatMessageDTO> findPaginated(@RequestParam("page") int page, @RequestParam("size") int size,
@PathVariable String userName, Principal principal) {
List<ChatMessageDTO> chatMessageDTOs = chatService.getMessagePage(principal.getName(), userName, page, size);
return chatMessageDTOs;
}
@GetMapping(value = "/get/messages/{userName}/{lastMessageTime}")
@ResponseBody
public List<ChatMessageDTO> sendNewMessages(@PathVariable String userName, @PathVariable String lastMessageTime, Principal principal) {
public List<ChatMessageDTO> sendNewMessages(@PathVariable String userName, @PathVariable String lastMessageTime,
Principal principal) {
System.out.println("Last message time = " + lastMessageTime);
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// date/time
@ -68,9 +81,9 @@ public class ChatMessageController {
List<ChatMessageDTO> chatMessageDTOs = chatService.getNewMessages(principal.getName(), userName, date);
return chatMessageDTOs;
}
@GetMapping("/get/users")
public List<String> getAllOtherUsers(Principal principal)
{
public List<String> getAllOtherUsers(Principal principal) {
return userService.findAllOtherUsers(principal.getName());
}
}

View File

@ -70,23 +70,25 @@ public class DemoRestController {
}
@GetMapping("/roles")
public List<UserRole> getAllRoles()
{
public List<UserRole> getAllRoles() {
return userRoleRepository.findAll();
}
@GetMapping("/ciphers")
public List<MessageCipher> getAllCiphers()
{
public List<MessageCipher> getAllCiphers() {
return messageCipherRepository.findAll();
}
@GetMapping("/messages")
public List<ChatMessage> getAllMessages()
{
public List<ChatMessage> getAllMessages() {
return chatMessageRepository.findAll();
}
@GetMapping("/regular-users")
public List<String> getAllRegularUsers() {
return userRoleRepository.getAllRegularUser();
}
// @RequestMapping(value = "/", method = RequestMethod.POST)
// public ResponseEntity<Car> update(@RequestBody Car car) {
//
@ -99,19 +101,18 @@ public class DemoRestController {
// }
@PostMapping(value = "/post-message", consumes = { "application/json" })
public ResponseEntity<MessageCipher> postMessage(@RequestBody MessageCipher messageCipher)
{
public ResponseEntity<MessageCipher> postMessage(@RequestBody MessageCipher messageCipher) {
System.out.println("Message cipher = " + messageCipher);
messageCipherRepository.save(messageCipher);
return new ResponseEntity<MessageCipher>(HttpStatus.OK);
}
@GetMapping("/logout")
public ModelAndView logoutPage()
{
public ModelAndView logoutPage() {
ModelAndView modelAndView = new ModelAndView("restLogout");
return modelAndView;
}
@RequestMapping(value = "perform_logout", method = RequestMethod.POST)
public String performLogout(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();

View File

@ -1,34 +1,37 @@
package org.ros.chatto.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import org.ros.chatto.dto.UserRegistrationDTO;
import org.ros.chatto.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class RegisterController {
public class RegistrationController {
@Autowired
private UserService userService;
@GetMapping("/registration")
public ModelAndView registrationForm()
{
ModelAndView modelAndView = new ModelAndView("registration");
modelAndView.addObject("userDTO",new UserRegistrationDTO());
return modelAndView;
public String registrationForm(Model model) {
model.addAttribute("userRegistrationDTO", new UserRegistrationDTO());
return "registration";
}
@PostMapping("/perform_registration")
public ModelAndView performRegistration(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, @ModelAttribute("userDTO") UserRegistrationDTO userRegistrationDTO)
{
ModelAndView modelAndView = new ModelAndView("user/home");
public String performRegistration(Model model,
@ModelAttribute("userRegistrationDTO") @Valid UserRegistrationDTO userRegistrationDTO, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
System.out.println("Input has errors!");
return "registration";
}
userService.registerUser(userRegistrationDTO);
return modelAndView;
return "user/home";
}
}

View File

@ -9,6 +9,6 @@ import lombok.Data;
@Data
public class ChatMessageDTO {
private String toUser, fromUser;
private MessageCipher messageCipher;
private MessageCipherDTO messageCipher;
private Date messageTime;
}

View File

@ -0,0 +1,25 @@
package org.ros.chatto.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class MessageCipherDTO {
private String iv;
private int v;
@JsonProperty("iter")
private int iterations;
@JsonProperty("ks")
private int keySize;
@JsonProperty("ts")
private int tagSize;
private String mode;
private String adata;
private String cipher;
private String salt;
@JsonProperty("ct")
private String cipherText;
}

View File

@ -0,0 +1,14 @@
package org.ros.chatto.dto;
import java.util.Date;
import org.ros.chatto.model.MessageCipher;
import lombok.Data;
@Data
public class ReencryptionDTO {
private String toUser, fromUser;
private MessageCipher messageCipher;
private Date messageTime;
}

View File

@ -1,14 +0,0 @@
package org.ros.chatto.dto;
import java.sql.Date;
import org.ros.chatto.model.MessageCipher;
import lombok.Data;
@Data
public class UserPublicDTO {
String fromUser, toUser;
MessageCipher messageCipher;
Date messageTime;
}

View File

@ -1,23 +1,35 @@
package org.ros.chatto.dto;
import javax.persistence.Transient;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class UserRegistrationDTO {
@Size(min = 4, max = 10, message = "Username must be between 4 and 10 characters")
@NotBlank(message = " Username should not be blank")
@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")
@NotBlank(message = " Password should not be blank")
@Pattern(regexp = "^.*(?=.{6,})(?=.*d)(?=.*[A-Z])(?=.*[a-z])(?=.*[!@#$%^&*? ]).*$", message = "Invalid password format")
private String password;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,21 @@
package org.ros.chatto.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.Getter;
import lombok.Setter;
@Entity
@Getter
@Setter
@Table(name="status")
public class ApplicationStatus {
@Id
private int id;
private String name;
@Column(name="value")
private boolean done;
}

View File

@ -20,7 +20,7 @@ this is what the json will look like*/
@Entity
@Table(name = "message_ciphers")
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = { "id"}, allowGetters = false)
//@JsonIgnoreProperties(value = { "id"}, allowGetters = false)
public class MessageCipher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@ -1,9 +1,11 @@
package org.ros.chatto.repository;
import java.awt.print.Pageable;
import java.util.Date;
import java.util.List;
import org.ros.chatto.model.ChatMessage;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
@ -22,4 +24,14 @@ public interface ChatMessageRepository extends JpaRepository<ChatMessage, Long>
+ "(m.fromUser.userName = ?1 or m.fromUser.userName = ?2) and"
+ "(m.messageTime > ?3) order by m.messageTime asc")
public List<ChatMessage> getNewMessages(String fromUser, String toUser, Date lastMessageTime);
@Query("select m from ChatMessage m where (m.toUser.userName = ?1 or m.toUser.userName = ?2) and "
+ "(m.fromUser.userName = ?1 or m.fromUser.userName = ?2) order by m.messageTime asc")
public List<ChatMessage> getAllMessages(String fromUser, String toUser, PageRequest pageRequest);
// DELETE FROM Country c WHERE c.population < :p
// @Query("delete from ChatMessage m where where (m.toUser.userName = ?1 or m.toUser.userName = ?2) and"
// + " (m.fromUser.userName = ?1 or m.fromUser.userName = ?2)")
// public void deleteConversation(String fromUser, String toUser);
}

View File

@ -14,4 +14,7 @@ public interface UserRoleRepository extends JpaRepository<UserRole, Long>{
@Query("select ur from UserRole ur where ur.user.userName = ?1")
public List<UserRole> findByUser(String username);
@Query("select ur.user.userName from UserRole ur where ur.role.roleID = 2")
public List<String> getAllRegularUser();
}

View File

@ -4,14 +4,23 @@ import java.util.Date;
import java.util.List;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.model.MessageCipher;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.ChatMessage;
import org.springframework.data.domain.PageRequest;
public interface ChatService {
void saveNewMessage(String fromUser, String toUser , MessageCipher messageCipher);
public void saveNewMessage(String fromUser, String toUser, MessageCipherDTO messageCipherDTO);
List<ChatMessageDTO> getAllMessages(String fromUser, String toUser);
public List<ChatMessageDTO> getAllMessages(String fromUser, String toUser);
List<ChatMessageDTO> getMessagePage(int page, int size);
public List<ChatMessageDTO> getMessagePage(String fromUser, String toUser, int page, int size);
List<ChatMessageDTO> getNewMessages(String fromUser, String toUser, Date lastMessageTime);
public List<ChatMessageDTO> getNewMessages(String fromUser, String toUser, Date lastMessageTime);
public void reencryptMessages(List<ReencryptionDTO> reencryptionDTOs);
public List<ReencryptionDTO> getAllMessagesForReencryption(String fromUser, String toUser);
public List<ChatMessageDTO> getAllMessages(String name, String userName, PageRequest pageRequest);
}

View File

@ -2,8 +2,13 @@ package org.ros.chatto.service;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.ChatMessage;
import org.ros.chatto.model.ChatUser;
import org.ros.chatto.model.MessageCipher;
@ -29,8 +34,9 @@ public class ChatServiceImpl implements ChatService {
@Autowired
MyConversionService myConversionService;
public void saveNewMessage(String fromUserName, String toUserName, MessageCipher messageCipher) {
public void saveNewMessage(String fromUserName, String toUserName, MessageCipherDTO messageCipherDTO) {
MessageCipher messageCipher = myConversionService.convertToMessageCipher(messageCipherDTO);
ChatUser fromUser = userRepository.findByUserName(fromUserName);
ChatUser toUser = userRepository.findByUserName(toUserName);
ChatMessage chatMessage = new ChatMessage();
@ -51,11 +57,17 @@ public class ChatServiceImpl implements ChatService {
}
@Override
public List<ChatMessageDTO> getMessagePage(int page, int size) {
public List<ReencryptionDTO> getAllMessagesForReencryption(String fromUser, String toUser) {
return myConversionService.convertToReencryptionDTOs(chatMessageRepository.getAllMessages(fromUser, toUser));
}
@Override
public List<ChatMessageDTO> getMessagePage(String fromUser, String toUser, int page, int size) {
// Sort sort = Sort
Page<ChatMessage> chatMessages = chatMessageRepository.findAll(PageRequest.of(page, size));
List<ChatMessageDTO> chatMessageDTOs = myConversionService.convertToChatMessageDTOs(chatMessages);
return chatMessageDTOs;
// Page<ChatMessage> chatMessages = chatMessageRepository.getAllMessages(fromUser, toUser,PageRequest.of(page, size));
// List<ChatMessageDTO> chatMessageDTOs = myConversionService.convertToChatMessageDTOs(chatMessages);
// return chatMessageDTOs;
return myConversionService.convertToChatMessageDTOs(chatMessageRepository.getAllMessages(fromUser, toUser,PageRequest.of(page, size)));
}
@Override
@ -65,4 +77,20 @@ public class ChatServiceImpl implements ChatService {
// List<ChatMessageDTO> chatMessageDTOs
return myConversionService.convertToChatMessageDTOs(chatMessages);
}
@Override
@Transactional
public void reencryptMessages(List<ReencryptionDTO> reencryptionDTOs) {
// TODO Auto-generated method stub
// chatMessageRepository.saveAll(chatMessages);
List<MessageCipher> messageCiphers = reencryptionDTOs.stream().map(reencryptionDTO -> reencryptionDTO.getMessageCipher()).collect(Collectors.toList());
messageCipherRepository.saveAll(messageCiphers);
}
@Override
public List<ChatMessageDTO> getAllMessages(String name, String userName, PageRequest pageRequest) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -2,7 +2,6 @@ package org.ros.chatto.service;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
@ -10,9 +9,15 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.ros.chatto.ChattoApplication;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.ros.chatto.model.ApplicationStatus;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.PropertySource;
@ -51,6 +56,12 @@ public class DBInitializerService {
private Connection connection;
@PersistenceContext
EntityManager entityManager;
private final String tablesCreatedKey = "tables_created";
private final String rolesPopulatedKey = "roles_populated";
// public DBInitializerService(Connection connection) {
// this.connection = connection;
// // TODO Auto-generated constructor stub
@ -125,9 +136,29 @@ public class DBInitializerService {
@EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() throws SQLException, IOException {
// setProperties();
System.out.println("Hello world, I have just started up");
System.out.println("Initializing database");
List<ApplicationStatus> applicationStatusList = getStatusList();
Map<String, Boolean> statusMap = listToMap(applicationStatusList);
// applicationStatus.
connectDB();
/*
* if (statusMap.get(tablesCreatedKey) == null ||
* !statusMap.get(tablesCreatedKey)) {
* System.out.println("Initializing database"); if (getNumTables() == 0) {
* populateDB(); System.out.println("Tables created"); } ApplicationStatus
* status = new ApplicationStatus(); status.setName(tablesCreatedKey);
* status.setDone(true);
*
* }
*
* if (statusMap.get(rolesPopulatedKey) == null ||
* !statusMap.get(rolesPopulatedKey)) { System.out.println("Populating roles");
* }
*/
if (getNumTables() == 0)
populateDB();
closeConnection();
@ -191,6 +222,23 @@ public class DBInitializerService {
// prop.store(object, comments);
}
List<ApplicationStatus> getStatusList() {
// List<Object[]> persons = entityManager.createNativeQuery("SELECT * FROM Person" ).getResultList();
List<ApplicationStatus> applicationStatus = entityManager
.createQuery("from ApplicationStatus s", ApplicationStatus.class).getResultList();
applicationStatus.stream().forEach(status -> status.getName());
// System.out.println(applicationStatus.get(0).getName() + applicationStatus.get(0).isDone());
return applicationStatus;
}
Map<String, Boolean> listToMap(List<ApplicationStatus> applicationStatusList) {
Map<String, Boolean> statusMap = new HashMap<>();
for (ApplicationStatus applicationStatus : applicationStatusList) {
statusMap.put(applicationStatus.getName(), applicationStatus.isDone());
}
return statusMap;
}
public void closeConnection() throws SQLException {
connection.close();
}

View File

@ -5,7 +5,10 @@ import java.util.stream.Collectors;
import org.modelmapper.ModelMapper;
import org.ros.chatto.dto.ChatMessageDTO;
import org.ros.chatto.dto.MessageCipherDTO;
import org.ros.chatto.dto.ReencryptionDTO;
import org.ros.chatto.model.ChatMessage;
import org.ros.chatto.model.MessageCipher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
@ -42,6 +45,18 @@ public class MyConversionService {
return chatMessageDTO;
}
public ChatMessage convertToChatMessage(ChatMessageDTO chatMessageDTO)
{
ChatMessage chatMessage = modelMapper.map(chatMessageDTO, ChatMessage.class);
return chatMessage;
}
public MessageCipher convertToMessageCipher(MessageCipherDTO messageCipherDTO)
{
MessageCipher messageCipher = modelMapper.map(messageCipherDTO, MessageCipher.class);
return messageCipher;
}
public List<ChatMessageDTO> convertToChatMessageDTOs(List<ChatMessage> chatMessages)
{
return chatMessages.stream()
@ -49,6 +64,33 @@ public class MyConversionService {
.collect(Collectors.toList());
}
public ReencryptionDTO convertToReencryptionDTO(ChatMessage chatMessage)
{
ReencryptionDTO reencryptionDTO = modelMapper.map(chatMessage, ReencryptionDTO.class);
return reencryptionDTO;
}
public ChatMessage convertToChatMessage(ReencryptionDTO reencryptionDTO)
{
ChatMessage chatMessage = modelMapper.map(reencryptionDTO, ChatMessage.class);
return chatMessage;
}
public List<ReencryptionDTO> convertToReencryptionDTOs(List<ChatMessage> chatMessages)
{
return chatMessages.stream()
.map(chatMessage -> convertToReencryptionDTO(chatMessage))
.collect(Collectors.toList());
}
public Iterable<ChatMessage> convertoToChatMessages(List<ChatMessageDTO> chatMessageDTOs)
{
return chatMessageDTOs.stream()
.map(chatMessageDTO -> convertToChatMessage(chatMessageDTO)).collect(Collectors.toList());
}
public List<ChatMessageDTO> convertToChatMessageDTOs(Page<ChatMessage> chatMessages)
{
return chatMessages.stream()

View File

@ -11,4 +11,5 @@ public interface UserService {
public void saveChatUser(ChatUser user);
public List<String> findAllOtherUsers(String userName);
public void registerUser(UserRegistrationDTO userRegistrationDTO);
public List<String> getAllRegularUsers();
}

View File

@ -62,4 +62,10 @@ public class UserServiceImpl implements UserService{
return userRepositoryCustom.getAllUserNames(userName);
}
@Override
public List<String> getAllRegularUsers() {
// TODO Auto-generated method stub
return userRoleRepository.getAllRegularUser();
}
}

View File

@ -1 +1,2 @@
num-tables = SELECT COUNT(*) as num_tables FROM information_schema.tables WHERE table_schema = ? and TABLE_TYPE='BASE TABLE'
#tables-created =

View File

@ -0,0 +1,135 @@
console.log('Hello world!');
var getAllMessagesURL = `http://${hostAddress}/api/admin/get/messages/`; //hostAddress set in thymeleaf backend
var reencryptURL = `http://${hostAddress}/api/admin/post/re-encrypt`;
var getAllRegularUsersURL = `http://${hostAddress}/api/regular-users`;
var username = sessionStorage.getItem('username');
var password = sessionStorage.getItem('password');
var authToken = 'Basic ' + btoa(username + ":" + password);
var iterations = 10000;
function handleAddToAdminForm() {
document.getElementById('addUserToAdminForm').addEventListener(
'submit',
function(e) {
e.preventDefault();
getAllRegularUsers()
// .then(usernamesArray => {
// console.lo
// });
});
}
function handleChangePassphraseForm() {
document.getElementById('changePassphraseForm').addEventListener(
'submit',
function(e) {
e.preventDefault();
let changePassphraseDropDown = document.getElementById('changePassphraseDropDown');
let user = changePassphraseDropDown.value;
let passphraseOld = document.getElementById('passphraseOld');
let passphraseNew = document.getElementById('passphraseNew');
// let messageCipherNew = {};
console.log(user);
getAllMessages(user)
.then(json => {
console.log(json);
return json;
})
.then(json => {
let jsonNew = [];
let messageCiphers = [];
let chatMessageDTOs = [];
if (json.length > 0) {
json.forEach(function(obj) {
let newObj = obj;
let messageID = obj.messageCipher.id;
let plainText = sjcl.decrypt(passphraseOld.value, JSON.stringify(obj.messageCipher));
let messageCipherNew = sjcl.encrypt(passphraseNew.value, plainText, { mode: "gcm", ts: 128, adata: "", iter: iterations });
// let plainText = sjcl.decrypt("password", JSON.stringify(obj.messageCipher));
// let messageCipherNew = sjcl.encrypt("password2", plainText, { mode: "gcm", ts: 128, adata: "", iter: iterations });
// console.log(messageCipherNew)
let messageCipherNewObj = JSON.parse(messageCipherNew);
// console.log(messageCipherNewObj);
messageCipherNewObj.id = messageID;
newObj.messageCipher = messageCipherNewObj;
// obj.messageCipher = messageCipherNewObj;
// console.log(obj.messageCipher);
// console.log(plainText);
// console.log(messageCipherNewObj);
jsonNew.push(newObj);
messageCiphers.push(messageCipherNewObj);
// let messageCipherJson = JSON.stringify(messageCipherNewObj);
let fromUser = sessionStorage.getItem('username');
let chatMessageDTO = {
"toUser": user,
"fromUser": username,
"messageCipher": messageCipherNewObj
}
chatMessageDTOs.push(chatMessageDTO);
});
// console.log(jsonNew);
}
// sendReencryptedMessages(JSON.stringify(jsonNew));
console.log
sendReencryptedMessages(JSON.stringify(chatMessageDTOs));
// sendReencryptedMessages(JSON.stringify(messageCiphers));
return jsonNew;
})
.then(json => {
json.forEach(function(obj) {
let plainText = sjcl.decrypt("password2", JSON.stringify(obj.messageCipher));
console.log(plainText);
})
});
});
}
async function getAllMessages(user) {
let headers = new Headers();
// headers.append('Accept','application/json')
// headers.append('Content-Type', 'application/json');
headers.append('Authorization', authToken);
let response = await fetch(`${getAllMessagesURL}${user}`, {
method: 'GET',
headers: headers
});
let data = await response.json();
return data;
}
async function getAllRegularUsers() {
let headers = new Headers();
// headers.append('Accept','application/json')
// headers.append('Content-Type', 'application/json');
headers.append('Authorization', authToken);
let response = await fetch(`${getAllRegularUsersURL}`, {
method: 'GET',
headers: headers
});
let data = await response.json();
return data;
}
function sendReencryptedMessages(chatMessageDTOs) {
let headers = new Headers();
// console.log("Token = " + btoa("hmm" + ":" + "hmm"))
// headers.append('Accept','application/json')
headers.append('Content-Type', 'application/json');
headers.append('Authorization', authToken);
fetch(reencryptURL, {
method: 'POST',
headers: headers,
body: chatMessageDTOs
})
.then(response => console.log(response));
}
handleAddToAdminForm();
handleChangePassphraseForm();

View File

@ -52,10 +52,20 @@ function getSelectedUser() {
function handleChatForm() {
let chatInput = document.getElementById('chatInput');
let myForm = document.getElementById('chatMessageForm').addEventListener(
'submit', function (e) {
let myForm = document.getElementById('chatMessageForm');
myForm.addEventListener(
'submit',
function(e) {
e.preventDefault();
let user = getSelectedUser();
if (!myForm.checkValidity()) {
console.log("error");
myForm.classList.add('was-validated');
return;
}
myForm.classList.add('was-validated');
if (!isCheckedUser) {
window.alert('please select a user');
return;
@ -235,8 +245,7 @@ parent.addDelegatedListener("click", "input[type='radio']", function (event) {
}
});
}
else {
} else {
console.log("Stored messages = " + sessionStorage.getItem(this.value));
@ -302,4 +311,3 @@ parent.addDelegatedListener("click", "input[type='radio']", function (event) {
handleChatForm();

View File

@ -1,13 +1,110 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<div th:replace="fragments/head :: headFragment">
<title id="pageTitle">Admin Home</title>
</div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script>
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" data-template-prefix="../" defer="defer" th:if="false"></script>
<script th:src="@{js/admin.js}" src="../../static/js/admin.js" defer="defer"></script>
<link th:href="@{/css/master.css}" href="../../static/css/master.css" rel="stylesheet" th:if="false">
<link th:href="@{/css/colors.css}" href="../../static/css/colors.css" rel="stylesheet" th:if="false">
</head>
<!-- TODO
Make user admin / remove user from admin
Change E2E passphrase
Delete Messages
-->
<body>
admin page
<form action="#" th:action="@{/perform_logout}" method="POST">
<input type="submit" value="logout">
<div th:include="fragments/navbar :: navbarFragment"></div>
<header>
<div class="container bg-primary">
<div class="row">
<div class="col-sm py-5">
<h1 class="display-4 text-center">Admin Page</h1>
<p class="alert-danger px-2">Warning: these settings can be dangerous..</p>
</div>
</div>
<div class="row">
<div class="col-sm col-md-4">
<h4>Make User an Admin</h4>
<form id="addUserToAdminForm">
<div class="form-group">
<label for="addUserToDropDown">Select User:</label>
<select class="form-control custom-select" size="4" id="addUserToAdminDropDown">
<option th:each="userName : ${userNames}"
th:value="${userName}"
th:text="#{${userName}}">
Wireframe
</option>
</select>
</div>
<div class="form-group">
<button class="btn btn-danger form-control">Make admin</button>
</div>
</form>
</div>
<div class="col-sm col-md-4">
<h4>Change passphrases</h4>
<form id="changePassphraseForm">
<div class="form-group">
<label for="changePassphraseDropDown">Select User:</label>
<select class="form-control" id="changePassphraseDropDown">
<option th:each="userName : ${userNames}"
th:value="${userName}"
th:text="#{${userName}}">
Wireframe
</option>
</select>
</div>
<div class="form-group">
<label for="passphraseOld">Passphrase Old</label>
<input type="password" id="passphraseOld" class="form-control">
</div>
<div class="form-group">
<label for="passphraseNew">Passphrase New</label>
<input type="password" id="passphraseNew" class="form-control">
</div>
<div class="form-group">
<button class="btn btn-danger form-control">Change Passphrase</button>
</div>
</form>
</div>
</div>
<div class="row">
<!-- <div class="col-sm"></div> -->
<div class="col-sm d-lg-block">
<div class="d-flex justify-content-center">
<div class="py-5">
<h4 class="p-2 text-center">Logout</h4>
<form action="#" th:action="@{/logout}" method="POST">
<!-- <input type="submit" value="logout"> -->
<!-- <input type="hidden" th:name="${_csrf.parameterName}"
th:value="${_csrf.token}" /> -->
<div class="form-group">
<button class="btn btn-secondary form-control">Logout</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</header>
</body>
</html>

View File

@ -45,7 +45,7 @@
<div class="row">
<div class="col-sm">
<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">
<div class="card text-white bg-primary mb-3 card-form rounded mx-auto">
<div class="card-body rounded">
<!-- <h4 class="card-title">Chat</h4> -->
@ -57,7 +57,7 @@
<div class="card-text">
</div>
<form action="#" th:object="${chatMessageDTO}" method="post" id="chatMessageForm">
<form action="#" th:object="${chatMessageDTO}" method="post" id="chatMessageForm" class="needs-validation" novalidate>
<div class="row">
<div class="col-3">
<div class="form-group">
@ -74,14 +74,18 @@
<div class="my-form-inputs container">
<div class="form-group">
<label for="chatInput">Your message: </label>
<textarea class="form-control" type="text" id="chatInput"></textarea>
<textarea class="form-control" type="text" id="chatInput" required></textarea>
<div class="invalid-feedback">
Cannot be empty
</div>
</div>
<div class="form-group">
<label for="passphrase">Passphrase: </label>
<input class="form-control" type="password" id="passphrase">
<input class="form-control" type="password" id="passphrase" required>
</div>
<div class="form-group">
<input class="form-control btn btn-secondary" type="submit" value="Send">
<button class="btn btn-secondary">Submit</button>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<div th:replace="fragments/head :: headFragment">
@ -34,13 +34,16 @@
<a href="home.html" th:href="@{/}" class="nav-link">Home</a>
</li>
<li class="nav-item">
<a href="user/home.html" th:href="@{/user}" class="nav-link">User Area</a>
<a href="user/home.html" sec:authorize="isFullyAuthenticated()" th:href="@{/user}" class="nav-link">User Area</a>
</li>
<li class="nav-item">
<a th:href="chat" href="chat.html" class="nav-link">Chat</a>
</li>
<li class="nav-item">
<a th:href="login" href="login.html" class="nav-link">Login</a>
<a th:href="login" sec:authorize="!isFullyAuthenticated()" href="login.html" class="nav-link">Login</a>
</li>
<li class="nav-item">
<a th:href="registration" sec:authorize="!isFullyAuthenticated()" href="registration.html" class="nav-link">Register</a>
</li>
<li class="nav-item">
<a href="#" class="nav-link">About</a>
@ -48,7 +51,16 @@
<li class="nav-item">
<a href="#" class="nav-link">Contact</a>
</li>
<li class="nav-item">
<a href="#" sec:authorize="isFullyAuthenticated()" th:href="@{/admin}" class="nav-link">
Admin Area
</a>
</li>
<li class="nav-item">
<a href="#" sec:authorize="isFullyAuthenticated()" th:text="${#authentication.name}" class="nav-link text-white font-weight-bold">
nova
</a>
</li>
</ul>
</div>
</div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<div th:replace="fragments/head :: headFragment">
@ -35,7 +35,7 @@
<th:block th:each="userName: ${userNames}">
<div th:text="${userName}"></div>
</th:block> -->
<span th:if="${message}"> Welcome <span th:text="${message}"></span></span>
<span th:if="${message}"> </span> Welcome <span th:text="${#authentication.name}">nova</span>
<p>Chatto is a minimal, end to end encrypted chat application.</p>
<!-- <button class="btn btn-secondary"> <a href="registration.html" th:href="{@/registration}">Get Started</a></button> -->
<a class="btn btn-secondary" href="registration.html" th:href="@{/registration}">Get Started</a>
@ -53,12 +53,14 @@
<li>
<p class="lead">
<!-- <i class="fas fa-check"></i> -->
Self Hosted</p>
Self Hosted
</p>
</li>
<li>
<p class="lead">
<!-- <i class="fas fa-check"></i> -->
End To End Encrypted Messaging</p>
End To End Encrypted Messaging
</p>
</li>
<li>
<p class="lead">
@ -68,7 +70,8 @@
<li>
<p class="lead">
<!-- <i class="fas fa-check"></i> -->
Built With Java And Spring</p>
Built With Java And Spring
</p>
</li>
</ul>
</p>

View File

@ -55,7 +55,7 @@
<legend>Please Sign In</legend> -->
<h2 class="card-title">Please Sign In</h2>
<div th:if="${param.error}" class="alert alert-danger">
Invalid username or password.
Error signing in. Please check your username and password.
</div>
<div th:if="${param.logout}" class="alert alert-success">
You have been logged out.

View File

@ -1,18 +1,73 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<div th:replace="fragments/head :: headFragment">
<title id="pageTitle">Registration</title>
</div>
<script src="https://code.jquery.com/jquery-2.1.4.min.js" th:if="false"></script>
<script src="http://blackpeppersoftware.github.io/thymeleaf-fragment.js/thymeleaf-fragment.js" defer="defer" th:if="false"></script>
</head>
<body>
<form action="#" th:action="@{/perform_registration}"
th:object=${userDTO} method="POST">
<label>Enter user name: </label> <input th:field="*{userName}"
type="text" name="username" id="username"> <br> <br>
<label>Enter password: </label> <input th:field="*{password}"
type="password" name="password" id="password"> <br> <br>
<input type="password" id="password-repeat">
<input type="submit" value="Submit">
<div th:include="fragments/navbar :: navbarFragment"></div>
<header>
<div class="container">
<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-body rounded">
<!-- <h4 class="card-title">Chat</h4> -->
<!-- <div class="form-group">
<textarea id="chatTextArea" class="form-control-lg py-2" disabled></textarea>
</div> -->
<!-- <form action="#" th:action="@{/seedstartermng}" th:object="${seedStarter}" method="post"> -->
<!-- th:action="@{/api/chat}" -->
<!-- <label>First Name</label>
<div th:classappend="${#fields.hasErrors('firstName')} ? 'input-icon right' : ''">
<i th:if="${#fields.hasErrors('firstName')}" class="fa fa-exclamation tooltips" data-original-title="please enter a valid first name" data-container="body"></i>
<input type="text" class="form-control" maxlength="32" th:field="*{firstName}" placeholder="Between 1 and 32 characters" />
<span th:if="${#fields.hasErrors('firstName')}" class="help-block" th:errors="*{firstName}"></span>
</div> -->
<div class="card-text">
<h2 class="card-title">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>
</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>
</div>
<div class="form-group">
<label for="password-repeat">Repeat password: </label>
<input class="form-control" type="password" id="password-repeat" required>
</div>
<div class="form-group">
<input class="form-control btn btn-secondary" type="submit" value="Submit">
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
</body>
</html>