add login by email and social login

This commit is contained in:
PEDSF 2020-11-15 11:54:28 +01:00
parent 446d06ec46
commit 285e018b1a
69 changed files with 1809 additions and 77 deletions

53
pom.xml
View file

@ -41,6 +41,7 @@
<modelmapper.version>2.3.8</modelmapper.version>
<nohttp-checkstyle.version>0.0.4.RELEASE</nohttp-checkstyle.version>
<spring-format.version>0.0.25</spring-format.version>
<spring-cloud-starter-security.version>2.2.4.RELEASE</spring-cloud-starter-security.version>
</properties>
<!-- ===================================================================== -->
@ -87,6 +88,15 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
<version>${spring-cloud-starter-security.version}</version>
</dependency>
<!-- ========================================================= DATABASES -->
<dependency>
@ -158,12 +168,50 @@
<version>${webjars-sockjs-client.version}</version>
</dependency>
<!-- =========================================== THYMELEAF SECURITY -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!-- OAuth2 Client -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-jose</artifactId>
<version>5.3.4.RELEASE</version>
</dependency>
<!-- JWT library -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- =========================================================== TESTING -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
@ -176,6 +224,11 @@
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.3.3.RELEASE</version>
</dependency>
</dependencies>
<!-- ===================================================================== -->

View file

@ -39,6 +39,10 @@ public final class CommonAttribute {
public static final String PET_TYPE = "type";
public static final String USER = "user";
public static final String USER_ID = "id";
public static final String VETS = "vets";
public static final String VISIT = "visit";

View file

@ -21,6 +21,26 @@ public final class CommonEndPoint {
public static final String PETS_ID_EDIT = "/pets/{petId}/edit";
public static final String USERS_ID = "/users/{userId}";
public static final String USERS_EDIT = "/users/edit";
public static final String USERS_ID_EDIT = "/users/{ownerId}/edit";
public static final String USERS_NEW = "/users/new";
public static final String LOGIN = "/login";
public static final String LOGIN_SUCCESS = "/login/success";
public static final String OAUTH2_SUCCESS = "/oauth2/success";
public static final String LOGOUT = "/logout";
public static final String LOGOUT_SUCCESS = "/logout/success";
public static final String REGISTER = "/register";
public static final String VETS = "/vets";
public static final String VETS_HTML = "/vets.html";

View file

@ -19,6 +19,14 @@ public final class CommonError {
public static final String REQUIRED_MESSAGE = "required";
public static final String FORMAT_BETWEEN = "Length should be between : ";
public static final String FORMAT_LESS = "Length should less than : ";
public static final String EMAIL_FORMAT = "Not a valid email address !";
public static final String PHONE_FORMAT = "Not a valid phone number !";
private CommonError() {
throw new IllegalStateException("Utility class");
}

View file

@ -0,0 +1,45 @@
package org.springframework.samples.petclinic.common;
public class CommonParameter {
public static final int CITY_MAX = 50;
public static final int COUNTRY_MAX = 50;
public static final int EMAIL_MAX = 255;
public static final int EMAIL_MIN = 4;
public static final String EMAIL_REGEXP = "^[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$";
public static final int FIRSTNAME_MAX = 50;
public static final int FIRSTNAME_MIN = 2;
public static final int LASTNAME_MAX = 50;
public static final int LASTNAME_MIN = 2;
public static final int PASSWORD_MAX = 255;
public static final int PASSWORD_MIN = 4;
public static final int PHONE_MAX = 14;
public static final String PHONE_REGEXP = "^(?:(?:\\+|00)33[\\s.-]{0,3}(?:\\(0\\)[\\s.-]{0,3})?|0)[1-9](?:(?:[\\s.-]?\\d{2}){4}|\\d{2}(?:[\\s.-]?\\d{3}){2}|)$";
public static final int STATUS_MAX = 10;
public static final int STREET_MAX = 50;
public static final int ROLE_MAX = 10;
public static final int ZIP_MAX = 6;
public static final int ZIP_MIN = 5;
private CommonParameter() {
throw new IllegalStateException("Utility class");
}
}

View file

@ -7,6 +7,8 @@ package org.springframework.samples.petclinic.common;
*/
public final class CommonView {
public static final String HOME = "redirect:/";
public static final String OWNER_OWNERS_R = "redirect:/owners/";
public static final String OWNER_OWNERS_ID_R = "redirect:/owners/{ownerId}";
@ -21,6 +23,32 @@ 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_LOGIN = "/login";
public static final String USER_LOGIN_R = "redirect:/login";
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_DETAILS = "users/userDetails";
public static final String USER_ADD = "users/user-add";
public static final String USER_READ = "users/user-read";
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";
public static final String USER_UPDATE_PASSWORD_R = "redirect:/users/password/edit/";
public static final String VET_VETS_LIST = "vets/vetList";
public static final String VISIT_CREATE_OR_UPDATE = "pets/createOrUpdateVisitForm";

View file

@ -35,6 +35,24 @@ public class CommonWebSocket {
public static final String PET_DELETED_ERROR = "Error deleting Pet !";
public static final String USER_LOGGED_IN = "User %s %s logged in successfully !";
public static final String USER_LOGGED_OUT = "User logged out successfully !";
public static final String USER_FIND_ERROR = "No User found !";
public static final String USER_CREATED = "User created";
public static final String USER_CREATION_ERROR = "Error creating User !";
public static final String USER_UPDATED = "User updated";
public static final String USER_UPDATED_ERROR = "Error updating User !";
public static final String USER_DELETED = "User deleted";
public static final String USER_DELETED_ERROR = "Error deleting User !";
public static final String VET_FIND_ERROR = "No Vet found !";
public static final String VET_CREATED = "Vet created";

View file

@ -0,0 +1,130 @@
package org.springframework.samples.petclinic.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.oauth2.client.CommonOAuth2Provider;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Configuration
@EnableWebSecurity
@PropertySource("classpath:application.properties")
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static final String CLIENT_PROPERTY_KEY = "spring.security.oauth2.client.registration.";
@Autowired
private UserDetailsService userDetailsService;
@Resource
private Environment env;
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager customAuthenticationManager() throws Exception {
return authenticationManager();
}
@Bean
public OAuth2AuthorizedClientService authorizedClientService() {
return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.authorizeRequests()
.antMatchers("/").anonymous()
.antMatchers("/login", "/logout", "/register").permitAll()
.antMatchers("/websocket/**", "/topic/**", "/app/**").permitAll()
.antMatchers("/resources/**").permitAll()
.antMatchers("/**").authenticated()
.antMatchers("/h2-console/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/login/success", true)
.usernameParameter("email")
.passwordParameter("password")
.failureUrl("/login")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/logout/success")
.invalidateHttpSession(true)
.permitAll()
.and()
.oauth2Login()
.loginPage("/login")
.defaultSuccessUrl("/oauth2/success", true)
.failureUrl("/login")
.clientRegistrationRepository(clientRegistrationRepository())
.authorizedClientService(authorizedClientService())
.and()
.csrf().disable();
// @formatter:on
}
private static final List<String> clients = Arrays.asList("google", "facebook", "github");
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
List<ClientRegistration> registrations = clients.stream().map(c -> getRegistration(c))
.filter(registration -> registration != null).collect(Collectors.toList());
return new InMemoryClientRegistrationRepository(registrations);
}
private ClientRegistration getRegistration(String client) {
String clientId = env.getProperty(CLIENT_PROPERTY_KEY + client + ".client-id");
if (clientId == null) {
return null;
}
String clientSecret = env.getProperty(CLIENT_PROPERTY_KEY + client + ".client-secret");
if (client.equals(AuthProvider.google.name())) {
return CommonOAuth2Provider.GOOGLE.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
}
if (client.equals(AuthProvider.facebook.name())) {
return CommonOAuth2Provider.FACEBOOK.getBuilder(client).clientId(clientId).clientSecret(clientSecret)
.build();
}
if (client.equals(AuthProvider.github.name())) {
return CommonOAuth2Provider.GITHUB.getBuilder(client).clientId(clientId).clientSecret(clientSecret).build();
}
return null;
}
}

View file

@ -19,7 +19,6 @@ 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.model.common.WebSocketMessage;
import org.springframework.samples.petclinic.validator.PetDTOValidator;
import org.springframework.samples.petclinic.service.*;
import org.springframework.stereotype.Controller;

View file

@ -0,0 +1,235 @@
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.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.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;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
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.Map;
/**
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Slf4j
@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 RoleService roleService;
private final SecurityServiceImpl securityService;
private final BCryptPasswordEncoder bCryptPasswordEncoder;
public UserController(UserService userService, RoleService roleService, SecurityServiceImpl securityService,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.userService = userService;
this.roleService = roleService;
this.securityService = securityService;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
@InitBinder("user")
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields(CommonAttribute.USER_ID);
}
Map<String, String> oauth2AuthenticationUrls = new HashMap<>();
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@Autowired
private OAuth2AuthorizedClientService authorizedClientService;
@GetMapping(CommonEndPoint.REGISTER)
public String initCreationForm(Map<String, Object> model) {
UserDTO user = new UserDTO();
model.put(CommonAttribute.USER, user);
return CommonView.USER_CREATE_OR_UPDATE;
}
@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;
}
try {
userService.findByEmail(user.getEmail());
result.rejectValue("email", "5", "Email already exist !");
sendErrorMessage(CommonWebSocket.USER_CREATION_ERROR);
return CommonView.USER_CREATE_OR_UPDATE;
}
catch (Exception ex) {
}
// set default role
user.addRole(roleService.findByName(ROLE_USER));
// encode password because we get clear password
user.setPassword(bCryptPasswordEncoder.encode(user.getPassword()));
user.setMatchingPassword(user.getPassword());
user = this.userService.save(user);
sendSuccessMessage(CommonWebSocket.USER_CREATED);
return CommonView.HOME + user.getId();
}
@GetMapping(CommonEndPoint.LOGIN)
public String initLoginForm(Map<String, Object> model) {
if (model.containsKey(CommonAttribute.USER)) {
return CommonView.HOME;
}
else {
UserDTO user = new UserDTO();
model.put(CommonAttribute.USER, user);
}
Iterable<ClientRegistration> clientRegistrations = null;
ResolvableType type = ResolvableType.forInstance(clientRegistrationRepository).as(Iterable.class);
if (type != ResolvableType.NONE && ClientRegistration.class.isAssignableFrom(type.resolveGenerics()[0])) {
clientRegistrations = (Iterable<ClientRegistration>) clientRegistrationRepository;
}
clientRegistrations.forEach(registration -> oauth2AuthenticationUrls.put(registration.getClientName(),
authorizationRequestBaseUri + "/" + registration.getRegistrationId()));
model.put("urls", oauth2AuthenticationUrls);
return CommonView.USER_LOGIN;
}
@GetMapping(CommonEndPoint.LOGIN_SUCCESS)
public String postLogin(Model model, Authentication authentication) {
UserDTO user = userService.findByEmail(authentication.getName());
model.addAttribute(CommonAttribute.USER, user);
String message = String.format(CommonWebSocket.USER_LOGGED_IN, user.getFirstName(), user.getLastName());
sendSuccessMessage(message );
return CommonView.HOME;
}
@GetMapping(CommonEndPoint.OAUTH2_SUCCESS)
public String postLogin(Model model, OAuth2AuthenticationToken authentication) {
OAuth2AuthorizedClient client = authorizedClientService .loadAuthorizedClient(authentication.getAuthorizedClientRegistrationId(), authentication.getName());
String userInfoEndpointUri = client.getClientRegistration().getProviderDetails().getUserInfoEndpoint().getUri();
UserDTO user = userService.findByEmail(authentication.getName());
if( user!=null) {
model.addAttribute(CommonAttribute.USER, user);
String message = String.format(CommonWebSocket.USER_LOGGED_IN, user.getFirstName(), user.getLastName());
sendSuccessMessage(message);
}
return CommonView.HOME;
}
@GetMapping(CommonEndPoint.LOGOUT)
public String logout(HttpServletRequest request, HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
new SecurityContextLogoutHandler().logout(request, response, authentication);
}
sendSuccessMessage(CommonWebSocket.USER_LOGGED_OUT);
return CommonView.USER_LOGIN_R;
}
@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());
model.addAttribute(CommonAttribute.USER, user);
return CommonView.USER_CREATE_OR_UPDATE;
}
return CommonView.HOME;
}
@PostMapping(CommonEndPoint.USERS_ID_EDIT)
public String processUpdateOwnerForm(@ModelAttribute(CommonAttribute.USER) @Valid UserDTO user,
BindingResult result, @PathVariable("userId") int userId) {
if (result.hasErrors()) {
sendErrorMessage(CommonWebSocket.USER_UPDATED_ERROR);
return CommonView.USER_CREATE_OR_UPDATE;
}
else {
user.setId(userId);
this.userService.save(user);
sendSuccessMessage(CommonWebSocket.USER_UPDATED);
return CommonView.USER_USERS_ID_R;
}
}
@GetMapping(CommonEndPoint.USERS_ID)
public ModelAndView showOwner(@PathVariable("userId") int userId) {
ModelAndView modelAndView = new ModelAndView(CommonView.USER_DETAILS);
UserDTO user = this.userService.findById(userId);
modelAndView.addObject(CommonAttribute.USER, user);
return modelAndView;
}
}

View file

@ -26,7 +26,6 @@ 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.model.common.WebSocketMessage;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.VisitService;
import org.springframework.samples.petclinic.validator.VisitDTOValidator;

View file

@ -0,0 +1,12 @@
package org.springframework.samples.petclinic.dto;
import java.io.Serializable;
/**
* Simple Data Transfert Object representing a list of roles.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
public class RoleDTO extends NamedDTO implements Serializable {
}

View file

@ -0,0 +1,156 @@
package org.springframework.samples.petclinic.dto;
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 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;
@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;
private Set<RoleDTO> roles;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
private String street1;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
private String street2;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
private String street3;
@Size(min = CommonParameter.ZIP_MIN, max = CommonParameter.ZIP_MAX,
message = CommonError.FORMAT_BETWEEN + CommonParameter.ZIP_MIN + " AND " + CommonParameter.ZIP_MAX + " !")
private String zipCode;
@Size(max = CommonParameter.CITY_MAX, message = CommonError.FORMAT_LESS + CommonParameter.CITY_MAX + " !")
private String city;
@Size(max = CommonParameter.COUNTRY_MAX, message = CommonError.FORMAT_LESS + CommonParameter.COUNTRY_MAX + " !")
private String country;
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getMatchingPassword() {
return matchingPassword;
}
public void setMatchingPassword(String matchingPassword) {
this.matchingPassword = matchingPassword;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
protected Set<RoleDTO> getRolesInternal() {
if (this.roles == null) {
this.roles = new HashSet<>();
}
return this.roles;
}
protected void setRolesInternal(Set<RoleDTO> roles) {
this.roles = roles;
}
@XmlElement
public List<RoleDTO> getRoles() {
List<RoleDTO> sortedRoles = new ArrayList<>(getRolesInternal());
PropertyComparator.sort(sortedRoles, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedRoles);
}
public void addRole(RoleDTO role) {
getRolesInternal().add(role);
}
public String getStreet1() {
return street1;
}
public void setStreet1(String street1) {
this.street1 = street1;
}
public String getStreet2() {
return street2;
}
public void setStreet2(String street2) {
this.street2 = street2;
}
public String getStreet3() {
return street3;
}
public void setStreet3(String street3) {
this.street3 = street3;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import java.util.ArrayList;
import java.util.Collections;
@ -33,6 +33,7 @@ import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.core.style.ToStringCreator;
import org.springframework.samples.petclinic.common.CommonAttribute;
import org.springframework.samples.petclinic.model.common.Person;
/**
* Simple JavaBean domain object representing an owner.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import java.time.LocalDate;
import java.util.ArrayList;
@ -34,6 +34,7 @@ import javax.persistence.Transient;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.common.NamedEntity;
/**
* Simple business object representing a pet.

View file

@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import org.springframework.samples.petclinic.model.common.NamedEntity;
import javax.persistence.Entity;
import javax.persistence.Table;

View file

@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import org.springframework.samples.petclinic.model.common.NamedEntity;
import java.io.Serializable;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import java.util.ArrayList;
import java.util.Collections;
@ -31,6 +31,7 @@ import javax.xml.bind.annotation.XmlElement;
import org.springframework.beans.support.MutableSortDefinition;
import org.springframework.beans.support.PropertyComparator;
import org.springframework.samples.petclinic.model.common.Person;
/**
* Simple JavaBean domain object representing a veterinarian.

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import java.util.ArrayList;
import java.util.List;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.business;
import java.time.LocalDate;
@ -23,6 +23,7 @@ import javax.persistence.Table;
import javax.validation.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.samples.petclinic.model.common.BaseEntity;
/**
* Simple JavaBean domain object representing a visit.

View file

@ -0,0 +1,7 @@
package org.springframework.samples.petclinic.model.common;
public enum AuthProvider {
local, facebook, google, github
}

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.common;
import java.io.Serializable;

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.common;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;

View file

@ -13,11 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.samples.petclinic.model;
package org.springframework.samples.petclinic.model.common;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.common.CommonParameter;
import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;
/**
* Simple JavaBean domain object representing an person.
@ -27,12 +31,16 @@ import javax.validation.constraints.NotEmpty;
@MappedSuperclass
public class Person extends BaseEntity {
@Column(name = "first_name")
@NotEmpty
@Size(min = CommonParameter.FIRSTNAME_MIN, max = CommonParameter.FIRSTNAME_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.FIRSTNAME_MIN + " AND " + CommonParameter.FIRSTNAME_MAX + " !")
@Column(name = "first_name", length = CommonParameter.FIRSTNAME_MAX)
private String firstName;
@Column(name = "last_name")
@NotEmpty
@Size(min = CommonParameter.LASTNAME_MIN, max = CommonParameter.LASTNAME_MAX, message = CommonError.FORMAT_BETWEEN
+ CommonParameter.LASTNAME_MIN + " AND " + CommonParameter.LASTNAME_MAX + " !")
@Column(name = "last_name", length = CommonParameter.LASTNAME_MAX)
private String lastName;
public String getFirstName() {

View file

@ -0,0 +1,12 @@
package org.springframework.samples.petclinic.model.common;
import org.springframework.samples.petclinic.model.common.NamedEntity;
import javax.persistence.*;
import java.io.Serializable;
@Entity(name = "Role")
@Table(name = "roles")
public class Role extends NamedEntity implements Serializable {
}

View file

@ -0,0 +1,208 @@
package org.springframework.samples.petclinic.model.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 javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.XmlElement;
import java.io.Serializable;
import java.util.*;
/**
* Class used to manage application users
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Entity(name = "User")
@Table(name = "users")
public class User extends Person implements Serializable {
@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", 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 + " !")
@Column(name = "password", length = CommonParameter.PASSWORD_MAX)
private String password;
@NotNull
@Enumerated(EnumType.STRING)
private AuthProvider provider;
@Column(name = "provider_id")
private String providerId;
@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;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;
@NotNull
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
@Column(name = "street1", length = CommonParameter.STREET_MAX)
private String street1;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
@Column(name = "street2", length = CommonParameter.STREET_MAX)
private String street2;
@Size(max = CommonParameter.STREET_MAX, message = CommonError.FORMAT_LESS + CommonParameter.STREET_MAX + " !")
@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;
@Size(max = CommonParameter.COUNTRY_MAX, message = CommonError.FORMAT_LESS + CommonParameter.COUNTRY_MAX + " !")
@Column(name = "country", length = CommonParameter.COUNTRY_MAX)
private String country;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Boolean getEmailVerified() {
return emailVerified;
}
public void setEmailVerified(Boolean emailVerified) {
this.emailVerified = emailVerified;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public AuthProvider getProvider() {
return provider;
}
public void setProvider(AuthProvider provider) {
this.provider = provider;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
protected Set<Role> getRolesInternal() {
if (this.roles == null) {
this.roles = new HashSet<>();
}
return this.roles;
}
protected void setRolesInternal(Set<Role> roles) {
this.roles = roles;
}
@XmlElement
public List<Role> getRoles() {
List<Role> sortedRoles = new ArrayList<>(getRolesInternal());
PropertyComparator.sort(sortedRoles, new MutableSortDefinition("name", true, true));
return Collections.unmodifiableList(sortedRoles);
}
public int getNrOfRoles() {
return getRolesInternal().size();
}
public void addRole(Role role) {
getRolesInternal().add(role);
}
public String getStreet1() {
return street1;
}
public void setStreet1(String street1) {
this.street1 = street1;
}
public String getStreet2() {
return street2;
}
public void setStreet2(String street2) {
this.street2 = street2;
}
public String getStreet3() {
return street3;
}
public void setStreet3(String street3) {
this.street3 = street3;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}

View file

@ -21,7 +21,7 @@ import java.util.List;
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.Owner;
import org.springframework.samples.petclinic.model.business.Owner;
import org.springframework.transaction.annotation.Transactional;
/**

View file

@ -19,8 +19,8 @@ import java.util.List;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.business.Pet;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.transaction.annotation.Transactional;
/**

View file

@ -16,9 +16,8 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.business.PetType;
import java.util.Collection;
import java.util.List;
/**

View file

@ -0,0 +1,42 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.samples.petclinic.model.common.Role;
import org.springframework.data.repository.Repository;
import java.util.List;
/**
* Repository class for <code>Role</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 RoleRepository extends Repository<Role, Integer> {
/**
* Retrieve a {@link Role} from the data store by id.
* @param roleId the id to search for
* @return the {@link Role} if found
*/
Role findById(Integer roleId);
/**
* Retrieve a {@link Role} from the data store by id.
* @param name the name to search for
* @return the {@link Role} if found
*/
Role findByName(String name);
/**
* Retrieve all {@link Role}s from the data store
* @return a Collection of {@link Role}s (or an empty Collection if none
*/
List<Role> findAll();
/**
* Save a {@link Role} to the data store, either inserting or updating it.
* @param role the {@link Role} to save
*/
Role save(Role role);
}

View file

@ -16,9 +16,8 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.model.business.Specialty;
import java.util.Collection;
import java.util.List;
/**

View file

@ -0,0 +1,36 @@
package org.springframework.samples.petclinic.repository;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.common.User;
import java.util.List;
/**
* Repository class for <code>User</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 UserRepository extends Repository<User, Integer> {
User findById(Integer id);
User findByEmail(String email);
Boolean existsByEmail(String email);
/**
* Retrieve all {@link User}s from the data store
* @return a Collection of {@link User}s (or an empty Collection if none
*/
List<User> findAll();
/**
* Save an {@link User} to the data store, either inserting or updating it.
* @param user the {@link User} to save
*/
User save(User user);
void deleteById(Integer id);
}

View file

@ -19,7 +19,7 @@ import java.util.Collection;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.model.business.Vet;
import org.springframework.transaction.annotation.Transactional;
/**

View file

@ -15,12 +15,11 @@
*/
package org.springframework.samples.petclinic.repository;
import java.util.Collection;
import java.util.List;
import org.springframework.data.repository.Repository;
import org.springframework.samples.petclinic.model.BaseEntity;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.model.common.BaseEntity;
import org.springframework.samples.petclinic.model.business.Visit;
/**
* Repository class for <code>Visit</code> domain objects All method names are compliant

View file

@ -4,8 +4,8 @@ import org.modelmapper.ModelMapper;
import org.modelmapper.internal.util.Lists;
import org.springframework.samples.petclinic.dto.OwnerDTO;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.business.Owner;
import org.springframework.samples.petclinic.model.business.Pet;
import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.repository.PetTypeRepository;

View file

@ -4,9 +4,9 @@ import org.modelmapper.ModelMapper;
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.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.business.Owner;
import org.springframework.samples.petclinic.model.business.Pet;
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;

View file

@ -2,7 +2,7 @@ package org.springframework.samples.petclinic.service;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.PetTypeDTO;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.repository.PetTypeRepository;
import org.springframework.stereotype.Service;

View file

@ -0,0 +1,86 @@
package org.springframework.samples.petclinic.service;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.RoleDTO;
import org.springframework.samples.petclinic.model.common.Role;
import org.springframework.samples.petclinic.repository.RoleRepository;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Simple Service between Specialty entity and SpecialtyDTO Data Transfert Object.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Service("RoleService")
public class RoleService implements BaseService<Role, RoleDTO> {
private final RoleRepository roleRepository;
private final ModelMapper modelMapper = new ModelMapper();
public RoleService(RoleRepository roleRepository) {
this.roleRepository = roleRepository;
}
@Override
public Role dtoToEntity(RoleDTO dto) {
if (dto != null) {
return modelMapper.map(dto, Role.class);
}
return new Role();
}
@Override
public RoleDTO entityToDTO(Role entity) {
if (entity != null) {
return modelMapper.map(entity, RoleDTO.class);
}
return new RoleDTO();
}
@Override
public List<RoleDTO> entitiesToDTOS(List<Role> entities) {
List<RoleDTO> dtos = new ArrayList<>();
entities.forEach(entity -> dtos.add(entityToDTO(entity)));
return dtos;
}
@Override
public List<Role> dtosToEntities(List<RoleDTO> dtos) {
List<Role> entities = new ArrayList<>();
dtos.forEach(dto -> entities.add(dtoToEntity(dto)));
return entities;
}
@Override
public RoleDTO findById(int id) {
return entityToDTO(roleRepository.findById(id));
}
@Override
public List<RoleDTO> findAll() {
return entitiesToDTOS(roleRepository.findAll());
}
@Override
public RoleDTO save(RoleDTO dto) {
Role role = dtoToEntity(dto);
role = roleRepository.save(role);
return entityToDTO(role);
}
public RoleDTO findByName(String name) {
return entityToDTO(roleRepository.findByName(name));
}
}

View file

@ -0,0 +1,9 @@
package org.springframework.samples.petclinic.service;
public interface SecurityService {
String findLoggedInUsername();
void autoLogin(String email, String motDePasse);
}

View file

@ -0,0 +1,50 @@
package org.springframework.samples.petclinic.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
@Slf4j
@Service("SecurityService")
public class SecurityServiceImpl implements SecurityService {
private final AuthenticationManager authenticationManager;
private final UserDetailsServiceImpl userDetailsService;
@Autowired
public SecurityServiceImpl(AuthenticationManager authenticationManager, UserDetailsServiceImpl userDetailsService) {
this.authenticationManager = authenticationManager;
this.userDetailsService = userDetailsService;
}
@Override
public String findLoggedInUsername() {
Object userDetails = SecurityContextHolder.getContext().getAuthentication().getDetails();
if (userDetails instanceof UserDetails) {
return ((UserDetails) userDetails).getUsername();
}
return null;
}
@Override
public void autoLogin(String email, String motDePasse) {
UserDetails userDetails = userDetailsService.loadUserByUsername(email);
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
userDetails, motDePasse, userDetails.getAuthorities());
authenticationManager.authenticate(usernamePasswordAuthenticationToken);
if (usernamePasswordAuthenticationToken.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
}
}
}

View file

@ -2,13 +2,11 @@ package org.springframework.samples.petclinic.service;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.SpecialtyDTO;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.model.business.Specialty;
import org.springframework.samples.petclinic.repository.SpecialtyRepository;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
/**

View file

@ -0,0 +1,50 @@
package org.springframework.samples.petclinic.service;
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.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 {
private final UserService userService;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
public UserDetailsServiceImpl(UserService userService) {
this.userService = userService;
}
@Override
public UserDetails loadUserByUsername(String email) {
UserDTO userDTO = userService.findByEmail(email);
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);
}
}

View file

@ -0,0 +1,117 @@
package org.springframework.samples.petclinic.service;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.RoleDTO;
import org.springframework.samples.petclinic.dto.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.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Simple Service between User entity and UserDTO Data Transfert Object.
*
* @author Paul-Emmanuel DOS SANTOS FACAO
*/
@Service("UserService")
public class UserService implements BaseService<User, UserDTO> {
private final UserRepository userRepository;
private final ModelMapper modelMapper = new ModelMapper();
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public User dtoToEntity(UserDTO dto) {
if (dto == null) {
return null;
}
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;
}
@Override
public UserDTO entityToDTO(User entity) {
if (entity == null) {
return null;
}
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;
}
@Override
public List<UserDTO> entitiesToDTOS(List<User> entities) {
List<UserDTO> dtos = new ArrayList<>();
entities.forEach(entity -> dtos.add(entityToDTO(entity)));
return dtos;
}
@Override
public List<User> dtosToEntities(List<UserDTO> dtos) {
List<User> entities = new ArrayList<>();
dtos.forEach(dto -> entities.add(dtoToEntity(dto)));
return entities;
}
@Override
public UserDTO findById(int id) {
User user = userRepository.findById(id);
return entityToDTO(user);
}
@Override
public List<UserDTO> findAll() {
List<User> users = userRepository.findAll();
return entitiesToDTOS(users);
}
@Override
public UserDTO save(UserDTO dto) {
User user = dtoToEntity(dto);
user = userRepository.save(user);
return entityToDTO(user);
}
public UserDTO findByEmail(String email) {
User user = userRepository.findByEmail(email);
return entityToDTO(user);
}
}

View file

@ -4,15 +4,14 @@ import org.modelmapper.ModelMapper;
import org.modelmapper.internal.util.Lists;
import org.springframework.samples.petclinic.dto.SpecialtyDTO;
import org.springframework.samples.petclinic.dto.VetDTO;
import org.springframework.samples.petclinic.model.Specialty;
import org.springframework.samples.petclinic.model.business.Specialty;
import org.springframework.samples.petclinic.repository.SpecialtyRepository;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.model.business.Vet;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
/**

View file

@ -2,7 +2,7 @@ package org.springframework.samples.petclinic.service;
import org.modelmapper.ModelMapper;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.model.business.Visit;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.stereotype.Service;

View file

@ -17,7 +17,7 @@ package org.springframework.samples.petclinic.validator;
import org.springframework.samples.petclinic.common.CommonAttribute;
import org.springframework.samples.petclinic.common.CommonError;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.business.Pet;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

View file

@ -23,3 +23,35 @@ logging.level.org.springframework=INFO
# Maximum time static resources should be cached
spring.resources.cache.cachecontrol.max-age=12h
########################################################################## DEBUG
#logging.level.root=DEBUG
#logging.level.org.springframework.web: DEBUG
#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
spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=- 1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.security.oauth2.client.registration.google.client-id=${OAUTH2_GOOGLE_CLIENT_ID}
spring.security.oauth2.client.registration.google.client-secret=${OAUTH2_GOOGLE_CLIENT_SECRET}
spring.security.oauth2.client.registration.github.client-id=${OAUTH2_GITHUB_CLIENT_ID}
spring.security.oauth2.client.registration.github.client-secret=${OAUTH2_GITHUB_CLIENT_SECRET}
#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>

View file

@ -51,3 +51,17 @@ INSERT INTO visits VALUES (1, 7, '2013-01-01', 'rabies shot');
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 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_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

@ -5,6 +5,8 @@ DROP TABLE visits IF EXISTS;
DROP TABLE pets IF EXISTS;
DROP TABLE types IF EXISTS;
DROP TABLE owners IF EXISTS;
DROP TABLE roles IF EXISTS;
DROP TABLE users IF EXISTS;
CREATE TABLE vets (
@ -62,3 +64,36 @@ CREATE TABLE visits (
);
ALTER TABLE visits ADD CONSTRAINT fk_visits_pets FOREIGN KEY (pet_id) REFERENCES pets (id);
CREATE INDEX visits_pet_id ON visits (pet_id);
CREATE TABLE roles (
id INTEGER IDENTITY PRIMARY KEY,
name VARCHAR(20) NOT NULL
);
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)
);
CREATE INDEX users_email ON users (email);
CREATE TABLE public.users_roles (
user_id INTEGER NOT NULL,
role_id INTEGER NOT NULL
);
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);

View file

@ -1,7 +0,0 @@
#footer {
position:absolute;
bottom:0;
width:100%;
height:60px; /* Height of the footer */
background:#6cf;
}

View file

@ -0,0 +1,141 @@
#footer {
position:absolute;
bottom:0;
width:100%;
height:60px; /* Height of the footer */
background:#6cf;
}
body {
font-family: Arial, Helvetica, sans-serif;
}
* {
box-sizing: border-box;
}
/* style the container */
.container {
position: relative;
border-radius: 5px;
background-color: #f2f2f2;
padding: 20px 0 30px 0;
}
/* style inputs and link buttons */
input,
.btn {
width: 100%;
padding: 12px;
border: none;
border-radius: 4px;
margin: 5px 0;
opacity: 0.85;
display: inline-block;
font-size: 17px;
line-height: 20px;
text-decoration: none; /* remove underline from anchors */
}
input:hover,
.btn:hover {
opacity: 1;
}
/* add appropriate colors to fb, twitter and google buttons */
.fb {
background-color: #3B5998;
color: white;
}
.twitter {
background-color: #55ACEE;
color: white;
}
.google {
background-color: #dd4b39;
color: white;
}
.github {
background-color: #dd4b39;
color: white;
}
/* style the submit button */
input[type=submit] {
background-color: #4CAF50;
color: white;
cursor: pointer;
}
input[type=submit]:hover {
background-color: #45a049;
}
/* Two-column layout */
.col {
float: left;
width: 50%;
margin: auto;
padding: 0 50px;
margin-top: 6px;
}
/* Clear floats after the columns */
.row:after {
content: "";
display: table;
clear: both;
}
/* vertical line */
.vl {
position: absolute;
left: 50%;
transform: translate(-50%);
border: 2px solid #ddd;
height: 175px;
}
/* text inside the vertical line */
.vl-innertext {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
background-color: #f1f1f1;
border: 1px solid #ccc;
border-radius: 50%;
padding: 8px 10px;
}
/* hide some text on medium and large screens */
.hide-md-lg {
display: none;
}
/* bottom container */
.bottom-container {
text-align: center;
background-color: #666;
border-radius: 0px 0px 4px 4px;
}
/* Responsive layout - when the screen is less than 650px wide, make the two columns stack on top of each other instead of next to each other */
@media screen and (max-width: 650px) {
.col {
width: 100%;
margin-top: 0;
}
/* hide the vertical line */
.vl {
display: none;
}
/* show the hidden text on small screens */
.hide-md-lg {
display: block;
text-align: center;
}
}

View file

@ -9,6 +9,8 @@
<div class="col-sm-10">
<div th:switch="${type}">
<input th:case="'text'" class="form-control" type="text" th:field="*{__${name}__}" />
<input th:case="'password'" class="form-control" type="password" th:field="*{__${name}__}" />
<input th:case="'email'" class="form-control" type="email" th:field="*{__${name}__}" />
<input th:case="'date'" class="form-control" type="text" th:field="*{__${name}__}"
placeholder="YYYY-MM-DD" title="Enter a date in this format: YYYY-MM-DD"
pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))"/>

View file

@ -17,9 +17,9 @@
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<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/static/css/style.css}" />
<!--link rel="stylesheet" th:href="@{/resources/css/style.css}" /-->
</head>
<body>
@ -69,12 +69,24 @@
<span>Veterinarians</span>
</li>
<li
th:replace="::menuItem ('/oups','error','trigger a RuntimeException to see how it is handled','warning-sign','Error')">
<li th:replace="::menuItem ('/oups','error','trigger a RuntimeException to see how it is handled','warning-sign','Error')">
<span class="glyphicon glyphicon-warning-sign" aria-hidden="true"></span>
<span>Error</span>
</li>
<li th:replace="::menuItem ('/login','user','login for known users','log-in','Login')" sec:authorize="!isAuthenticated()">
<span class="glyphicon glyphicon-log-in" aria-hidden="true"></span>
<span>Sign In</span>
</li>
<li th:replace="::menuItem ('/logout','user','logout','log-out','Logout')" sec:authorize="isAuthenticated()">
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span>
<span>Logout</span>
</li>
<li th:replace="::menuItem ('/users/edit','user','Edit user profile','user','Profile')" sec:authorize="isAuthenticated()">
<span class="glyphicon glyphicon-user" aria-hidden="true"></span>
<span>My account</span>
</li>
</ul>
</div>
</div>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'users')}">
<body>
<h2 style="text-align:center">Login Manually</h2>
<form th:object="${user}" class="form-horizontal" id="login-user-form" method="post">
<div class="form-group has-feedback">
<input
th:replace="~{fragments/inputField :: input ('Email', 'email', 'email')}" />
<input
th:replace="~{fragments/inputField :: input ('Password', 'password', 'password')}" />
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button class="btn btn-default" type="submit">Login</button>
<a href="/register" class="btn btn-default" ><span class="glyphicon glyphicon-pencil" aria-hidden="true"></span>
<span>Register</span></a>
</div>
</div>
</form>
<h2 style="text-align:center">Login with Social Media</h2>
<div class="row col-md-12">
<div class="col-md-6 offset-md-3" th:each="url : ${urls}" >
<a th:if="${url.key == 'Google'}" th:href="${url.value}" class="btn btn-bloc" style="background-color: #dd4b39;color: white">
<i class="fa fa-google fa-fw"></i> Login with Google
</a>
<a th:if="${url.key == 'Twitter'}" th:href="${url.value}" class="btn btn-bloc" style="background-color: #55ACEE;color: white">
<i class="fa fa-twitter fa-fw"></i> Login with Twitter
</a>
<a th:if="${url.key == 'GitHub'}" th:href="${url.value}" class="btn btn-bloc" style="background-color: white;color: black">
<i class="fa fa-github fa-fw"></i> Login with Github
</a>
<a th:if="${url.key == 'Facebook'}" th:href="${url.value}" class="btn btn-bloc" style="background-color: #3B5998;color: white">
<i class="fa fa-facebook fa-fw"></i> Login with Facebook
</a>
</div>
</div>
</body>

View file

@ -0,0 +1,44 @@
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'users')}">
<body>
<h2>User</h2>
<form th:object="${user}" class="form-horizontal" id="add-user-form" method="post">
<div class="form-group has-feedback">
<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 ('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
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
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>
</div>
</div>
</form>
</body>
</html>

View file

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org"
th:replace="~{fragments/layout :: layout (~{::body},'owners')}">
<body>
<h2>Owner Information</h2>
<table class="table table-striped" th:object="${owner}">
<tr>
<th>Name</th>
<td><b th:text="*{firstName + ' ' + lastName}"></b></td>
</tr>
<tr>
<th>Address</th>
<td th:text="*{address}"></td>
</tr>
<tr>
<th>City</th>
<td th:text="*{city}"></td>
</tr>
<tr>
<th>Telephone</th>
<td th:text="*{telephone}"></td>
</tr>
</table>
<a th:href="@{{id}/edit(id=${owner.id})}" class="btn btn-default">Edit
Owner</a>
<a th:href="@{{id}/pets/new(id=${owner.id})}" class="btn btn-default">Add
New Pet</a>
<br />
<br />
<br />
<h2>Pets and Visits</h2>
<table class="table table-striped">
<tr th:each="pet : ${owner.pets}">
<td valign="top">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd th:text="${pet.name}"></dd>
<dt>Birth Date</dt>
<dd
th:text="${#temporals.format(pet.birthDate, 'yyyy-MM-dd')}"></dd>
<dt>Type</dt>
<dd th:text="${pet.type}"></dd>
</dl>
</td>
<td valign="top">
<table class="table-condensed">
<thead>
<tr>
<th>Visit Date</th>
<th>Description</th>
</tr>
</thead>
<tr th:each="visit : ${pet.visits}">
<td th:text="${#temporals.format(visit.date, 'yyyy-MM-dd')}"></td>
<td th:text="${visit?.description}"></td>
</tr>
<tr>
<td><a
th:href="@{{ownerId}/pets/{petId}/edit(ownerId=${owner.id},petId=${pet.id})}">Edit
Pet</a></td>
<td><a
th:href="@{{ownerId}/pets/{petId}/visits/new(ownerId=${owner.id},petId=${pet.id})}">Add
Visit</a></td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View file

@ -13,18 +13,13 @@ import org.springframework.boot.test.context.SpringBootTest;
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.dto.VetDTO;
import org.springframework.samples.petclinic.dto.VetsDTO;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.samples.petclinic.service.VetService;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import static org.assertj.core.api.Assertions.assertThat;

View file

@ -10,7 +10,7 @@ import org.springframework.samples.petclinic.common.CommonEndPoint;
import org.springframework.samples.petclinic.common.CommonView;
import org.springframework.samples.petclinic.dto.PetDTO;
import org.springframework.samples.petclinic.dto.VisitDTO;
import org.springframework.samples.petclinic.model.Visit;
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.test.web.servlet.MockMvc;

View file

@ -39,7 +39,7 @@ import org.springframework.samples.petclinic.common.CommonView;
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.Visit;
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.test.web.servlet.MockMvc;

View file

@ -24,9 +24,8 @@ import org.mockito.Mock;
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.PetType;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.PetTypeService;
import java.text.ParseException;
import java.util.ArrayList;

View file

@ -30,9 +30,8 @@ import org.mockito.Mock;
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.PetType;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.service.PetService;
import org.springframework.samples.petclinic.service.PetTypeService;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;

View file

@ -16,6 +16,7 @@
package org.springframework.samples.petclinic.model;
import org.junit.jupiter.api.Test;
import org.springframework.samples.petclinic.model.business.Vet;
import org.springframework.util.SerializationUtils;
import static org.assertj.core.api.Assertions.assertThat;

View file

@ -25,14 +25,14 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.samples.petclinic.model.Owner;
import org.springframework.samples.petclinic.model.business.Owner;
import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.business.Pet;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.Vet;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.model.business.Vet;
import org.springframework.samples.petclinic.repository.VetRepository;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.model.business.Visit;
import org.springframework.samples.petclinic.repository.VisitRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

View file

@ -19,7 +19,7 @@ package org.springframework.samples.petclinic.service;
import java.util.Collection;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.samples.petclinic.model.BaseEntity;
import org.springframework.samples.petclinic.model.common.BaseEntity;
/**
* Utility methods for handling entities. Separate from the BaseEntity class mainly
@ -27,7 +27,7 @@ import org.springframework.samples.petclinic.model.BaseEntity;
*
* @author Juergen Hoeller
* @author Sam Brannen
* @see org.springframework.samples.petclinic.model.BaseEntity
* @see BaseEntity
* @since 29.10.2003
*/
public abstract class EntityUtils {

View file

@ -12,9 +12,9 @@ import org.springframework.samples.petclinic.common.CommonAttribute;
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.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.business.Owner;
import org.springframework.samples.petclinic.model.business.Pet;
import org.springframework.samples.petclinic.model.business.PetType;
import org.springframework.samples.petclinic.repository.OwnerRepository;
import org.springframework.samples.petclinic.repository.PetRepository;
import org.springframework.samples.petclinic.repository.PetTypeRepository;

View file

@ -8,13 +8,12 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.samples.petclinic.common.CommonAttribute;
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.model.Owner;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.samples.petclinic.model.PetType;
import org.springframework.samples.petclinic.model.business.Owner;
import org.springframework.samples.petclinic.model.business.Pet;
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;

View file

@ -9,7 +9,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.samples.petclinic.dto.VetDTO;
import org.springframework.samples.petclinic.model.Vet;
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.stereotype.Service;

View file

@ -24,7 +24,7 @@ import javax.validation.Validator;
import org.junit.jupiter.api.Test;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.samples.petclinic.model.Person;
import org.springframework.samples.petclinic.model.common.Person;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;