Add custom error page, add profile settings page '/profile/'
This commit is contained in:
parent
147f590410
commit
bb6059d63e
28 changed files with 401 additions and 35 deletions
|
@ -1,11 +1,13 @@
|
||||||
package ru.redrise.marinesco;
|
package ru.redrise.marinesco;
|
||||||
|
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
//@PreAuthorize("hasRole('USER')")
|
||||||
@Controller
|
@Controller
|
||||||
public class RootController {
|
public class RootController {
|
||||||
|
|
||||||
@GetMapping("/")
|
@GetMapping("/")
|
||||||
public String home(){
|
public String home(){
|
||||||
return "root";
|
return "root";
|
||||||
|
|
|
@ -7,6 +7,7 @@ import org.springframework.boot.ApplicationArguments;
|
||||||
import org.springframework.boot.ApplicationRunner;
|
import org.springframework.boot.ApplicationRunner;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import ru.redrise.marinesco.data.RolesRepository;
|
import ru.redrise.marinesco.data.RolesRepository;
|
||||||
|
@ -25,13 +26,14 @@ public class ShinyApplicationRunner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ApplicationRunner appRunner() {
|
public ApplicationRunner appRunner(PasswordEncoder encoder) {
|
||||||
return args -> {
|
return args -> {
|
||||||
if (isFirstRun()) {
|
if (isFirstRun()) {
|
||||||
|
log.info("Application first run");
|
||||||
setRoles();
|
setRoles();
|
||||||
setAdmin(args);
|
setAdmin(args, encoder);
|
||||||
} else
|
} else
|
||||||
log.info("NOT FIRST APPLICATION RUN; DB Already set up");
|
log.info("Regular run");
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,7 +47,7 @@ public class ShinyApplicationRunner {
|
||||||
new UserRole(null, "User", UserRole.Type.USER)));
|
new UserRole(null, "User", UserRole.Type.USER)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAdmin(ApplicationArguments args) {
|
private void setAdmin(ApplicationArguments args, PasswordEncoder encoder) {
|
||||||
List<String> login = args.getOptionValues("admin_login");
|
List<String> login = args.getOptionValues("admin_login");
|
||||||
List<String> password = args.getOptionValues("admin_password");
|
List<String> password = args.getOptionValues("admin_password");
|
||||||
|
|
||||||
|
@ -53,10 +55,11 @@ public class ShinyApplicationRunner {
|
||||||
|
|
||||||
if (login == null || login.size() == 0 || password == null || password.size() == 0) {
|
if (login == null || login.size() == 0 || password == null || password.size() == 0) {
|
||||||
log.warn("No administrator credentials provided, using defaults:\n * Login: root\n * Password: root\n Expected: --admin_login LOGIN --admin_password PASSWORD "); // TODO: Move into properties i18n
|
log.warn("No administrator credentials provided, using defaults:\n * Login: root\n * Password: root\n Expected: --admin_login LOGIN --admin_password PASSWORD "); // TODO: Move into properties i18n
|
||||||
var adminUser = new User("root", "root", "SuperAdmin", adminRoleOnlyAthority);
|
var adminUser = new User("root", encoder.encode("root"), "SuperAdmin", adminRoleOnlyAthority);
|
||||||
users.save(adminUser);
|
users.save(adminUser);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
users.save(new User(login.get(0), password.get(0), "SuperAdmin", adminRoleOnlyAthority));
|
log.info("SuperAdmin role created\n * Login: {}", login.get(0));
|
||||||
|
users.save(new User(login.get(0), encoder.encode(password.get(0)), "SuperAdmin", adminRoleOnlyAthority));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
package ru.redrise.marinesco;
|
package ru.redrise.marinesco;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
|
||||||
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.FetchType;
|
||||||
import jakarta.persistence.GeneratedValue;
|
import jakarta.persistence.GeneratedValue;
|
||||||
import jakarta.persistence.GenerationType;
|
import jakarta.persistence.GenerationType;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
|
@ -14,14 +15,12 @@ import jakarta.persistence.Table;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import ru.redrise.marinesco.security.UserRole;
|
import ru.redrise.marinesco.security.UserRole;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "\"USER\"")
|
@Table(name = "\"USER\"")
|
||||||
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
|
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class User implements UserDetails{
|
public class User implements UserDetails{
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
@ -29,14 +28,21 @@ public class User implements UserDetails{
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
private final String username;
|
|
||||||
private final String password;
|
|
||||||
private final String displayname;
|
|
||||||
|
|
||||||
@ManyToMany
|
|
||||||
private final List<UserRole> authorities;
|
|
||||||
|
|
||||||
|
private final String username;
|
||||||
|
private String password;
|
||||||
|
private String displayname;
|
||||||
|
|
||||||
|
@ManyToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER)
|
||||||
|
private final List<UserRole> authorities;
|
||||||
|
|
||||||
|
public User(String username, String password, String displayname, List<UserRole> authorities){
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
this.displayname = displayname;
|
||||||
|
this.authorities = authorities;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAccountNonExpired() {
|
public boolean isAccountNonExpired() {
|
||||||
return true;
|
return true;
|
||||||
|
|
23
src/main/java/ru/redrise/marinesco/UserSettingsForm.java
Normal file
23
src/main/java/ru/redrise/marinesco/UserSettingsForm.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package ru.redrise.marinesco;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class UserSettingsForm {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@NotEmpty(message = "Display name could not be blank")
|
||||||
|
public String displayname;
|
||||||
|
public String newPassword;
|
||||||
|
|
||||||
|
public boolean isNewPasswordSet(){
|
||||||
|
return ! newPassword.isBlank();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNewPasswordValid(){
|
||||||
|
final int newPasswordLength = newPassword.length();
|
||||||
|
return newPasswordLength > 8 && newPasswordLength < 32;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,10 +2,14 @@ package ru.redrise.marinesco.security;
|
||||||
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
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.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
import ru.redrise.marinesco.data.RolesRepository;
|
import ru.redrise.marinesco.data.RolesRepository;
|
||||||
import ru.redrise.marinesco.data.UserRepository;
|
import ru.redrise.marinesco.data.UserRepository;
|
||||||
|
|
||||||
|
@ -16,20 +20,33 @@ public class RegistrationController {
|
||||||
private RolesRepository rolesRepo;
|
private RolesRepository rolesRepo;
|
||||||
private PasswordEncoder passwordEncoder;
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
public RegistrationController(UserRepository userRepo, RolesRepository rolesRepo, PasswordEncoder passwordEncoder){
|
public RegistrationController(UserRepository userRepo, RolesRepository rolesRepo, PasswordEncoder passwordEncoder) {
|
||||||
this.userRepo = userRepo;
|
this.userRepo = userRepo;
|
||||||
this.rolesRepo = rolesRepo;
|
this.rolesRepo = rolesRepo;
|
||||||
this.passwordEncoder = passwordEncoder;
|
this.passwordEncoder = passwordEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ModelAttribute(name = "registrationForm")
|
||||||
|
public RegistrationForm form() {
|
||||||
|
return new RegistrationForm();
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping
|
@GetMapping
|
||||||
public String registerForm(){
|
public String registerForm() {
|
||||||
return "registration";
|
return "registration";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping
|
@PostMapping
|
||||||
public String postMethodName(RegistrationForm registrationForm) {
|
public String postMethodName(@Valid RegistrationForm registerForm, Errors errors, Model model) {
|
||||||
userRepo.save(registrationForm.toUser(passwordEncoder, rolesRepo));
|
if (registerForm.isPasswordsNotEqual()){
|
||||||
|
model.addAttribute("passwordsMismatch", "Passwords must be the same.");
|
||||||
|
return "registration";
|
||||||
|
}
|
||||||
|
if (errors.hasErrors()) {
|
||||||
|
return "registration";
|
||||||
|
}
|
||||||
|
|
||||||
|
userRepo.save(registerForm.toUser(passwordEncoder, rolesRepo));
|
||||||
return "redirect:/login";
|
return "redirect:/login";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,28 @@ package ru.redrise.marinesco.security;
|
||||||
|
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
import jakarta.validation.constraints.NotNull;
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import ru.redrise.marinesco.User;
|
import ru.redrise.marinesco.User;
|
||||||
import ru.redrise.marinesco.data.RolesRepository;
|
import ru.redrise.marinesco.data.RolesRepository;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class RegistrationForm {
|
public class RegistrationForm {
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@Size(min=3, max=32, message="Username must be at least 3 characters long. Should not exceed 32 characters.")
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
@Size(min=8, max = 32, message="Password must be at least 8 characters long. Should not exceed 32 characters.")
|
||||||
private String password;
|
private String password;
|
||||||
|
|
||||||
|
private String passwordConfirm;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private String fullname;
|
@NotEmpty(message = "Display name could not be blank")
|
||||||
@NotNull
|
|
||||||
private String displayname;
|
private String displayname;
|
||||||
|
|
||||||
public User toUser(PasswordEncoder passwordEncoder, RolesRepository rolesRepo){
|
public User toUser(PasswordEncoder passwordEncoder, RolesRepository rolesRepo){
|
||||||
|
@ -25,4 +33,7 @@ public class RegistrationForm {
|
||||||
displayname,
|
displayname,
|
||||||
rolesRepo.findByType(UserRole.Type.USER));
|
rolesRepo.findByType(UserRole.Type.USER));
|
||||||
}
|
}
|
||||||
|
public boolean isPasswordsNotEqual(){
|
||||||
|
return ! password.equals(passwordConfirm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,19 +45,25 @@ public class SecurityConfig {
|
||||||
public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
|
public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception {
|
||||||
return http
|
return http
|
||||||
.authorizeHttpRequests(autorize -> autorize
|
.authorizeHttpRequests(autorize -> autorize
|
||||||
|
.requestMatchers(mvc.pattern("/favicon.ico")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/jquery.js")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/styles/**")).permitAll()
|
.requestMatchers(mvc.pattern("/styles/**")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/images/*")).permitAll()
|
.requestMatchers(mvc.pattern("/images/*")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/register")).permitAll()
|
.requestMatchers(mvc.pattern("/register")).permitAll()
|
||||||
.requestMatchers(mvc.pattern("/login")).permitAll()
|
.requestMatchers(mvc.pattern("/login")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/error")).permitAll()
|
||||||
|
.requestMatchers(mvc.pattern("/")).hasAnyRole("ADMIN", "USER")
|
||||||
|
.requestMatchers(mvc.pattern("/profile/**")).hasAnyRole("ADMIN", "USER")
|
||||||
.requestMatchers(PathRequest.toH2Console()).permitAll()
|
.requestMatchers(PathRequest.toH2Console()).permitAll()
|
||||||
//.requestMatchers(mvc.pattern("/design/**")).hasRole("USER")
|
//.requestMatchers(mvc.pattern("/design/**")).hasRole("USER")
|
||||||
.anyRequest().denyAll())
|
.anyRequest().denyAll())
|
||||||
|
//.anyRequest().permitAll())
|
||||||
.formLogin(formLoginConfigurer -> formLoginConfigurer
|
.formLogin(formLoginConfigurer -> formLoginConfigurer
|
||||||
.loginPage("/login")
|
.loginPage("/login")
|
||||||
.loginProcessingUrl("/auth")
|
//.loginProcessingUrl("/auth")
|
||||||
.usernameParameter("login")
|
.usernameParameter("login")
|
||||||
.passwordParameter("pwd")
|
.passwordParameter("pwd")
|
||||||
//.defaultSuccessUrl("/", true)
|
.defaultSuccessUrl("/")
|
||||||
)
|
)
|
||||||
// .formLogin(Customizer.withDefaults())
|
// .formLogin(Customizer.withDefaults())
|
||||||
//.oauth2Login(c -> c.loginPage("/login"))
|
//.oauth2Login(c -> c.loginPage("/login"))
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package ru.redrise.marinesco.security;
|
||||||
|
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.validation.Errors;
|
||||||
|
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.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import ru.redrise.marinesco.User;
|
||||||
|
import ru.redrise.marinesco.UserSettingsForm;
|
||||||
|
import ru.redrise.marinesco.data.UserRepository;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Controller
|
||||||
|
@RequestMapping("/profile")
|
||||||
|
public class UserSettingsController {
|
||||||
|
private final UserRepository userRepo;
|
||||||
|
private final PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
public UserSettingsController(UserRepository userRepo, PasswordEncoder passwordEncoder){
|
||||||
|
this.userRepo = userRepo;
|
||||||
|
this.passwordEncoder = passwordEncoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute
|
||||||
|
public void addMisc(Model model, @AuthenticationPrincipal User user){
|
||||||
|
final String displayName = user.getDisplayname();
|
||||||
|
model.addAttribute("header_text", "Welcome " + displayName);
|
||||||
|
|
||||||
|
UserSettingsForm form = new UserSettingsForm();
|
||||||
|
form.setDisplayname(displayName);
|
||||||
|
model.addAttribute( "userSettingsForm", form);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ModelAttribute
|
||||||
|
public UserSettingsForm addSettingsForm(){
|
||||||
|
return new UserSettingsForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping
|
||||||
|
public String getPage(){
|
||||||
|
return "redirect:/profile/settings";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/settings")
|
||||||
|
public String getSettingsFirstPage(){
|
||||||
|
return "user_settings";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/settings")
|
||||||
|
public String getSettingsPage(@Valid UserSettingsForm userSettingsForm,
|
||||||
|
Errors errors,
|
||||||
|
@AuthenticationPrincipal User user,
|
||||||
|
Model model){
|
||||||
|
if (errors.hasErrors())
|
||||||
|
return "user_settings";
|
||||||
|
if (! user.getDisplayname().equals(userSettingsForm.getDisplayname()))
|
||||||
|
user.setDisplayname(userSettingsForm.getDisplayname());
|
||||||
|
if (userSettingsForm.isNewPasswordSet()){
|
||||||
|
if (userSettingsForm.isNewPasswordValid()){
|
||||||
|
user.setPassword(passwordEncoder.encode(userSettingsForm.getNewPassword()));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
model.addAttribute("password_incorrect", "Password must be at least 8 characters long. Should not exceed 32 characters.");
|
||||||
|
return "user_settings";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("{} {}", userSettingsForm.getDisplayname(), userSettingsForm.getNewPassword());
|
||||||
|
userRepo.save(user);
|
||||||
|
|
||||||
|
return "user_settings";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
package ru.redrise.marinesco.web;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
|
||||||
|
import jakarta.servlet.RequestDispatcher;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
@Controller
|
||||||
|
public class HandyErrorController implements ErrorController{
|
||||||
|
|
||||||
|
@ModelAttribute(name = "code")
|
||||||
|
public String addMisc(HttpServletRequest request){
|
||||||
|
return request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping("/error")
|
||||||
|
public String handleError(){
|
||||||
|
return "error";
|
||||||
|
}
|
||||||
|
}
|
2
src/main/resources/static/jquery.js
vendored
Normal file
2
src/main/resources/static/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
53
src/main/resources/templates/error.html
Normal file
53
src/main/resources/templates/error.html
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style type=text/css>
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #212121;
|
||||||
|
color: #cfcfcf;
|
||||||
|
font-family: Terminus;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
border-width: 3px;
|
||||||
|
border-style: double;
|
||||||
|
padding: 8px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bli {
|
||||||
|
padding: 3px;
|
||||||
|
background-color: #D00000;
|
||||||
|
animation: blinker 2.5s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blinker {
|
||||||
|
50% {
|
||||||
|
opacity: 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title th:text="${code}">title</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class='error'>
|
||||||
|
ОШИБКА: <span class='bli' th:text="${code}"></span>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -16,6 +16,7 @@
|
||||||
<br /><a href="/h2">H2</a>
|
<br /><a href="/h2">H2</a>
|
||||||
<form class="form-signin" method="post" action="/login">
|
<form class="form-signin" method="post" action="/login">
|
||||||
<h2 class="form-signin-heading">Please sign in</h2>
|
<h2 class="form-signin-heading">Please sign in</h2>
|
||||||
|
<br /><span class="validationError" th:if="${param.error}">Unable to login. Check your username and password.</span>
|
||||||
<p>
|
<p>
|
||||||
<label for="username" class="sr-only">Username</label>
|
<label for="username" class="sr-only">Username</label>
|
||||||
<input type="text" id="username" name="login" class="form-control" placeholder="Username" required autofocus>
|
<input type="text" id="username" name="login" class="form-control" placeholder="Username" required autofocus>
|
||||||
|
|
|
@ -5,20 +5,34 @@
|
||||||
<title>Marinesco - registration form</title>
|
<title>Marinesco - registration form</title>
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
||||||
|
<script src="@{/jquery.js}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Register</h1>
|
<h1>Register</h1>
|
||||||
<img th:src="@{/images/logo.svg}" />
|
<img th:src="@{/images/logo.svg}" />
|
||||||
<form method="POST" th:action="@{/register}" id="registerForm">
|
<form class="regForm" method="POST" th:action="@{/register}" th:object="${registrationForm}">
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('username')}" th:errors="*{username}">Error</span>
|
||||||
|
<br />
|
||||||
<label for="username">Username: </label>
|
<label for="username">Username: </label>
|
||||||
<input type="text" name="login" /><br />
|
<input type="text" name="username" id="username"/><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Error</span>
|
||||||
|
<br />
|
||||||
<label for="password">Password: </label>
|
<label for="password">Password: </label>
|
||||||
<input type="password" name="pwd" /><br />
|
<input type="password" name="password" id="password"/><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${passwordsMismatch} != null" th:text="${passwordsMismatch}">false</span>
|
||||||
|
<br />
|
||||||
<label for="confirm">Confirm password: </label>
|
<label for="confirm">Confirm password: </label>
|
||||||
<input type="password" name="confirm" /><br />
|
<input type="password" name="passwordConfirm" id="passwordConfirm" /><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('displayname')}" th:errors="*{displayname}">Error</span>
|
||||||
|
<br />
|
||||||
<label for="displayname">Displayed name: </label>
|
<label for="displayname">Displayed name: </label>
|
||||||
<input type="text" name="fullname" /><br />
|
<input type="text" name="displayname" id="displayname"/><br />
|
||||||
|
|
||||||
<input type="submit" value="Register" />
|
<input type="submit" value="Register" />
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
26
src/main/resources/templates/user_settings.html
Normal file
26
src/main/resources/templates/user_settings.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Marinesco - Profile settings</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 th:text="${header_text}">welcome</h1>
|
||||||
|
<form method="POST" th:action="@{/profile/settings}" th:object="${userSettingsForm}"> <!-- -->
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('displayname')}" th:errors="*{displayname}">Error</span>
|
||||||
|
<br />
|
||||||
|
<label for="displayname">Displayed name: </label>
|
||||||
|
<input type="text" name="displayname" id="displayname" th:value="${userSettingsForm.displayname}" /><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${password_incorrect} != null" th:text="${password_incorrect}">false</span>
|
||||||
|
<br />
|
||||||
|
<label for="password">New password: </label>
|
||||||
|
<input type="password" name="newPassword" id="newPassword" /><br />
|
||||||
|
|
||||||
|
<input type="submit" value="Save Changes" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
target/classes/ru/redrise/marinesco/UserSettingsForm.class
Normal file
BIN
target/classes/ru/redrise/marinesco/UserSettingsForm.class
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
2
target/classes/static/jquery.js
vendored
Normal file
2
target/classes/static/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
53
target/classes/templates/error.html
Normal file
53
target/classes/templates/error.html
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<style type=text/css>
|
||||||
|
html {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #212121;
|
||||||
|
color: #cfcfcf;
|
||||||
|
font-family: Terminus;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
border-width: 3px;
|
||||||
|
border-style: double;
|
||||||
|
padding: 8px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bli {
|
||||||
|
padding: 3px;
|
||||||
|
background-color: #D00000;
|
||||||
|
animation: blinker 2.5s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes blinker {
|
||||||
|
50% {
|
||||||
|
opacity: 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title th:text="${code}">title</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class='error'>
|
||||||
|
ОШИБКА: <span class='bli' th:text="${code}"></span>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -16,6 +16,7 @@
|
||||||
<br /><a href="/h2">H2</a>
|
<br /><a href="/h2">H2</a>
|
||||||
<form class="form-signin" method="post" action="/login">
|
<form class="form-signin" method="post" action="/login">
|
||||||
<h2 class="form-signin-heading">Please sign in</h2>
|
<h2 class="form-signin-heading">Please sign in</h2>
|
||||||
|
<br /><span class="validationError" th:if="${param.error}">Unable to login. Check your username and password.</span>
|
||||||
<p>
|
<p>
|
||||||
<label for="username" class="sr-only">Username</label>
|
<label for="username" class="sr-only">Username</label>
|
||||||
<input type="text" id="username" name="login" class="form-control" placeholder="Username" required autofocus>
|
<input type="text" id="username" name="login" class="form-control" placeholder="Username" required autofocus>
|
||||||
|
|
|
@ -5,20 +5,34 @@
|
||||||
<title>Marinesco - registration form</title>
|
<title>Marinesco - registration form</title>
|
||||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
||||||
|
<script src="@{/jquery.js}"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<h1>Register</h1>
|
<h1>Register</h1>
|
||||||
<img th:src="@{/images/logo.svg}" />
|
<img th:src="@{/images/logo.svg}" />
|
||||||
<form method="POST" th:action="@{/register}" id="registerForm">
|
<form class="regForm" method="POST" th:action="@{/register}" th:object="${registrationForm}">
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('username')}" th:errors="*{username}">Error</span>
|
||||||
|
<br />
|
||||||
<label for="username">Username: </label>
|
<label for="username">Username: </label>
|
||||||
<input type="text" name="login" /><br />
|
<input type="text" name="username" id="username"/><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('password')}" th:errors="*{password}">Error</span>
|
||||||
|
<br />
|
||||||
<label for="password">Password: </label>
|
<label for="password">Password: </label>
|
||||||
<input type="password" name="pwd" /><br />
|
<input type="password" name="password" id="password"/><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${passwordsMismatch} != null" th:text="${passwordsMismatch}">false</span>
|
||||||
|
<br />
|
||||||
<label for="confirm">Confirm password: </label>
|
<label for="confirm">Confirm password: </label>
|
||||||
<input type="password" name="confirm" /><br />
|
<input type="password" name="passwordConfirm" id="passwordConfirm" /><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('displayname')}" th:errors="*{displayname}">Error</span>
|
||||||
|
<br />
|
||||||
<label for="displayname">Displayed name: </label>
|
<label for="displayname">Displayed name: </label>
|
||||||
<input type="text" name="fullname" /><br />
|
<input type="text" name="displayname" id="displayname"/><br />
|
||||||
|
|
||||||
<input type="submit" value="Register" />
|
<input type="submit" value="Register" />
|
||||||
</form>
|
</form>
|
||||||
</body>
|
</body>
|
||||||
|
|
26
target/classes/templates/user_settings.html
Normal file
26
target/classes/templates/user_settings.html
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Marinesco - Profile settings</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||||
|
<link rel="stylesheet" th:href="@{/styles/styles.css}" />
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 th:text="${header_text}">welcome</h1>
|
||||||
|
<form method="POST" th:action="@{/profile/settings}" th:object="${userSettingsForm}"> <!-- -->
|
||||||
|
<span class="validationError" th:if="${#fields.hasErrors('displayname')}" th:errors="*{displayname}">Error</span>
|
||||||
|
<br />
|
||||||
|
<label for="displayname">Displayed name: </label>
|
||||||
|
<input type="text" name="displayname" id="displayname" th:value="${userSettingsForm.displayname}" /><br />
|
||||||
|
|
||||||
|
<span class="validationError" th:if="${password_incorrect} != null" th:text="${password_incorrect}">false</span>
|
||||||
|
<br />
|
||||||
|
<label for="password">New password: </label>
|
||||||
|
<input type="password" name="newPassword" id="newPassword" /><br />
|
||||||
|
|
||||||
|
<input type="submit" value="Save Changes" />
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue