Skip to content

Commit

Permalink
Merge pull request #50 from Shaderock/3-ub-01-sign-up-user-into-appli…
Browse files Browse the repository at this point in the history
…cation

#3-ub-01-sign-up-user-into-application
  • Loading branch information
Shaderock authored Dec 24, 2022
2 parents 107d61e + 8b0c685 commit 5116fb2
Show file tree
Hide file tree
Showing 21 changed files with 333 additions and 5 deletions.
32 changes: 32 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions .idea/libraries/Maven__com_auth0_java_jwt_4_2_1.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.2.1</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.shaderock.backend.auth.registration;

import com.shaderock.backend.model.entity.user.AppUser;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController()
@RequestMapping("/api/register")
@RequiredArgsConstructor
public class RegistrationController {
private final RegistrationService registrationService;
private final BCryptPasswordEncoder passwordEncoder;

@Value(value = "${application.frontend.url}")
private String frontendUrl;

@PostMapping("/verify-user")
public ResponseEntity<Boolean> isUserRegistered(@RequestParam @NotNull final String email) {
return ResponseEntity.ok((registrationService.isUserRegistered(email)));
}

@PostMapping
public ResponseEntity<Void> registerUser(@RequestBody @Valid final RegistrationForm registrationForm) {
AppUser appUser = AppUser.builder()
.email(registrationForm.getEmail())
.password(passwordEncoder.encode(registrationForm.getPassword()))
.firstName(registrationForm.getFirstName())
.lastName(registrationForm.getLastName())
.build();

registrationService.registerUser(appUser);
return ResponseEntity.noContent().build();
}

@PostMapping("/confirm-email")
public ResponseEntity<Void> confirmEmail(@RequestParam @NotNull String token) {
registrationService.confirmEmail(token);
return ResponseEntity.status(HttpStatus.FOUND).header("Location", frontendUrl).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.shaderock.backend.auth.registration;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice()
public class RegistrationControllerAdvice {
@ExceptionHandler(UserAlreadyRegisteredException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public UserRegisteredMessage userAlreadyRegistered(UserAlreadyRegisteredException userAlreadyRegisteredException) {
return new UserRegisteredMessage(userAlreadyRegisteredException.getMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.shaderock.backend.auth.registration;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;

@Data
public class RegistrationForm {

@NotNull(message = "Email is not provided")
@NotBlank(message = "Email is empty")
@Email
private String email;

@NotNull(message = "Password is not provided")
@Size(min = 8, max = 25, message = "Password is not between 8 and 25 characters length")
private String password;
@NotNull(message = "First name is not provided")
@NotBlank(message = "First name is empty")
private String firstName;
@NotNull(message = "Last name is not provided")
@NotBlank(message = "Last name is empty")
private String lastName;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.shaderock.backend.auth.registration;

import com.shaderock.backend.messaging.exception.TokenNotFoundException;
import com.shaderock.backend.model.entity.user.AppUser;
import com.shaderock.backend.model.repository.AppUserRepository;
import com.shaderock.backend.model.type.Role;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class RegistrationService {
private final AppUserRepository userRepository;

@Transactional
public void registerUser(AppUser appUser) {
if (isUserRegistered(appUser.getEmail())) {
throw new UserAlreadyRegisteredException(appUser.getEmail());
}
appUser.setRegistrationToken(UUID.randomUUID().toString());
userRepository.save(appUser);
}

public boolean isUserRegistered(String email) {
Optional<AppUser> userOptional = userRepository.findByEmail(email);
return userOptional.isPresent() && userOptional.get().isEnabled();
}

@Transactional
public void confirmEmail(String token) {
Optional<AppUser> userOptional = userRepository.findByRegistrationToken(token);
if (userOptional.isEmpty()) {
throw new TokenNotFoundException(token);
}

userOptional.get().setEnabled(true);
userOptional.get().getRoles().add(Role.USER);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.shaderock.backend.auth.registration;

import lombok.Getter;

public class UserAlreadyRegisteredException extends RuntimeException {
@Getter
private final String email;

public UserAlreadyRegisteredException(String email) {
super(String.format("User with email=[%s] is already registered", email));
this.email = email;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.shaderock.backend.auth.registration;

import com.shaderock.backend.messaging.message.ErrorMessage;
import lombok.Getter;

@Getter
public class UserRegisteredMessage extends ErrorMessage {
private static final boolean USER_REGISTERED = true;

public UserRegisteredMessage(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.shaderock.backend.conf;

import com.shaderock.backend.auth.login.JWTRequestFilter;
import com.shaderock.backend.model.type.Role;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class BasicConfiguration {
private final JWTRequestFilter jwtRequestFilter;

@Bean
public AuthenticationManager authenticationManager(final AuthenticationConfiguration authenticationConfiguration)
throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http.cors().and().csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/api/login/**", "/api/register/**", "/api/food/**").permitAll()
.anyRequest().hasRole(Role.USER.getName())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.shaderock.backend.messaging.advice;

import com.shaderock.backend.messaging.message.ErrorMessage;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GeneralControllerAdvice {
@ExceptionHandler(Exception.class)
public ErrorMessage handleAnyException(Exception e) {
return ErrorMessage.builder().message(e.getMessage()).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.shaderock.backend.messaging.exception;

import lombok.Getter;

public class TokenNotFoundException extends RuntimeException {
@Getter
private final String token;

public TokenNotFoundException(String token) {
super(String.format("User with token=[%s] not found", token));
this.token = token;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.shaderock.backend.messaging.message;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
@Builder
public class ErrorMessage {
private final String message;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.shaderock.backend.model.entity.preference;

import com.shaderock.backend.model.entity.preference.notification.EmployeeNotificationConfig;
import com.shaderock.backend.model.entity.user.Employee;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
Expand Down Expand Up @@ -29,4 +30,8 @@ public class EmployeePreferenceConfig implements PreferenceConfig {
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "notification_config_id", nullable = false)
private EmployeeNotificationConfig notificationConfig;

@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class SupplierPreferenceConfig implements PreferenceConfig {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "supplier_id", referencedColumnName = "supplier_id", nullable = false)
@JoinColumn(name = "supplier_id", nullable = false)
private Supplier supplier;
@Column(nullable = false)
private Duration deliveryDuration;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public class AppUser implements UserDetails {
@Column(nullable = false)
private String password;

@Column(nullable = false)
private String firstName;
@Column(nullable = false)
private String lastName;

@Column(nullable = false, columnDefinition = "boolean default false")
private boolean isEnabled = false;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.shaderock.backend.model.repository;

import com.shaderock.backend.model.entity.user.AppUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
public interface AppUserRepository extends JpaRepository<AppUser, Long> {
Optional<AppUser> findByEmail(String email);

Optional<AppUser> findByRegistrationToken(String email);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"properties": [
{
"name": "application.frontend.url",
"type": "java.lang.String",
"description": "Description for application.frontend.url."
}
] }
Loading

0 comments on commit 5116fb2

Please sign in to comment.