Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrading Spring Boot to 1.1.3 #55

Merged
merged 3 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
20 changes: 11 additions & 9 deletions jwt-opa/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@
*/

plugins {
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'io.spring.dependency-management' version '1.1.3'
id 'java'
id 'jacoco'
id 'org.springframework.boot' version '2.5.7'
id 'org.springframework.boot' version '3.1.5'

// To upload the Artifact to Maven Central
// See: https://docs.gradle.org/current/userguide/publishing_maven.html
Expand All @@ -38,7 +38,7 @@ ext {
}

group 'com.alertavert'
version '0.9.0'
version '0.10.0'

// OpenJDK 17 LTS is the only Java version supported
sourceCompatibility = JavaVersion.VERSION_17
Expand Down Expand Up @@ -78,24 +78,27 @@ dependencies {
// See: https://stackoverflow.com/questions/29805622/could-not-find-or-load-main-class-org-gradle-wrapper-gradlewrappermain/31622432
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'

annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
compileOnly "org.projectlombok:lombok:${lombokVersion}"

implementation 'com.auth0:java-jwt:3.10.3'
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
implementation 'commons-codec:commons-codec:1.13'

testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}

annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-webflux'

// AWS SDK for Secrets Manager, see: https://docs.aws.amazon.com/code-samples/latest/catalog/code-catalog-javav2-example_code-secretsmanager.html
implementation "software.amazon.awssdk:secretsmanager:${awsSdkVersion}"

// For the @PostConstruct annotation
implementation 'javax.annotation:javax.annotation-api:1.3.2'

testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}

testImplementation "com.jayway.jsonpath:json-path-assert:$jsonpathVersion"
testImplementation "org.mockito:mockito-core:$mockitoVersion"

Expand All @@ -104,7 +107,6 @@ dependencies {
testImplementation "org.testcontainers:junit-jupiter:$tcVersion"
testImplementation "org.testcontainers:localstack:$tcVersion"
testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.12.326'

}

jacocoTestCoverageVerification {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@
@Service @Slf4j
public class ApiTokenAuthenticationFactory {

@Autowired
JwtTokenProvider provider;
private final JwtTokenProvider provider;

public ApiTokenAuthenticationFactory(JwtTokenProvider provider) {
this.provider = provider;
}

/**
* Creates an implementation of the {@link Authentication} interface which implements the
Expand All @@ -62,7 +65,7 @@ public Mono<Authentication> createAuthentication(String token) {
log.debug("Authenticating token {}...", token.substring(0, Math.min(MAX_TOKEN_LEN_LOG, token.length())));
try {
DecodedJWT jwt = provider.decode(token);
List<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(
List<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(
jwt.getClaim(JwtTokenProvider.ROLES).asArray(String.class));
String subject = jwt.getSubject();

Expand All @@ -72,6 +75,12 @@ public Mono<Authentication> createAuthentication(String token) {
} catch (JWTVerificationException exception) {
log.warn("Cannot validate API Token: {}", exception.getMessage());
return Mono.error(new BadCredentialsException("API Token invalid", exception));
} catch (IllegalArgumentException exception) {
log.warn("The Token is malformed: {}", exception.getMessage());
return Mono.error(new BadCredentialsException("API Token malformed", exception));
} catch (Exception ex) {
log.error("Unexpected error while validating token: {}", ex.getMessage());
return Mono.error(new BadCredentialsException("API Token malformed"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public Mono<AuthorizationDecision> check(
String path = request.getPath().toString();
for (String pattern : authRoutes) {
if (pathMatcher.match(pattern, path)) {
log.debug("Route is allowed to bypass authorization");
log.debug("Route {} is allowed to bypass authorization (matches: {})", path, pattern);
return Mono.just(new AuthorizationDecision(true));
}
}
Expand Down
16 changes: 12 additions & 4 deletions webapp-example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@
plugins {
id 'java'
id 'jacoco'
id 'org.springframework.boot' version '2.5.7'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'org.springframework.boot' version '3.1.5'
id 'io.spring.dependency-management' version '1.1.3'
}

group 'com.alertavert.opademo'
version = "0.3.0"
version = "0.4.0"

repositories {
// Adding local repository for Gradle to find jwt-opa before it gets published.
Expand All @@ -36,7 +36,7 @@ repositories {
ext {
// This can be changed to an yet-unpublished version by using mavenLocal()
// for local tests.
jwtOpaVersion = "0.9.0"
jwtOpaVersion = "0.10.0"
lombokVersion = "1.18.22"
tcVersion = "1.15.1"
}
Expand All @@ -48,9 +48,14 @@ bootJar {
dependencies {
// We use the actual dependency here, instead of depending on the module in the repository so
// as to emulate an actual project using jwt-opa externally.
// Uncomment the following line (and comment out the one below) to use the local version
// while developing.
implementation project (':jwt-opa')
// implementation "com.alertavert:jwt-opa:${jwtOpaVersion}"

// For the @PostConstruct annotation
implementation 'javax.annotation:javax.annotation-api:1.3.2'

compileOnly "org.projectlombok:lombok:${lombokVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"

Expand All @@ -64,6 +69,9 @@ dependencies {
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'
implementation 'commons-codec:commons-codec:1.13'

// For the @PostConstruct annotation
implementation 'javax.annotation:javax.annotation-api:1.3.2'

// Swagger 2 API & UI
// "Raw" JSON at http://localhost:8081/v2/api-docs
// UI at http://localhost:8081/swagger-ui/ (trailing slash matters)
Expand Down
15 changes: 8 additions & 7 deletions webapp-example/src/main/java/com/alertavert/opademo/DbInit.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
import com.alertavert.opademo.api.UserController;
import com.alertavert.opademo.data.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
Expand All @@ -35,19 +33,21 @@
/**
* Initializes the DB with a seed `admin` user and a random password, if it doesn't already exist.
*/
@Profile("debug")
@Slf4j
@Component
public class DbInit {
@Autowired
UserController controller;
private final UserController controller;

@Value("${db.admin.username:admin}")
String adminUsername;

@Value("${db.admin.password}")
String adminPassword;

public DbInit(UserController controller) {
this.controller = controller;
}


@PostConstruct
public void initDb() {
Expand All @@ -57,7 +57,7 @@ public void initDb() {
adminUsername, adminPassword);
}
User admin = new User(adminUsername, adminPassword, "SYSTEM");

log.info("Creating admin user: {}", adminUsername);
controller.create(admin)
.doOnSuccess(responseEntity -> {
if (!responseEntity.getStatusCode().equals(HttpStatus.CREATED)) {
Expand All @@ -68,14 +68,15 @@ public void initDb() {
}
})
.doOnError(ResponseStatusException.class, ex -> {
if (ex.getStatus().equals(HttpStatus.CONFLICT)) {
if (ex.getStatusCode().equals(HttpStatus.CONFLICT)) {
log.info("User [{}] already exists in database, use existing credentials",
adminUsername);
} else {
log.error("Unexpected error when creating SYSTEM user", ex);
System.exit(1);
}
})
.onErrorComplete()
.subscribe();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.jackson.Jacksonized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
Expand Down Expand Up @@ -54,14 +56,7 @@ public JwtController(JwtTokenProvider provider, ReactiveUsersRepository reposito
this.repository = repository;
}

@Data
@AllArgsConstructor
static class ApiToken {
String username;
List<String> roles;
@JsonProperty(API_TOKEN)
String apiToken;
}
record ApiToken(String username, List<String> roles, @JsonProperty(API_TOKEN) String apiToken) { }

@GetMapping(path = "/token/{user}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<ApiToken>> getToken(@PathVariable String user) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@

package com.alertavert.opademo.api;

import com.alertavert.opademo.DbInit;
import com.alertavert.opademo.data.ReactiveUsersRepository;
import com.alertavert.opa.jwt.JwtTokenProvider;
import com.alertavert.opademo.data.ReactiveUsersRepository;
import com.alertavert.opademo.data.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.Base64Utils;
import org.springframework.util.MimeTypeUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand All @@ -38,6 +34,7 @@
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.UUID;

import static com.alertavert.opa.Constants.BASIC_AUTH;
Expand All @@ -56,14 +53,13 @@
consumes = MimeTypeUtils.ALL_VALUE)
public class LoginController {

@Autowired
JwtTokenProvider provider;
private final JwtTokenProvider provider;
private final ReactiveUsersRepository repository;

@Autowired
ReactiveUsersRepository repository;

@Autowired
PasswordEncoder encoder;
public LoginController(JwtTokenProvider provider, ReactiveUsersRepository repository) {
this.provider = provider;
this.repository = repository;
}


@GetMapping
Expand All @@ -82,7 +78,7 @@ Mono<JwtController.ApiToken> login(
})
.doOnNext(apiToken ->
log.debug("User authenticated, user = {}, token = {}...",
apiToken.getUsername(), apiToken.getApiToken().substring(0, MAX_TOKEN_LEN_LOG)));
apiToken.username(), apiToken.apiToken().substring(0, MAX_TOKEN_LEN_LOG)));
}

@GetMapping("/reset/{username}")
Expand Down Expand Up @@ -114,7 +110,7 @@ public static Mono<String> usernameFromHeader(String credentials) {
log.debug("Extracting username from Authorization header");
if (credentials.startsWith(BASIC_AUTH)) {
return Mono.just(credentials.substring(BASIC_AUTH.length() + 1))
.map(enc -> Base64Utils.decode(enc.getBytes(StandardCharsets.UTF_8)))
.map(enc -> Base64.getDecoder().decode(enc.getBytes(StandardCharsets.UTF_8)))
.map(String::new)
.map(creds -> {
String[] userPass = creds.split(":");
Expand All @@ -126,7 +122,7 @@ public static Mono<String> usernameFromHeader(String credentials) {
}

public static Mono<String> credentialsToHeader(String credentials) {
String encoded = Base64Utils.encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
String encoded = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
return Mono.just(String.format("%s %s", BASIC_AUTH, encoded));
}
}
4 changes: 2 additions & 2 deletions webapp-example/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ keys:
# For a PASSPHRASE, the secret is simply read from SecretsManager/Vault
# The keypair is stored as a JSON-formatted secret, with two keys: "priv" and "pub".
location: keypair
name: ../private/ec-key
name: private/ec-key

logging:
level:
Expand Down Expand Up @@ -131,7 +131,7 @@ routes:
- "/health"
- "/demo"
- "/favicon.ico"
- "/login/reset/*"
#- "/login/reset/*"

# These will require the user to authenticate, but will not
# be subject to OPA Policies authorization check.
Expand Down
Loading
Loading