-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add account and plan rest apis * add subscription rest apis
- Loading branch information
1 parent
328fe81
commit ed21746
Showing
68 changed files
with
2,109 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
3 changes: 3 additions & 0 deletions
3
domain/src/test/java/io/github/gabrmsouza/subscription/domain/UnitTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
package io.github.gabrmsouza.subscription.domain; | ||
|
||
import org.junit.jupiter.api.Tag; | ||
import org.junit.jupiter.api.extension.ExtendWith; | ||
import org.mockito.junit.jupiter.MockitoExtension; | ||
|
||
@Tag("unitTest") | ||
@ExtendWith(MockitoExtension.class) | ||
public class UnitTest { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...authentication/AuthenticationGateway.java → ...entcredentials/AuthenticationGateway.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
...hentication/ClientCredentialsManager.java → ...credentials/ClientCredentialsManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
.../authentication/GetClientCredentials.java → ...ientcredentials/GetClientCredentials.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...cation/KeycloakAuthenticationGateway.java → ...ntials/KeycloakAuthenticationGateway.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...hentication/RefreshClientCredentials.java → ...credentials/RefreshClientCredentials.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
...souza/subscription/infrastructure/authentication/principal/AccountFromUserIdResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package io.github.gabrmsouza.subscription.infrastructure.authentication.principal; | ||
|
||
import io.github.gabrmsouza.subscription.domain.account.Account; | ||
import io.github.gabrmsouza.subscription.domain.account.idp.UserId; | ||
|
||
import java.util.Optional; | ||
import java.util.function.Function; | ||
|
||
public interface AccountFromUserIdResolver extends Function<UserId, Optional<Account>> { | ||
} |
41 changes: 41 additions & 0 deletions
41
...brmsouza/subscription/infrastructure/authentication/principal/CodeflixAuthentication.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package io.github.gabrmsouza.subscription.infrastructure.authentication.principal; | ||
|
||
import org.springframework.security.authentication.AbstractAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
|
||
import java.util.Collection; | ||
|
||
|
||
public class CodeflixAuthentication extends AbstractAuthenticationToken { | ||
|
||
private final Jwt jwt; | ||
private final CodeflixUser user; | ||
|
||
/** | ||
* Creates a token with the supplied array of authorities. | ||
* | ||
* @param authorities the collection of <tt>GrantedAuthority</tt>s for the principal | ||
* represented by this authentication object. | ||
*/ | ||
public CodeflixAuthentication(Jwt jwt, CodeflixUser user, Collection<? extends GrantedAuthority> authorities) { | ||
super(authorities); | ||
this.jwt = jwt; | ||
this.user = user; | ||
} | ||
|
||
@Override | ||
public Object getCredentials() { | ||
return jwt; | ||
} | ||
|
||
@Override | ||
public Object getPrincipal() { | ||
return user; | ||
} | ||
|
||
@Override | ||
public boolean isAuthenticated() { | ||
return true; | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
.../github/gabrmsouza/subscription/infrastructure/authentication/principal/CodeflixUser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.github.gabrmsouza.subscription.infrastructure.authentication.principal; | ||
|
||
import io.github.gabrmsouza.subscription.domain.account.idp.UserId; | ||
import io.github.gabrmsouza.subscription.infrastructure.exceptions.ForbiddenException; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
|
||
import java.util.Optional; | ||
|
||
public record CodeflixUser( | ||
String name, | ||
String idpUserId, | ||
String accountId | ||
) implements User { | ||
|
||
private static final String ACCOUNT_ID = "account_id"; | ||
private static final String NAME = "name"; | ||
|
||
public static CodeflixUser fromJwt(Jwt jwt, AccountFromUserIdResolver accountResolver) { | ||
final var idpUserId = jwt.getSubject(); | ||
return new CodeflixUser( | ||
jwt.getClaimAsString(NAME), | ||
idpUserId, | ||
Optional.ofNullable(jwt.getClaimAsString(ACCOUNT_ID)) | ||
.or(() -> accountResolver.apply(new UserId(idpUserId)).map(acc -> acc.userId().value())) | ||
.orElseThrow(() -> ForbiddenException.with("Could not resolve account from user")) | ||
); | ||
} | ||
} |
87 changes: 87 additions & 0 deletions
87
...gabrmsouza/subscription/infrastructure/authentication/principal/KeycloakJwtConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package io.github.gabrmsouza.subscription.infrastructure.authentication.principal; | ||
|
||
import io.github.gabrmsouza.subscription.domain.account.AccountGateway; | ||
import org.springframework.core.convert.converter.Converter; | ||
import org.springframework.security.authentication.AbstractAuthenticationToken; | ||
import org.springframework.security.core.GrantedAuthority; | ||
import org.springframework.security.core.authority.SimpleGrantedAuthority; | ||
import org.springframework.security.oauth2.jwt.Jwt; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.util.*; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.Stream; | ||
|
||
@Component | ||
public class KeycloakJwtConverter implements Converter<Jwt, AbstractAuthenticationToken> { | ||
|
||
private final AccountGateway accountGateway; | ||
private final KeycloakAuthoritiesConverter authoritiesConverter; | ||
|
||
public KeycloakJwtConverter(final AccountGateway accountGateway) { | ||
this.accountGateway = Objects.requireNonNull(accountGateway); | ||
this.authoritiesConverter = new KeycloakAuthoritiesConverter(); | ||
} | ||
|
||
@Override | ||
public AbstractAuthenticationToken convert(final Jwt jwt) { | ||
return new CodeflixAuthentication(jwt, extractPrincipal(jwt), extractAuthorities(jwt)); | ||
} | ||
|
||
private CodeflixUser extractPrincipal(final Jwt jwt) { | ||
return CodeflixUser.fromJwt(jwt, accountGateway::accountOfUserId); | ||
} | ||
|
||
private Collection<? extends GrantedAuthority> extractAuthorities(final Jwt jwt) { | ||
return this.authoritiesConverter.convert(jwt); | ||
} | ||
|
||
static class KeycloakAuthoritiesConverter implements Converter<Jwt, Collection<GrantedAuthority>> { | ||
|
||
private static final String REALM_ACCESS = "realm_access"; | ||
private static final String ROLES = "roles"; | ||
private static final String RESOURCE_ACCESS = "resource_access"; | ||
private static final String SEPARATOR = "_"; | ||
private static final String ROLE_PREFIX = "ROLE_"; | ||
|
||
@Override | ||
public Collection<GrantedAuthority> convert(final Jwt jwt) { | ||
final var realmRoles = extractRealmRoles(jwt); | ||
final var resourceRoles = extractResourceRoles(jwt); | ||
|
||
return Stream.concat(realmRoles, resourceRoles) | ||
.map(role -> new SimpleGrantedAuthority(ROLE_PREFIX + role.toUpperCase())) | ||
.collect(Collectors.toSet()); | ||
} | ||
|
||
private Stream<String> extractResourceRoles(final Jwt jwt) { | ||
|
||
final Function<Map.Entry<String, Object>, Stream<String>> mapResource = | ||
resource -> { | ||
final var key = resource.getKey(); | ||
final var value = (Map) resource.getValue(); | ||
final var roles = (Collection<String>) value.get(ROLES); | ||
return roles.stream().map(role -> key.concat(SEPARATOR).concat(role)); | ||
}; | ||
|
||
final Function<Set<Map.Entry<String, Object>>, Collection<String>> mapResources = | ||
resources -> resources.stream() | ||
.flatMap(mapResource) | ||
.toList(); | ||
|
||
return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS)) | ||
.map(Map::entrySet) | ||
.map(mapResources) | ||
.orElse(Collections.emptyList()) | ||
.stream(); | ||
} | ||
|
||
private Stream<String> extractRealmRoles(final Jwt jwt) { | ||
return Optional.ofNullable(jwt.getClaimAsMap(REALM_ACCESS)) | ||
.map(resource -> (Collection<String>) resource.get(ROLES)) | ||
.orElse(Collections.emptyList()) | ||
.stream(); | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
.../java/io/github/gabrmsouza/subscription/infrastructure/authentication/principal/User.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package io.github.gabrmsouza.subscription.infrastructure.authentication.principal; | ||
|
||
public interface User { | ||
String name(); | ||
String idpUserId(); | ||
String accountId(); | ||
} |
51 changes: 51 additions & 0 deletions
51
...o/github/gabrmsouza/subscription/infrastructure/configuration/GlobalExceptionHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package io.github.gabrmsouza.subscription.infrastructure.configuration; | ||
|
||
import io.github.gabrmsouza.subscription.domain.exceptions.DomainException; | ||
import io.github.gabrmsouza.subscription.domain.exceptions.InternalErrorException; | ||
import io.github.gabrmsouza.subscription.domain.validation.Error; | ||
import io.github.gabrmsouza.subscription.domain.validation.handler.Notification; | ||
import io.github.gabrmsouza.subscription.infrastructure.exceptions.ForbiddenException; | ||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.HttpStatusCode; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.validation.FieldError; | ||
import org.springframework.validation.ObjectError; | ||
import org.springframework.web.bind.MethodArgumentNotValidException; | ||
import org.springframework.web.bind.annotation.ExceptionHandler; | ||
import org.springframework.web.bind.annotation.RestControllerAdvice; | ||
import org.springframework.web.context.request.WebRequest; | ||
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; | ||
|
||
import java.util.List; | ||
|
||
@RestControllerAdvice | ||
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler { | ||
|
||
@Override | ||
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) { | ||
return ResponseEntity.unprocessableEntity() | ||
.body(Notification.create(covertError(ex.getBindingResult().getAllErrors()))); | ||
} | ||
|
||
@ExceptionHandler(DomainException.class) | ||
public ResponseEntity<?> handleDomainException(DomainException ex) { | ||
return ResponseEntity.unprocessableEntity().body(ex.getErrors()); | ||
} | ||
|
||
@ExceptionHandler(InternalErrorException.class) | ||
public ResponseEntity<?> handleInternalErrorException(InternalErrorException ex) { | ||
return ResponseEntity.internalServerError().body(new Error("", ex.getMessage())); | ||
} | ||
|
||
@ExceptionHandler(ForbiddenException.class) | ||
public ResponseEntity<?> handleForbiddenException(ForbiddenException ex) { | ||
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new Error("Authentication", ex.getMessage())); | ||
} | ||
|
||
private List<Error> covertError(List<ObjectError> allErrors) { | ||
return allErrors.stream() | ||
.map(e -> new Error(((FieldError) e).getField(), e.getDefaultMessage())) | ||
.toList(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.