Browse Source
Architecture improvements and validation
Architecture improvements and validation
Improved system architecture and added sample regex validation to registrationmaster
Rohan Sircar
5 years ago
31 changed files with 978 additions and 338 deletions
-
19chatto/pom.xml
-
2chatto/src/main/java/org/ros/chatto/RESTAuthenticationEntryPoint.java
-
21chatto/src/main/java/org/ros/chatto/controller/AdminController.java
-
87chatto/src/main/java/org/ros/chatto/controller/AdminRESTController.java
-
49chatto/src/main/java/org/ros/chatto/controller/ChatMessageController.java
-
53chatto/src/main/java/org/ros/chatto/controller/DemoRestController.java
-
31chatto/src/main/java/org/ros/chatto/controller/RegistrationController.java
-
2chatto/src/main/java/org/ros/chatto/dto/ChatMessageDTO.java
-
25chatto/src/main/java/org/ros/chatto/dto/MessageCipherDTO.java
-
14chatto/src/main/java/org/ros/chatto/dto/ReencryptionDTO.java
-
14chatto/src/main/java/org/ros/chatto/dto/UserPublicDTO.java
-
16chatto/src/main/java/org/ros/chatto/dto/UserRegistrationDTO.java
-
21chatto/src/main/java/org/ros/chatto/model/ApplicationStatus.java
-
2chatto/src/main/java/org/ros/chatto/model/MessageCipher.java
-
12chatto/src/main/java/org/ros/chatto/repository/ChatMessageRepository.java
-
3chatto/src/main/java/org/ros/chatto/repository/UserRoleRepository.java
-
25chatto/src/main/java/org/ros/chatto/service/ChatService.java
-
38chatto/src/main/java/org/ros/chatto/service/ChatServiceImpl.java
-
60chatto/src/main/java/org/ros/chatto/service/DBInitializerService.java
-
42chatto/src/main/java/org/ros/chatto/service/MyConversionService.java
-
1chatto/src/main/java/org/ros/chatto/service/UserService.java
-
6chatto/src/main/java/org/ros/chatto/service/UserServiceImpl.java
-
3chatto/src/main/resources/queries.properties
-
135chatto/src/main/resources/static/js/admin.js
-
396chatto/src/main/resources/static/js/chat.js
-
109chatto/src/main/resources/templates/admin/home.html
-
18chatto/src/main/resources/templates/chat.html
-
20chatto/src/main/resources/templates/fragments/navbar.html
-
13chatto/src/main/resources/templates/home.html
-
2chatto/src/main/resources/templates/login.html
-
77chatto/src/main/resources/templates/registration.html
@ -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 { |
|||
|
|||
@RequestMapping |
|||
public String viewManageUsers() { |
|||
return "/admin/home"; |
|||
} |
|||
|
|||
@Autowired |
|||
private UserService userService; |
|||
|
|||
@RequestMapping |
|||
public ModelAndView viewManageUsers(Principal principal) { |
|||
ModelAndView modelAndView = new ModelAndView("/admin/home"); |
|||
modelAndView.addObject("user", new String()); |
|||
modelAndView.addObject("userNames", userService.getAllRegularUsers()); |
|||
return modelAndView; |
|||
} |
|||
} |
@ -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); |
|||
//} |
@ -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"; |
|||
} |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
@ -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; |
|||
} |
|||
|
|||
|
|||
|
|||
} |
@ -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; |
|||
} |
@ -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 = |
@ -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(); |
@ -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"> |
|||
</form> |
|||
<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> |
@ -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"> |
|||
</form> |
|||
<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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue