Commit aeea3190 authored by Alex Segers's avatar Alex Segers

Merge branch 'AFP-91' into 'dev'

[AFP-91] 🔀 Manager Controller, Manager Service, & access token verification (@asegers)

See merge request !26
parents a9bda431 3075cea2
Pipeline #1706 failed with stage
in 38 seconds
...@@ -34,3 +34,6 @@ build/ ...@@ -34,3 +34,6 @@ build/
### VS Code ### ### VS Code ###
.vscode/ .vscode/
### Mac OS ###
.DS_Store
...@@ -50,13 +50,6 @@ ...@@ -50,13 +50,6 @@
<artifactId>reactor-test</artifactId> <artifactId>reactor-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<!--<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>-->
<!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker --> <!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker -->
<dependency> <dependency>
<groupId>com.github.javafaker</groupId> <groupId>com.github.javafaker</groupId>
...@@ -101,6 +94,12 @@ ...@@ -101,6 +94,12 @@
<groupId>io.projectreactor.kafka</groupId> <groupId>io.projectreactor.kafka</groupId>
<artifactId>reactor-kafka</artifactId> <artifactId>reactor-kafka</artifactId>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.google.api-client/google-api-client -->
<dependency>
<groupId>com.google.api-client</groupId>
<artifactId>google-api-client</artifactId>
<version>1.31.1</version>
</dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
......
package com.afp.ordermanagement.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessToken {
}
package com.afp.ordermanagement.annotation;
import com.afp.ordermanagement.service.ManagerTokenVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
public class AccessTokenResolver implements HandlerMethodArgumentResolver {
@Autowired
ManagerTokenVerifier managerTokenVerifier;
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
boolean hasClassAnnotation = methodParameter.getDeclaringClass().isAnnotationPresent(AuthManagerController.class);
boolean hasMethodAnnotation = methodParameter.hasMethodAnnotation(AuthManagerResponse.class);
boolean hasParameterAnnotation = methodParameter.hasParameterAnnotation(AccessToken.class);
return (hasClassAnnotation || hasMethodAnnotation) && hasParameterAnnotation;
}
@Override
public Mono<Object> resolveArgument(MethodParameter methodParameter, BindingContext bindingContext, ServerWebExchange serverWebExchange) {
String accessToken = managerTokenVerifier.getTokenHeader(serverWebExchange);
return Mono.just(accessToken);
}
}
package com.afp.ordermanagement.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthManagerController {
}
package com.afp.ordermanagement.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthManagerResponse {
}
package com.afp.ordermanagement.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface ManagerPayload {
String value() default "";
String pluck() default "";
}
package com.afp.ordermanagement.annotation;
import com.afp.ordermanagement.model.Manager;
import com.afp.ordermanagement.service.ManagerTokenVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.reactive.BindingContext;
import org.springframework.web.reactive.result.method.HandlerMethodArgumentResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
public class ManagerPayloadResolver implements HandlerMethodArgumentResolver {
@Autowired
ManagerTokenVerifier managerTokenVerifier;
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
boolean hasClassAnnotation = methodParameter.getDeclaringClass().isAnnotationPresent(AuthManagerController.class);
boolean hasMethodAnnotation = methodParameter.hasMethodAnnotation(AuthManagerResponse.class);
boolean hasParameterAnnotation = methodParameter.hasParameterAnnotation(ManagerPayload.class);
return (hasClassAnnotation || hasMethodAnnotation) && hasParameterAnnotation;
}
@Override
public Mono<Object> resolveArgument(MethodParameter methodParameter, BindingContext bindingContext, ServerWebExchange serverWebExchange) {
String accessToken = managerTokenVerifier.getTokenHeader(serverWebExchange);
ManagerPayload annotation = methodParameter.getParameterAnnotation(ManagerPayload.class);
String value = annotation.value(), pluck = annotation.pluck(), selectedField;
List<Field> declaredFields = Arrays.asList(Manager.class.getDeclaredFields());
Manager manager = managerTokenVerifier.createManagerFromToken(accessToken);
if (value.isEmpty() && pluck.isEmpty()) {
return Mono.just(manager);
} else {
selectedField = value.isEmpty() ? pluck : value;
if (!declaredFields.contains(selectedField))
return Mono.just(manager);
try {
Field field = Manager.class.getDeclaredField(selectedField);
field.setAccessible(true);
return Mono.just(field.get(manager));
} catch (Exception ignore) {
return Mono.just(manager);
}
}
}
}
\ No newline at end of file
package com.afp.ordermanagement.config;
import com.afp.ordermanagement.annotation.AccessTokenResolver;
import com.afp.ordermanagement.annotation.ManagerPayloadResolver;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;
@Configuration
//@ConditionalOnClass(EnableWebFlux.class) // checks that WebFlux is on the classpath
//@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
//@EnableWebFlux
public class AnnotationConfig implements WebFluxConfigurer {
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
configurer.addCustomResolver(accessTokenResolver());
configurer.addCustomResolver(managerPayloadResolver());
}
@Bean
public AccessTokenResolver accessTokenResolver() {
return new AccessTokenResolver();
}
@Bean
public ManagerPayloadResolver managerPayloadResolver() {
return new ManagerPayloadResolver();
}
}
package com.afp.ordermanagement.config;
import com.afp.ordermanagement.exception.BadAccessTokenException;
import com.afp.ordermanagement.service.ManagerTokenVerifier;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Component
public class AuthWebFilter implements WebFilter {
@Autowired
ManagerTokenVerifier managerTokenVerifier;
@Override
public Mono<Void> filter(ServerWebExchange serverWebExchange, WebFilterChain webFilterChain) {
// String origin = serverWebExchange.getRequest().getHeaders().getOrigin();
// if (managerTokenVerifier.hasTokenHeader(serverWebExchange)) {
// String token = managerTokenVerifier.getTokenHeader(serverWebExchange);
// if (managerTokenVerifier.isTokenValid(token))
return webFilterChain.filter(serverWebExchange);
// }
// return Mono.error(new BadAccessTokenException());
}
}
package com.afp.ordermanagement.config;
import com.afp.ordermanagement.model.Manager;
import com.github.javafaker.Faker;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.lang.reflect.Field;
import java.util.Arrays;
import static java.util.Objects.isNull;
@Configuration
public class BeanConfig {
@Bean
public static Faker faker() {
return new Faker();
}
@Bean
public static FieldCombiner fieldCombiner() { return new FieldCombiner(); }
public static class FieldCombiner {
public Object combine(Object target, Object source, String[] omitFields) {
try {
for (Field field: Manager.class.getDeclaredFields()) {
String fieldName = field.getName();
field.setAccessible(true);
Object sourceValue = field.get(source);
if (!isNull(sourceValue) && Arrays.binarySearch(omitFields, fieldName) <= 0)
field.set(target, sourceValue);
}
} catch (Exception ignore) { }
return target;
}
public Object combine(Object target, Object source) {
return this.combine(target, source, new String[0]);
}
}
}
package com.afp.ordermanagement.controller; package com.afp.ordermanagement.controller;
import com.afp.ordermanagement.annotation.AuthManagerController;
import com.afp.ordermanagement.annotation.ManagerPayload;
import com.afp.ordermanagement.exception.ResourceNotFoundException;
import com.afp.ordermanagement.model.Manager; import com.afp.ordermanagement.model.Manager;
import com.afp.ordermanagement.repository.ManagerRepository; import com.afp.ordermanagement.service.ManagerService;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux; import reactor.core.publisher.Mono;
import javax.validation.Valid;
@Validated
@RestController @RestController
@RequestMapping("/api") @AuthManagerController
@RequestMapping("/api/managers/")
public class ManagerController { public class ManagerController {
@Autowired @Autowired
ManagerRepository managerRepository; ManagerService managerService;
@GetMapping("/manager") @PostMapping("/auth")
public Flux<Manager> getAllManagers() { public ResponseEntity<Mono<Manager>> signUpOrLogInManager(@ManagerPayload Manager managerPayload) {
System.out.println("here"); Mono<Manager> manager = managerService.getByEmail(managerPayload.getEmail())
Flux<Manager> managerFlux = managerRepository.findAll(); .switchIfEmpty(managerService.create(managerPayload));
return managerFlux; return ResponseEntity.ok(manager);
} }
@GetMapping("/account")
public ResponseEntity<Mono<Manager>> getManagerDetails(@ManagerPayload Manager managerPayload) {
Mono<Manager> existingManager = managerService.getByEmail(managerPayload.getEmail())
.switchIfEmpty(Mono.error(new ResourceNotFoundException()));
return ResponseEntity.ok(existingManager);
}
@PatchMapping("/account")
public ResponseEntity<Mono<Manager>> updateManagerDetails(@ManagerPayload Manager managerPayload, @Valid @RequestBody Manager managerBody) {
return ResponseEntity.ok(managerService.updateByEmail(managerPayload.getEmail(), managerBody));
}
@DeleteMapping("/account")
public ResponseEntity deleteManagerDetails(@ManagerPayload Manager managerPayload) {
managerService.getByEmail(managerPayload.getEmail())
.switchIfEmpty(Mono.error(new ResourceNotFoundException()))
.flatMap(managerService::delete);
return ResponseEntity.noContent().build();
}
} }
package com.afp.ordermanagement.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
public class BadAccessTokenException extends RuntimeException {
public BadAccessTokenException() {
super("Invalid access token");
}
}
package com.afp.ordermanagement.exception;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.support.WebExchangeBindException;
import org.springframework.web.server.ServerWebExchange;
@ControllerAdvice
@RequiredArgsConstructor
public class ControllerExceptionAdvice {
@ExceptionHandler(BadAccessTokenException.class)
public ResponseEntity<ErrorResponse> handleBadAccessTokenException(RuntimeException exc, ServerWebExchange exchange) {
final HttpStatus status = HttpStatus.UNAUTHORIZED;
final ServerHttpRequest request = exchange.getRequest();
return new ResponseEntity<>(new ErrorResponse(
status.value(),
request.getPath().value(),
status.getReasonPhrase(),
exc.getMessage()
), status);
}
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleEntityNotFoundException(RuntimeException exc, ServerWebExchange exchange) {
final HttpStatus status = HttpStatus.NOT_FOUND;
final ServerHttpRequest request = exchange.getRequest();
return new ResponseEntity<>(new ErrorResponse(
status.value(),
request.getPath().value(),
status.getReasonPhrase(),
exc.getMessage()
), status);
}
@ExceptionHandler(RuntimeException.class)
public ResponseEntity<ErrorResponse> handleRuntimeException(RuntimeException exc, ServerWebExchange exchange) {
final HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
final ServerHttpRequest request = exchange.getRequest();
return new ResponseEntity<>(new ErrorResponse(
status.value(),
request.getPath().value(),
status.getReasonPhrase(),
exc.getMessage()
), status);
}
@ExceptionHandler(WebExchangeBindException.class)
public ResponseEntity<ErrorResponse> webExchangeBindException(WebExchangeBindException exc, ServerWebExchange exchange) {
final HttpStatus status = exc.getStatus();
final ServerHttpRequest request = exchange.getRequest();
if (InvalidEntityResponse.isEntityValid(exc.getTarget())) {
return new ResponseEntity<>(new ErrorResponse(
status.value(),
request.getPath().value(),
status.getReasonPhrase(),
exc.getMessage()
), status);
}
return new ResponseEntity<>(new InvalidEntityResponse(
status.value(),
request.getPath().value(),
status.getReasonPhrase(),
"Validation failed",
exc.getTarget()
), status);
}
}
package com.afp.ordermanagement.exception;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import java.time.Instant;
@Data
@RequiredArgsConstructor
public class ErrorResponse {
public final Integer status;
public final String path, error, message;
public String timestamp = Instant.now().toString();
}
package com.afp.ordermanagement.exception;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class InvalidEntityResponse extends ErrorResponse {
public List<Map<String, String>> fieldErrors;
public InvalidEntityResponse(Integer status, String path, String type, String message, Object entity) {
super(status, path, type, message);
this.fieldErrors = InvalidEntityResponse.parseViolations(entity);
}
public static boolean isEntityValid(Object entity) {
return validator.validate(entity).isEmpty();
}
static private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
static private List<Map<String, String>> parseViolations(Object entity) {
List<Map<String, String>> fieldErrors = new ArrayList<>();
for (ConstraintViolation<Object> cv : validator.validate(entity)) {
Map<String, String> errorMap = new HashMap<String, String>() {{
put("field", cv.getPropertyPath().toString());
put("message", cv.getMessage());
}};
fieldErrors.add(errorMap);
}
return fieldErrors;
}
}
package com.afp.ordermanagement.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException() {
super("Resource not found");
}
public ResourceNotFoundException(String message) {
super(message);
}
}
...@@ -4,12 +4,10 @@ import lombok.Data; ...@@ -4,12 +4,10 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.URL; import org.hibernate.validator.constraints.URL;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.Document;
import javax.validation.constraints.*; import javax.validation.constraints.*;
import java.lang.reflect.Field;
import static java.util.Objects.isNull;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
...@@ -18,30 +16,18 @@ public class Manager { ...@@ -18,30 +16,18 @@ public class Manager {
@Id @Id
private String id; private String id;
@Size(min = 1, max = 250, message = "'firstName' must be between 1 & 250 characters") @Size(min = 1, max = 250)
@NotNull @NotNull()
private String firstName, lastName; private String firstName, lastName;
@Indexed(unique = true)
@Email @Email
private String email; private String email;
@NotNull @Size(min = 21, max = 21)
@Size(min = 21, max = 21, message = "'googleId' is invalid") @NotNull()
private String googleId; private String googleId;
@URL(message = "'imageUrl' must be a valid URL") @URL
private String imageUrl; private String imageUrl;
static public Manager combine(Manager target, Manager source) {
try {
for (Field field: Manager.class.getDeclaredFields()) {
String fieldName = field.getName();
Object sourceValue = field.get(source);
if (!isNull(sourceValue) && !fieldName.equals("id"))
field.set(target, sourceValue);
}
} catch (Exception ignore) { }
return target;
}
} }
...@@ -2,8 +2,10 @@ package com.afp.ordermanagement.repository; ...@@ -2,8 +2,10 @@ package com.afp.ordermanagement.repository;
import com.afp.ordermanagement.model.Manager; import com.afp.ordermanagement.model.Manager;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository; import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
//import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
// import reactor.core.publisher.Mono;
//@Repository
@Repository
public interface ManagerRepository extends ReactiveMongoRepository<Manager, String> { public interface ManagerRepository extends ReactiveMongoRepository<Manager, String> {
Mono<Manager> findByEmail(String email);
} }
...@@ -6,8 +6,11 @@ import com.github.javafaker.Faker; ...@@ -6,8 +6,11 @@ import com.github.javafaker.Faker;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@Component @Component
...@@ -15,29 +18,43 @@ public class ManagerSeeder { ...@@ -15,29 +18,43 @@ public class ManagerSeeder {
@Autowired @Autowired
ManagerRepository managerRepository; ManagerRepository managerRepository;
static private final Faker FAKER = new Faker(); @Autowired
Faker faker;
@Autowired
Environment env;
// Verify that the current environment is NOT production or test
boolean isEnvDevelopment() {
return env.acceptsProfiles(Profiles.of("default", "dev", "development", "local"));
}
static private final int SEED_COUNT = 1; // Number of seed documents generated
static private final int SEED_COUNT = 10;
// Create manager seeds as soon as app is up and running
@EventListener @EventListener
public void seedManager(ContextRefreshedEvent event) { public void seedManagers(ContextRefreshedEvent event) {
managerRepository // Only generate seeds if in development environment
.deleteAll() if (isEnvDevelopment()) {
.subscribe();
IntStream.range(0, SEED_COUNT).forEach(n -> {
managerRepository managerRepository
.insert(generateManager()) .deleteAll()
.subscribe(); .subscribe();
}); IntStream.range(0, SEED_COUNT + 1).forEach(n -> {
managerRepository
.insert(generateManager())
.subscribe();
});
}
} }
private Manager generateManager(){ private Manager generateManager(){
Manager manager = new Manager(); Manager manager = new Manager();
manager.setFirstName(FAKER.name().firstName()); manager.setFirstName(faker.name().firstName());
manager.setLastName(FAKER.name().lastName()); manager.setLastName(faker.name().lastName());
manager.setEmail(FAKER.internet().emailAddress()); manager.setEmail(faker.internet().emailAddress());
manager.setGoogleId(FAKER.number().digits(21)); manager.setGoogleId(faker.number().digits(21));
manager.setImageUrl(FAKER.internet().url()); manager.setImageUrl("https://picsum.photos/200/200");
return manager; return manager;
} }
} }
package com.afp.ordermanagement.service; package com.afp.ordermanagement.service;
import com.afp.ordermanagement.model.Manager;
import com.afp.ordermanagement.repository.ManagerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import static com.afp.ordermanagement.config.BeanConfig.FieldCombiner;
@Service
public class ManagerService { public class ManagerService {
@Autowired
FieldCombiner fieldCombiner;
@Autowired
ManagerRepository managerRepository;
public Mono<Manager> getByEmail(String email) {
return managerRepository.findByEmail(email);
}
public Mono<Manager> create(Manager newManager) {
return managerRepository.save(newManager);
}
public Mono<Manager> updateByEmail(String email, Manager managerUpdates) {
return this.getByEmail(email)
.map(existingManager -> (Manager) fieldCombiner.combine(existingManager, managerUpdates, new String[] { "id", "email"}))
.flatMap(managerRepository::save);
}
public Mono<Void> delete(Manager manager) {
return managerRepository.delete(manager);
}
} }
package com.afp.ordermanagement.service;
import com.afp.ordermanagement.model.Manager;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ServerWebExchange;
import java.util.Collections;
import static java.util.Objects.isNull;
@Service
public class ManagerTokenVerifier {
private final String CLIENT_ID = "925243198137-hhe2e3ejlethf321hh7tbm7ontc19cpj.apps.googleusercontent.com";
private final JacksonFactory jsonFactory = new JacksonFactory();
private final HttpTransport transport = new NetHttpTransport();
private final GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
.setAudience(Collections.singletonList(CLIENT_ID))
.build();
public boolean isTokenValid(String idTokenString) {
try {
verifier.verify(idTokenString);
return true;
} catch (Exception ignore) { }
return false;
}
private GoogleIdToken.Payload createPayloadFromToken(String idTokenString) {
GoogleIdToken idToken = null;
try {
idToken = verifier.verify(idTokenString);
} catch (Exception ignore) { }
assert idToken != null;
return idToken.getPayload();
}
public Manager createManagerFromToken(String idTokenString) {
GoogleIdToken.Payload payload = this.createPayloadFromToken(idTokenString);
Manager newManager = new Manager();
newManager.setFirstName((String) payload.get("given_name"));
newManager.setLastName((String) payload.get("family_name"));
newManager.setEmail(payload.getEmail());
newManager.setGoogleId(payload.getSubject());
newManager.setImageUrl((String) payload.get("picture"));
return newManager;
}
public boolean hasTokenHeader(ServerWebExchange serverWebExchange) {
HttpHeaders headers = serverWebExchange
.getRequest()
.getHeaders();
String accessToken = headers
.getFirst("Authorization");
return !isNull(accessToken);
}
public String getTokenHeader(ServerWebExchange serverWebExchange) {
HttpHeaders headers = serverWebExchange
.getRequest()
.getHeaders();
String accessToken = headers
.getFirst("Authorization")
.replace("Bearer ", "");
return accessToken;
}
}
package com.afp.ordermanagement.UNIT_TESTS.helper;
import com.github.javafaker.Faker;
//import com.sun.tools.javac.util.List;
import org.junit.jupiter.api.Assertions;
import static org.junit.jupiter.api.Assertions.*;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.lang.reflect.ParameterizedType;
import java.util.List;
public abstract class ValidatableEntity<E> {
private final Class<E> childClass;
static private final Faker FAKER = new Faker();
@SuppressWarnings("unchecked")
public ValidatableEntity() {
this.childClass = (
(Class<E>) (
(ParameterizedType) this.getClass()
.getGenericSuperclass()
)
.getActualTypeArguments()[0]
);
}
private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
// assertFieldValidation(String fieldName, Object input, boolean isValidInput)
public ConstraintViolation<E> createViolation(String fieldName, Object fieldValue) {
return validator
.validateValue(this.childClass, fieldName, fieldValue)
.stream()
.findAny()
.orElse(null);
}
// assertFieldValidation(String fieldName, Object input, boolean isValidInput)
public void assertFieldValidation(String fieldName, Object input, boolean expected) {
boolean isValid = validator.validateValue(this.childClass, fieldName, input).isEmpty();
String v = expected ? "valid" : "invalid";
String errMsg = String.format("Expected '%s' to be a %s value for field '%s'", input, v, fieldName);
if (isValid != expected) fail(errMsg);
}
}
package com.afp.ordermanagement.UNIT_TESTS.model;
import com.afp.ordermanagement.UNIT_TESTS.helper.ValidatableEntity;
import com.afp.ordermanagement.model.Manager;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class ManagerTests extends ValidatableEntity<Manager> {
@ParameterizedTest
@CsvSource(value = {"null:false", "\"\":false", "Jerry:true"}, delimiter = ':')
public void shouldValidateNameFields(String input, boolean expected) {
assertFieldValidation("firstName", input, expected);
assertFieldValidation("lastName", input, expected);
}
@ParameterizedTest
@CsvSource(value = {"null:false", "\"\":false", "poop:false", "jerry@hotmail.com:true"}, delimiter = ':')
public void shouldValidateEmailField(String input, boolean expected) {
assertFieldValidation("email", input, expected);
}
@ParameterizedTest
@CsvSource(value = {"null:false", "\"\":false"}, delimiter = ':')
public void shouldValidateGoogleIdField(String input, boolean expected) {
assertFieldValidation("googleId", input, expected);
}
@ParameterizedTest
@CsvSource(value = {"\"\",false", "https://picsum.photos/200,true", "htps: picsum.photos/200,false"})
public void shouldValidateImageUrl(String input, boolean expected) {
assertFieldValidation("imageUrl", input, expected);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment