Skip to content

Commit

Permalink
Merge branch 'feature/controller/user-controller' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
serjihsklovski committed Jun 19, 2017
2 parents 0bc19de + d8a2995 commit 772e1a2
Show file tree
Hide file tree
Showing 14 changed files with 301 additions and 17 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ private void registerAdmin() {
admin.setUsername("admin");
admin.setEmail("admin@teapot.org");
admin.setPassword(passwordEncoder.encode("1234"));
admin.setActivated(true);
admin.setFirstName("Cake");
admin.setLastName("Lover");
admin.setRegistrationDate(LocalDateTime.now());
Expand All @@ -82,6 +83,7 @@ private void registerDaleCooper() {
user.setUsername("dale_cooper");
user.setEmail("dale_cooper@twin.peaks");
user.setPassword(passwordEncoder.encode("1234"));
user.setActivated(true);
user.setFirstName("Dale");
user.setLastName("Cooper");
user.setRegistrationDate(LocalDateTime.now());
Expand All @@ -98,6 +100,7 @@ private void registerLoraPalmer() {
user.setUsername("lora_palmer");
user.setEmail("lora_palmer@twin.peaks");
user.setPassword(passwordEncoder.encode("1234"));
user.setActivated(true);
user.setFirstName("Lora");
user.setLastName("Palmer");
user.setRegistrationDate(LocalDateTime.now());
Expand All @@ -114,6 +117,7 @@ private void registerSherlockHolmes() {
user.setUsername("sherlock_holmes");
user.setEmail("sherlock_holmes@baker.st");
user.setPassword(passwordEncoder.encode("1234"));
user.setActivated(true);
user.setFirstName("Sherlock");
user.setLastName("Holmes");
user.setRegistrationDate(LocalDateTime.now());
Expand All @@ -130,6 +134,7 @@ private void registerDoctorWatson() {
user.setUsername("dr_watson");
user.setEmail("dr_watson@baker.st");
user.setPassword(passwordEncoder.encode("1234"));
user.setActivated(true);
user.setFirstName("John");
user.setLastName("Watson");
user.setRegistrationDate(LocalDateTime.now());
Expand Down
29 changes: 18 additions & 11 deletions src/main/java/org/teapot/backend/controller/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.WebRequest;
import org.teapot.backend.controller.exception.BadRequestException;
import org.teapot.backend.controller.exception.ForbiddenException;
import org.teapot.backend.controller.exception.ResourceNotFoundException;
import org.teapot.backend.model.User;
import org.teapot.backend.model.UserAuthority;
import org.teapot.backend.repository.UserRepository;
import org.teapot.backend.util.VerificationMailSender;

import javax.servlet.http.HttpServletResponse;
import java.time.LocalDate;
Expand All @@ -31,6 +33,9 @@ public class UserController {
@Autowired
private PasswordEncoder passwordEncoder;

@Autowired
private VerificationMailSender verificationMailSender;

/**
* Метод доступен всем пользователям, в том числе и неавторизованным.
* Возвращает список всех пользователей, либо список не всех пользователей,
Expand Down Expand Up @@ -82,8 +87,8 @@ public User getUser(@PathVariable String idOrUsername) {
* указанным id не найден в базе данных - устанавливает код состояния
* 404 Not Found, выбрасывая исключение {@link ResourceNotFoundException}.
* Если пользователь с таким id найден, то его данные изменяются на новые
* данные (кроме даты регистрации). Если никаких ошибок не
* произошло - устаналивается код состояния 204 No Content.
* данные (кроме даты регистрации и значения isActivated). Если никаких
* ошибок не произошло - устаналивается код состояния 204 No Content.
*
* @param id идентификатор изменяемого пользователя
* @param user объект, содержащий новые данные пользователя
Expand All @@ -100,6 +105,7 @@ public void updateUser(@PathVariable Long id,

user.setId(id);
user.setRegistrationDate(existingUser.getRegistrationDate());
user.setActivated(existingUser.isActivated());
// если пароль изменился
if (!passwordEncoder.matches(user.getPassword(), existingUser.getPassword())) {
user.setPassword(passwordEncoder.encode(user.getPassword()));
Expand Down Expand Up @@ -160,18 +166,18 @@ public void deleteUser(@PathVariable Long id) {
@ResponseStatus(HttpStatus.CREATED)
public User registerUser(@RequestBody User user,
HttpServletResponse response,
WebRequest request,
Authentication auth) {
if (userRepository.findByEmail(user.getEmail()) != null) {
throw new BadRequestException();
}

user.setRegistrationDate(LocalDateTime.now());
if (auth == null) {
user.setAuthority(UserAuthority.USER);
user.setAvailable(false);
userRepository.save(user);
// TODO: генерация VerificationToken и передача в почтовый сервис
user = userRepository.save(user);
verificationMailSender.createTokenAndSend(user, request.getLocale());
} else if (auth.getAuthorities().contains(UserAuthority.ADMIN)) {
user.setActivated(true);
userRepository.save(user);
}

Expand All @@ -187,11 +193,11 @@ public User registerUser(@RequestBody User user,
* неуказанные в параметрах данные не изменяются. Пользователь с
* ролью ADMIN может изменить любыые данные, кроме даты регистрации.
* Пользователь, идентификатор которого равен идентификатору в
* маппинге '/{id}', может изменить только username, firstName или
* lastName. В случае успеха устаналивает код состояния 204 No Content.
* В случае, если ресурс с указанным id не найден - код состояния
* 404 Not Found. Если доступ пользователю к ресурсу запрещен -
* код состояния 403 Forbidden.
* маппинге '/{id}', может изменить только username, firstName, lastName
* и isAvailable (отолько на false). В случае успеха устаналивает код
* состояния 204 No Content. В случае, если ресурс с указанным id не
* найден - код состояния 404 Not Found. Если доступ пользователю к
* ресурсу запрещен - код состояния 403 Forbidden.
*
* @param id идентификатор пользователя, данные которого нужно
* изменить
Expand Down Expand Up @@ -240,6 +246,7 @@ public void patchUser(@PathVariable Long id,
} else if (auth.getName().equals(user.getEmail())) {

if (username != null) user.setUsername(firstName);
if ((available != null) && (!available)) user.setAvailable(false);
if (firstName != null) user.setFirstName(firstName);
if (lastName != null) user.setLastName(lastName);
if (birthday != null) user.setBirthday(birthday);
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/org/teapot/backend/model/User.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.teapot.backend.model;

import com.fasterxml.jackson.annotation.JsonIgnore;

import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
Expand All @@ -26,6 +28,9 @@ public class User {
@Column(name = "is_available")
private Boolean isAvailable = true;

@Column(name = "is_activated")
private Boolean isActivated = false;

@Column(name = "first_name", length = 32)
private String firstName;

Expand All @@ -42,6 +47,10 @@ public class User {

private String description;

@JsonIgnore
@OneToOne(mappedBy = "user", fetch = FetchType.LAZY, orphanRemoval = true)
private VerificationToken verificationToken;

public User() {
}

Expand Down Expand Up @@ -85,6 +94,14 @@ public void setAvailable(Boolean available) {
isAvailable = available;
}

public Boolean isActivated() {
return isActivated;
}

public void setActivated(Boolean activated) {
isActivated = activated;
}

public String getFirstName() {
return firstName;
}
Expand Down Expand Up @@ -133,6 +150,14 @@ public void setDescription(String description) {
this.description = description;
}

public VerificationToken getVerificationToken() {
return verificationToken;
}

public void setVerificationToken(VerificationToken verificationToken) {
this.verificationToken = verificationToken;
}

@Override
public int hashCode() {
return Objects.hash(
Expand Down
68 changes: 68 additions & 0 deletions src/main/java/org/teapot/backend/model/VerificationToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.teapot.backend.model;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "verification_token")
public class VerificationToken {

@Id
@GeneratedValue
private Long id;

@Column(nullable = false, unique = true, length = 32, updatable = false)
private String token;

@Column(nullable = false)
private LocalDateTime expireDateTime = LocalDateTime.now().plusDays(1);

@Column(name = "is_activated")
private Boolean isActivated = false;

@OneToOne(fetch = FetchType.LAZY)
private User user;

public VerificationToken() {
}

public Long getId() {
return id;
}

public String getToken() {
return token;
}

public LocalDateTime getExpireDateTime() {
return expireDateTime;
}

public Boolean isActivated() {
return isActivated;
}

public User getUser() {
return user;
}

public void setId(Long id) {
this.id = id;
}

public void setToken(String token) {
this.token = token;
}

public void setExpireDateTime(LocalDateTime expireDateTime) {
this.expireDateTime = expireDateTime;
}

public void setActivated(Boolean activated) {
isActivated = activated;
}

public void setUser(User user) {
this.user = user;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.teapot.backend.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.transaction.annotation.Transactional;
import org.teapot.backend.model.VerificationToken;

@Transactional
public interface VerificationTokenRepository extends JpaRepository<VerificationToken, Long> {

VerificationToken findByToken(String token);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public UserDetails loadUserByUsername(String username)
return new org.springframework.security.core.userdetails.User(
user.getEmail(),
user.getPassword(),
user.isAvailable(),
user.isAvailable() && user.isActivated(),
true,
true,
true,
Expand Down
48 changes: 48 additions & 0 deletions src/main/java/org/teapot/backend/util/RandomSequenceGenerator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.teapot.backend.util;

import org.springframework.stereotype.Component;

import java.security.SecureRandom;

@Component
public final class RandomSequenceGenerator {

public static final String CHARACTER_STRING = "0123456789abcdefghijklmnopqrstuvwxyz";
public static final int MIN_LENGTH = 1;
public static final int MAX_LENGTH = 256;
public static final int MIN_NOTATION = 2;
public static final int MAX_NOTATION = CHARACTER_STRING.length();

public String generateSequence(int length, int notation, boolean caseSensitive) {
if (length > MAX_LENGTH) {
length = MAX_LENGTH;
} else if (length < MIN_LENGTH) {
length = MIN_LENGTH;
}

if (notation > MAX_NOTATION) {
notation = MAX_NOTATION;
} else if (notation < MIN_NOTATION) {
notation = MIN_NOTATION;
}

SecureRandom random = new SecureRandom();
StringBuilder stringBuilder = new StringBuilder(length);

for (int i = 0; i < length; i++) {
String c = String.valueOf(CHARACTER_STRING.charAt(random.nextInt(notation)));

if (caseSensitive) {
c = random.nextBoolean() ? c.toUpperCase() : c;
}

stringBuilder.append(c);
}

return stringBuilder.toString();
}

public String generateSequence(int length) {
return generateSequence(length, MAX_NOTATION, true);
}
}
54 changes: 54 additions & 0 deletions src/main/java/org/teapot/backend/util/VerificationMailSender.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package org.teapot.backend.util;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;
import org.teapot.backend.model.User;
import org.teapot.backend.model.VerificationToken;
import org.teapot.backend.repository.TeapotPropertyRepository;
import org.teapot.backend.repository.VerificationTokenRepository;

import java.util.Locale;


@Component
public class VerificationMailSender {

@Autowired
private TeapotPropertyRepository propertyRepository;

@Autowired
private VerificationTokenRepository tokenRepository;

@Autowired
private VerificationTokenGenerator generator;

@Autowired
private MessageSource messages;

@Autowired
private JavaMailSender mailSender;

public void createTokenAndSend(User user, Locale locale) {
new Thread(() -> {
VerificationToken verificationToken = generator.generateToken();
verificationToken.setUser(user);
tokenRepository.save(verificationToken);

String confirmUrl = "https://"
+ propertyRepository.findByName("site-uri").getValue()
+ "/confirmRegistration?token="
+ verificationToken.getToken();

SimpleMailMessage mailMessage = new SimpleMailMessage();
mailMessage.setTo(user.getEmail());
mailMessage.setSubject(messages.getMessage(
"mail.confirm.subject", null, locale));
mailMessage.setText(messages.getMessage(
"mail.confirm.text", new String[]{confirmUrl}, locale));
mailSender.send(mailMessage);
}).start();
}
}
Loading

0 comments on commit 772e1a2

Please sign in to comment.