Skip to content

Commit

Permalink
Jwt improvements & Updates to support latest dev release of GraalVM n…
Browse files Browse the repository at this point in the history
…ative image (#8874)

* Updates to native image configuration to support latest release of GraalVM. (#8838)

* Jwt improvements (#8865)

Signed-off-by: David Kral <[email protected]>

* Update changelog to document 8838 and 8865

---------

Signed-off-by: David Kral <[email protected]>
Co-authored-by: Tomas Langer <[email protected]>
Co-authored-by: David Král <[email protected]>
  • Loading branch information
3 people authored Jun 12, 2024
1 parent c8306d1 commit 963c07d
Show file tree
Hide file tree
Showing 32 changed files with 2,219 additions and 154 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Java 21 is required to use Helidon 4.0.10.
- WebServer: Fix handling of invalid end of line in HTTP header parsing. Added tests [8843](https://github.com/helidon-io/helidon/pull/8843)
- WebServer: Retrieve the correct requested URI info path value, indpt of the routing path used to locate the handler [8844](https://github.com/helidon-io/helidon/pull/8844)
- WebServer: register routing in weighted order of Server and HTTP Features [8840](https://github.com/helidon-io/helidon/pull/8840)
- Native Image: Updates to support latest dev release of GraalVM native image [8838](https://github.com/helidon-io/helidon/pull/8838)
- Security: JWT improvements [8865](https://github.com/helidon-io/helidon/pull/8865)

## [4.0.9]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#
# Copyright (c) 2024 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

Args=--initialize-at-build-time=io.helidon.common.configurable.LruCache \
--initialize-at-build-time=io.helidon.common.configurable.LruCacheConfig
6 changes: 5 additions & 1 deletion etc/checkstyle-suppressions.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!--
Copyright (c) 2016, 2023 Oracle and/or its affiliates.
Copyright (c) 2016, 2024 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,6 +43,10 @@ record here.
<suppress files="integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaExtension\.java"
checks="FileLength"/>

<!-- This can be removed once Jwt class has all Validators removed. They are deprecated now -->
<suppress files="security/jwt/src/main/java/io/helidon/security/jwt/Jwt\.java"
checks="FileLength"/>

<!--
- Module exclusions
-->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2019, 2023 Oracle and/or its affiliates.
# Copyright (c) 2019, 2024 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -40,7 +40,12 @@ Args=--features=io.helidon.integrations.graal.mp.nativeimage.extension.HelidonMp
--initialize-at-build-time=jakarta.interceptor \
--initialize-at-build-time=jakarta.annotation \
--initialize-at-build-time=com.sun.beans.TypeResolver \
--initialize-at-build-time=com.sun.beans.WeakCache \
--initialize-at-build-time=java.beans.PropertyDescriptor \
--initialize-at-build-time=java.beans.MethodRef \
--initialize-at-build-time=org.yaml.snakeyaml \
--initialize-at-build-time=org.jvnet.hk2 \
--initialize-at-build-time=java.lang.annotation.Annotation \
--initialize-at-build-time=io.helidon \
--initialize-at-build-time=org.eclipse.microprofile \
--report-unsupported-elements-at-runtime
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#
# Copyright (c) 2024 Oracle and/or its affiliates.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

Args=--initialize-at-build-time=io.helidon.logging.jul.JulMdcPropagator
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import io.helidon.security.jwt.Jwt;
import io.helidon.security.jwt.JwtException;
import io.helidon.security.jwt.JwtHeaders;
import io.helidon.security.jwt.JwtValidator;
import io.helidon.security.jwt.SignedJwt;
import io.helidon.security.jwt.Validator;
import io.helidon.security.jwt.jwk.Jwk;
Expand Down Expand Up @@ -278,27 +279,27 @@ AuthenticationResponse authenticate(ProviderRequest providerRequest, LoginConfig
Errors errors = signedJwt.verifySignature(verifyKeys.get(), defaultJwk.get());
if (errors.isValid()) {
Jwt jwt = signedJwt.getJwt();

List<Validator<Jwt>> validators = new LinkedList<>();
JwtValidator.Builder valBuilder = JwtValidator.builder();
if (expectedIssuer != null) {
// validate issuer
Jwt.addIssuerValidator(validators, expectedIssuer, true);
valBuilder.addIssuerValidator(expectedIssuer);
}
if (expectedAudiences.size() > 0) {
if (!expectedAudiences.isEmpty()) {
// validate audience(s)
Jwt.addAudienceValidator(validators, expectedAudiences, true);
valBuilder.addAudienceValidator(expectedAudiences);
}
// validate user principal is present
Jwt.addUserPrincipalValidator(validators);
validators.add(Jwt.ExpirationValidator.create(Instant.now(),
(int) clockSkew.getSeconds(),
ChronoUnit.SECONDS,
true));
valBuilder.addUserPrincipalValidator()
.addExpirationValidator(builder -> builder.now(Instant.now())
.allowedTimeSkew(clockSkew)
.mandatory(true));
if (expectedMaxTokenAge != null) {
Jwt.addMaxTokenAgeValidator(validators, expectedMaxTokenAge, clockSkew, true);
valBuilder.addMaxTokenAgeValidator(builder -> builder.expectedMaxTokenAge(expectedMaxTokenAge)
.allowedTimeSkew(clockSkew)
.mandatory(true));
}

Errors validate = jwt.validate(validators);
JwtValidator jwtValidator = valBuilder.build();
Errors validate = jwtValidator.validate(jwt);

if (validate.isValid()) {
return AuthenticationResponse.success(buildSubject(jwt, signedJwt));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.security.jwt;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import io.helidon.common.Errors;

/**
* Audience claim validator.
*/
public final class AudienceValidator extends OptionalValidator {
private final Set<String> expectedAudience;

private AudienceValidator(Builder builder) {
super(builder);
this.expectedAudience = Set.copyOf(builder.expectedAudience);
}

/**
* Return a new Builder instance.
*
* @return new builder instance
*/
public static Builder builder() {
return new Builder()
.addClaim(Jwt.AUDIENCE)
.mandatory(true);
}

@Override
public void validate(Jwt jwt, Errors.Collector collector, List<ClaimValidator> validators) {
Optional<List<String>> jwtAudiences = jwt.audience();
jwtAudiences.ifPresent(jwtAudience -> {
if (expectedAudience.stream().anyMatch(jwtAudiences.get()::contains)) {
return;
}
collector.fatal(jwt, "Audience must contain " + expectedAudience + ", yet it is: " + jwtAudiences);
});
super.validate(Jwt.AUDIENCE, jwtAudiences, collector);
}

/**
* Builder of the {@link AudienceValidator}.
*/
public static final class Builder extends OptionalValidator.BaseBuilder<Builder, AudienceValidator> {

private Set<String> expectedAudience = new HashSet<>();

private Builder() {
}

@Override
public AudienceValidator build() {
return new AudienceValidator(this);
}

/**
* Add expected audience value.
*
* @param audience expected audience
* @return updated builder instance
*/
public Builder addExpectedAudience(String audience) {
Objects.requireNonNull(audience);
expectedAudience.add(audience);
return this;
}

/**
* Overwrite previously set audience with the new {@link Set} of values.
*
* @param expectedAudience expected audience values
* @return updated builder instance
*/
public Builder expectedAudience(Set<String> expectedAudience) {
Objects.requireNonNull(expectedAudience);
this.expectedAudience = new HashSet<>(expectedAudience);
return this;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright (c) 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.helidon.security.jwt;

import java.util.List;
import java.util.Set;

import io.helidon.common.Errors;

/**
* JWT claim validator.
*/
public interface ClaimValidator {

/**
* Scope of the JWT claims.
*
* @return JWT claim scope
*/
JwtScope jwtScope();

/**
* Handled JWT claims.
*
* @return claims
*/
Set<String> claims();

/**
* Validate JWT against this class's configuration.
*
* @param jwt jwt to validate
* @param collector collector of error messages to add problems to. Use {@link Errors.Collector#fatal(Object, String)}
* to mark the validation as a failure
* @param validators immutable list of all currently processed claim validators
*/
void validate(Jwt jwt, Errors.Collector collector, List<ClaimValidator> validators);

}
Loading

0 comments on commit 963c07d

Please sign in to comment.