diff --git a/auto-configure/pom.xml b/auto-configure/pom.xml
index d1cfc5a..41760c1 100644
--- a/auto-configure/pom.xml
+++ b/auto-configure/pom.xml
@@ -29,6 +29,12 @@
true
+
+ org.projectlombok
+ lombok
+ true
+
+
com.codeyapa
core
@@ -40,6 +46,12 @@
spring-boot-starter-test
test
+
+
+ org.projectlombok
+ lombok
+ true
+
\ No newline at end of file
diff --git a/auto-configure/src/test/java/com/codeyapa/rest/secured/autoconfiguration/SecurityAutoConfigurationTest.java b/auto-configure/src/test/java/com/codeyapa/rest/secured/autoconfiguration/SecurityAutoConfigurationTest.java
index ab3a2a2..eaac56a 100644
--- a/auto-configure/src/test/java/com/codeyapa/rest/secured/autoconfiguration/SecurityAutoConfigurationTest.java
+++ b/auto-configure/src/test/java/com/codeyapa/rest/secured/autoconfiguration/SecurityAutoConfigurationTest.java
@@ -3,6 +3,7 @@
import org.junit.Test;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.autoconfigure.AutoConfigurations.of;
public class SecurityAutoConfigurationTest {
@@ -15,7 +16,21 @@ public void shouldCreateAutoConfiguredBeanIfStarterIsEnabled() {
.withPropertyValues(properties(true))
.withConfiguration(of(SecurityAutoConfiguration.class));
- this.applicationContextRunner.run(context -> {});
+ this.applicationContextRunner.run(
+ context -> {
+ });
+ }
+
+ @Test
+ public void shouldNotCreateAutoConfiguredBeanIfStarterIsNotEnabled() {
+ applicationContextRunner =
+ new WebApplicationContextRunner()
+ .withPropertyValues(properties(false))
+ .withConfiguration(of(SecurityAutoConfiguration.class));
+
+ this.applicationContextRunner.run(
+ context -> {
+ });
}
private String[] properties(boolean enabled) {
diff --git a/core/pom.xml b/core/pom.xml
index b5d34c9..cf04e98 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -11,6 +11,16 @@
core
+
+ ${project.build.directory}/jacoco.exec
+
+ src/main/java/com/codeyapa/rest/secured/core/domain/*
+
+
+ src/main/java/com/codeyapa/rest/secured/core/domain/*
+
+
+
org.springframework.boot
@@ -24,11 +34,56 @@
provided
+
+ org.projectlombok
+ lombok
+ true
+
+
org.springframework.boot
spring-boot-starter-test
test
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.vintage
+ junit-vintage-engine
+ ${junit.jupiter.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-suite-api
+ test
+
+
+ org.junit.platform
+ junit-platform-runner
+ test
+
\ No newline at end of file
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/domain/GrantType.java b/core/src/main/java/com/codeyapa/rest/secured/core/domain/GrantType.java
new file mode 100644
index 0000000..0720248
--- /dev/null
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/domain/GrantType.java
@@ -0,0 +1,14 @@
+package com.codeyapa.rest.secured.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public enum GrantType {
+ @JsonProperty("password")
+ PASSWORD,
+
+ @JsonProperty("refresh_token")
+ REFRESH_TOKEN,
+
+ @JsonProperty("client_credentials")
+ CLIENT_CREDENTIALS
+}
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/domain/IssueTokenRequest.java b/core/src/main/java/com/codeyapa/rest/secured/core/domain/IssueTokenRequest.java
new file mode 100644
index 0000000..1a9fae0
--- /dev/null
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/domain/IssueTokenRequest.java
@@ -0,0 +1,49 @@
+package com.codeyapa.rest.secured.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@AllArgsConstructor
+@Getter
+@EqualsAndHashCode
+public class IssueTokenRequest {
+ @JsonProperty("client_id")
+ private String clientId;
+
+ @JsonProperty("grant_type")
+ private GrantType grantType;
+
+ private String scope;
+ private String username;
+ private String password;
+ private int confidenceLevel;
+
+ @JsonProperty("refresh_token")
+ private String refreshToken;
+
+ public IssueTokenRequest(String clientId, String username, String password, String scope) {
+ this.clientId = clientId;
+ this.grantType = GrantType.PASSWORD;
+
+ this.username = username;
+ this.password = password;
+ this.scope = scope;
+ }
+
+ public IssueTokenRequest(String clientId, String refreshToken) {
+ this.clientId = clientId;
+ this.grantType = GrantType.REFRESH_TOKEN;
+ this.refreshToken = refreshToken;
+ }
+
+ public IssueTokenRequest(String clientId, String scope, int confidenceLevel) {
+ this.clientId = clientId;
+ this.scope = scope;
+ this.confidenceLevel = confidenceLevel;
+ this.grantType = GrantType.CLIENT_CREDENTIALS;
+ }
+}
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/domain/IssueTokenResponse.java b/core/src/main/java/com/codeyapa/rest/secured/core/domain/IssueTokenResponse.java
new file mode 100644
index 0000000..7c1f03a
--- /dev/null
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/domain/IssueTokenResponse.java
@@ -0,0 +1,35 @@
+package com.codeyapa.rest.secured.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+public class IssueTokenResponse {
+ @JsonProperty("access_token")
+ String accessToken;
+
+ @JsonProperty("refresh_token")
+ String refreshToken;
+
+ @JsonProperty("expires_in")
+ Integer expiresIn;
+
+ @JsonProperty("token_type")
+ String tokenType;
+
+ @JsonProperty("scope")
+ String scope;
+
+ @JsonProperty("confidence_level")
+ Integer confidenceLevel;
+}
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/domain/Token.java b/core/src/main/java/com/codeyapa/rest/secured/core/domain/Token.java
new file mode 100644
index 0000000..7412739
--- /dev/null
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/domain/Token.java
@@ -0,0 +1,34 @@
+package com.codeyapa.rest.secured.core.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+import java.time.Instant;
+import java.util.Set;
+
+@Builder
+@Value
+@AllArgsConstructor
+@EqualsAndHashCode
+public class Token {
+ String tokenId;
+ String userId;
+ String clientId;
+ Instant validFrom;
+ Instant expiresAt;
+ Instant issuedAt;
+ String issuer;
+ String scope;
+ Integer confidenceLevel;
+ Set audience;
+
+ public boolean isFutureToken() {
+ return !validFrom.isBefore(Instant.now());
+ }
+
+ public boolean isExpired() {
+ return expiresAt.isBefore(Instant.now());
+ }
+}
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/domain/ValidateTokenRequest.java b/core/src/main/java/com/codeyapa/rest/secured/core/domain/ValidateTokenRequest.java
new file mode 100644
index 0000000..d0862a2
--- /dev/null
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/domain/ValidateTokenRequest.java
@@ -0,0 +1,14 @@
+package com.codeyapa.rest.secured.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@Getter
+@AllArgsConstructor
+public class ValidateTokenRequest {
+ @JsonProperty("access_token")
+ private String accessToken;
+}
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/domain/ValidateTokenResponse.java b/core/src/main/java/com/codeyapa/rest/secured/core/domain/ValidateTokenResponse.java
new file mode 100644
index 0000000..bb8a28e
--- /dev/null
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/domain/ValidateTokenResponse.java
@@ -0,0 +1,85 @@
+package com.codeyapa.rest.secured.core.domain;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Optional;
+
+import static java.time.Instant.ofEpochSecond;
+
+@JsonInclude(JsonInclude.Include.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+@NoArgsConstructor
+@AllArgsConstructor
+@Getter
+@Setter
+@Slf4j
+public class ValidateTokenResponse {
+ @JsonProperty("active")
+ private boolean active;
+
+ @JsonProperty("jti")
+ private String tokenId;
+
+ @JsonProperty("iss")
+ private String issuer;
+
+ @JsonProperty("sub")
+ private String subject;
+
+ @JsonProperty("aud")
+ private Collection audience;
+
+ @JsonProperty("iat")
+ private Long issuedAt;
+
+ @JsonProperty("nbf")
+ private Long notBefore;
+
+ @JsonProperty("exp")
+ private Long expirationTime;
+
+ @JsonProperty("scope")
+ private String scope;
+
+ @JsonProperty("confidence_level")
+ private Integer confidenceLevel;
+
+ @JsonProperty("client_id")
+ private String clientId;
+
+ @JsonProperty("token_type")
+ private String tokenType;
+
+ public Optional toToken() {
+ if (!active || subject == null) {
+ return Optional.empty();
+ }
+ try {
+ return Optional.of(
+ Token.builder()
+ .userId(subject)
+ .clientId(clientId)
+ .issuedAt(ofEpochSecond(issuedAt))
+ .expiresAt(ofEpochSecond(expirationTime))
+ .validFrom(ofEpochSecond(notBefore))
+ .confidenceLevel(confidenceLevel)
+ .audience(audience == null ? Collections.emptySet() : new HashSet<>(audience))
+ .scope(scope)
+ .tokenId(tokenId)
+ .build());
+ } catch (IllegalArgumentException | NullPointerException e) {
+ log.error("The token could not be parsed as a valid JWT.", e);
+ return Optional.empty();
+ }
+ }
+}
diff --git a/core/src/main/java/com/codeyapa/rest/secured/core/properties/SecurityProperties.java b/core/src/main/java/com/codeyapa/rest/secured/core/properties/SecurityProperties.java
index bed9e45..e73ab0e 100644
--- a/core/src/main/java/com/codeyapa/rest/secured/core/properties/SecurityProperties.java
+++ b/core/src/main/java/com/codeyapa/rest/secured/core/properties/SecurityProperties.java
@@ -1,8 +1,12 @@
package com.codeyapa.rest.secured.core.properties;
+import lombok.Getter;
+import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "rest.security")
+@Getter
+@Setter
public class SecurityProperties {
private boolean enabled = true;
}
diff --git a/pom.xml b/pom.xml
index ba4273b..a38773d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,6 +23,11 @@
2.1.6.RELEASE
0.8.4
8.14.1
+ 1.16.16
+ jacoco
+ reuseReports
+ ${project.build.directory}/jacoco.exec
+ 5.5.1
@@ -40,7 +45,14 @@
nimbus-jose-jwt
${nimbus-jose-jwt.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+