add password modification

This commit is contained in:
PEDSF 2020-11-22 12:11:07 +01:00
parent 285e018b1a
commit 47866a9f5d
65 changed files with 1632 additions and 250 deletions

View file

@ -98,6 +98,13 @@
<version>${spring-cloud-starter-security.version}</version>
</dependency>
<!-- ============================================================ MAIL -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<!-- ========================================================= DATABASES -->
<dependency>
<groupId>com.h2database</groupId>

View file

@ -8,6 +8,7 @@ package org.springframework.samples.petclinic.common;
public final class CommonAttribute {
public static final String DESCRIPTION = "description";
public static final String ID = "id";
public static final String NAME = "name";
@ -15,7 +16,7 @@ public final class CommonAttribute {
public static final String OWNER = "owner";
public static final String OWNER_ID = "id";
public static final String OWNER_ID = "ownerId";
public static final String OWNER_LAST_NAME = "lastName";
@ -39,9 +40,11 @@ public final class CommonAttribute {
public static final String PET_TYPE = "type";
public static final String TOKEN = "token";
public static final String USER = "user";
public static final String USER_ID = "id";
public static final String USER_ID = "userId";
public static final String VETS = "vets";

View file

@ -25,7 +25,9 @@ public final class CommonEndPoint {
public static final String USERS_EDIT = "/users/edit";
public static final String USERS_ID_EDIT = "/users/{ownerId}/edit";
public static final String USERS_ID_EDIT = "/users/{userId}/edit";
public static final String USERS_ID_EDIT_PASSWORD = "/users/{userId}/edit/password";
public static final String USERS_NEW = "/users/new";
@ -35,6 +37,8 @@ public final class CommonEndPoint {
public static final String OAUTH2_SUCCESS = "/oauth2/success";
public static final String CONFIRM_ACCOUNT = "/confirm-account";
public static final String LOGOUT = "/logout";
public static final String LOGOUT_SUCCESS = "/logout/success";

View file

@ -6,6 +6,8 @@ public class CommonParameter {
public static final int COUNTRY_MAX = 50;
public static final String DEFAULT_PROVIDER = "local";
public static final int EMAIL_MAX = 255;
public static final int EMAIL_MIN = 4;
@ -32,6 +34,8 @@ public class CommonParameter {
public static final int STREET_MAX = 50;
public static final int TOKEN_EXPIRATION = 60 * 24;
public static final int ROLE_MAX = 10;
public static final int ZIP_MAX = 6;

View file

@ -23,7 +23,7 @@ public final class CommonView {
public static final String PET_CREATE_OR_UPDATE = "pets/createOrUpdatePetForm";
public static final String USER_REGISTRATION = "users/registration";
public static final String USER_REGISTRATION = "users/userRegistrationForm";
public static final String USER_LOGIN = "/login";
@ -31,7 +31,9 @@ public final class CommonView {
public static final String USER_USERS_ID_R = "redirect:/users/{userId}";
public static final String USER_CREATE_OR_UPDATE = "users/createOrUpdateUserForm";
public static final String USER_UPDATE = "users/userUpdateForm";
public static final String USER_CHANGE_PASSWORD = "users/userChangePasswordForm";
public static final String USER_DETAILS = "users/userDetails";
@ -41,8 +43,6 @@ public final class CommonView {
public static final String USER_READ_R = "redirect:/users/read/";
public static final String USER_UPDATE = "users/user-update";
public static final String USER_UPDATE_R = "redirect:/users/edit/";
public static final String USER_UPDATE_PASSWORD = "users/user-password";

View file

@ -0,0 +1,110 @@
package org.springframework.samples.petclinic.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.StringTemplateResolver;
import java.util.Collections;
@Configuration
public class MailConfig {
public static final String EMAIL_TEMPLATE_ENCODING = "UTF-8";
@Value("${spring.mail.host}")
private String mailHost;
@Value("${spring.mail.port}")
private String mailPort;
@Value("${spring.mail.protocol}")
private String mailProtocol;
@Value("${spring.mail.username}")
private String mailUsername;
@Value("${spring.mail.password}")
private String mailPassword;
@Bean
public JavaMailSender mailSender() {
final JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
// Basic mail sender configuration, based on emailconfig.properties
mailSender.setHost(mailHost);
mailSender.setPort(Integer.parseInt(mailPort));
mailSender.setProtocol(mailProtocol);
mailSender.setUsername(mailUsername);
mailSender.setPassword(mailPassword);
return mailSender;
}
@Bean
public ResourceBundleMessageSource emailMessageSource() {
final ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("mail/MailMessages");
return messageSource;
}
@Bean
public ITemplateEngine emailTemplateEngine() {
final SpringTemplateEngine emailTemplateEngine = new SpringTemplateEngine();
// Resolver for TEXT emails
emailTemplateEngine.addTemplateResolver(textTemplateResolver());
// Resolver for HTML emails (except the editable one)
emailTemplateEngine.addTemplateResolver(htmlTemplateResolver());
// Resolver for HTML editable emails (which will be treated as a String)
emailTemplateEngine.addTemplateResolver(stringTemplateResolver());
// Message source, internationalization specific to emails
emailTemplateEngine.setTemplateEngineMessageSource(emailMessageSource());
return emailTemplateEngine;
}
private ITemplateResolver textTemplateResolver() {
final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(Integer.valueOf(1));
templateResolver.setResolvablePatterns(Collections.singleton("text/*"));
templateResolver.setPrefix("/mail/");
templateResolver.setSuffix(".txt");
templateResolver.setTemplateMode(TemplateMode.TEXT);
templateResolver.setCharacterEncoding(EMAIL_TEMPLATE_ENCODING);
templateResolver.setCacheable(false);
return templateResolver;
}
private ITemplateResolver htmlTemplateResolver() {
final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setOrder(Integer.valueOf(2));
templateResolver.setResolvablePatterns(Collections.singleton("html/*"));
templateResolver.setPrefix("/mail/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding(EMAIL_TEMPLATE_ENCODING);
templateResolver.setCacheable(false);
return templateResolver;
}
private ITemplateResolver stringTemplateResolver() {
final StringTemplateResolver templateResolver = new StringTemplateResolver();
templateResolver.setOrder(Integer.valueOf(3));
// No resolvable pattern, will simply process as a String template everything not
// previously matched
templateResolver.setTemplateMode("HTML5");
templateResolver.setCacheable(false);
return templateResolver;
}
}

View file

@ -6,7 +6,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.samples.petclinic.model.common.AuthProvider;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@ -59,12 +58,13 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:off
http.authorizeRequests()
.antMatchers("/").anonymous()
.antMatchers("/login", "/logout", "/register").permitAll()
.antMatchers("/").permitAll()
.antMatchers("/login", "/logout", "/register","/confirm-account").permitAll()
.antMatchers("/websocket/**", "/topic/**", "/app/**").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers("/**").authenticated()
.antMatchers("/h2-console/**").permitAll()
.antMatchers("/**").authenticated()
.antMatchers("/edit/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
@ -94,10 +94,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// @formatter:on
}
private static final List<String> clients = Arrays.asList("google", "facebook", "github");
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
List<String> clients = Arrays.asList("google", "facebook", "github");
List<ClientRegistration> registrations = clients.stream().map(c -> getRegistration(c))
.filter(registration -> registration != null).collect(Collectors.toList());
@ -113,14 +115,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
String clientSecret = env.getProperty(CLIENT_PROPERTY_KEY + client + ".client-secret");
if (client.equals(AuthProvider.google.name())) {
if (client.equals("google")) {
return CommonOAuth2Provider.GOOGLE.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
}
if (client.equals(AuthProvider.facebook.name())) {
if (client.equals("facebook")) {
return CommonOAuth2Provider.FACEBOOK.getBuilder(client).clientId(clientId).clientSecret(clientSecret)
.build();
}
if (client.equals(AuthProvider.github.name())) {
if (client.equals("github")) {
return CommonOAuth2Provider.GITHUB.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
}

View file

@ -18,8 +18,8 @@ package org.springframework.samples.petclinic.controller;
import org.springframework.samples.petclinic.common.*;
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
import org.springframework.samples.petclinic.dto.*;
import org.springframework.samples.petclinic.service.OwnerService;
import org.springframework.samples.petclinic.service.VisitService;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.VisitService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;

View file

@ -19,8 +19,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.common.*;
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
import org.springframework.samples.petclinic.dto.*;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.samples.petclinic.service.business.PetTypeService;
import org.springframework.samples.petclinic.validator.PetDTOValidator;
import org.springframework.samples.petclinic.service.*;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.StringUtils;

View file

@ -3,23 +3,15 @@ package org.springframework.samples.petclinic.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.samples.petclinic.common.CommonAttribute;
import org.springframework.samples.petclinic.common.CommonEndPoint;
import org.springframework.samples.petclinic.common.CommonView;
import org.springframework.samples.petclinic.common.CommonWebSocket;
import org.springframework.data.repository.query.Param;
import org.springframework.samples.petclinic.common.*;
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
import org.springframework.samples.petclinic.dto.UserDTO;
import org.springframework.samples.petclinic.service.RoleService;
import org.springframework.samples.petclinic.service.SecurityServiceImpl;
import org.springframework.samples.petclinic.service.UserService;
import org.springframework.samples.petclinic.dto.common.CredentialDTO;
import org.springframework.samples.petclinic.dto.common.MessageDTO;
import org.springframework.samples.petclinic.dto.common.UserDTO;
import org.springframework.samples.petclinic.service.common.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
@ -27,18 +19,16 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.security.Principal;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
@ -48,30 +38,25 @@ import java.util.Map;
@Controller
public class UserController extends WebSocketSender {
private static final String ROLE_ADMIN = "ADMIN";
private static final String ROLE_STAFF = "STAFF";
private static final String ROLE_USER = "USER";
private static final String authorizationRequestBaseUri = "oauth2/authorization";
private final UserService userService;
private final CredentialService credentialService;
private final RoleService roleService;
private final SecurityServiceImpl securityService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final EmailService emailService;
public UserController(UserService userService, RoleService roleService, SecurityServiceImpl securityService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
public UserController(UserService userService, CredentialService credentialService, RoleService roleService, SecurityServiceImpl securityService, EmailService emailService) {
this.userService = userService;
this.credentialService = credentialService;
this.roleService = roleService;
this.securityService = securityService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.emailService = emailService;
}
@InitBinder("user")
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(CommonAttribute.USER_ID);
@ -89,36 +74,48 @@ public class UserController extends WebSocketSender {
public String initCreationForm(Map<String, Object> model) {
UserDTO user = new UserDTO();
model.put(CommonAttribute.USER, user);
return CommonView.USER_CREATE_OR_UPDATE;
return CommonView.USER_REGISTRATION;
}
@PostMapping(CommonEndPoint.REGISTER)
public String processCreationForm(@ModelAttribute(CommonAttribute.USER) @Valid UserDTO user, BindingResult result) {
if (result.hasErrors()) {
sendErrorMessage(CommonWebSocket.USER_CREATION_ERROR);
return CommonView.USER_CREATE_OR_UPDATE;
return CommonView.USER_REGISTRATION;
}
try {
userService.findByEmail(user.getEmail());
if(userService.existByEmail(user.getEmail())) {
result.rejectValue("email", "5", "Email already exist !");
sendErrorMessage(CommonWebSocket.USER_CREATION_ERROR);
return CommonView.USER_CREATE_OR_UPDATE;
}
catch (Exception ex) {
return CommonView.USER_REGISTRATION;
}
// set default role
user.addRole(roleService.findByName(ROLE_USER));
user.addRole(roleService.findByName("ROLE_USER"));
// encode password because we get clear password
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
user.setMatchingPassword(user.getPassword());
user.encode(user.getPassword());
user = this.userService.save(user);
sendSuccessMessage(CommonWebSocket.USER_CREATED);
return CommonView.HOME + user.getId();
CredentialDTO credential = new CredentialDTO(user);
credential = credentialService.save(credential);
sendSuccessMessage(CommonWebSocket.USER_CREATED);
// send confirmation mail
MessageDTO message = new MessageDTO(
user.getFirstName(), user.getLastName(),
"admin@petclinic.com",
user.getEmail(),
"New connexion",
"Your attempt to create new account. To confirm your account, please click here : ",
"http://localhost:8080/confirm-account?token=" + credential.getToken());
// emailService.sendMailAsynch(message, Locale.getDefault());
log.info(message.toString());
return CommonView.HOME + user.getId();
}
@GetMapping(CommonEndPoint.LOGIN)
@ -140,7 +137,7 @@ public class UserController extends WebSocketSender {
}
clientRegistrations.forEach(registration -> oauth2AuthenticationUrls.put(registration.getClientName(),
authorizationRequestBaseUri + "/" + registration.getRegistrationId()));
"oauth2/authorization/" + registration.getRegistrationId()));
model.put("urls", oauth2AuthenticationUrls);
return CommonView.USER_LOGIN;
@ -148,30 +145,85 @@ public class UserController extends WebSocketSender {
@GetMapping(CommonEndPoint.LOGIN_SUCCESS)
public String postLogin(Model model, Authentication authentication) {
UserDTO user = userService.findByEmail(authentication.getName());
UserDTO user = (UserDTO) authentication.getPrincipal();
model.addAttribute(CommonAttribute.USER, user);
String message = String.format(CommonWebSocket.USER_LOGGED_IN, user.getFirstName(), user.getLastName());
sendSuccessMessage(message );
sendSuccessMessage(message);
return CommonView.HOME;
}
@GetMapping(CommonEndPoint.OAUTH2_SUCCESS)
public String postLogin(Model model, OAuth2AuthenticationToken authentication) {
String firstName = authentication.getPrincipal().getAttribute("given_name");
String lastName = authentication.getPrincipal().getAttribute("family_name");
OAuth2AuthorizedClient client = authorizedClientService .loadAuthorizedClient(authentication.getAuthorizedClientRegistrationId(), authentication.getName());
CredentialDTO credential = credentialService.findByAuthentication(authentication);
String userInfoEndpointUri = client.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();
if( credential.isNew()) {
UserDTO user = userService.findByEmail(authentication.getName());
// first time authentification with this provider
credential = credentialService.saveNew(authentication);
String email = credential.getEmail();
if( user!=null) {
model.addAttribute(CommonAttribute.USER, user);
UserDTO user = userService.findByEmail(email);
String message = String.format(CommonWebSocket.USER_LOGGED_IN, user.getFirstName(), user.getLastName());
if(user == null) {
user = new UserDTO();
user.setEmail(email);
user.encode(credential.getPassword());
user.setFirstName(firstName);
user.setLastName(lastName);
user.setEnabled(true);
user.addRole(roleService.findByName("ROLE_USER"));
user = userService.save(user);
}
// send confirmation mail
MessageDTO message = new MessageDTO(
firstName, lastName,
"admin@petclinic.com",
credential.getEmail(),
"New connexion from " + credential.getProvider(),
"Your attempt to connect from " + credential.getProvider() + " To confirm this connection, please click the link below : ",
"http://localhost:8080/confirm-account?token=" + credential.getToken());
log.info(message.toString());
emailService.sendMailAsynch(message, Locale.getDefault());
// disconnect
authentication.eraseCredentials();
SecurityContextHolder.clearContext();
} else if( credential.isVerified()) {
securityService.autoLogin(credential.getEmail(),credential.getPassword());
String message = String.format(CommonWebSocket.USER_LOGGED_IN, firstName, lastName);
sendSuccessMessage(message);
}
return CommonView.HOME;
}
@RequestMapping(value = CommonEndPoint.CONFIRM_ACCOUNT, method = { RequestMethod.GET, RequestMethod.POST })
public String confirmUserAccount(@RequestParam(CommonAttribute.TOKEN) String token, Model model) {
CredentialDTO credential = credentialService.findByToken(token);
if (!credential.isNew() && credential.isNotExpired()) {
credential.setVerified(true);
credential.setToken("");
credential.setExpiration(null);
credential = credentialService.save(credential);
// find corresponding user
UserDTO user = userService.findByEmail(credential.getEmail());
securityService.autoLogin(credential.getEmail(),credential.getPassword());
model.addAttribute(CommonAttribute.USER, user);
return CommonView.USER_UPDATE;
}
return CommonView.HOME;
}
@ -188,43 +240,48 @@ public class UserController extends WebSocketSender {
@GetMapping(CommonEndPoint.LOGOUT_SUCCESS)
public String postLogout(Model model) {
sendSuccessMessage(CommonWebSocket.USER_LOGGED_OUT);
return CommonView.HOME;
}
@GetMapping(CommonEndPoint.USERS_EDIT)
public String initUpdateOwnerForm(Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (!authentication.getName().equals("anonymousUser")) {
UserDTO user = userService.findByEmail(authentication.getName());
try {
UserDTO user = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
model.addAttribute(CommonAttribute.USER, user);
return CommonView.USER_CREATE_OR_UPDATE;
model.addAttribute(CommonAttribute.USER_ID, user.getId());
return CommonView.USER_UPDATE;
} catch (Exception exception) {
// user don't have profile
}
return CommonView.HOME;
}
@PostMapping(CommonEndPoint.USERS_ID_EDIT)
@PostMapping(CommonEndPoint.USERS_EDIT)
public String processUpdateOwnerForm(@ModelAttribute(CommonAttribute.USER) @Valid UserDTO user,
BindingResult result, @PathVariable("userId") int userId) {
BindingResult result, Model model) {
if (result.hasErrors()) {
sendErrorMessage(CommonWebSocket.USER_UPDATED_ERROR);
return CommonView.USER_CREATE_OR_UPDATE;
return CommonView.USER_UPDATE;
}
else {
user.setId(userId);
this.userService.save(user);
if(!user.getPassword().equals(user.getMatchingPassword())) {
sendErrorMessage(CommonWebSocket.USER_UPDATED_ERROR);
return CommonView.USER_UPDATE;
}
else {
user = userService.save(user);
model.addAttribute(CommonAttribute.USER, user);
sendSuccessMessage(CommonWebSocket.USER_UPDATED);
return CommonView.USER_USERS_ID_R;
return CommonView.HOME;
}
}
@GetMapping(CommonEndPoint.USERS_ID)
public ModelAndView showOwner(@PathVariable("userId") int userId) {
public ModelAndView showOwner(@PathVariable("userId") Integer userId) {
ModelAndView modelAndView = new ModelAndView(CommonView.USER_DETAILS);
UserDTO user = this.userService.findById(userId);
@ -232,4 +289,62 @@ public class UserController extends WebSocketSender {
return modelAndView;
}
@GetMapping("/user/{userId}/edit/password")
public String editPassword(@PathVariable("userId") Integer userId, Model model){
try {
UserDTO operator = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
UserDTO user = userService.findById(userId);
if (user.equals(operator) || operator.getRoles().contains(roleService.findByName("ROLE_ADMIN"))) {
model.addAttribute(CommonAttribute.USER, user);
model.addAttribute(CommonAttribute.USER_ID, user.getId());
return CommonView.USER_CHANGE_PASSWORD;
}
} catch (Exception exception) {
// user don't have profile
}
return CommonView.HOME;
}
@PostMapping("/user/{userId}/edit/password")
public String updatePassword(@ModelAttribute(CommonAttribute.USER) @Valid UserDTO user, BindingResult bindingResult,
@PathVariable(CommonAttribute.USER_ID) Integer userId,
@Param("oldPassword") String oldPassword,
@Param("newPassword") String newPassword,
@Param("newMatchingPassword") String newMatchingPassword, Model model) {
// verify the matching with old password
if(!user.matches(oldPassword)){
bindingResult.rejectValue("password", "6", "Bad password !");
model.addAttribute(CommonAttribute.USER, user);
return CommonView.USER_CHANGE_PASSWORD;
}
// verify matching between two password
if(!newPassword.equals(newMatchingPassword)){
bindingResult.rejectValue("password", "7", "Bad matching password !");
model.addAttribute(CommonAttribute.USER, user);
return CommonView.USER_CHANGE_PASSWORD;
}
try {
UserDTO operator = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (user.equals(operator) || operator.getRoles().contains(roleService.findByName("ROLE_ADMIN"))) {
// encode password
user.encode(newPassword);
user = userService.save(user);
model.addAttribute(CommonAttribute.USER, user);
return CommonView.USER_UPDATE_R;
}
} catch (NullPointerException exception) {
log.error(exception.getMessage());
}
return CommonView.HOME;
}
}

View file

@ -21,7 +21,7 @@ import org.springframework.samples.petclinic.common.CommonView;
import org.springframework.samples.petclinic.common.CommonWebSocket;
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
import org.springframework.samples.petclinic.dto.VetsDTO;
import org.springframework.samples.petclinic.service.VetService;
import org.springframework.samples.petclinic.service.business.VetService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

View file

@ -26,8 +26,8 @@ import org.springframework.samples.petclinic.common.CommonWebSocket;
import org.springframework.samples.petclinic.controller.common.WebSocketSender;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.VisitService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.samples.petclinic.service.business.VisitService;
import org.springframework.samples.petclinic.validator.VisitDTOValidator;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;

View file

@ -0,0 +1,12 @@
package org.springframework.samples.petclinic.dto.common;
import org.springframework.samples.petclinic.dto.NamedDTO;
/**
* Simple Data Transfert Object representing a Authorization Provider.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
public class AuthProviderDTO extends NamedDTO {
}

View file

@ -0,0 +1,122 @@
package org.springframework.samples.petclinic.dto.common;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.common.CommonParameter;
import org.springframework.samples.petclinic.dto.BaseDTO;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;
/**
* Simple Data Transfert Object representing a Credential.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
public class CredentialDTO extends BaseDTO {
@NotNull
private String provider;
@NotNull
@Size(min = CommonParameter.EMAIL_MIN, max = CommonParameter.EMAIL_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.EMAIL_MIN + " AND " + CommonParameter.EMAIL_MAX + " !")
@Pattern(regexp = CommonParameter.EMAIL_REGEXP, message = CommonError.EMAIL_FORMAT)
private String email;
@NotNull
private Boolean verified;
private String token;
private Date expiration;
@NotNull
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
private String password;
public CredentialDTO(UserDTO user) {
this.verified = false;
this.setToken();
this.setExpiration();
this.setProvider(CommonParameter.DEFAULT_PROVIDER);
this.email = user.getEmail();
this.password = user.getId().toString();
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public void setDefaultProvider() {
this.provider = CommonParameter.DEFAULT_PROVIDER;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean isVerified() {
return verified;
}
public void setVerified(Boolean verified) {
this.verified = verified;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Date getExpiration() {
return expiration;
}
public void setExpiration(Date expiration) {
this.expiration = expiration;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setExpiration() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Timestamp(cal.getTime().getTime()));
cal.add(Calendar.MINUTE, CommonParameter.TOKEN_EXPIRATION);
this.expiration = cal.getTime();
}
public void setToken() {
this.token = UUID.randomUUID().toString();
}
public boolean isNotExpired() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Timestamp(cal.getTime().getTime()));
return this.expiration.after(Date.from(Instant.now()));
}
}

View file

@ -0,0 +1,126 @@
package org.springframework.samples.petclinic.dto.common;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.common.CommonParameter;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.io.Serializable;
public class MessageDTO implements Serializable {
@NotNull
@Size(min = CommonParameter.FIRSTNAME_MIN, max = CommonParameter.FIRSTNAME_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.FIRSTNAME_MIN + " AND " + CommonParameter.FIRSTNAME_MAX + " !")
private String firstName;
@NotNull
@Size(min = CommonParameter.LASTNAME_MIN, max = CommonParameter.LASTNAME_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.LASTNAME_MIN + " AND " + CommonParameter.LASTNAME_MAX + " !")
private String lastName;
@NotNull
@Size(min = CommonParameter.EMAIL_MIN, max = CommonParameter.EMAIL_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.EMAIL_MIN + " AND " + CommonParameter.EMAIL_MAX + " !")
@Pattern(regexp = CommonParameter.EMAIL_REGEXP, message = CommonError.EMAIL_FORMAT)
private String from;
@NotNull
@Size(min = CommonParameter.EMAIL_MIN, max = CommonParameter.EMAIL_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.EMAIL_MIN + " AND " + CommonParameter.EMAIL_MAX + " !")
@Pattern(regexp = CommonParameter.EMAIL_REGEXP, message = CommonError.EMAIL_FORMAT)
private String to;
@NotNull
private String subject;
@NotNull
private String content;
private String link;
@JsonCreator
public MessageDTO(@JsonProperty("firstName") String firstName, @JsonProperty("lastName") String lastName,
@JsonProperty("from") String from, @JsonProperty("to") String to, @JsonProperty("subject") String subject,
@JsonProperty("content") String content, @JsonProperty("link") String link) {
this.firstName = firstName;
this.lastName = lastName;
this.from = from;
this.to = to;
this.subject = subject;
this.content = content;
this.link = link;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
@Override
public String toString() {
return "MessageDTO{" +
"first name='" + firstName + '\'' +
", last name='" + lastName + '\'' +
", from='" + from + '\'' +
", to='" + to + '\'' +
", subject='" + subject + '\'' +
", content='" + content + '\'' +
", link='" + link + '\'' +
'}';
}
}

View file

@ -1,4 +1,6 @@
package org.springframework.samples.petclinic.dto;
package org.springframework.samples.petclinic.dto.common;
import org.springframework.samples.petclinic.dto.NamedDTO;
import java.io.Serializable;
@ -8,5 +10,4 @@ import java.io.Serializable;
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
public class RoleDTO extends NamedDTO implements Serializable {
}

View file

@ -1,37 +1,48 @@
package org.springframework.samples.petclinic.dto;
package org.springframework.samples.petclinic.dto.common;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.common.CommonParameter;
import org.springframework.samples.petclinic.dto.PersonDTO;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlElement;
import java.io.Serializable;
import java.util.*;
public class UserDTO extends PersonDTO implements Serializable {
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
private String password;
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
private String matchingPassword;
public class UserDTO extends PersonDTO implements Serializable, UserDetails {
@Size(min = CommonParameter.EMAIL_MIN, max = CommonParameter.EMAIL_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.EMAIL_MIN + " AND " + CommonParameter.EMAIL_MAX + " !")
@Pattern(regexp = CommonParameter.EMAIL_REGEXP, message = CommonError.EMAIL_FORMAT)
private String email;
@Size(max = CommonParameter.PHONE_MAX, message = CommonError.FORMAT_LESS + CommonParameter.PHONE_MAX)
@Pattern(regexp = CommonParameter.PHONE_REGEXP, message = CommonError.PHONE_FORMAT)
private String telephone;
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
private String password;
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
private String matchingPassword;
private boolean enabled;
private boolean accountNonExpired;
private boolean accountNonLocked;
private boolean credentialsNonExpired;
private Set<RoleDTO> roles;
@Size(max = CommonParameter.PHONE_MAX, message = CommonError.FORMAT_LESS + CommonParameter.PHONE_MAX)
// @Pattern(regexp = CommonParameter.PHONE_REGEXP, message = CommonError.PHONE_FORMAT)
private String telephone;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
private String street1;
@ -51,6 +62,28 @@ public class UserDTO extends PersonDTO implements Serializable {
@Size(max = CommonParameter.COUNTRY_MAX, message = CommonError.FORMAT_LESS + CommonParameter.COUNTRY_MAX + " !")
private String country;
public UserDTO() {
super();
this.enabled = false;
this.accountNonLocked = true;
this.accountNonExpired = true;
this.credentialsNonExpired = true;
}
@Override
public String getUsername() {
return email;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String getPassword() {
return password;
}
@ -67,20 +100,49 @@ public class UserDTO extends PersonDTO implements Serializable {
this.matchingPassword = matchingPassword;
}
public String getEmail() {
return email;
@Override
public boolean isEnabled() {
return enabled;
}
public void setEmail(String email) {
this.email = email;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getTelephone() {
return telephone;
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
this.roles.forEach(role -> grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())));
return grantedAuthorities;
}
protected Set<RoleDTO> getRolesInternal() {
@ -101,10 +163,27 @@ public class UserDTO extends PersonDTO implements Serializable {
return Collections.unmodifiableList(sortedRoles);
}
public int getNrOfRoles() {
return getRolesInternal().size();
}
public void addRole(RoleDTO role) {
getRolesInternal().add(role);
}
public void setRoles(Set<RoleDTO> roles) {
this.roles = roles;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getStreet1() {
return street1;
}
@ -153,4 +232,37 @@ public class UserDTO extends PersonDTO implements Serializable {
this.country = country;
}
@Override
public String toString() {
return "UserDTO{" +
"email='" + email + '\'' +
", password='" + password + '\'' +
", matchingPassword='" + matchingPassword + '\'' +
", user enabled=" + enabled +
", account not expired=" + accountNonExpired +
", account not locked=" + accountNonLocked +
", credentials not xxpired=" + credentialsNonExpired +
", roles=" + roles +
", telephone='" + telephone + '\'' +
", street1='" + street1 + '\'' +
", street2='" + street2 + '\'' +
", street3='" + street3 + '\'' +
", zipCode='" + zipCode + '\'' +
", city='" + city + '\'' +
", country='" + country + '\'' +
'}';
}
public void encode(String rawPassword) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
this.password = bCryptPasswordEncoder.encode(rawPassword);
this.matchingPassword = this.password;
}
public boolean matches(String rawPassword) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder.matches(rawPassword, this.password);
}
}

View file

@ -21,8 +21,7 @@ import java.util.Locale;
import org.springframework.format.Formatter;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.PetTypeService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.stereotype.Component;
/**

View file

@ -1,7 +1,15 @@
package org.springframework.samples.petclinic.model.common;
public enum AuthProvider {
import javax.persistence.Entity;
import javax.persistence.Table;
local, facebook, google, github
/**
* Class used to manage Authorization providers
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Entity(name = "AuthProvider")
@Table(name = "auth_providers")
public class AuthProvider extends NamedEntity {
}

View file

@ -0,0 +1,112 @@
package org.springframework.samples.petclinic.model.common;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.common.CommonParameter;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;
/**
* Class used to manage Credentials for users
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Entity(name = "Credential")
@Table(name = "credentials")
public class Credential extends BaseEntity {
private static final int TOKEN_EXPIRATION = 60 * 24;
@NotNull
@Column(name = "provider_id")
private Integer providerId;
@NotNull
@Size(min = CommonParameter.EMAIL_MIN, max = CommonParameter.EMAIL_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.EMAIL_MIN + " AND " + CommonParameter.EMAIL_MAX + " !")
@Pattern(regexp = CommonParameter.EMAIL_REGEXP, message = CommonError.EMAIL_FORMAT)
@Column(name = "email", length = CommonParameter.EMAIL_MAX)
private String email;
@NotNull
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
@Column(name = "password", length = CommonParameter.PASSWORD_MAX)
private String password;
@NotNull
@Column(name = "verified")
private Boolean verified;
@Column(name = "token")
private String token;
@Column(name = "expiration")
private Date expiration;
public Integer getProviderId() {
return providerId;
}
public void setProviderId(Integer providerId) {
this.providerId = providerId;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean isVerified() {
return verified;
}
public void setVerified(Boolean verified) {
this.verified = verified;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Date getExpiration() {
return expiration;
}
public void setExpiration(Date expirationDate) {
this.expiration = expirationDate;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public void setExpiration() {
Calendar cal = Calendar.getInstance();
cal.setTime(new Timestamp(cal.getTime().getTime()));
cal.add(Calendar.MINUTE, TOKEN_EXPIRATION);
this.expiration = cal.getTime();
}
public void setToken() {
this.token = UUID.randomUUID().toString();
}
}

View file

@ -1,7 +1,5 @@
package org.springframework.samples.petclinic.model.common;
import org.springframework.samples.petclinic.model.common.NamedEntity;
import javax.persistence.*;
import java.io.Serializable;

View file

@ -4,6 +4,9 @@ import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.common.CommonParameter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
@ -20,7 +23,7 @@ import java.util.*;
*/
@Entity(name = "User")
@Table(name = "users")
public class User extends Person implements Serializable {
public class User extends Person implements Serializable, UserDetails {
@NotNull
@Size(min = CommonParameter.EMAIL_MIN, max = CommonParameter.EMAIL_MAX, message = CommonError.FORMAT_BETWEEN
@ -29,34 +32,38 @@ public class User extends Person implements Serializable {
@Column(name = "email", unique = true, length = CommonParameter.EMAIL_MAX)
private String email;
@NotNull
@Column(name = "email_verified")
private Boolean emailVerified = false;
@NotNull
@Size(min = CommonParameter.PASSWORD_MIN, max = CommonParameter.PASSWORD_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
+ CommonParameter.PASSWORD_MIN + " AND " + CommonParameter.PASSWORD_MAX + " !")
@Column(name = "password", length = CommonParameter.PASSWORD_MAX)
private String password;
@NotNull
@Enumerated(EnumType.STRING)
private AuthProvider provider;
@Column(name = "enabled")
private boolean enabled;
@Column(name = "provider_id")
private String providerId;
@NotNull
@Column(name = "account_unexpired")
private boolean accountNonExpired;
@Size(max = CommonParameter.PHONE_MAX, message = CommonError.FORMAT_LESS + CommonParameter.PHONE_MAX)
@Pattern(regexp = CommonParameter.PHONE_REGEXP, message = CommonError.PHONE_FORMAT)
@Column(name = "telephone", length = CommonParameter.EMAIL_MAX)
private String telephone;
@NotNull
@Column(name = "account_unlocked")
private boolean accountNonLocked;
@NotNull
@Column(name = "credential_unexpired")
private boolean credentialsNonExpired;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;
@NotNull
@Size(max = CommonParameter.PHONE_MAX, message = CommonError.FORMAT_LESS + CommonParameter.PHONE_MAX)
// @Pattern(regexp = CommonParameter.PHONE_REGEXP, message = CommonError.PHONE_FORMAT)
@Column(name = "telephone", length = CommonParameter.EMAIL_MAX)
private String telephone;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
@Column(name = "street1", length = CommonParameter.STREET_MAX)
private String street1;
@ -69,12 +76,10 @@ public class User extends Person implements Serializable {
@Column(name = "street3", length = CommonParameter.STREET_MAX)
private String street3;
@NotNull
@Size(max = CommonParameter.ZIP_MAX, message = CommonError.FORMAT_LESS + CommonParameter.ZIP_MAX + " !")
@Column(name = "zip_code", length = CommonParameter.ZIP_MAX)
private String zipCode;
@NotNull
@Size(max = CommonParameter.CITY_MAX, message = CommonError.FORMAT_LESS + CommonParameter.CITY_MAX + " !")
@Column(name = "city", length = CommonParameter.CITY_MAX)
private String city;
@ -83,6 +88,11 @@ public class User extends Person implements Serializable {
@Column(name = "country", length = CommonParameter.COUNTRY_MAX)
private String country;
@Override
public String getUsername() {
return email;
}
public String getEmail() {
return email;
}
@ -91,14 +101,7 @@ public class User extends Person implements Serializable {
this.email = email;
}
public Boolean getEmailVerified() {
return emailVerified;
}
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}
@Override
public String getPassword() {
return password;
}
@ -107,28 +110,50 @@ public class User extends Person implements Serializable {
this.password = password;
}
public AuthProvider getProvider() {
return provider;
@Override
public boolean isEnabled() {
return enabled;
}
public void setProvider(AuthProvider provider) {
this.provider = provider;
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getProviderId() {
return providerId;
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
public void setAccountNonExpired(boolean accountNonExpired) {
this.accountNonExpired = accountNonExpired;
}
public String getTelephone() {
return telephone;
@Override
public boolean isAccountNonLocked() {
return accountNonLocked;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
public void setAccountNonLocked(boolean accountNonLocked) {
this.accountNonLocked = accountNonLocked;
}
@Override
public boolean isCredentialsNonExpired() {
return credentialsNonExpired;
}
public void setCredentialsNonExpired(boolean credentialsNonExpired) {
this.credentialsNonExpired = credentialsNonExpired;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
this.roles.forEach(role -> grantedAuthorities.add(new SimpleGrantedAuthority(role.getName())));
return grantedAuthorities;
}
protected Set<Role> getRolesInternal() {
@ -157,6 +182,20 @@ public class User extends Person implements Serializable {
getRolesInternal().add(role);
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getStreet1() {
return street1;
}
@ -205,4 +244,5 @@ public class User extends Person implements Serializable {
this.country = country;
}
}

View file

@ -0,0 +1,43 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.common.AuthProvider;
import java.util.List;
/**
* Repository class for <code>AuthProvider</code> domain objects All method names are
* compliant with Spring Data naming conventions so this interface can easily be extended
* for Spring
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
public interface AuthProviderRepository extends Repository<AuthProvider, Integer> {
/**
* Retrieve a {@link AuthProvider} from the data store by id.
* @param providerId the id to search for
* @return the {@link AuthProvider} if found
*/
AuthProvider findById(Integer providerId);
/**
* Retrieve a {@link AuthProvider} from the data store by id.
* @param providerName the name to search for
* @return the {@link AuthProvider} if found
*/
AuthProvider findByName(String providerName);
/**
* Retrieve all {@link AuthProvider}s from the data store
* @return a Collection of {@link AuthProvider}s (or an empty Collection if none
*/
List<AuthProvider> findAll();
/**
* Save a {@link AuthProvider} to the data store, either inserting or updating it.
* @param authProvider the {@link AuthProvider} to save
*/
AuthProvider save(AuthProvider authProvider);
}

View file

@ -0,0 +1,51 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.query.Param;
import org.springframework.samples.petclinic.model.common.Credential;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Repository class for <code>Credential</code> domain objects All method names are
* compliant with Spring Data naming conventions so this interface can easily be extended
* for Spring
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
public interface CredentialRepository extends Repository<Credential, Integer> {
/**
* Retrieve a {@link Credential} from the data store by email.
* @param email the email to search for
* @param providerId the provider to search for authorization
* @return the {@link Credential} if found
*/
@Query("SELECT c FROM Credential c WHERE c.email = :email AND c.providerId = :providerId")
@Transactional(readOnly = true)
Credential findByEmailAndProvider(@Param("email") String email, @Param("providerId") Integer providerId);
/**
* * Retrieve a {@link Credential} from the data store by token.
* @param token the token to search for
* @return the {@link Credential} if found
*/
@Query("SELECT DISTINCT c FROM Credential c WHERE c.token = :token")
@Transactional(readOnly = true)
Credential findByToken(@Param("token") String token);
/**
* Retrieve all {@link Credential}s from the data store
* @return a Collection of {@link Credential}s (or an empty Collection if none
*/
List<Credential> findAll();
/**
* Save a {@link Credential} to the data store, either inserting or updating it.
* @param credential the {@link Credential} to save
*/
Credential save(Credential credential);
}

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import java.util.List;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import org.modelmapper.ModelMapper;
import org.modelmapper.internal.util.Lists;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.OwnerDTO;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.PetTypeDTO;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.SpecialtyDTO;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import org.modelmapper.ModelMapper;
import org.modelmapper.internal.util.Lists;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.business;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.VisitDTO;

View file

@ -0,0 +1,87 @@
package org.springframework.samples.petclinic.service.common;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.common.AuthProviderDTO;
import org.springframework.samples.petclinic.model.common.AuthProvider;
import org.springframework.samples.petclinic.repository.AuthProviderRepository;
import org.springframework.samples.petclinic.service.business.BaseService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Simple Service between AuthProvider entity and AuthProviderDTO Data Transfert Object.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Service("AuthProviderService")
public class AuthProviderService implements BaseService<AuthProvider, AuthProviderDTO> {
private final AuthProviderRepository authProviderRepository;
private final ModelMapper modelMapper = new ModelMapper();
public AuthProviderService(AuthProviderRepository authProviderRepository) {
this.authProviderRepository = authProviderRepository;
}
@Override
public AuthProvider dtoToEntity(AuthProviderDTO dto) {
if (dto != null) {
return modelMapper.map(dto, AuthProvider.class);
}
return new AuthProvider();
}
@Override
public AuthProviderDTO entityToDTO(AuthProvider entity) {
if (entity != null) {
return modelMapper.map(entity, AuthProviderDTO.class);
}
return new AuthProviderDTO();
}
@Override
public List<AuthProviderDTO> entitiesToDTOS(List<AuthProvider> entities) {
List<AuthProviderDTO> dtos = new ArrayList<>();
entities.forEach(entity -> dtos.add(entityToDTO(entity)));
return dtos;
}
@Override
public List<AuthProvider> dtosToEntities(List<AuthProviderDTO> dtos) {
List<AuthProvider> entities = new ArrayList<>();
dtos.forEach(dto -> entities.add(dtoToEntity(dto)));
return entities;
}
@Override
public AuthProviderDTO findById(int providerId) {
return entityToDTO(authProviderRepository.findById(providerId));
}
@Override
public List<AuthProviderDTO> findAll() {
return entitiesToDTOS(authProviderRepository.findAll());
}
@Override
public AuthProviderDTO save(AuthProviderDTO dto) {
AuthProvider authProvider = dtoToEntity(dto);
authProvider = authProviderRepository.save(authProvider);
return entityToDTO(authProvider);
}
public AuthProviderDTO findByName(String providerName) {
return entityToDTO(authProviderRepository.findByName(providerName));
}
}

View file

@ -0,0 +1,129 @@
package org.springframework.samples.petclinic.service.common;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.common.CommonParameter;
import org.springframework.samples.petclinic.dto.common.CredentialDTO;
import org.springframework.samples.petclinic.dto.common.UserDTO;
import org.springframework.samples.petclinic.model.common.AuthProvider;
import org.springframework.samples.petclinic.model.common.Credential;
import org.springframework.samples.petclinic.repository.AuthProviderRepository;
import org.springframework.samples.petclinic.repository.CredentialRepository;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service;
/**
* Simple Service between User entity and UserDTO Data Transfert Object.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Service("CredentialService")
public class CredentialService {
private final CredentialRepository credentialRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
private final AuthProviderRepository authProviderRepository;
private final ModelMapper modelMapper = new ModelMapper();
public CredentialService(CredentialRepository credentialRepository, BCryptPasswordEncoder bCryptPasswordEncoder, AuthProviderRepository authProviderRepository) {
this.credentialRepository = credentialRepository;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
this.authProviderRepository = authProviderRepository;
}
public Credential dtoToEntity(CredentialDTO dto) {
if (dto != null) {
Credential entity = modelMapper.map(dto, Credential.class);
AuthProvider authProvider = authProviderRepository.findByName(dto.getProvider());
if (authProvider == null) {
authProvider = authProviderRepository.findByName(CommonParameter.DEFAULT_PROVIDER);
}
entity.setProviderId(authProvider.getId());
return entity;
}
return new Credential();
}
public CredentialDTO entityToDTO(Credential entity) {
if (entity != null) {
CredentialDTO dto = modelMapper.map(entity, CredentialDTO.class);
AuthProvider authProvider = authProviderRepository.findById(entity.getProviderId());
if (authProvider == null) {
dto.setProvider(CommonParameter.DEFAULT_PROVIDER);
}
else {
dto.setProvider(authProvider.getName());
}
return dto;
}
return new CredentialDTO();
}
public CredentialDTO findByEmailAndProvider(String email, String provider) {
AuthProvider authProvider = authProviderRepository.findByName(provider);
Credential credential = credentialRepository.findByEmailAndProvider(email, authProvider.getId());
return entityToDTO(credential);
}
public CredentialDTO findByToken(String token) {
Credential credential = credentialRepository.findByToken(token);
return entityToDTO(credential);
}
public CredentialDTO findByAuthentication(OAuth2AuthenticationToken authentication) {
String email = authentication.getPrincipal().getAttribute("email");
String provider = authentication.getAuthorizedClientRegistrationId();
AuthProvider authProvider = authProviderRepository.findByName(provider);
Credential credential = credentialRepository.findByEmailAndProvider(email, authProvider.getId());
return entityToDTO(credential);
}
public CredentialDTO save(CredentialDTO dto) {
Credential credential = dtoToEntity(dto);
credential = credentialRepository.save(credential);
return entityToDTO(credential);
}
public CredentialDTO saveNew(UserDTO user) {
Credential credential = new Credential();
AuthProvider authProvider = authProviderRepository.findByName(CommonParameter.DEFAULT_PROVIDER);
credential.setEmail(user.getEmail());
credential.setProviderId(authProvider.getId());
credential.setPassword(user.getPassword());
credential.setVerified(false);
credential.setToken();
credential.setExpiration();
credential = credentialRepository.save(credential);
return entityToDTO(credential);
}
public CredentialDTO saveNew(OAuth2AuthenticationToken authentication) {
Credential credential = new Credential();
AuthProvider authProvider = authProviderRepository.findByName(authentication.getAuthorizedClientRegistrationId());
credential.setEmail(authentication.getPrincipal().getAttribute("email"));
credential.setProviderId(authProvider.getId());
credential.setPassword(authentication.getPrincipal().getAttribute("sub"));
credential.setVerified(false);
credential.setToken();
credential.setExpiration();
credential = credentialRepository.save(credential);
return entityToDTO(credential);
}
}

View file

@ -0,0 +1,97 @@
package org.springframework.samples.petclinic.service.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.samples.petclinic.dto.common.MessageDTO;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.context.Context;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.util.Date;
import java.util.Locale;
@Slf4j
@Service("EmailService")
public class EmailService {
private static final String EMAIL_TEMPLATE = "email.html";
private static final String PNG_MIME = "image/png";
@Value("${mail.background}")
private String mailBackground;
@Value("${mail.banner}")
private String mailBanner;
@Value("${mail.logo}")
private String mailLogo;
@Autowired
protected JavaMailSender mailSender;
@Autowired
protected ITemplateEngine templateEngine;
/**
* sendMailAsynch : for the controller MailController
* send mail asynchronously
*
* @param messageDTO : message to be send by mail
* @param locale : not used now
*/
@Async
public void sendMailAsynch(MessageDTO messageDTO, Locale locale){
sendMail(messageDTO, locale);
}
/**
* sendMail : build mail according to a Thymeleaf template and a MessageDTO object
* @param messageDTO : message to be send by mail
* @param locale : not used now
*/
public void sendMail(MessageDTO messageDTO, Locale locale) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper message;
// Prepare the evaluation context
Context ctx = new Context(locale);
ctx.setVariable("toFirstName", messageDTO.getFirstName());
ctx.setVariable("toLastName", messageDTO.getLastName());
ctx.setVariable("mailSubject", messageDTO.getSubject());
ctx.setVariable("mailContent", messageDTO.getContent());
ctx.setVariable("mailLink", messageDTO.getLink());
ctx.setVariable("mailDate", new Date());
// Create the HTML body using Thymeleaf
String output = templateEngine.process(EMAIL_TEMPLATE, ctx);
try {
message = new MimeMessageHelper(mimeMessage, true /* multipart */, "UTF-8");
message.setFrom(messageDTO.getFrom());
message.setTo(messageDTO.getTo());
message.setSubject(messageDTO.getSubject());
message.setText(output, true /* isHtml */);
message.addInline("mailBackground", new ClassPathResource(mailBackground), PNG_MIME);
message.addInline("mailBanner", new ClassPathResource(mailBanner), PNG_MIME);
message.addInline("mailLogo", new ClassPathResource(mailLogo), PNG_MIME);
}
catch (MessagingException ex) {
log.error("Error sending mail !", ex);
}
// Send mail
this.mailSender.send(mimeMessage);
}
}

View file

@ -1,9 +1,10 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.common;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.RoleDTO;
import org.springframework.samples.petclinic.dto.common.RoleDTO;
import org.springframework.samples.petclinic.model.common.Role;
import org.springframework.samples.petclinic.repository.RoleRepository;
import org.springframework.samples.petclinic.service.business.BaseService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.common;
public interface SecurityService {

View file

@ -1,4 +1,4 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

View file

@ -1,20 +1,14 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.dto.RoleDTO;
import org.springframework.samples.petclinic.dto.UserDTO;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.samples.petclinic.dto.common.UserDTO;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.Set;
@Slf4j
@Service("UserDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@ -36,15 +30,7 @@ public class UserDetailsServiceImpl implements UserDetailsService {
if (userDTO == null)
throw new UsernameNotFoundException("User not found with email :" + email);
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (RoleDTO role : userDTO.getRoles()) {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getName()));
}
return new org.springframework.security.core.userdetails.User(userDTO.getEmail(), userDTO.getMatchingPassword(),
grantedAuthorities);
return userDTO;
}
}

View file

@ -1,11 +1,12 @@
package org.springframework.samples.petclinic.service;
package org.springframework.samples.petclinic.service.common;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.RoleDTO;
import org.springframework.samples.petclinic.dto.UserDTO;
import org.springframework.samples.petclinic.dto.common.RoleDTO;
import org.springframework.samples.petclinic.dto.common.UserDTO;
import org.springframework.samples.petclinic.model.common.Role;
import org.springframework.samples.petclinic.model.common.User;
import org.springframework.samples.petclinic.repository.UserRepository;
import org.springframework.samples.petclinic.service.business.BaseService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
@ -37,13 +38,14 @@ public class UserService implements BaseService<User, UserDTO> {
User user = modelMapper.map(dto, User.class);
user.setPassword(dto.getPassword());
/*
if (dto.getRoles() != null) {
for (RoleDTO roleDTO : dto.getRoles()) {
Role role = modelMapper.map(roleDTO, Role.class);
user.addRole(role);
}
}
*/
return user;
}
@ -56,14 +58,14 @@ public class UserService implements BaseService<User, UserDTO> {
UserDTO userDto = modelMapper.map(entity, UserDTO.class);
userDto.setPassword(entity.getPassword());
userDto.setMatchingPassword(entity.getPassword());
/*
if (entity.getRoles() != null) {
for (Role role : entity.getRoles()) {
RoleDTO roleDTO = modelMapper.map(role, RoleDTO.class);
userDto.addRole(roleDTO);
}
}
*/
return userDto;
}
@ -114,4 +116,8 @@ public class UserService implements BaseService<User, UserDTO> {
return entityToDTO(user);
}
public boolean existByEmail(String email) {
return userRepository.existsByEmail(email);
}
}

View file

@ -30,8 +30,6 @@ spring.resources.cache.cachecontrol.max-age=12h
#logging.level.org.hibernate: DEBUG
#logging.level.org.springframework.context.annotation=TRACE
spring.datasource.hikari.connectionTimeout=20000
spring.datasource.hikari.maximumPoolSize=5
spring.datasource.initialize=true
@ -42,6 +40,7 @@ spring.datasource.password=
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
######################################################################### OAUTH2
spring.security.oauth2.client.registration.google.client-id=${OAUTH2_GOOGLE_CLIENT_ID}
spring.security.oauth2.client.registration.google.client-secret=${OAUTH2_GOOGLE_CLIENT_SECRET}
@ -52,6 +51,17 @@ spring.security.oauth2.client.registration.github.client-secret=${OAUTH2_GITHUB_
#spring.security.oauth2.client.registration.facebook.client-id=<your client id>
#spring.security.oauth2.client.registration.facebook.client-secret=<your client secret>
#spring.security.oauth2.client.registration.twitter.client-id=<your client id>
#spring.security.oauth2.client.registration.twitter.client-secret=<your client secret>
#################################################################### SPRING MAIL
spring.mail.host=smtp.mailtrap.io
spring.mail.port=2525
spring.mail.protocol=smtp
spring.mail.username=${SPRING_MAIL_USERNAME}
spring.mail.password=${SPRING_MAIL_PASSWORD}
mail.background=static/resources/images/mail-background.png
mail.banner=static/resources/images/mail-banner.png
mail.logo=static/resources/images/mail-logo.png

View file

@ -52,16 +52,30 @@ INSERT INTO visits VALUES (2, 8, '2013-01-02', 'rabies shot');
INSERT INTO visits VALUES (3, 8, '2013-01-03', 'neutered');
INSERT INTO visits VALUES (4, 7, '2013-01-04', 'spayed');
INSERT INTO roles VALUES (1,'ADMIN');
INSERT INTO roles VALUES (2,'STAFF');
INSERT INTO roles VALUES (3,'USER');
INSERT INTO roles (id, name) VALUES
(1,'ROLE_ADMIN'),
(2,'ROLE_STAFF'),
(3,'ROLE_USER');
INSERT INTO users VALUES (1, 'George', 'Franklin', 'georges.franklin@petclinic.com', true,'$2a$10$8KypNYtPopFo8Sk5jbKJ4.lCKeBhdApsrkmFfhwjB8nCls8qpzjZG', 'local', null, '6085551023', '110 W. Liberty St.','','',12354,'Madison','USA');
INSERT INTO users VALUES (2, 'Betty', 'Davis', 'betty.davis@petclinic.com', true, '$2a$10$InKx/fhX3CmLi8zKpHYx/.ETHUlZwvT1xn.Za/pp2JR0iEtYV9a9O', 'local', null, '6085551749','638 Cardinal Ave.', '', '', 6546, 'Sun Prairie', 'USA');
INSERT INTO users VALUES (3, 'Eduardo', 'Rodriquez', 'eduardo.rodriguez@petclinic.com', true, '$2a$10$P55nbvVibHpoyWzenHngjOf.oEmcj74mI/VJaUZwGX9v8klctzsNW', 'local', null, '6085558763','2693 Commerce St.', '', '', 65454, 'McFarland', 'USA');
INSERT INTO users (id, first_name, last_name, email, password, enabled, telephone, street1, zip_code, city, country) VALUES
(1, 'George', 'Franklin', 'georges.franklin@petclinic.com', '$2a$10$8KypNYtPopFo8Sk5jbKJ4.lCKeBhdApsrkmFfhwjB8nCls8qpzjZG', true, '6085551023', '110 W. Liberty St.',12354,'Madison','USA'),
(2, 'Betty', 'Davis', 'betty.davis@petclinic.com', '$2a$10$InKx/fhX3CmLi8zKpHYx/.ETHUlZwvT1xn.Za/pp2JR0iEtYV9a9O', true, '6085551749','638 Cardinal Ave.', 6546, 'Sun Prairie', 'USA'),
(3, 'Eduardo', 'Rodriquez', 'eduardo.rodriguez@petclinic.com', '$2a$10$P55nbvVibHpoyWzenHngjOf.oEmcj74mI/VJaUZwGX9v8klctzsNW', true, '6085558763','2693 Commerce St.', 65454, 'McFarland', 'USA'),
(4, 'Paul-Emmanuel','DOS SANTOS FACAO','pedsf.fullstack@gmail.com','$2a$10$AzoUxi1IQFJMzLHcCGmDjuDHAQqAcAiRLz6UMeItdTL3mMWxMZEPC', true, '6085558763','2693 Commerce St.', 65454, 'McFarland', 'USA');
INSERT INTO users_roles (user_id, role_id) VALUES
(1,1),(1,2),(1,3),
(2,3),(3,3);
INSERT INTO auth_providers (id, name) VALUES
(1,'local'),
(2,'google'),
(3,'github'),
(4,'twitter');
INSERT INTO credentials (provider_id, email, password, verified) VALUES
(1, 'georges.franklin@petclinic.com', '$2a$10$8KypNYtPopFo8Sk5jbKJ4.lCKeBhdApsrkmFfhwjB8nCls8qpzjZG', true),
(1, 'betty.davis@petclinic.com', '$2a$10$InKx/fhX3CmLi8zKpHYx/.ETHUlZwvT1xn.Za/pp2JR0iEtYV9a9O', true),
(1, 'eduardo.rodriguez@petclinic.com', '$2a$10$P55nbvVibHpoyWzenHngjOf.oEmcj74mI/VJaUZwGX9v8klctzsNW', true),
(2, 'pedsf.fullstack@gmail.com','117496521794255275093', true);
INSERT INTO users_roles VALUES (1,1);
INSERT INTO users_roles VALUES (1,2);
INSERT INTO users_roles VALUES (1,3);
INSERT INTO users_roles VALUES (2,3);
INSERT INTO users_roles VALUES (3,3);

View file

@ -7,7 +7,10 @@ DROP TABLE types IF EXISTS;
DROP TABLE owners IF EXISTS;
DROP TABLE roles IF EXISTS;
DROP TABLE users IF EXISTS;
DROP TABLE users_email IF EXISTS;
DROP TABLE users_roles IF EXISTS;
DROP TABLE auth_providers IF EXISTS;
DROP TABLE credentials IF EXISTS;
CREATE TABLE vets (
id INTEGER IDENTITY PRIMARY KEY,
@ -72,21 +75,22 @@ CREATE TABLE roles (
CREATE INDEX roles_name ON roles (name);
CREATE TABLE users (
id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR_IGNORECASE(30) NOT NULL,
email VARCHAR(50) NOT NULL,
email_verified BOOLEAN NOT NULL,
password VARCHAR(255) NOT NULL,
provider VARCHAR(20),
provider_id VARCHAR(20),
telephone VARCHAR(20),
street1 VARCHAR(50),
street2 VARCHAR(50),
street3 VARCHAR(50),
zip_code VARCHAR(6),
city VARCHAR(80),
country VARCHAR(50)
id INTEGER IDENTITY PRIMARY KEY,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR_IGNORECASE(30) NOT NULL,
email VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
enabled BOOLEAN NOT NULL,
account_unexpired BOOLEAN NOT NULL DEFAULT true,
account_unlocked BOOLEAN NOT NULL DEFAULT true,
credential_unexpired BOOLEAN NOT NULL DEFAULT true,
telephone VARCHAR(20),
street1 VARCHAR(50),
street2 VARCHAR(50),
street3 VARCHAR(50),
zip_code VARCHAR(6),
city VARCHAR(80),
country VARCHAR(50)
);
CREATE INDEX users_email ON users (email);
@ -97,3 +101,21 @@ CREATE TABLE public.users_roles (
ALTER TABLE users_roles ADD CONSTRAINT fk_users_roles_user_id FOREIGN KEY (user_id) REFERENCES users (id);
ALTER TABLE users_roles ADD CONSTRAINT fk_users_roles_role_id FOREIGN KEY (role_id) REFERENCES roles (id);
CREATE INDEX users_roles_user_id ON users_roles (user_id);
CREATE TABLE auth_providers (
id INTEGER IDENTITY PRIMARY KEY,
name VARCHAR(20) NOT NULL
);
CREATE INDEX auth_providers_name ON auth_providers (name);
CREATE TABLE credentials (
id INTEGER IDENTITY PRIMARY KEY,
provider_id INTEGER NOT NULL,
email VARCHAR(50) NOT NULL,
password VARCHAR(255) NOT NULL,
verified BOOLEAN NOT NULL,
token VARCHAR(255) DEFAULT NULL,
expiration DATE DEFAULT NULL
);
ALTER TABLE credentials ADD CONSTRAINT fk_credentials_provider_id FOREIGN KEY (provider_id) REFERENCES auth_providers (id);
CREATE INDEX credentials_email ON credentials (email);

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
<head>
<title data-th-remove="all">Template for HTML email (editable)</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<div style="background-color: #ccc; background-image: url(images/background.png)"
data-th-style="|background-color: #ccc; background-image: url(cid:mailBackground)|">
<div style="padding: 40px;">
<div style="margin-left: auto; margin-right: auto; min-width: 320px; border: solid 1px #555; border-radius: 6px;">
<div style="background-color: #555; padding: 4px 4px 0px 4px; text-align: left;">
<div style="background-image: url(images/logo-background.png);"
data-th-style="|background-image: url(cid:mailBanner)|">
<img style="border: solid 1px #555; border-radius: 6px;"
src="images/shop-logo.png" data-th-src="|cid:mailLogo|" />
</div>
</div>
<div style="padding: 15px 15px 30px 15px; font-family: Verdana, Arial, Helvetica, sans-serif; font-size:1em; background-color: #fff; color:#333;">
<h1>
<span data-th-text="${mailSubject}">Subject</span></p>
</h1>
<p>
<span>Hello </span>
<b data-th-text="${toFirstName}">FirstName</b>
<b data-th-text="${toLastName}">toLastName</b>!
</p>
<p th:utext="${#strings.replace(#strings.escapeXml(mailContent),'&#10;','&lt;br&gt;')}">mail content</p>
<a th:href="@{${mailLink}}">Approve the connection</a>
<p>
<span data-th-text="${#dates.format(mailDate)}">date</span>
</p>
<p>Regards </p>
</div>
<div style="background-color: #555; padding: 4px 4px 0px 4px; text-align: right;">
<div style="background-image: url(images/logo-background.png);"
data-th-style="|background-image: url(cid:mailBackground)|">
<img style="border: solid 1px #555; border-radius: 6px;"
src="images/shop-logo.png" data-th-src="|cid:mailBanner|" />
</div>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -19,7 +19,7 @@
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" th:href="@{/resources/css/petclinic.css}" />
<!--link rel="stylesheet" th:href="@{/resources/css/style.css}" /-->
</head>
<body>

View file

@ -21,8 +21,9 @@
<div class="col-sm-offset-2 col-sm-10">
<button
th:with="text=${owner['new']} ? 'Add Owner' : 'Update Owner'"
class="btn btn-default" type="submit" th:text="${text}">Add
Owner</button>
class="btn btn-default" type="submit" th:text="${text}">Add Owner
</button>
<a class="btn btn-default" href="/">Cancel</a>
</div>
</div>
</form>

View file

@ -0,0 +1,53 @@
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'users')}">
<body>
<h2>Change password</h2>
<form th:object="${user}" class="form-horizontal" id="add-user-form" method="post">
<input type="hidden" th:field="${user.id}" />
<input type="hidden" th:field="${user.firstName}" />
<input type="hidden" th:field="${user.lastName}" />
<input type="hidden" th:field="${user.password}" />
<input type="hidden" th:field="${user.matchingPassword}" />
<input type="hidden" th:field="${user.enabled}" />
<input type="hidden" th:field="${user.accountNonExpired}" />
<input type="hidden" th:field="${user.accountNonLocked}" />
<input type="hidden" th:field="${user.credentialsNonExpired}" />;
<input type="hidden" th:field="${user.roles}" />
<input type="hidden" th:field="${user.telephone}" />
<input type="hidden" th:field="${user.street1}" />
<input type="hidden" th:field="${user.street2}" />
<input type="hidden" th:field="${user.street3}" />
<input type="hidden" th:field="${user.zipCode}" />
<input type="hidden" th:field="${user.city}" />
<input type="hidden" th:field="${user.country}" />
<div class="form-group has-feedback">
<div class="form-group">
<label for="email">Email address</label>
<input type="email" th:field="${user.email}" class="form-control input-lg" id="email" readonly>
<small id="emailHelp" class="form-text text-muted">We'll never share your emailConfiguration with anyone else.</small>
</div>
<div class="form-group">
<label for="oldPassword">Present password</label>
<input type="password" name="oldPassword" class="form-control input-lg" id="oldPassword" th:placeholder="password" value="">
</div>
<div class="form-group">
<label for="newPassword">New password</label>
<input type="password" name="newPassword" class="form-control input-lg" id="newPassword" th:placeholder="password" value="">
</div>
<div class="form-group">
<label for="newMatchingPassword">Password</label>
<input type="password" name="newMatchingPassword" class="form-control input-lg" id="newMatchingPassword" th:placeholder="password" value="">
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-default" type="submit">Change password</button>
<a class="btn btn-default" th:href="@{/}">Cancel</a>
</div>
</div>
</div>
</form>
</body>
</html>

View file

@ -3,9 +3,16 @@
<body>
<h2>User</h2>
<h2>User registration</h2>
<form th:object="${user}" class="form-horizontal" id="add-user-form" method="post">
<input type="hidden" th:field="${user.enabled}" />
<input type="hidden" th:field="${user.accountNonExpired}" />
<input type="hidden" th:field="${user.accountNonLocked}" />
<input type="hidden" th:field="${user.credentialsNonExpired}" />
<div class="form-group has-feedback">
<input
th:replace="~{fragments/inputField :: input ('Email', 'email', 'email')}" />
<input
th:replace="~{fragments/inputField :: input ('First Name', 'firstName', 'text')}" />
<input
@ -14,8 +21,6 @@
th:replace="~{fragments/inputField :: input ('Password', 'password', 'password')}" />
<input
th:replace="~{fragments/inputField :: input ('Repeat password', 'matchingPassword', 'password')}" />
<input
th:replace="~{fragments/inputField :: input ('Email', 'email', 'email')}" />
<input
th:replace="~{fragments/inputField :: input ('Phone', 'telephone', 'text')}" />
<input
@ -33,10 +38,8 @@
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button
th:with="text=${user['new']} ? 'Register' : 'Update User'"
class="btn btn-default" type="submit" th:text="${text}">Add User</button>
<a class="btn btn-default" href="/">Cancel</a>
<button class="btn btn-default" type="submit">Register</button>
<a class="btn btn-default" th:href="@{/}">Cancel</a>
</div>
</div>
</form>

View file

@ -0,0 +1,49 @@
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'users')}">
<body>
<h2>User update</h2>
<form th:object="${user}" class="form-horizontal" id="add-user-form" method="post">
<input type="hidden" th:field="${user.id}" />
<input type="hidden" th:field="${user.password}" />
<input type="hidden" th:field="${user.matchingPassword}" />
<input type="hidden" th:field="${user.enabled}" />
<input type="hidden" th:field="${user.accountNonExpired}" />
<input type="hidden" th:field="${user.accountNonLocked}" />
<input type="hidden" th:field="${user.credentialsNonExpired}" />
<input type="hidden" th:field="${user.roles}" />
<div class="form-group has-feedback">
<input
th:replace="~{fragments/inputField :: input ('Email', 'email', 'email')}" disabled />
<input
th:replace="~{fragments/inputField :: input ('First Name', 'firstName', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Last Name', 'lastName', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Phone', 'telephone', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Address', 'street1', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Address', 'street2', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Address', 'street3', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('City', 'city', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Zip code', 'zipCode', 'text')}" />
<input
th:replace="~{fragments/inputField :: input ('Country', 'country', 'text')}" />
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-default" type="submit">Update</button>
<a class="btn btn-default" th:href="@{'/user/' + ${user.id} + '/edit/password'}">Change password</a>
<a class="btn btn-default" th:href="@{/}">Cancel</a>
</div>
</div>
</form>
</body>
</html>

View file

@ -1,6 +1,5 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'home')}">
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'home')}">
<body>
@ -13,4 +12,4 @@
</body>
</html>
</html>

View file

@ -12,7 +12,7 @@ import org.springframework.samples.petclinic.dto.OwnerDTO;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.service.OwnerService;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

View file

@ -40,8 +40,8 @@ import org.springframework.samples.petclinic.dto.OwnerDTO;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.service.OwnerService;
import org.springframework.samples.petclinic.service.VisitService;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.VisitService;
import org.springframework.test.web.servlet.MockMvc;
import static org.hamcrest.Matchers.empty;

View file

@ -11,8 +11,8 @@ import org.springframework.samples.petclinic.common.CommonView;
import org.springframework.samples.petclinic.dto.OwnerDTO;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.service.OwnerService;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

View file

@ -42,9 +42,9 @@ import org.springframework.samples.petclinic.dto.OwnerDTO;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.formatter.PetTypeFormatter;
import org.springframework.samples.petclinic.service.OwnerService;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.PetTypeService;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.samples.petclinic.service.business.PetTypeService;
import org.springframework.test.web.servlet.MockMvc;
/**

View file

@ -15,7 +15,7 @@ import org.springframework.samples.petclinic.common.CommonEndPoint;
import org.springframework.samples.petclinic.common.CommonView;
import org.springframework.samples.petclinic.dto.VetsDTO;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.samples.petclinic.service.VetService;
import org.springframework.samples.petclinic.service.business.VetService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

View file

@ -19,7 +19,6 @@ package org.springframework.samples.petclinic.controller;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@ -40,10 +39,9 @@ import org.springframework.samples.petclinic.common.CommonView;
import org.springframework.samples.petclinic.dto.SpecialtyDTO;
import org.springframework.samples.petclinic.dto.VetDTO;
import org.springframework.samples.petclinic.dto.VetsDTO;
import org.springframework.samples.petclinic.service.VetService;
import org.springframework.samples.petclinic.service.business.VetService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
import java.nio.charset.StandardCharsets;
import java.util.List;

View file

@ -12,7 +12,7 @@ import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.model.business.Visit;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

View file

@ -40,8 +40,8 @@ import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.model.business.Visit;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.VisitService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.samples.petclinic.service.business.VisitService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

View file

@ -25,7 +25,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.formatter.PetTypeFormatter;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.business.PetService;
import java.text.ParseException;
import java.util.ArrayList;

View file

@ -31,7 +31,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.formatter.PetTypeFormatter;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.business.PetService;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

View file

@ -19,6 +19,9 @@ import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.repository.PetTypeRepository;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.samples.petclinic.service.business.PetTypeService;
import org.springframework.stereotype.Service;
import java.time.LocalDate;

View file

@ -17,6 +17,9 @@ import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.repository.PetTypeRepository;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.samples.petclinic.service.business.OwnerService;
import org.springframework.samples.petclinic.service.business.PetService;
import org.springframework.samples.petclinic.service.business.PetTypeService;
import org.springframework.stereotype.Service;
import java.time.LocalDate;

View file

@ -12,6 +12,7 @@ import org.springframework.samples.petclinic.dto.VetDTO;
import org.springframework.samples.petclinic.model.business.Vet;
import org.springframework.samples.petclinic.repository.SpecialtyRepository;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.samples.petclinic.service.business.VetService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;