[SECARSP-111] +Auth API [SECARSP-115 +Get Devices]
authorm.dalakov <m.dalakov@samsung.com>
Tue, 20 Feb 2018 13:30:08 +0000 (15:30 +0200)
committerDmytro Lomtiev <d.lomtev@samsung.com>
Fri, 16 Mar 2018 14:16:49 +0000 (16:16 +0200)
Change-Id: Ia891c87e1569ec43eccdc11a626fe3c11aa5aba8

25 files changed:
server/.gitignore
server/pom.xml
server/src/main/java/com/samsung/samserver/SamserverApp.java
server/src/main/java/com/samsung/samserver/config/MicroserviceSecurityConfiguration.java
server/src/main/java/com/samsung/samserver/domain/Authority.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/domain/User.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/repository/AuthorityRepository.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/repository/UserRepository.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/security/DomainUserDetailsService.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/service/dto/UserDTO.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/service/dto/UserMapper.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/service/impl/MailService.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/service/impl/UserService.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/web/rest/UserResource.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/web/rest/controller/IRestDashboard.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/web/rest/controller/IRestDevice.java
server/src/main/java/com/samsung/samserver/web/rest/controller/impl/RestDashboard.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/web/rest/controller/impl/RestDevice.java [moved from server/src/main/java/com/samsung/samserver/web/rest/controller/RestDevice.java with 94% similarity]
server/src/main/java/com/samsung/samserver/web/rest/service/AccountService.java [new file with mode: 0644]
server/src/main/java/com/samsung/samserver/web/rest/service/UserJWTService.java [new file with mode: 0644]
server/src/main/resources/.h2.server.properties
server/src/main/resources/config/liquibase/authorities.csv [new file with mode: 0644]
server/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
server/src/main/resources/config/liquibase/users.csv [new file with mode: 0644]
server/src/main/resources/config/liquibase/users_authorities.csv [new file with mode: 0644]

index c2cb540..f70b10c 100644 (file)
@@ -5,6 +5,8 @@
 /src/test/javascript/coverage/
 /src/test/javascript/PhantomJS*/
 
+/src/main/resources/.h2.server.properties
+
 ######################
 # Node
 ######################
index e2fa264..aff4ed5 100644 (file)
@@ -57,7 +57,7 @@
         <dockerfile-maven-plugin.version>1.3.4</dockerfile-maven-plugin.version>
         <jacoco-maven-plugin.version>0.7.9</jacoco-maven-plugin.version>
         <scala-maven-plugin.version>3.2.2</scala-maven-plugin.version>
-        <sonar-maven-plugin.version>3.2</sonar-maven-plugin.version>
+        <sonar-maven-plugin.version>3.4.0.905</sonar-maven-plugin.version>
 
         <!-- Sonar properties -->
         <sonar.exclusions>src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, target/www/**/*.*</sonar.exclusions>
@@ -82,6 +82,7 @@
 
         <apache.commons-codec.version>1.11</apache.commons-codec.version>
         <springfox-swagger-ui.version>2.8.0</springfox-swagger-ui.version>
+        <jackson-dataformats-binary.version>2.9.4</jackson-dataformats-binary.version>
 
     </properties>
 
             <version>${apache.commons-codec.version}</version>
         </dependency>
 
+        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.dataformat/jackson-dataformats-binary -->
+        <dependency>
+            <groupId>com.fasterxml.jackson.dataformat</groupId>
+            <artifactId>jackson-dataformats-binary</artifactId>
+            <version>${jackson-dataformats-binary.version}</version>
+            <type>pom</type>
+        </dependency>
+
     </dependencies>
 
     <build>
index 2ad581e..cc26c39 100644 (file)
@@ -68,7 +68,7 @@ public class SamserverApp {
                 "run with both the 'dev' and 'cloud' profiles at the same time.");
         }
         managementDocket.enable(false);
-        apiDocket.select().paths(PathSelectors.regex("/api/device-service.*")).build();
+        apiDocket.select().paths(PathSelectors.regex("/api/device-service.*|/dashboard.*")).build();
     }
 
     /**
index bb0ff25..bbbe86c 100644 (file)
@@ -20,6 +20,14 @@ import org.springframework.security.config.http.SessionCreationPolicy;
 import org.springframework.security.data.repository.query.SecurityEvaluationContextExtension;
 import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;
 
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.web.filter.CorsFilter;
+import javax.annotation.PostConstruct;
+import org.springframework.beans.factory.BeanInitializationException;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+
 @Configuration
 @Import(SecurityProblemSupport.class)
 @EnableWebSecurity
@@ -30,11 +38,36 @@ public class MicroserviceSecurityConfiguration extends WebSecurityConfigurerAdap
 
     private final SecurityProblemSupport problemSupport;
 
-    public MicroserviceSecurityConfiguration(TokenProvider tokenProvider, SecurityProblemSupport problemSupport) {
+    private final AuthenticationManagerBuilder authenticationManagerBuilder;
+
+    private final UserDetailsService userDetailsService;
+
+    private final CorsFilter corsFilter;
+
+    public MicroserviceSecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, UserDetailsService userDetailsService,TokenProvider tokenProvider,CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
+        this.authenticationManagerBuilder = authenticationManagerBuilder;
+        this.userDetailsService = userDetailsService;
         this.tokenProvider = tokenProvider;
+        this.corsFilter = corsFilter;
         this.problemSupport = problemSupport;
     }
 
+    @PostConstruct
+    public void init() {
+        try {
+            authenticationManagerBuilder
+                    .userDetailsService(userDetailsService)
+                    .passwordEncoder(passwordEncoder());
+        } catch (Exception e) {
+            throw new BeanInitializationException("Security configuration failed", e);
+        }
+    }
+
+    @Bean
+    public PasswordEncoder passwordEncoder() {
+        return new BCryptPasswordEncoder();
+    }
+
     @Override
     public void configure(WebSecurity web) throws Exception {
         web.ignoring()
@@ -67,7 +100,9 @@ public class MicroserviceSecurityConfiguration extends WebSecurityConfigurerAdap
             .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
         .and()
             .authorizeRequests()
-            //.antMatchers("/api/**").authenticated()
+            .antMatchers("/dashboard/auth/login").permitAll()
+            .antMatchers("/dashboard/auth/register").permitAll()
+            .antMatchers("/dashboard/**").authenticated()
             .antMatchers("/management/health").permitAll()
             .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN)
             .antMatchers("/swagger-resources/configuration/ui").permitAll()
diff --git a/server/src/main/java/com/samsung/samserver/domain/Authority.java b/server/src/main/java/com/samsung/samserver/domain/Authority.java
new file mode 100644 (file)
index 0000000..6ab74d5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.domain;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Column;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+
+/**
+ * An authority (a security role) used by Spring Security.
+ */
+@Entity
+@Table(name = "jhi_authority")
+
+public class Authority implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @NotNull
+    @Size(max = 50)
+    @Id
+    @Column(length = 50)
+    private String name;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        Authority authority = (Authority) o;
+
+        return !(name != null ? !name.equals(authority.name) : authority.name != null);
+    }
+
+    @Override
+    public int hashCode() {
+        return name != null ? name.hashCode() : 0;
+    }
+
+    @Override
+    public String toString() {
+        return "Authority{" +
+            "name='" + name + '\'' +
+            "}";
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/domain/User.java b/server/src/main/java/com/samsung/samserver/domain/User.java
new file mode 100644 (file)
index 0000000..1827fc6
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package  com.samsung.samserver.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.samsung.samserver.config.Constants;
+import org.apache.commons.lang3.StringUtils;
+import org.hibernate.annotations.BatchSize;
+import org.hibernate.validator.constraints.Email;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Pattern;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.time.Instant;
+
+/**
+ * A user.
+ */
+@Entity
+@Table(name = "jhi_user")
+
+public class User extends AbstractAuditingEntity implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @Id
+    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
+    @SequenceGenerator(name = "sequenceGenerator")
+    private Long id;
+
+    @NotNull
+    @Pattern(regexp = Constants.LOGIN_REGEX)
+    @Size(min = 1, max = 50)
+    @Column(length = 50, unique = true, nullable = false)
+    private String login;
+
+    @JsonIgnore
+    @NotNull
+    @Size(min = 60, max = 60)
+    @Column(name = "password_hash", length = 60)
+    private String password;
+
+    @Size(max = 50)
+    @Column(name = "first_name", length = 50)
+    private String firstName;
+
+    @Size(max = 50)
+    @Column(name = "last_name", length = 50)
+    private String lastName;
+
+    @Email
+    @Size(min = 5, max = 100)
+    @Column(length = 100, unique = true)
+    private String email;
+
+    @NotNull
+    @Column(nullable = false)
+    private boolean activated = false;
+
+    @Size(min = 2, max = 6)
+    @Column(name = "lang_key", length = 6)
+    private String langKey;
+
+    @Size(max = 256)
+    @Column(name = "image_url", length = 256)
+    private String imageUrl;
+
+    @Size(max = 20)
+    @Column(name = "activation_key", length = 20)
+    @JsonIgnore
+    private String activationKey;
+
+    @Size(max = 20)
+    @Column(name = "reset_key", length = 20)
+    @JsonIgnore
+    private String resetKey;
+
+    @Column(name = "reset_date")
+    private Instant resetDate = null;
+
+    @JsonIgnore
+    @ManyToMany
+    @JoinTable(
+        name = "jhi_user_authority",
+        joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "id")},
+        inverseJoinColumns = {@JoinColumn(name = "authority_name", referencedColumnName = "name")})
+
+    @BatchSize(size = 20)
+    private Set<Authority> authorities = new HashSet<>();
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getLogin() {
+        return login;
+    }
+
+    // Lowercase the login before saving it in database
+    public void setLogin(String login) {
+        this.login = StringUtils.lowerCase(login, Locale.ENGLISH);
+    }
+
+    public String getPassword() {
+        return password;
+    }
+
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    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 getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getImageUrl() {
+        return imageUrl;
+    }
+
+    public void setImageUrl(String imageUrl) {
+        this.imageUrl = imageUrl;
+    }
+
+    public boolean getActivated() {
+        return activated;
+    }
+
+    public void setActivated(boolean activated) {
+        this.activated = activated;
+    }
+
+    public String getActivationKey() {
+        return activationKey;
+    }
+
+    public void setActivationKey(String activationKey) {
+        this.activationKey = activationKey;
+    }
+
+    public String getResetKey() {
+        return resetKey;
+    }
+
+    public void setResetKey(String resetKey) {
+        this.resetKey = resetKey;
+    }
+
+    public Instant getResetDate() {
+        return resetDate;
+    }
+
+    public void setResetDate(Instant resetDate) {
+        this.resetDate = resetDate;
+    }
+
+    public String getLangKey() {
+        return langKey;
+    }
+
+    public void setLangKey(String langKey) {
+        this.langKey = langKey;
+    }
+
+    public Set<Authority> getAuthorities() {
+        return authorities;
+    }
+
+    public void setAuthorities(Set<Authority> authorities) {
+        this.authorities = authorities;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        User user = (User) o;
+        return !(user.getId() == null || getId() == null) && Objects.equals(getId(), user.getId());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(getId());
+    }
+
+    @Override
+    public String toString() {
+        return "User{" +
+            "login='" + login + '\'' +
+            ", firstName='" + firstName + '\'' +
+            ", lastName='" + lastName + '\'' +
+            ", email='" + email + '\'' +
+            ", imageUrl='" + imageUrl + '\'' +
+            ", activated='" + activated + '\'' +
+            ", langKey='" + langKey + '\'' +
+            ", activationKey='" + activationKey + '\'' +
+            "}";
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/repository/AuthorityRepository.java b/server/src/main/java/com/samsung/samserver/repository/AuthorityRepository.java
new file mode 100644 (file)
index 0000000..b06cb87
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.repository;
+
+
+import com.samsung.samserver.domain.Authority;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+/**
+ * Spring Data JPA repository for the Authority entity.
+ */
+public interface AuthorityRepository extends JpaRepository<Authority, String> {
+}
diff --git a/server/src/main/java/com/samsung/samserver/repository/UserRepository.java b/server/src/main/java/com/samsung/samserver/repository/UserRepository.java
new file mode 100644 (file)
index 0000000..2ead8d4
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.repository;
+
+import com.samsung.samserver.domain.User;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.EntityGraph;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import java.util.List;
+import java.util.Optional;
+import java.time.Instant;
+
+/**
+ * Spring Data JPA repository for the User entity.
+ */
+@Repository
+public interface UserRepository extends JpaRepository<User, Long> {
+
+    Optional<User> findOneByActivationKey(String activationKey);
+
+    List<User> findAllByActivatedIsFalseAndCreatedDateBefore(Instant dateTime);
+
+    Optional<User> findOneByResetKey(String resetKey);
+
+    Optional<User> findOneByEmailIgnoreCase(String email);
+
+    Optional<User> findOneByLogin(String login);
+
+    @EntityGraph(attributePaths = "authorities")
+    Optional<User> findOneWithAuthoritiesById(Long id);
+
+    @EntityGraph(attributePaths = "authorities")
+    Optional<User> findOneWithAuthoritiesByLogin(String login);
+
+    @EntityGraph(attributePaths = "authorities")
+    Optional<User> findOneWithAuthoritiesByEmail(String email);
+
+    Page<User> findAllByLoginNot(Pageable pageable, String login);
+}
diff --git a/server/src/main/java/com/samsung/samserver/security/DomainUserDetailsService.java b/server/src/main/java/com/samsung/samserver/security/DomainUserDetailsService.java
new file mode 100644 (file)
index 0000000..a209ec3
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.security;
+
+
+import com.samsung.samserver.domain.User;
+import com.samsung.samserver.repository.UserRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.security.core.AuthenticationException;
+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.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Authenticate a user from the database.
+ */
+@Component("userDetailsService")
+public class DomainUserDetailsService implements UserDetailsService {
+
+    private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);
+
+    private final UserRepository userRepository;
+
+    public DomainUserDetailsService(UserRepository userRepository) {
+        this.userRepository = userRepository;
+    }
+
+    @Override
+    @Transactional
+    public UserDetails loadUserByUsername(final String login) {
+        log.debug("Authenticating {}", login);
+        String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
+        Optional<User> userByEmailFromDatabase = userRepository.findOneWithAuthoritiesByEmail(lowercaseLogin);
+        return userByEmailFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user)).orElseGet(() -> {
+            Optional<User> userByLoginFromDatabase = userRepository.findOneWithAuthoritiesByLogin(lowercaseLogin);
+            return userByLoginFromDatabase.map(user -> createSpringSecurityUser(lowercaseLogin, user))
+                .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the " +
+                    "database"));
+        });
+    }
+
+    private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) {
+        if (!user.getActivated()) {
+            throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated");
+        }
+        List<GrantedAuthority> grantedAuthorities = user.getAuthorities().stream()
+            .map(authority -> new SimpleGrantedAuthority(authority.getName()))
+            .collect(Collectors.toList());
+        return new org.springframework.security.core.userdetails.User(user.getLogin(),
+            user.getPassword(),
+            grantedAuthorities);
+    }
+
+
+    /**
+     * This exception is thrown in case of a not activated user trying to authenticate.
+     */
+    public static class UserNotActivatedException extends AuthenticationException {
+
+        private static final long serialVersionUID = 1L;
+
+        public UserNotActivatedException(String message) {
+            super(message);
+        }
+
+        public UserNotActivatedException(String message, Throwable t) {
+            super(message, t);
+        }
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/service/dto/UserDTO.java b/server/src/main/java/com/samsung/samserver/service/dto/UserDTO.java
new file mode 100644 (file)
index 0000000..9820909
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.service.dto;
+
+
+
+import com.samsung.samserver.config.Constants;
+import com.samsung.samserver.domain.Authority;
+import com.samsung.samserver.domain.User;
+import org.hibernate.validator.constraints.Email;
+import org.hibernate.validator.constraints.NotBlank;
+
+import javax.validation.constraints.*;
+import java.time.Instant;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * A DTO representing a user, with his authorities.
+ */
+public class UserDTO {
+
+    private Long id;
+
+    @NotBlank
+    @Pattern(regexp = Constants.LOGIN_REGEX)
+    @Size(min = 1, max = 50)
+    private String login;
+
+    @Size(max = 50)
+    private String firstName;
+
+    @Size(max = 50)
+    private String lastName;
+
+    @Email
+    @Size(min = 5, max = 100)
+    private String email;
+
+    @Size(max = 256)
+    private String imageUrl;
+
+    private boolean activated = false;
+
+    @Size(min = 2, max = 6)
+    private String langKey;
+
+    private String createdBy;
+
+    private Instant createdDate;
+
+    private String lastModifiedBy;
+
+    private Instant lastModifiedDate;
+
+    private Set<String> authorities;
+
+    public UserDTO() {
+        // Empty constructor needed for Jackson.
+    }
+
+    public UserDTO(User user) {
+        this.id = user.getId();
+        this.login = user.getLogin();
+        this.firstName = user.getFirstName();
+        this.lastName = user.getLastName();
+        this.email = user.getEmail();
+        this.activated = user.getActivated();
+        this.imageUrl = user.getImageUrl();
+        this.langKey = user.getLangKey();
+        this.authorities = user.getAuthorities().stream()
+            .map(Authority::getName)
+            .collect(Collectors.toSet());
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public String getLogin() {
+        return login;
+    }
+
+    public void setLogin(String login) {
+        this.login = login;
+    }
+
+    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 getEmail() {
+        return email;
+    }
+
+    public void setEmail(String email) {
+        this.email = email;
+    }
+
+    public String getImageUrl() {
+        return imageUrl;
+    }
+
+    public void setImageUrl(String imageUrl) {
+        this.imageUrl = imageUrl;
+    }
+
+    public boolean isActivated() {
+        return activated;
+    }
+
+    public void setActivated(boolean activated) {
+        this.activated = activated;
+    }
+
+    public String getLangKey() {
+        return langKey;
+    }
+
+    public void setLangKey(String langKey) {
+        this.langKey = langKey;
+    }
+
+    public String getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(String createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public Instant getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(Instant createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public String getLastModifiedBy() {
+        return lastModifiedBy;
+    }
+
+    public void setLastModifiedBy(String lastModifiedBy) {
+        this.lastModifiedBy = lastModifiedBy;
+    }
+
+    public Instant getLastModifiedDate() {
+        return lastModifiedDate;
+    }
+
+    public void setLastModifiedDate(Instant lastModifiedDate) {
+        this.lastModifiedDate = lastModifiedDate;
+    }
+
+    public Set<String> getAuthorities() {
+        return authorities;
+    }
+
+    public void setAuthorities(Set<String> authorities) {
+        this.authorities = authorities;
+    }
+
+    @Override
+    public String toString() {
+        return "UserDTO{" +
+            "login='" + login + '\'' +
+            ", firstName='" + firstName + '\'' +
+            ", lastName='" + lastName + '\'' +
+            ", email='" + email + '\'' +
+            ", imageUrl='" + imageUrl + '\'' +
+            ", activated=" + activated +
+            ", langKey='" + langKey + '\'' +
+            ", createdBy=" + createdBy +
+            ", createdDate=" + createdDate +
+            ", lastModifiedBy='" + lastModifiedBy + '\'' +
+            ", lastModifiedDate=" + lastModifiedDate +
+            ", authorities=" + authorities +
+            "}";
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/service/dto/UserMapper.java b/server/src/main/java/com/samsung/samserver/service/dto/UserMapper.java
new file mode 100644 (file)
index 0000000..2234334
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.service.dto;
+
+import com.samsung.samserver.domain.Authority;
+import com.samsung.samserver.domain.User;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Mapper for the entity User and its DTO called UserDTO.
+ *
+ * Normal mappers are generated using MapStruct, this one is hand-coded as MapStruct
+ * support is still in beta, and requires a manual step with an IDE.
+ */
+@Service
+public class UserMapper {
+
+    public UserDTO userToUserDTO(User user) {
+        return new UserDTO(user);
+    }
+
+    public List<UserDTO> usersToUserDTOs(List<User> users) {
+        return users.stream()
+            .filter(Objects::nonNull)
+            .map(this::userToUserDTO)
+            .collect(Collectors.toList());
+    }
+
+    public User userDTOToUser(UserDTO userDTO) {
+        if (userDTO == null) {
+            return null;
+        } else {
+            User user = new User();
+            user.setId(userDTO.getId());
+            user.setLogin(userDTO.getLogin());
+            user.setFirstName(userDTO.getFirstName());
+            user.setLastName(userDTO.getLastName());
+            user.setEmail(userDTO.getEmail());
+            user.setImageUrl(userDTO.getImageUrl());
+            user.setActivated(userDTO.isActivated());
+            user.setLangKey(userDTO.getLangKey());
+            Set<Authority> authorities = this.authoritiesFromStrings(userDTO.getAuthorities());
+            if (authorities != null) {
+                user.setAuthorities(authorities);
+            }
+            return user;
+        }
+    }
+
+    public List<User> userDTOsToUsers(List<UserDTO> userDTOs) {
+        return userDTOs.stream()
+            .filter(Objects::nonNull)
+            .map(this::userDTOToUser)
+            .collect(Collectors.toList());
+    }
+
+    public User userFromId(Long id) {
+        if (id == null) {
+            return null;
+        }
+        User user = new User();
+        user.setId(id);
+        return user;
+    }
+
+    public Set<Authority> authoritiesFromStrings(Set<String> strings) {
+        return strings.stream().map(string -> {
+            Authority auth = new Authority();
+            auth.setName(string);
+            return auth;
+        }).collect(Collectors.toSet());
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/service/impl/MailService.java b/server/src/main/java/com/samsung/samserver/service/impl/MailService.java
new file mode 100644 (file)
index 0000000..c97eaee
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.service.impl;
+
+import com.samsung.samserver.domain.User;
+import io.github.jhipster.config.JHipsterProperties;
+
+import org.apache.commons.lang3.CharEncoding;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.MessageSource;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.mail.javamail.MimeMessageHelper;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+import org.thymeleaf.context.Context;
+import org.thymeleaf.spring4.SpringTemplateEngine;
+
+import javax.mail.internet.MimeMessage;
+import java.util.Locale;
+
+/**
+ * Service for sending emails.
+ * <p>
+ * We use the @Async annotation to send emails asynchronously.
+ */
+@Service
+public class MailService {
+
+    private final Logger log = LoggerFactory.getLogger(MailService.class);
+
+    private static final String USER = "user";
+
+    private static final String BASE_URL = "baseUrl";
+
+    private final JHipsterProperties jHipsterProperties;
+
+    private final JavaMailSender javaMailSender;
+
+    private final MessageSource messageSource;
+
+    private final SpringTemplateEngine templateEngine;
+
+    public MailService(JHipsterProperties jHipsterProperties, JavaMailSender javaMailSender,
+            MessageSource messageSource, SpringTemplateEngine templateEngine) {
+
+        this.jHipsterProperties = jHipsterProperties;
+        this.javaMailSender = javaMailSender;
+        this.messageSource = messageSource;
+        this.templateEngine = templateEngine;
+    }
+
+    @Async
+    public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) {
+        log.debug("Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}",
+            isMultipart, isHtml, to, subject, content);
+
+        // Prepare message using a Spring helper
+        MimeMessage mimeMessage = javaMailSender.createMimeMessage();
+        try {
+            MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, CharEncoding.UTF_8);
+            message.setTo(to);
+            message.setFrom(jHipsterProperties.getMail().getFrom());
+            message.setSubject(subject);
+            message.setText(content, isHtml);
+            javaMailSender.send(mimeMessage);
+            log.debug("Sent email to User '{}'", to);
+        } catch (Exception e) {
+            if (log.isDebugEnabled()) {
+                log.warn("Email could not be sent to user '{}'", to, e);
+            } else {
+                log.warn("Email could not be sent to user '{}': {}", to, e.getMessage());
+            }
+        }
+    }
+
+    @Async
+    public void sendEmailFromTemplate(User user, String templateName, String titleKey) {
+        Locale locale = Locale.forLanguageTag(user.getLangKey());
+        Context context = new Context(locale);
+        context.setVariable(USER, user);
+        context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl());
+        String content = templateEngine.process(templateName, context);
+        String subject = messageSource.getMessage(titleKey, null, locale);
+        sendEmail(user.getEmail(), subject, content, false, true);
+
+    }
+
+    @Async
+    public void sendActivationEmail(User user) {
+        log.debug("Sending activation email to '{}'", user.getEmail());
+        sendEmailFromTemplate(user, "activationEmail", "email.activation.title");
+    }
+
+    @Async
+    public void sendCreationEmail(User user) {
+        log.debug("Sending creation email to '{}'", user.getEmail());
+        sendEmailFromTemplate(user, "creationEmail", "email.activation.title");
+    }
+
+    @Async
+    public void sendPasswordResetMail(User user) {
+        log.debug("Sending password reset email to '{}'", user.getEmail());
+        sendEmailFromTemplate(user, "passwordResetEmail", "email.reset.title");
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/service/impl/UserService.java b/server/src/main/java/com/samsung/samserver/service/impl/UserService.java
new file mode 100644 (file)
index 0000000..8a1f29f
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.service.impl;
+
+
+import com.samsung.samserver.service.dto.UserDTO;
+
+import com.samsung.samserver.config.Constants;
+import com.samsung.samserver.domain.*;
+import com.samsung.samserver.repository.AuthorityRepository;
+import com.samsung.samserver.repository.UserRepository;
+import com.samsung.samserver.security.AuthoritiesConstants;
+import com.samsung.samserver.security.SecurityUtils;
+import org.apache.commons.lang3.RandomStringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * Service class for managing users.
+ */
+@Service
+@Transactional
+public class UserService {
+
+    private final Logger log = LoggerFactory.getLogger(UserService.class);
+
+    private final UserRepository userRepository;
+
+    private final PasswordEncoder passwordEncoder;
+
+    private final AuthorityRepository authorityRepository;
+
+    public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) {
+        this.userRepository = userRepository;
+        this.passwordEncoder = passwordEncoder;
+        this.authorityRepository = authorityRepository;
+    }
+
+    public Optional<User> activateRegistration(String key) {
+        log.debug("Activating user for activation key {}", key);
+        return userRepository.findOneByActivationKey(key)
+            .map(user -> {
+                // activate given user for the registration key.
+                user.setActivated(true);
+                user.setActivationKey(null);
+                log.debug("Activated user: {}", user);
+                return user;
+            });
+    }
+
+    public Optional<User> completePasswordReset(String newPassword, String key) {
+       log.debug("Reset user password for reset key {}", key);
+
+       return userRepository.findOneByResetKey(key)
+           .filter(user -> user.getResetDate().isAfter(Instant.now().minusSeconds(86400)))
+           .map(user -> {
+                user.setPassword(passwordEncoder.encode(newPassword));
+                user.setResetKey(null);
+                user.setResetDate(null);
+                return user;
+           });
+    }
+
+    public Optional<User> requestPasswordReset(String mail) {
+        return userRepository.findOneByEmailIgnoreCase(mail)
+            .filter(User::getActivated)
+            .map(user -> {
+                user.setResetKey(RandomUtil.generateResetKey());
+                user.setResetDate(Instant.now());
+                return user;
+            });
+    }
+
+    public User registerUser(UserDTO userDTO, String password) {
+
+        User newUser = new User();
+        Authority authority = authorityRepository.findOne(AuthoritiesConstants.USER);
+        Set<Authority> authorities = new HashSet<>();
+        String encryptedPassword = passwordEncoder.encode(password);
+        newUser.setLogin(userDTO.getLogin());
+        // new user gets initially a generated password
+        newUser.setPassword(encryptedPassword);
+        newUser.setFirstName(userDTO.getFirstName());
+        newUser.setLastName(userDTO.getLastName());
+        newUser.setEmail(userDTO.getEmail());
+        newUser.setImageUrl(userDTO.getImageUrl());
+        newUser.setLangKey(userDTO.getLangKey());
+        // new user is not active
+        newUser.setActivated(false);
+        // new user gets registration key
+        newUser.setActivationKey(RandomUtil.generateActivationKey());
+        authorities.add(authority);
+        newUser.setAuthorities(authorities);
+        userRepository.save(newUser);
+        log.debug("Created Information for User: {}", newUser);
+        return newUser;
+    }
+
+    public User createUser(UserDTO userDTO) {
+        User user = new User();
+        user.setLogin(userDTO.getLogin());
+        user.setFirstName(userDTO.getFirstName());
+        user.setLastName(userDTO.getLastName());
+        user.setEmail(userDTO.getEmail());
+        user.setImageUrl(userDTO.getImageUrl());
+        if (userDTO.getLangKey() == null) {
+            user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language
+        } else {
+            user.setLangKey(userDTO.getLangKey());
+        }
+        if (userDTO.getAuthorities() != null) {
+            Set<Authority> authorities = userDTO.getAuthorities().stream()
+                .map(authorityRepository::findOne)
+                .collect(Collectors.toSet());
+            user.setAuthorities(authorities);
+        }
+        String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword());
+        user.setPassword(encryptedPassword);
+        user.setResetKey(RandomUtil.generateResetKey());
+        user.setResetDate(Instant.now());
+        user.setActivated(true);
+        userRepository.save(user);
+        log.debug("Created Information for User: {}", user);
+        return user;
+    }
+
+    /**
+     * Update basic information (first name, last name, email, language) for the current user.
+     *
+     * @param firstName first name of user
+     * @param lastName last name of user
+     * @param email email id of user
+     * @param langKey language key
+     * @param imageUrl image URL of user
+     */
+    public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) {
+        SecurityUtils.getCurrentUserLogin()
+            .flatMap(userRepository::findOneByLogin)
+            .ifPresent(user -> {
+                user.setFirstName(firstName);
+                user.setLastName(lastName);
+                user.setEmail(email);
+                user.setLangKey(langKey);
+                user.setImageUrl(imageUrl);
+                log.debug("Changed Information for User: {}", user);
+            });
+    }
+
+    /**
+     * Update all information for a specific user, and return the modified user.
+     *
+     * @param userDTO user to update
+     * @return updated user
+     */
+    public Optional<UserDTO> updateUser(UserDTO userDTO) {
+        return Optional.of(userRepository
+            .findOne(userDTO.getId()))
+            .map(user -> {
+                user.setLogin(userDTO.getLogin());
+                user.setFirstName(userDTO.getFirstName());
+                user.setLastName(userDTO.getLastName());
+                user.setEmail(userDTO.getEmail());
+                user.setImageUrl(userDTO.getImageUrl());
+                user.setActivated(userDTO.isActivated());
+                user.setLangKey(userDTO.getLangKey());
+                Set<Authority> managedAuthorities = user.getAuthorities();
+                managedAuthorities.clear();
+                userDTO.getAuthorities().stream()
+                    .map(authorityRepository::findOne)
+                    .forEach(managedAuthorities::add);
+                log.debug("Changed Information for User: {}", user);
+                return user;
+            })
+            .map(UserDTO::new);
+    }
+
+    public void deleteUser(String login) {
+        userRepository.findOneByLogin(login).ifPresent(user -> {
+            userRepository.delete(user);
+            log.debug("Deleted User: {}", user);
+        });
+    }
+
+    public void changePassword(String password) {
+        SecurityUtils.getCurrentUserLogin()
+            .flatMap(userRepository::findOneByLogin)
+            .ifPresent(user -> {
+                String encryptedPassword = passwordEncoder.encode(password);
+                user.setPassword(encryptedPassword);
+                log.debug("Changed password for User: {}", user);
+            });
+    }
+
+    @Transactional(readOnly = true)
+    public Page<UserDTO> getAllManagedUsers(Pageable pageable) {
+        return userRepository.findAllByLoginNot(pageable, Constants.ANONYMOUS_USER).map(UserDTO::new);
+    }
+
+    @Transactional(readOnly = true)
+    public Optional<User> getUserWithAuthoritiesByLogin(String login) {
+        return userRepository.findOneWithAuthoritiesByLogin(login);
+    }
+
+    @Transactional(readOnly = true)
+    public Optional<User> getUserWithAuthorities(Long id) {
+        return userRepository.findOneWithAuthoritiesById(id);
+    }
+
+    @Transactional(readOnly = true)
+    public Optional<User> getUserWithAuthorities() {
+        return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneWithAuthoritiesByLogin);
+    }
+
+    /**
+     * Not activated users should be automatically deleted after 3 days.
+     * <p>
+     * This is scheduled to get fired everyday, at 01:00 (am).
+     */
+    @Scheduled(cron = "0 0 1 * * ?")
+    public void removeNotActivatedUsers() {
+        List<User> users = userRepository.findAllByActivatedIsFalseAndCreatedDateBefore(Instant.now().minus(3, ChronoUnit.DAYS));
+        for (User user : users) {
+            log.debug("Deleting not activated user {}", user.getLogin());
+            userRepository.delete(user);
+        }
+    }
+
+    /**
+     * @return a list of all the authorities
+     */
+    public List<String> getAuthorities() {
+        return authorityRepository.findAll().stream().map(Authority::getName).collect(Collectors.toList());
+    }
+
+    /**
+     * Utility class for generating random Strings.
+     */
+    public static final class RandomUtil {
+
+        private static final int DEF_COUNT = 20;
+
+        private RandomUtil() {
+        }
+
+        /**
+         * Generate a password.
+         *
+         * @return the generated password
+         */
+        public static String generatePassword() {
+            return RandomStringUtils.randomAlphanumeric(DEF_COUNT);
+        }
+
+        /**
+         * Generate an activation key.
+         *
+         * @return the generated activation key
+         */
+        public static String generateActivationKey() {
+            return RandomStringUtils.randomNumeric(DEF_COUNT);
+        }
+
+        /**
+         * Generate a reset key.
+         *
+         * @return the generated reset key
+         */
+        public static String generateResetKey() {
+            return RandomStringUtils.randomNumeric(DEF_COUNT);
+        }
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/web/rest/UserResource.java b/server/src/main/java/com/samsung/samserver/web/rest/UserResource.java
new file mode 100644 (file)
index 0000000..3ddb009
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.web.rest;
+
+import com.codahale.metrics.annotation.Timed;
+import com.samsung.samserver.config.Constants;
+import com.samsung.samserver.domain.User;
+import com.samsung.samserver.repository.UserRepository;
+import com.samsung.samserver.security.AuthoritiesConstants;
+import com.samsung.samserver.service.impl.MailService;
+import com.samsung.samserver.service.impl.UserService;
+import com.samsung.samserver.service.dto.UserDTO;
+import com.samsung.samserver.web.rest.errors.BadRequestAlertException;
+import com.samsung.samserver.web.rest.errors.UserServiceError;
+import com.samsung.samserver.web.rest.util.HeaderUtil;
+import com.samsung.samserver.web.rest.util.PaginationUtil;
+import io.github.jhipster.web.util.ResponseUtil;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.access.annotation.Secured;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.*;
+
+/**
+ * REST controller for managing users.
+ * <p>
+ * This class accesses the User entity, and needs to fetch its collection of authorities.
+ * <p>
+ * For a normal use-case, it would be better to have an eager relationship between User and Authority,
+ * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join
+ * which would be good for performance.
+ * <p>
+ * We use a View Model and a DTO for 3 reasons:
+ * <ul>
+ * <li>We want to keep a lazy association between the user and the authorities, because people will
+ * quite often do relationships with the user, and we don't want them to get the authorities all
+ * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users'
+ * application because of this use-case.</li>
+ * <li> Not having an outer join causes n+1 requests to the database. This is not a real issue as
+ * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests,
+ * but then all authorities come from the cache, so in fact it's much better than doing an outer join
+ * (which will get lots of data from the database, for each HTTP call).</li>
+ * <li> As this manages users, for security reasons, we'd rather have a DTO layer.</li>
+ * </ul>
+ * <p>
+ * Another option would be to have a specific JPA entity graph to handle this case.
+ */
+@RestController
+@RequestMapping("/api")
+public class UserResource {
+
+    private final Logger log = LoggerFactory.getLogger(UserResource.class);
+
+    private final UserRepository userRepository;
+
+    private final UserService userService;
+
+    private final MailService mailService;
+
+    public UserResource(UserRepository userRepository, UserService userService, MailService mailService) {
+
+        this.userRepository = userRepository;
+        this.userService = userService;
+        this.mailService = mailService;
+    }
+
+    /**
+     * POST  /users  : Creates a new user.
+     * <p>
+     * Creates a new user if the login and email are not already used, and sends an
+     * mail with an activation link.
+     * The user needs to be activated on creation.
+     *
+     * @param userDTO the user to create
+     * @return the ResponseEntity with status 201 (Created) and with body the new user, or with status 400 (Bad Request) if the login or email is already in use
+     * @throws URISyntaxException if the Location URI syntax is incorrect
+     * @throws BadRequestAlertException 400 (Bad Request) if the login or email is already in use
+     */
+    @PostMapping("/users")
+    @Timed
+    @Secured(AuthoritiesConstants.ADMIN)
+    public ResponseEntity<User> createUser(@Valid @RequestBody UserDTO userDTO) throws URISyntaxException {
+        log.debug("REST request to save User : {}", userDTO);
+
+        if (userDTO.getId() != null) {
+            throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists");
+            // Lowercase the user login before comparing with database
+        } else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) {
+            throw new UserServiceError.LoginAlreadyUsedException();
+        } else if (userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).isPresent()) {
+            throw new UserServiceError.EmailAlreadyUsedException();
+        } else {
+            User newUser = userService.createUser(userDTO);
+            mailService.sendCreationEmail(newUser);
+            return ResponseEntity.created(new URI("/api/users/" + newUser.getLogin()))
+                .headers(HeaderUtil.createAlert( "A user is created with identifier " + newUser.getLogin(), newUser.getLogin()))
+                .body(newUser);
+        }
+    }
+
+    /**
+     * PUT /users : Updates an existing User.
+     *
+     * @param userDTO the user to update
+     * @return the ResponseEntity with status 200 (OK) and with body the updated user
+     * @throws UserServiceError.EmailAlreadyUsedException 400 (Bad Request) if the email is already in use
+     * @throws UserServiceError.LoginAlreadyUsedException 400 (Bad Request) if the login is already in use
+     */
+    @PutMapping("/users")
+    @Timed
+    @Secured(AuthoritiesConstants.ADMIN)
+    public ResponseEntity<UserDTO> updateUser(@Valid @RequestBody UserDTO userDTO) {
+        log.debug("REST request to update User : {}", userDTO);
+        Optional<User> existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
+        if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
+            throw new UserServiceError.EmailAlreadyUsedException();
+        }
+        existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase());
+        if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) {
+            throw new UserServiceError.LoginAlreadyUsedException();
+        }
+        Optional<UserDTO> updatedUser = userService.updateUser(userDTO);
+
+        return ResponseUtil.wrapOrNotFound(updatedUser,
+            HeaderUtil.createAlert("A user is updated with identifier " + userDTO.getLogin(), userDTO.getLogin()));
+    }
+
+    /**
+     * GET /users : get all users.
+     *
+     * @param pageable the pagination information
+     * @return the ResponseEntity with status 200 (OK) and with body all users
+     */
+    @GetMapping("/users")
+    @Timed
+    public ResponseEntity<List<UserDTO>> getAllUsers(Pageable pageable) {
+        final Page<UserDTO> page = userService.getAllManagedUsers(pageable);
+        HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(page, "/api/users");
+        return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK);
+    }
+
+    /**
+     * @return a string list of the all of the roles
+     */
+    @GetMapping("/users/authorities")
+    @Timed
+    @Secured(AuthoritiesConstants.ADMIN)
+    public List<String> getAuthorities() {
+        return userService.getAuthorities();
+    }
+
+    /**
+     * GET /users/:login : get the "login" user.
+     *
+     * @param login the login of the user to find
+     * @return the ResponseEntity with status 200 (OK) and with body the "login" user, or with status 404 (Not Found)
+     */
+    @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
+    @Timed
+    public ResponseEntity<UserDTO> getUser(@PathVariable String login) {
+        log.debug("REST request to get User : {}", login);
+        return ResponseUtil.wrapOrNotFound(
+            userService.getUserWithAuthoritiesByLogin(login)
+                .map(UserDTO::new));
+    }
+
+    /**
+     * DELETE /users/:login : delete the "login" User.
+     *
+     * @param login the login of the user to delete
+     * @return the ResponseEntity with status 200 (OK)
+     */
+    @DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
+    @Timed
+    @Secured(AuthoritiesConstants.ADMIN)
+    public ResponseEntity<Void> deleteUser(@PathVariable String login) {
+        log.debug("REST request to delete User: {}", login);
+        userService.deleteUser(login);
+        return ResponseEntity.ok().headers(HeaderUtil.createAlert( "A user is deleted with identifier " + login, login)).build();
+    }
+}
diff --git a/server/src/main/java/com/samsung/samserver/web/rest/controller/IRestDashboard.java b/server/src/main/java/com/samsung/samserver/web/rest/controller/IRestDashboard.java
new file mode 100644 (file)
index 0000000..65b11f9
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.web.rest.controller;
+
+import com.samsung.samserver.domain.Device;
+import com.samsung.samserver.web.rest.service.AccountService;
+import com.samsung.samserver.web.rest.service.UserJWTService;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+import io.swagger.annotations.*;
+
+import javax.validation.Valid;
+import java.util.List;
+import com.samsung.samserver.web.rest.errors.UserServiceError;
+
+/**
+ * Rest Interface for dashboard.
+ *
+ * @author <A HREF="mailto:m.dalakov@samsung.com">Mykhailo Dalakov</A>
+ * @version 1.0
+ */
+@RequestMapping("/dashboard")
+public interface IRestDashboard {
+
+    /**
+     * GET  /devices : get all the devices.
+     *
+     * @return the ResponseEntity with status 200 (OK) and the list of devices in body
+     */
+    @ApiOperation(value = "getAllDevices", notes = "The “getAllDevices” operation is used to obtain all registered devices.")
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "OK. Request executed successfully."),
+            @ApiResponse(code = 400, message = "Bad Request. The reason is contained in the tags “errorKey”, “message”, “title”"),
+            @ApiResponse(code = 500, message = "Internal server error. The user has to repeat action.")
+    })
+    @RequestMapping(value ="/devices", method = RequestMethod.GET)
+    public List<Device> getAllDevices();
+
+
+    /**
+     * POST request login
+     *
+     * @return the ResponseEntity with status 200 (OK) and JWT
+     */
+    @ApiOperation(value = "login", notes = "The “login” operation is used to authenticate user and receive JWT token.")
+    @ApiResponses(value = {
+            @ApiResponse(code = 200, message = "OK. Login was successful."),
+            @ApiResponse(code = 400, message = "Bad Request. The reason is contained in the tags “errorKey”, “message”, “title”"),
+            @ApiResponse(code = 500, message = "Internal server error. The user has to repeat action.")
+    })
+    @RequestMapping(value ="/auth/login", method = RequestMethod.POST)
+    public ResponseEntity<UserJWTService.JWTToken> login(
+            @Valid @ApiParam(value = "Login Data", required = true) @RequestBody UserJWTService.LoginVM loginVM);
+
+
+    /**
+     * POST request register the user.
+     *
+     * @param managedUserVM the managed user View Model
+     * @throws UserServiceError.InvalidPasswordException 400 (Bad Request) if the password is incorrect
+     * @throws UserServiceError.EmailAlreadyUsedException 400 (Bad Request) if the email is already used
+     * @throws UserServiceError.LoginAlreadyUsedException 400 (Bad Request) if the login is already used
+     */
+    @ApiOperation(value = "register", notes = "The “register” operation is used to register new user.")
+    @ApiResponses(value = {
+            @ApiResponse(code = 201, message = "Register was successful. New User was created."),
+            @ApiResponse(code = 400, message = "Bad Request. The reason is contained in the tags “errorKey”, “message”, “title”"),
+            @ApiResponse(code = 500, message = "Internal server error. The user has to repeat action.")
+    })
+    @RequestMapping(value ="/auth/register", method = RequestMethod.POST)
+    @ResponseStatus(HttpStatus.CREATED)
+    public void registerAccount(
+            @Valid @ApiParam(value = "Managed User", required = true) @RequestBody AccountService.ManagedUserVM managedUserVM);
+
+
+}
index b23cae1..2008b0d 100644 (file)
@@ -51,7 +51,6 @@ public interface IRestDevice {
         @ApiResponse(code = 200, message = "OK. The updates are found and should apply by device."),
         @ApiResponse(code = 304, message = "Not Modified. The updates aren’t required"),
         @ApiResponse(code = 400, message = "Bad Request. The reason is contained in the tags “errorKey”, “message”, “title”"),
-        @ApiResponse(code = 404, message = "Device not found. The device should be registered"),
         @ApiResponse(code = 500, message = "Internal server error. The device has to repeat action")
     })
     @RequestMapping(value ="/get-updates", method = RequestMethod.GET, produces = {
@@ -70,7 +69,6 @@ public interface IRestDevice {
     @ApiResponses(value = {
         @ApiResponse(code = 200, message = "OK. Send data was successful."),
         @ApiResponse(code = 400, message = "Bad Request. The reason is contained in the tags “errorKey”, “message”, “title”"),
-        @ApiResponse(code = 404, message = "Device not found. The device should be registered"),
         @ApiResponse(code = 500, message = "Internal server error. The device has to repeat action")
     })
     @RequestMapping(value ="/send-data", method = RequestMethod.POST)
@@ -89,7 +87,6 @@ public interface IRestDevice {
     @ApiResponses(value = {
             @ApiResponse(code = 200, message = "OK. The data are found and should apply by device."),
             @ApiResponse(code = 400, message = "Bad Request. The reason is contained in the tags “errorKey”, “message”, “title”"),
-            @ApiResponse(code = 404, message = "Device not found. The device should be registered"),
             @ApiResponse(code = 500, message = "Internal server error. The device has to repeat action")
     })
     @RequestMapping(value ="/get-udata", method = RequestMethod.GET)
diff --git a/server/src/main/java/com/samsung/samserver/web/rest/controller/impl/RestDashboard.java b/server/src/main/java/com/samsung/samserver/web/rest/controller/impl/RestDashboard.java
new file mode 100644 (file)
index 0000000..21a5e5f
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.web.rest.controller.impl;
+
+import com.samsung.samserver.domain.Device;
+import com.samsung.samserver.service.DeviceService;
+import com.samsung.samserver.web.rest.controller.IRestDashboard;
+import com.samsung.samserver.web.rest.service.AccountService;
+import com.samsung.samserver.web.rest.service.UserJWTService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+/**
+ * REST dashboard controller.
+ *
+ * @author <A HREF="mailto:m.dalakov@samsung.com">Mykhailo Dalakov</A>
+ * @version 1.0
+ */
+@RestController
+public class RestDashboard  implements IRestDashboard {
+
+    private final Logger log = LoggerFactory.getLogger(RestDashboard.class);
+
+    @Autowired
+    private DeviceService deviceService;
+
+    @Autowired
+    private UserJWTService userJWTService;
+
+    @Autowired
+    private AccountService accountService;
+
+    /**
+     * GET  /devices : get all the devices.
+     *
+     * @return the ResponseEntity with status 200 (OK) and the list of devices in body
+     */
+    @Override
+    public List<Device> getAllDevices() {
+        return deviceService.findAll();
+    }
+
+    /**
+     * POST request login
+     *
+     * @return the ResponseEntity with status 200 (OK) and JWT
+     */
+    @Override
+    public ResponseEntity<UserJWTService.JWTToken> login(@Valid @RequestBody UserJWTService.LoginVM loginVM) {
+        return userJWTService.authorize(loginVM);
+    }
+
+    /**
+     * POST request register the user.
+     *
+     * @param managedUserVM the managed user View Model
+     */
+    @Override
+    public void registerAccount(@Valid @RequestBody AccountService.ManagedUserVM managedUserVM){
+        accountService.registerAccount(managedUserVM);
+    }
+
+}
@@ -3,8 +3,9 @@
  * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
  * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
  */
-package com.samsung.samserver.web.rest.controller;
+package com.samsung.samserver.web.rest.controller.impl;
 
+import com.samsung.samserver.web.rest.controller.IRestDevice;
 import com.samsung.samserver.web.rest.service.*;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
diff --git a/server/src/main/java/com/samsung/samserver/web/rest/service/AccountService.java b/server/src/main/java/com/samsung/samserver/web/rest/service/AccountService.java
new file mode 100644 (file)
index 0000000..06529b6
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * In Samsung Ukraine R&D Center (SRK under a contract between)
+ * LLC "Samsung Electronics Co", Ltd (Seoul, Republic of Korea)
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ */
+package com.samsung.samserver.web.rest.service;
+
+import com.samsung.samserver.domain.User;
+import com.samsung.samserver.repository.UserRepository;
+import com.samsung.samserver.security.SecurityUtils;
+import com.samsung.samserver.service.impl.MailService;
+import com.samsung.samserver.service.impl.UserService;
+import com.samsung.samserver.service.dto.UserDTO;
+
+import com.samsung.samserver.web.rest.errors.InternalServerErrorException;
+import com.samsung.samserver.web.rest.errors.UserServiceError;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import javax.validation.constraints.Size;
+import java.util.*;
+
+/**
+ * REST service for managing the current user's account.
+ */
+@Service
+public class AccountService {
+
+    private final Logger log = LoggerFactory.getLogger(AccountService.class);
+
+    private final UserRepository userRepository;
+
+    private final UserService userService;
+
+    private final MailService mailService;
+
+    public AccountService(UserRepository userRepository, UserService userService, MailService mailService) {
+
+        this.userRepository = userRepository;
+        this.userService = userService;
+        this.mailService = mailService;
+    }
+
+    /**
+     * POST  /register : register the user.
+     *
+     * @param managedUserVM the managed user View Model
+     * @throws UserServiceError.InvalidPasswordException 400 (Bad Request) if the password is incorrect
+     * @throws UserServiceError.EmailAlreadyUsedException 400 (Bad Request) if the email is already used
+     * @throws UserServiceError.LoginAlreadyUsedException 400 (Bad Request) if the login is already used
+     */
+    public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) {
+        if (!checkPasswordLength(managedUserVM.getPassword())) {
+            throw new UserServiceError.InvalidPasswordException();
+        }
+        userRepository.findOneByLogin(managedUserVM.getLogin().toLowerCase()).ifPresent(u -> {throw new UserServiceError.LoginAlreadyUsedException();});
+        userRepository.findOneByEmailIgnoreCase(managedUserVM.getEmail()).ifPresent(u -> {throw new UserServiceError.EmailAlreadyUsedException();});
+        User user = userService.registerUser(managedUserVM, managedUserVM.getPassword());
+        mailService.sendActivationEmail(user);
+    }
+
+    /**
+     * GET  /activate : activate the registered user.
+     *
+     * @param key the activation key
+     * @throws RuntimeException 500 (Internal Server Error) if the user couldn't be activated
+     */
+    public void activateAccount(@RequestParam(value = "key") String key) {
+        Optional<User> user = userService.activateRegistration(key);
+        if (!user.isPresent()) {
+            throw new InternalServerErrorException("No user was found for this reset key");
+        }
+    }
+
+    /**
+     * GET  /authenticate : check if the user is authenticated, and return its login.
+     *
+     * @param request the HTTP request
+     * @return the login if the user is authenticated
+     */
+    public String isAuthenticated(HttpServletRequest request) {
+        log.debug("REST request to check if the current user is authenticated");
+        return request.getRemoteUser();
+    }
+
+    /**
+     * GET  /account : get the current user.
+     *
+     * @return the current user
+     * @throws RuntimeException 500 (Internal Server Error) if the user couldn't be returned
+     */
+    public UserDTO getAccount() {
+        return userService.getUserWithAuthorities()
+            .map(UserDTO::new)
+            .orElseThrow(() -> new InternalServerErrorException("User could not be found"));
+    }
+
+    /**
+     * POST  /account : update the current user information.
+     *
+     * @param userDTO the current user information
+     * @throws UserServiceError.EmailAlreadyUsedException 400 (Bad Request) if the email is already used
+     * @throws RuntimeException 500 (Internal Server Error) if the user login wasn't found
+     */
+    public void saveAccount(@Valid @RequestBody UserDTO userDTO) {
+        final String userLogin = SecurityUtils.getCurrentUserLogin().orElseThrow(() -> new InternalServerErrorException("Current user login not found"));
+        Optional<User> existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail());
+        if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) {
+            throw new UserServiceError.EmailAlreadyUsedException();
+        }
+        Optional<User> user = userRepository.findOneByLogin(userLogin);
+        if (!user.isPresent()) {
+            throw new InternalServerErrorException("User could not be found");
+        }
+        userService.updateUser(userDTO.getFirstName(), userDTO.getLastName(), userDTO.getEmail(),
+            userDTO.getLangKey(), userDTO.getImageUrl());
+   }
+
+    /**
+     * POST  /account/change-password : changes the current user's password
+     *
+     * @param password the new password
+     * @throws UserServiceError.InvalidPasswordException 400 (Bad Request) if the new password is incorrect
+     */
+    public void changePassword(@RequestBody String password) {
+        if (!checkPasswordLength(password)) {
+            throw new UserServiceError.InvalidPasswordException();
+        }
+        userService.changePassword(password);
+   }
+
+    /**
+     * POST   /account/reset-password/init : Send an email to reset the password of the user
+     *
+     * @param mail the mail of the user
+     * @throws UserServiceError.EmailNotFoundException 400 (Bad Request) if the email address is not registered
+     */
+    public void requestPasswordReset(@RequestBody String mail) {
+       mailService.sendPasswordResetMail(
+           userService.requestPasswordReset(mail)
+               .orElseThrow(UserServiceError.EmailNotFoundException::new)
+       );
+    }
+
+    /**
+     * POST   /account/reset-password/finish : Finish to reset the password of the user
+     *
+     * @param keyAndPassword the generated key and the new password
+     * @throws UserServiceError.InvalidPasswordException 400 (Bad Request) if the password is incorrect
+     * @throws RuntimeException 500 (Internal Server Error) if the password could not be reset
+     */
+    public void finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) {
+        if (!checkPasswordLength(keyAndPassword.getNewPassword())) {
+            throw new UserServiceError.InvalidPasswordException();
+        }
+        Optional<User> user =
+            userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey());
+
+        if (!user.isPresent()) {
+            throw new InternalServerErrorException("No user was found for this reset key");
+        }
+    }
+
+    private static boolean checkPasswordLength(String password) {
+        return !StringUtils.isEmpty(password) &&
+            password.length() >= ManagedUserVM.PASSWORD_MIN_LENGTH &&
+            password.length() <= ManagedUserVM.PASSWORD_MAX_LENGTH;
+    }
+
+
+    /**
+     * View Model object for storing the user's key and password.
+     */
+    public static class KeyAndPasswordVM {
+
+        private String key;
+
+        private String newPassword;
+
+        public String getKey() {
+            return key;
+        }
+
+        public void setKey(String key) {
+            this.key = key;
+        }
+
+        public String getNewPassword() {
+            return newPassword;
+        }
+
+        public void setNewPassword(String newPassword) {
+            this.newPassword = newPassword;
+        }
+    }
+
+
+    /**
+     * View Model extending the UserDTO, which is meant to be used in the user management UI.
+     */
+    public static class ManagedUserVM extends UserDTO {
+
+        public static final int PASSWORD_MIN_LENGTH = 4;
+
+        public static final int PASSWORD_MAX_LENGTH = 100;
+
+        @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH)
+        private String password;
+
+        public ManagedUserVM() {
+            // Empty constructor needed for Jackson.
+        }
+
+        public String getPassword() {
+            return password;
+        }
+
+        public void setPassword(String password) {
+            this.password = password;
+        }
+
+        @Override
+        public String toString() {
+            return "ManagedUserVM{" +
+                "} " + super.toString();
+        }
+    }
+
+
+}
diff --git a/server/src/main/java/com/samsung/samserver/web/rest/service/UserJWTService.java b/server/src/main/java/com/samsung/samserver/web/rest/service/UserJWTService.java
new file mode 100644 (file)
index 0000000..8be15f1
--- /dev/null
@@ -0,0 +1,122 @@
+package com.samsung.samserver.web.rest.service;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.samsung.samserver.security.jwt.JWTConfigurer;
+import com.samsung.samserver.security.jwt.TokenProvider;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.RequestBody;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+/**
+ *  REST service implementation for get jwt token.
+ *
+ * @author <A HREF="mailto:m.dalakov@samsung.com">Mykhailo Dalakov</A>
+ * @version 1.0
+ */
+@Service
+public class UserJWTService {
+
+    private final TokenProvider tokenProvider;
+
+    private final AuthenticationManager authenticationManager;
+
+    public UserJWTService(TokenProvider tokenProvider, AuthenticationManager authenticationManager) {
+        this.tokenProvider = tokenProvider;
+        this.authenticationManager = authenticationManager;
+    }
+
+    public ResponseEntity<JWTToken> authorize(@Valid @RequestBody LoginVM loginVM) {
+
+        UsernamePasswordAuthenticationToken authenticationToken =
+            new UsernamePasswordAuthenticationToken(loginVM.getUsername(), loginVM.getPassword());
+
+        Authentication authentication = this.authenticationManager.authenticate(authenticationToken);
+        SecurityContextHolder.getContext().setAuthentication(authentication);
+        boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
+        String jwt = tokenProvider.createToken(authentication, rememberMe);
+        HttpHeaders httpHeaders = new HttpHeaders();
+        httpHeaders.add(JWTConfigurer.AUTHORIZATION_HEADER, "Bearer " + jwt);
+        return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
+    }
+
+
+    /**
+     * Object to return as body in JWT Authentication.
+     */
+    public static class JWTToken {
+
+        private String idToken;
+
+        JWTToken(String idToken) {
+            this.idToken = idToken;
+        }
+
+        @JsonProperty("id_token")
+        String getIdToken() {
+            return idToken;
+        }
+
+        void setIdToken(String idToken) {
+            this.idToken = idToken;
+        }
+    }
+
+
+    /**
+     * View Model object for storing a user's credentials.
+     */
+    public static class LoginVM {
+
+        @NotNull
+        @Size(min = 1, max = 50)
+        private String username;
+
+        @NotNull
+        @Size(min = AccountService.ManagedUserVM.PASSWORD_MIN_LENGTH, max = AccountService.ManagedUserVM.PASSWORD_MAX_LENGTH)
+        private String password;
+
+        private Boolean rememberMe;
+
+        public String getUsername() {
+            return username;
+        }
+
+        public void setUsername(String username) {
+            this.username = username;
+        }
+
+        public String getPassword() {
+            return password;
+        }
+
+        public void setPassword(String password) {
+            this.password = password;
+        }
+
+        public Boolean isRememberMe() {
+            return rememberMe;
+        }
+
+        public void setRememberMe(Boolean rememberMe) {
+            this.rememberMe = rememberMe;
+        }
+
+        @Override
+        public String toString() {
+            return "LoginVM{" +
+                    "username='" + username + '\'' +
+                    ", rememberMe=" + rememberMe +
+                    '}';
+        }
+    }
+}
index b4338b3..8703b58 100644 (file)
@@ -1,5 +1,5 @@
 #H2 Server Properties
-#Wed Feb 14 10:46:33 GMT+02:00 2018
+#Tue Feb 20 15:10:21 GMT+02:00 2018
 0=JHipster H2 (Memory)|org.h2.Driver|jdbc\:h2\:mem\:samserver|samserver
 webAllowOthers=true
 webPort=8082
diff --git a/server/src/main/resources/config/liquibase/authorities.csv b/server/src/main/resources/config/liquibase/authorities.csv
new file mode 100644 (file)
index 0000000..af5c6df
--- /dev/null
@@ -0,0 +1,3 @@
+name
+ROLE_ADMIN
+ROLE_USER
index d188852..a5605a2 100644 (file)
@@ -1,9 +1,9 @@
 <?xml version="1.0" encoding="utf-8"?>
 <databaseChangeLog
-    xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
-    xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-    xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
+        xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+        xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
+        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+        xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd
                         http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd">
 
     <property name="now" value="now()" dbms="h2"/>
         The initial schema has the '00000000000001' id, so that it is over-written if we re-generate it.
     -->
     <changeSet id="00000000000001" author="jhipster">
+        <createTable tableName="jhi_user">
+            <column name="id" type="bigint" autoIncrement="${autoIncrement}">
+                <constraints primaryKey="true" nullable="false"/>
+            </column>
+            <column name="login" type="varchar(50)">
+                <constraints unique="true" nullable="false" uniqueConstraintName="ux_user_login"/>
+            </column>
+            <column name="password_hash" type="varchar(60)"/>
+            <column name="first_name" type="varchar(50)"/>
+            <column name="last_name" type="varchar(50)"/>
+            <column name="email" type="varchar(100)">
+                <constraints unique="true" nullable="true" uniqueConstraintName="ux_user_email"/>
+            </column>
+            <column name="image_url" type="varchar(256)"/>
+            <column name="activated" type="boolean" valueBoolean="false">
+                <constraints nullable="false" />
+            </column>
+            <column name="lang_key" type="varchar(6)"/>
+            <column name="activation_key" type="varchar(20)"/>
+            <column name="reset_key" type="varchar(20)"/>
+            <column name="created_by" type="varchar(50)">
+                <constraints nullable="false"/>
+            </column>
+            <column name="created_date" type="timestamp" defaultValueDate="${now}">
+                <constraints nullable="false"/>
+            </column>
+            <column name="reset_date" type="timestamp">
+                <constraints nullable="true"/>
+            </column>
+            <column name="last_modified_by" type="varchar(50)"/>
+            <column name="last_modified_date" type="timestamp"/>
+        </createTable>
+        <createIndex indexName="idx_user_login"
+                     tableName="jhi_user"
+                     unique="true">
+            <column name="login" type="varchar(50)"/>
+        </createIndex>
+
+        <createIndex indexName="idx_user_email"
+                     tableName="jhi_user"
+                     unique="true">
+            <column name="email" type="varchar(100)"/>
+        </createIndex>
+        <createTable tableName="jhi_authority">
+            <column name="name" type="varchar(50)">
+                <constraints primaryKey="true" nullable="false"/>
+            </column>
+        </createTable>
+
+        <createTable tableName="jhi_user_authority">
+            <column name="user_id" type="bigint">
+                <constraints nullable="false"/>
+            </column>
+            <column name="authority_name" type="varchar(50)">
+                <constraints nullable="false"/>
+            </column>
+        </createTable>
+
+        <addPrimaryKey columnNames="user_id, authority_name" tableName="jhi_user_authority"/>
+
+        <addForeignKeyConstraint baseColumnNames="authority_name"
+                                 baseTableName="jhi_user_authority"
+                                 constraintName="fk_authority_name"
+                                 referencedColumnNames="name"
+                                 referencedTableName="jhi_authority"/>
+        <addForeignKeyConstraint baseColumnNames="user_id"
+                                 baseTableName="jhi_user_authority"
+                                 constraintName="fk_user_id"
+                                 referencedColumnNames="id"
+                                 referencedTableName="jhi_user"/>
+        <loadData encoding="UTF-8"
+                  file="config/liquibase/users.csv"
+                  separator=";"
+                  tableName="jhi_user">
+            <column name="activated" type="boolean"/>
+            <column name="created_date" type="timestamp"/>
+        </loadData>
+        <dropDefaultValue tableName="jhi_user" columnName="created_date" columnDataType="datetime"/>
+        <loadData encoding="UTF-8"
+                  file="config/liquibase/authorities.csv"
+                  separator=";"
+                  tableName="jhi_authority"/>
+
+        <loadData encoding="UTF-8"
+                  file="config/liquibase/users_authorities.csv"
+                  separator=";"
+                  tableName="jhi_user_authority"/>
         <createTable tableName="jhi_persistent_audit_event">
             <column name="event_id" type="bigint" autoIncrement="${autoIncrement}">
                 <constraints primaryKey="true" nullable="false"/>
diff --git a/server/src/main/resources/config/liquibase/users.csv b/server/src/main/resources/config/liquibase/users.csv
new file mode 100644 (file)
index 0000000..b25922b
--- /dev/null
@@ -0,0 +1,5 @@
+id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by
+1;system;$2a$10$mE.qmcV0mFU5NcKh73TZx.z4ueI/.bDWbj0T1BYyqP481kGGarKLG;System;System;system@localhost;;true;en;system;system
+2;anonymoususer;$2a$10$j8S5d7Sr7.8VTOYNviDPOeWX8KcYILUVJBsYV83Y5NtECayypx9lO;Anonymous;User;anonymous@localhost;;true;en;system;system
+3;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system
+4;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system
diff --git a/server/src/main/resources/config/liquibase/users_authorities.csv b/server/src/main/resources/config/liquibase/users_authorities.csv
new file mode 100644 (file)
index 0000000..06c5fee
--- /dev/null
@@ -0,0 +1,6 @@
+user_id;authority_name
+1;ROLE_ADMIN
+1;ROLE_USER
+3;ROLE_ADMIN
+3;ROLE_USER
+4;ROLE_USER