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> <artifactId>cache-api</artifactId>
</dependency> </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> <dependency>
<groupId>com.sun.xml.bind</groupId> <groupId>javax.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId> <artifactId>jaxb-api</artifactId>
<version>2.2.11</version> </dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency> </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> <dependency>
<groupId>org.thymeleaf.extras</groupId> <groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId> <artifactId>thymeleaf-extras-springsecurity5</artifactId>
@ -124,6 +126,7 @@
<!-- <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId> <!-- <dependency> <groupId>javax.cache</groupId> <artifactId>cache-api</artifactId>
<version>1.1.0</version> </dependency> --> <version>1.1.0</version> </dependency> -->
</dependencies> </dependencies>
<build> <build>

View File

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

View File

@ -1,14 +1,25 @@
package org.ros.chatto.controller; 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.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller @Controller
@RequestMapping("/admin") @RequestMapping("/admin")
public class AdminController { public class AdminController {
@RequestMapping @Autowired
public String viewManageUsers() { private UserService userService;
return "/admin/home";
} @RequestMapping
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 java.util.List;
import org.ros.chatto.dto.ChatMessageDTO; 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.model.MessageCipher;
import org.ros.chatto.service.ChatService; import org.ros.chatto.service.ChatService;
import org.ros.chatto.service.UserService; 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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; 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.ResponseBody;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@ -27,14 +30,14 @@ import org.springframework.web.bind.annotation.RestController;
public class ChatMessageController { public class ChatMessageController {
@Autowired @Autowired
private ChatService chatService; private ChatService chatService;
@Autowired @Autowired
private UserService userService; private UserService userService;
@PostMapping(value = "/post/message", consumes = { "application/json" }) @PostMapping(value = "/post/message", consumes = { "application/json" })
@ResponseBody @ResponseBody
public ResponseEntity<ChatMessageDTO> newMessage(@RequestBody ChatMessageDTO chatMessageDTO, Principal principal) { public ResponseEntity<ChatMessageDTO> newMessage(@RequestBody ChatMessageDTO chatMessageDTO, Principal principal) {
MessageCipher messageCipher = chatMessageDTO.getMessageCipher(); MessageCipherDTO messageCipher = chatMessageDTO.getMessageCipher();
String fromUser = principal.getName(); String fromUser = principal.getName();
String toUser = chatMessageDTO.getToUser(); String toUser = chatMessageDTO.getToUser();
System.out.println("Message cipher = " + messageCipher); System.out.println("Message cipher = " + messageCipher);
@ -46,31 +49,41 @@ public class ChatMessageController {
@ResponseBody @ResponseBody
public List<ChatMessageDTO> sendAllMessages(@PathVariable String userName, Principal principal) { public List<ChatMessageDTO> sendAllMessages(@PathVariable String userName, Principal principal) {
List<ChatMessageDTO> chatMessageDTOs = chatService.getAllMessages(principal.getName(), userName); List<ChatMessageDTO> chatMessageDTOs = chatService.getAllMessages(principal.getName(), userName);
return chatMessageDTOs; 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}") @GetMapping(value = "/get/messages/{userName}/{lastMessageTime}")
@ResponseBody @ResponseBody
public List<ChatMessageDTO> sendNewMessages(@PathVariable String userName, @PathVariable String lastMessageTime, Principal principal) { public List<ChatMessageDTO> sendNewMessages(@PathVariable String userName, @PathVariable String lastMessageTime,
System.out.println("Last message time = " + lastMessageTime ); Principal principal) {
System.out.println("Last message time = " + lastMessageTime);
DateTimeFormatter formatter = new DateTimeFormatterBuilder() DateTimeFormatter formatter = new DateTimeFormatterBuilder()
// date/time // date/time
.append(DateTimeFormatter.ISO_LOCAL_DATE_TIME) .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
// offset (hh:mm - "+00:00" when it's zero) // offset (hh:mm - "+00:00" when it's zero)
.optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd() .optionalStart().appendOffset("+HH:MM", "+00:00").optionalEnd()
// offset (hhmm - "+0000" when it's zero) // offset (hhmm - "+0000" when it's zero)
.optionalStart().appendOffset("+HHMM", "+0000").optionalEnd() .optionalStart().appendOffset("+HHMM", "+0000").optionalEnd()
// offset (hh - "Z" when it's zero) // offset (hh - "Z" when it's zero)
.optionalStart().appendOffset("+HH", "Z").optionalEnd() .optionalStart().appendOffset("+HH", "Z").optionalEnd()
// create formatter // create formatter
.toFormatter(); .toFormatter();
Date date = Date.from(OffsetDateTime.parse(lastMessageTime, formatter).toInstant()); Date date = Date.from(OffsetDateTime.parse(lastMessageTime, formatter).toInstant());
List<ChatMessageDTO> chatMessageDTOs = chatService.getNewMessages(principal.getName(), userName, date); List<ChatMessageDTO> chatMessageDTOs = chatService.getNewMessages(principal.getName(), userName, date);
return chatMessageDTOs; return chatMessageDTOs;
} }
@GetMapping("/get/users") @GetMapping("/get/users")
public List<String> getAllOtherUsers(Principal principal) public List<String> getAllOtherUsers(Principal principal) {
{
return userService.findAllOtherUsers(principal.getName()); return userService.findAllOtherUsers(principal.getName());
} }
} }

View File

@ -62,31 +62,33 @@ public class DemoRestController {
public ChatUser getUser() { public ChatUser getUser() {
return userRepository.findByUserName("hmm"); return userRepository.findByUserName("hmm");
} }
@GetMapping("/user") @GetMapping("/user")
public ChatUser currentUserName(Principal principal) { public ChatUser currentUserName(Principal principal) {
ChatUser user = userRepository.findByUserName(principal.getName()); ChatUser user = userRepository.findByUserName(principal.getName());
return user; return user;
} }
@GetMapping("/roles") @GetMapping("/roles")
public List<UserRole> getAllRoles() public List<UserRole> getAllRoles() {
{
return userRoleRepository.findAll(); return userRoleRepository.findAll();
} }
@GetMapping("/ciphers") @GetMapping("/ciphers")
public List<MessageCipher> getAllCiphers() public List<MessageCipher> getAllCiphers() {
{
return messageCipherRepository.findAll(); return messageCipherRepository.findAll();
} }
@GetMapping("/messages") @GetMapping("/messages")
public List<ChatMessage> getAllMessages() public List<ChatMessage> getAllMessages() {
{
return chatMessageRepository.findAll(); return chatMessageRepository.findAll();
} }
@GetMapping("/regular-users")
public List<String> getAllRegularUsers() {
return userRoleRepository.getAllRegularUser();
}
// @RequestMapping(value = "/", method = RequestMethod.POST) // @RequestMapping(value = "/", method = RequestMethod.POST)
// public ResponseEntity<Car> update(@RequestBody Car car) { // public ResponseEntity<Car> update(@RequestBody Car car) {
// //
@ -97,28 +99,27 @@ public class DemoRestController {
// // TODO: call persistence layer to update // // TODO: call persistence layer to update
// return new ResponseEntity<Car>(car, HttpStatus.OK); // return new ResponseEntity<Car>(car, HttpStatus.OK);
// } // }
@PostMapping(value="/post-message", consumes = {"application/json"}) @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); System.out.println("Message cipher = " + messageCipher);
messageCipherRepository.save(messageCipher); messageCipherRepository.save(messageCipher);
return new ResponseEntity<MessageCipher>(HttpStatus.OK); return new ResponseEntity<MessageCipher>(HttpStatus.OK);
} }
@GetMapping("/logout") @GetMapping("/logout")
public ModelAndView logoutPage() public ModelAndView logoutPage() {
{
ModelAndView modelAndView = new ModelAndView("restLogout"); ModelAndView modelAndView = new ModelAndView("restLogout");
return modelAndView; return modelAndView;
} }
@RequestMapping(value="perform_logout", method = RequestMethod.POST)
public String performLogout (HttpServletRequest request, HttpServletResponse response) { @RequestMapping(value = "perform_logout", method = RequestMethod.POST)
Authentication auth = SecurityContextHolder.getContext().getAuthentication(); public String performLogout(HttpServletRequest request, HttpServletResponse response) {
if (auth != null){ Authentication auth = SecurityContextHolder.getContext().getAuthentication();
new SecurityContextLogoutHandler().logout(request, response, auth); if (auth != null) {
} new SecurityContextLogoutHandler().logout(request, response, auth);
return "redirect:/users"; }
return "redirect:/users";
} }
} }

View File

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

View File

@ -9,6 +9,6 @@ import lombok.Data;
@Data @Data
public class ChatMessageDTO { public class ChatMessageDTO {
private String toUser, fromUser; private String toUser, fromUser;
private MessageCipher messageCipher; private MessageCipherDTO messageCipher;
private Date messageTime; 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; package org.ros.chatto.dto;
import javax.persistence.Transient; import javax.persistence.Transient;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
public class UserRegistrationDTO { 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; private String userName;
@Transient @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; private String password;
public String getUserName() { public String getUserName() {
return userName; return userName;
} }
public void setUserName(String userName) { public void setUserName(String userName) {
this.userName = userName; this.userName = userName;
} }
public String getPassword() { public String getPassword() {
return password; return password;
} }
public void setPassword(String password) { public void setPassword(String password) {
this.password = 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 @Entity
@Table(name = "message_ciphers") @Table(name = "message_ciphers")
@EntityListeners(AuditingEntityListener.class) @EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = { "id"}, allowGetters = false) //@JsonIgnoreProperties(value = { "id"}, allowGetters = false)
public class MessageCipher { public class MessageCipher {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)

View File

@ -1,9 +1,11 @@
package org.ros.chatto.repository; package org.ros.chatto.repository;
import java.awt.print.Pageable;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.ros.chatto.model.ChatMessage; 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.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository; 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.fromUser.userName = ?1 or m.fromUser.userName = ?2) and"
+ "(m.messageTime > ?3) order by m.messageTime asc") + "(m.messageTime > ?3) order by m.messageTime asc")
public List<ChatMessage> getNewMessages(String fromUser, String toUser, Date lastMessageTime); 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") @Query("select ur from UserRole ur where ur.user.userName = ?1")
public List<UserRole> findByUser(String username); 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 java.util.List;
import org.ros.chatto.dto.ChatMessageDTO; 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 { 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.Date;
import java.util.List; import java.util.List;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
import org.ros.chatto.dto.ChatMessageDTO; 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.ChatMessage;
import org.ros.chatto.model.ChatUser; import org.ros.chatto.model.ChatUser;
import org.ros.chatto.model.MessageCipher; import org.ros.chatto.model.MessageCipher;
@ -29,8 +34,9 @@ public class ChatServiceImpl implements ChatService {
@Autowired @Autowired
MyConversionService myConversionService; 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 fromUser = userRepository.findByUserName(fromUserName);
ChatUser toUser = userRepository.findByUserName(toUserName); ChatUser toUser = userRepository.findByUserName(toUserName);
ChatMessage chatMessage = new ChatMessage(); ChatMessage chatMessage = new ChatMessage();
@ -49,13 +55,19 @@ public class ChatServiceImpl implements ChatService {
List<ChatMessageDTO> chatMessageDTOs = myConversionService.convertToChatMessageDTOs(chatMessages); List<ChatMessageDTO> chatMessageDTOs = myConversionService.convertToChatMessageDTOs(chatMessages);
return chatMessageDTOs; return chatMessageDTOs;
} }
@Override
public List<ReencryptionDTO> getAllMessagesForReencryption(String fromUser, String toUser) {
return myConversionService.convertToReencryptionDTOs(chatMessageRepository.getAllMessages(fromUser, toUser));
}
@Override @Override
public List<ChatMessageDTO> getMessagePage(int page, int size) { public List<ChatMessageDTO> getMessagePage(String fromUser, String toUser, int page, int size) {
// Sort sort = Sort // Sort sort = Sort
Page<ChatMessage> chatMessages = chatMessageRepository.findAll(PageRequest.of(page, size)); // Page<ChatMessage> chatMessages = chatMessageRepository.getAllMessages(fromUser, toUser,PageRequest.of(page, size));
List<ChatMessageDTO> chatMessageDTOs = myConversionService.convertToChatMessageDTOs(chatMessages); // List<ChatMessageDTO> chatMessageDTOs = myConversionService.convertToChatMessageDTOs(chatMessages);
return chatMessageDTOs; // return chatMessageDTOs;
return myConversionService.convertToChatMessageDTOs(chatMessageRepository.getAllMessages(fromUser, toUser,PageRequest.of(page, size)));
} }
@Override @Override
@ -65,4 +77,20 @@ public class ChatServiceImpl implements ChatService {
// List<ChatMessageDTO> chatMessageDTOs // List<ChatMessageDTO> chatMessageDTOs
return myConversionService.convertToChatMessageDTOs(chatMessages); 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.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.sql.Connection; import java.sql.Connection;
@ -10,9 +9,15 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties; 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.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.PropertySource;
@ -51,6 +56,12 @@ public class DBInitializerService {
private Connection connection; private Connection connection;
@PersistenceContext
EntityManager entityManager;
private final String tablesCreatedKey = "tables_created";
private final String rolesPopulatedKey = "roles_populated";
// public DBInitializerService(Connection connection) { // public DBInitializerService(Connection connection) {
// this.connection = connection; // this.connection = connection;
// // TODO Auto-generated constructor stub // // TODO Auto-generated constructor stub
@ -125,9 +136,29 @@ public class DBInitializerService {
@EventListener(ApplicationReadyEvent.class) @EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup() throws SQLException, IOException { public void doSomethingAfterStartup() throws SQLException, IOException {
// setProperties(); // setProperties();
System.out.println("Hello world, I have just started up"); 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(); 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) if (getNumTables() == 0)
populateDB(); populateDB();
closeConnection(); closeConnection();
@ -163,7 +194,7 @@ public class DBInitializerService {
public void setProperties() throws IOException { public void setProperties() throws IOException {
// InputStream input = ChattoApplication.class.getClassLoader().getResourceAsStream("messages.properties"); // InputStream input = ChattoApplication.class.getClassLoader().getResourceAsStream("messages.properties");
OutputStream outputStream = new FileOutputStream("messages.properties"); OutputStream outputStream = new FileOutputStream("messages.properties");
// FileInputStream in = new FileInputStream("First.properties"); // FileInputStream in = new FileInputStream("First.properties");
// Properties props = new Properties(); // Properties props = new Properties();
// props.load(in); // props.load(in);
@ -175,14 +206,14 @@ public class DBInitializerService {
// out.close(); // out.close();
Properties prop = new Properties(); Properties prop = new Properties();
System.out.println("Hello from setProperties"); System.out.println("Hello from setProperties");
prop.setProperty("test.bindAddress", bindAddress); prop.setProperty("test.bindAddress", bindAddress);
prop.store(outputStream, null); prop.store(outputStream, null);
// if (input == null) { // if (input == null) {
// System.out.println("Sorry, unable to find messages.properties"); // System.out.println("Sorry, unable to find messages.properties");
// return; // return;
// } // }
// load a properties file from class path, inside static method // load a properties file from class path, inside static method
// prop.load(input); // prop.load(input);
// Object object = prop.setProperty("test.bindAddress", bindAddress); // Object object = prop.setProperty("test.bindAddress", bindAddress);
@ -191,6 +222,23 @@ public class DBInitializerService {
// prop.store(object, comments); // 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 { public void closeConnection() throws SQLException {
connection.close(); connection.close();
} }

View File

@ -5,7 +5,10 @@ import java.util.stream.Collectors;
import org.modelmapper.ModelMapper; import org.modelmapper.ModelMapper;
import org.ros.chatto.dto.ChatMessageDTO; 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.ChatMessage;
import org.ros.chatto.model.MessageCipher;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -42,6 +45,18 @@ public class MyConversionService {
return chatMessageDTO; 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) public List<ChatMessageDTO> convertToChatMessageDTOs(List<ChatMessage> chatMessages)
{ {
return chatMessages.stream() return chatMessages.stream()
@ -49,6 +64,33 @@ public class MyConversionService {
.collect(Collectors.toList()); .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) public List<ChatMessageDTO> convertToChatMessageDTOs(Page<ChatMessage> chatMessages)
{ {
return chatMessages.stream() return chatMessages.stream()

View File

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

View File

@ -62,4 +62,10 @@ public class UserServiceImpl implements UserService{
return userRepositoryCustom.getAllUserNames(userName); 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' 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

@ -37,68 +37,78 @@ var iterations = 100000;
// var user; // var user;
function getSelectedUser() { function getSelectedUser() {
for (var i = 0; i < toUserRadios.length; i++) { for (var i = 0; i < toUserRadios.length; i++) {
if (toUserRadios[i].checked) { if (toUserRadios[i].checked) {
let user = toUserRadios[i].value; let user = toUserRadios[i].value;
console.log('sending to user = ' + user); console.log('sending to user = ' + user);
isCheckedUser = true; isCheckedUser = true;
return user; return user;
} }
} }
} }
// console.log('Credentials = ' + JSON.parse(sessionStorage.getItem('credentials'))); // console.log('Credentials = ' + JSON.parse(sessionStorage.getItem('credentials')));
function handleChatForm() { function handleChatForm() {
let chatInput = document.getElementById('chatInput'); let chatInput = document.getElementById('chatInput');
let myForm = document.getElementById('chatMessageForm').addEventListener( let myForm = document.getElementById('chatMessageForm');
'submit', function (e) { myForm.addEventListener(
e.preventDefault(); 'submit',
let user = getSelectedUser(); function(e) {
if (!isCheckedUser) { e.preventDefault();
window.alert('please select a user'); let user = getSelectedUser();
return;
}
// console.log('second user = ' + user);
let messageContent = chatInput.value;
let localDate = new Date();
let messageLine = sprintf('%s %s %s: %s', localDate.toLocaleDateString(), localDate.toLocaleTimeString(), username, messageContent);
chatTextArea.append(messageLine + "\n");
chatTextArea.scrollTop = chatTextArea.scrollHeight;
// let messageCipher = sjcl.encrypt("password", messageContent);
let messageCipher = sjcl.encrypt(passphraseInput.value, messageContent,{mode: "gcm",ts: 128, adata: "",iter: iterations});
let messageCipherJson = JSON.parse(messageCipher);
// let messageCipherSpring = JSON.stringify(messageCipherJson);
// console.log('message cipher json ' + messageCipherJson);
// console.log('message cipher string ' + messageCipherSpring);
let chatMessageDTO = {
"toUser": user,
"messageCipher": messageCipherJson
}
// console.log(chatMessageDTO);
// console.log(JSON.stringify(chatMessageDTO));
messageSend(JSON.stringify(chatMessageDTO));
// sessionStorage.setItem('passphrase', passphraseInput.value);
// console.log(sessionStorage.getItem('passphrase'));
}) 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;
}
// console.log('second user = ' + user);
let messageContent = chatInput.value;
let localDate = new Date();
let messageLine = sprintf('%s %s %s: %s', localDate.toLocaleDateString(), localDate.toLocaleTimeString(), username, messageContent);
chatTextArea.append(messageLine + "\n");
chatTextArea.scrollTop = chatTextArea.scrollHeight;
// let messageCipher = sjcl.encrypt("password", messageContent);
let messageCipher = sjcl.encrypt(passphraseInput.value, messageContent, { mode: "gcm", ts: 128, adata: "", iter: iterations });
let messageCipherJson = JSON.parse(messageCipher);
// let messageCipherSpring = JSON.stringify(messageCipherJson);
// console.log('message cipher json ' + messageCipherJson);
// console.log('message cipher string ' + messageCipherSpring);
let chatMessageDTO = {
"toUser": user,
"messageCipher": messageCipherJson
}
// console.log(chatMessageDTO);
// console.log(JSON.stringify(chatMessageDTO));
messageSend(JSON.stringify(chatMessageDTO));
// sessionStorage.setItem('passphrase', passphraseInput.value);
// console.log(sessionStorage.getItem('passphrase'));
})
} }
function messageSend(chatMessageDTO) { function messageSend(chatMessageDTO) {
let headers = new Headers(); let headers = new Headers();
// console.log("Token = " + btoa("hmm" + ":" + "hmm")) // console.log("Token = " + btoa("hmm" + ":" + "hmm"))
// headers.append('Accept','application/json') // headers.append('Accept','application/json')
headers.append('Content-Type', 'application/json'); headers.append('Content-Type', 'application/json');
headers.append('Authorization', authToken); headers.append('Authorization', authToken);
fetch(postNewMessageUrl, { fetch(postNewMessageUrl, {
method: 'POST', method: 'POST',
headers: headers, headers: headers,
body: chatMessageDTO body: chatMessageDTO
}) })
.then(response => console.log(response)); .then(response => console.log(response));
} }
// function getMessages(toUser) { // function getMessages(toUser) {
@ -151,155 +161,153 @@ function messageSend(chatMessageDTO) {
// .then(data => console.log(data)); // .then(data => console.log(data));
async function getAllMessages(toUser) { async function getAllMessages(toUser) {
let headers = new Headers(); let headers = new Headers();
// headers.append('Accept','application/json') // headers.append('Accept','application/json')
// headers.append('Content-Type', 'application/json'); // headers.append('Content-Type', 'application/json');
headers.append('Authorization', authToken); headers.append('Authorization', authToken);
let response = await fetch(getAllMessagesUrl + toUser, { let response = await fetch(getAllMessagesUrl + toUser, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
let data = await response.json(); let data = await response.json();
return data; return data;
} }
async function getNewMessages(toUser, lastMessageTimeStamp) { async function getNewMessages(toUser, lastMessageTimeStamp) {
let headers = new Headers(); let headers = new Headers();
headers.append('Authorization', authToken); headers.append('Authorization', authToken);
let response = await fetch(`${getNewMessagesUrl}${toUser}/${lastMessageTimeStamp}`, { let response = await fetch(`${getNewMessagesUrl}${toUser}/${lastMessageTimeStamp}`, {
method: 'GET', method: 'GET',
headers: headers headers: headers
}); });
let data = await response.json(); let data = await response.json();
return data; return data;
} }
// getMessages('user2'); // getMessages('user2');
window.EventTarget.prototype.addDelegatedListener = function (type, delegateSelector, listener) { window.EventTarget.prototype.addDelegatedListener = function(type, delegateSelector, listener) {
this.addEventListener(type, function (event) { this.addEventListener(type, function(event) {
if (event.target && event.target.matches(delegateSelector)) { if (event.target && event.target.matches(delegateSelector)) {
listener.call(event.target, event) listener.call(event.target, event)
} }
}); });
} }
let parent = document.getElementById('chatMessageForm') let parent = document.getElementById('chatMessageForm')
parent.addDelegatedListener("click", "input[type='radio']", function (event) { parent.addDelegatedListener("click", "input[type='radio']", function(event) {
// if (sessionStorage.getItem('status') != null) { // if (sessionStorage.getItem('status') != null) {
if (passphraseInput.value == '') { if (passphraseInput.value == '') {
alert('Please input passphrase') alert('Please input passphrase')
return; return;
} }
console.log(this.value); console.log(this.value);
if (sessionStorage.getItem(this.value) == null) { if (sessionStorage.getItem(this.value) == null) {
chatTextArea.textContent = ''; chatTextArea.textContent = '';
getAllMessages(this.value) getAllMessages(this.value)
.then(json => { .then(json => {
console.log(json); console.log(json);
let i = 0; let i = 0;
let messageLog = []; let messageLog = [];
let lastMessageTimeStamp; let lastMessageTimeStamp;
// console.log("Json length = " + json.length); // console.log("Json length = " + json.length);
// //
// if(json.length == 0) // if(json.length == 0)
// { // {
// console.log("JSON LENGTH IS 0") // console.log("JSON LENGTH IS 0")
// } // }
if (json.length > 0) { if (json.length > 0) {
json.forEach(function (obj) { json.forEach(function(obj) {
// console.log(obj.toUser); // console.log(obj.toUser);
messageCipher = JSON.stringify(obj.messageCipher); messageCipher = JSON.stringify(obj.messageCipher);
console.log(messageCipher); console.log(messageCipher);
// let message = sjcl.decrypt("password", messageCipher); // let message = sjcl.decrypt("password", messageCipher);
let message = sjcl.decrypt(passphraseInput.value, messageCipher); let message = sjcl.decrypt(passphraseInput.value, messageCipher);
let utcDate = obj.messageTime; let utcDate = obj.messageTime;
lastMessageTimeStamp = utcDate; lastMessageTimeStamp = utcDate;
let localDate = new Date(utcDate); let localDate = new Date(utcDate);
let messageLine = sprintf('%s %s: %s ', localDate, obj.fromUser, message); let messageLine = sprintf('%s %s: %s ', localDate, obj.fromUser, message);
// localDate.`` // localDate.``
// console.log('localDate = ' + localDate); // console.log('localDate = ' + localDate);
console.log(messageLine); console.log(messageLine);
// chatTextArea.append(obj.fromUser + ": " + message + "\n"); // chatTextArea.append(obj.fromUser + ": " + message + "\n");
chatTextArea.append(messageLine + '\n'); chatTextArea.append(messageLine + '\n');
messageLog[i++] = messageLine; messageLog[i++] = messageLine;
chatTextArea.scrollTop = chatTextArea.scrollHeight; chatTextArea.scrollTop = chatTextArea.scrollHeight;
// console.log('Message log = ' + messageLog); // console.log('Message log = ' + messageLog);
}); });
sessionStorage.setItem(this.value, JSON.stringify(messageLog)); sessionStorage.setItem(this.value, JSON.stringify(messageLog));
// sessionStorage.clear(); // sessionStorage.clear();
console.log('Last message time = ' + lastMessageTimeStamp); console.log('Last message time = ' + lastMessageTimeStamp);
sessionStorage.setItem(this.value + '-time', lastMessageTimeStamp); sessionStorage.setItem(this.value + '-time', lastMessageTimeStamp);
} }
}); });
} } else {
else {
console.log("Stored messages = " + sessionStorage.getItem(this.value)); console.log("Stored messages = " + sessionStorage.getItem(this.value));
let storedMessages = JSON.parse(sessionStorage.getItem(this.value)); let storedMessages = JSON.parse(sessionStorage.getItem(this.value));
let lastMessageTime = sessionStorage.getItem(this.value + '-time'); let lastMessageTime = sessionStorage.getItem(this.value + '-time');
console.log("last message time stamp = " + lastMessageTime); console.log("last message time stamp = " + lastMessageTime);
if (lastMessageTime != null) { if (lastMessageTime != null) {
getNewMessages(this.value, lastMessageTime) getNewMessages(this.value, lastMessageTime)
.then(json => { .then(json => {
console.log(json) console.log(json)
if (json.length > 0) { if (json.length > 0) {
json.forEach(function (obj) { json.forEach(function(obj) {
let messageCipher = JSON.stringify(obj.messageCipher); let messageCipher = JSON.stringify(obj.messageCipher);
let message = sjcl.decrypt(passphraseInput.value, messageCipher); let message = sjcl.decrypt(passphraseInput.value, messageCipher);
// console.log(message); // console.log(message);
// chatTextArea.append(message + "\n"); // chatTextArea.append(message + "\n");
let utcDate = obj.messageTime; let utcDate = obj.messageTime;
lastMessageTimeStamp = utcDate; lastMessageTimeStamp = utcDate;
let localDate = new Date(utcDate); let localDate = new Date(utcDate);
let messageLine = sprintf('%s %s: %s', localDate, obj.fromUser, message); let messageLine = sprintf('%s %s: %s', localDate, obj.fromUser, message);
// localDate.`` // localDate.``
// console.log('localDate = ' + localDate); // console.log('localDate = ' + localDate);
console.log(messageLine); console.log(messageLine);
// chatTextArea.append(obj.fromUser + ": " + message + "\n"); // chatTextArea.append(obj.fromUser + ": " + message + "\n");
chatTextArea.append(messageLine + '\n'); chatTextArea.append(messageLine + '\n');
chatTextArea.scrollTop = chatTextArea.scrollHeight; chatTextArea.scrollTop = chatTextArea.scrollHeight;
storedMessages.push(messageLine); storedMessages.push(messageLine);
}) })
sessionStorage.setItem(this.value + '-time', lastMessageTimeStamp); sessionStorage.setItem(this.value + '-time', lastMessageTimeStamp);
sessionStorage.setItem(this.value, JSON.stringify(storedMessages)); sessionStorage.setItem(this.value, JSON.stringify(storedMessages));
console.log("this value stored" + sessionStorage.getItem(this.value)) console.log("this value stored" + sessionStorage.getItem(this.value))
console.log("last message time stamp = " + lastMessageTimeStamp); console.log("last message time stamp = " + lastMessageTimeStamp);
console.log(sessionStorage.getItem(this.value + '-time')); console.log(sessionStorage.getItem(this.value + '-time'));
} }
chatTextArea.textContent = ''; chatTextArea.textContent = '';
console.log("Stored messages 2 = " + storedMessages); console.log("Stored messages 2 = " + storedMessages);
storedMessages.forEach(function (messageLine) { storedMessages.forEach(function(messageLine) {
chatTextArea.append(messageLine + '\n'); chatTextArea.append(messageLine + '\n');
chatTextArea.scrollTop = chatTextArea.scrollHeight; chatTextArea.scrollTop = chatTextArea.scrollHeight;
}) })
}); });
} }
// sessionStorage.clear(); // sessionStorage.clear();
// chatTextArea.append(JSON.stringify(storedMessages)); // chatTextArea.append(JSON.stringify(storedMessages));
} }
// sessionStorage.setItem('status', 'ready'); // sessionStorage.setItem('status', 'ready');
// sessionStorage.setItem('this.value', messageLog); // sessionStorage.setItem('this.value', messageLog);
// console.log('Message log = ' + messageLog); // console.log('Message log = ' + messageLog);
// } // }
// let passphraseKey = this.value + '-passphrase'; // let passphraseKey = this.value + '-passphrase';
// sessionStorage.setItem(passphraseKey, passphraseInput.value); // sessionStorage.setItem(passphraseKey, passphraseInput.value);
// console.log(sessionStorage.getItem(passphraseKey)); // console.log(sessionStorage.getItem(passphraseKey));
}); });
handleChatForm(); handleChatForm();

View File

@ -1,13 +1,110 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org">
<head> <head>
<meta charset="UTF-8"> <div th:replace="fragments/head :: headFragment">
<title>Insert title here</title> <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> </head>
<!-- TODO
Make user admin / remove user from admin
Change E2E passphrase
Delete Messages
-->
<body> <body>
admin page <div th:include="fragments/navbar :: navbarFragment"></div>
<form action="#" th:action="@{/perform_logout}" method="POST">
<input type="submit" value="logout"> <header>
</form> <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> </body>
</html> </html>

View File

@ -45,7 +45,7 @@
<div class="row"> <div class="row">
<div class="col-sm"> <div class="col-sm">
<h4 class="display-4 text-center py-2">Chat</h4> <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"> <div class="card-body rounded">
<!-- <h4 class="card-title">Chat</h4> --> <!-- <h4 class="card-title">Chat</h4> -->
@ -57,7 +57,7 @@
<div class="card-text"> <div class="card-text">
</div> </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="row">
<div class="col-3"> <div class="col-3">
<div class="form-group"> <div class="form-group">
@ -65,8 +65,8 @@
<th:block th:each="userName: ${userNames}"> <th:block th:each="userName: ${userNames}">
<input class="form-control" type="radio" th:field="*{toUser}" th:value="${userName}"> <input class="form-control" type="radio" th:field="*{toUser}" th:value="${userName}">
<label class="btn btn-secondary" th:for="${#ids.prev('toUser')}" th:text="${userName}"> <label class="btn btn-secondary" th:for="${#ids.prev('toUser')}" th:text="${userName}">
Demo User Demo User
</label> </label>
</th:block> </th:block>
</div> </div>
</div> </div>
@ -74,14 +74,18 @@
<div class="my-form-inputs container"> <div class="my-form-inputs container">
<div class="form-group"> <div class="form-group">
<label for="chatInput">Your message: </label> <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>
<div class="form-group"> <div class="form-group">
<label for="passphrase">Passphrase: </label> <label for="passphrase">Passphrase: </label>
<input class="form-control" type="password" id="passphrase"> <input class="form-control" type="password" id="passphrase" required>
</div> </div>
<div class="form-group"> <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> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!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> <head>
<div th:replace="fragments/head :: headFragment"> <div th:replace="fragments/head :: headFragment">
@ -34,13 +34,16 @@
<a href="home.html" th:href="@{/}" class="nav-link">Home</a> <a href="home.html" th:href="@{/}" class="nav-link">Home</a>
</li> </li>
<li class="nav-item"> <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>
<li class="nav-item"> <li class="nav-item">
<a th:href="chat" href="chat.html" class="nav-link">Chat</a> <a th:href="chat" href="chat.html" class="nav-link">Chat</a>
</li> </li>
<li class="nav-item"> <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>
<li class="nav-item"> <li class="nav-item">
<a href="#" class="nav-link">About</a> <a href="#" class="nav-link">About</a>
@ -48,7 +51,16 @@
<li class="nav-item"> <li class="nav-item">
<a href="#" class="nav-link">Contact</a> <a href="#" class="nav-link">Contact</a>
</li> </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> </ul>
</div> </div>
</div> </div>

View File

@ -1,5 +1,5 @@
<!DOCTYPE HTML> <!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> <head>
<div th:replace="fragments/head :: headFragment"> <div th:replace="fragments/head :: headFragment">
@ -35,7 +35,7 @@
<th:block th:each="userName: ${userNames}"> <th:block th:each="userName: ${userNames}">
<div th:text="${userName}"></div> <div th:text="${userName}"></div>
</th:block> --> </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> <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> --> <!-- <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> <a class="btn btn-secondary" href="registration.html" th:href="@{/registration}">Get Started</a>
@ -53,12 +53,14 @@
<li> <li>
<p class="lead"> <p class="lead">
<!-- <i class="fas fa-check"></i> --> <!-- <i class="fas fa-check"></i> -->
Self Hosted</p> Self Hosted
</p>
</li> </li>
<li> <li>
<p class="lead"> <p class="lead">
<!-- <i class="fas fa-check"></i> --> <!-- <i class="fas fa-check"></i> -->
End To End Encrypted Messaging</p> End To End Encrypted Messaging
</p>
</li> </li>
<li> <li>
<p class="lead"> <p class="lead">
@ -68,7 +70,8 @@
<li> <li>
<p class="lead"> <p class="lead">
<!-- <i class="fas fa-check"></i> --> <!-- <i class="fas fa-check"></i> -->
Built With Java And Spring</p> Built With Java And Spring
</p>
</li> </li>
</ul> </ul>
</p> </p>

View File

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

View File

@ -1,18 +1,73 @@
<!DOCTYPE html> <!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org">
<head> <head>
<meta charset="UTF-8"> <div th:replace="fragments/head :: headFragment">
<title>Insert title here</title> <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> </head>
<body> <body>
<form action="#" th:action="@{/perform_registration}" <div th:include="fragments/navbar :: navbarFragment"></div>
th:object=${userDTO} method="POST"> <header>
<label>Enter user name: </label> <input th:field="*{userName}" <div class="container">
type="text" name="username" id="username"> <br> <br> <div class="row">
<label>Enter password: </label> <input th:field="*{password}" <div class="col-sm py-5">
type="password" name="password" id="password"> <br> <br> <!-- <h4 class="display-4 text-center py-2">Chat</h4> -->
<input type="password" id="password-repeat"> <div class="card text-white bg-primary mb-3 text-center card-form rounded mx-auto" id="login-card">
<input type="submit" value="Submit">
</form> <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> </body>
</html> </html>