From f9ad463eabe9e3e39695e2dd2f5f1367550e2be7 Mon Sep 17 00:00:00 2001 From: jan-olaveide <80393418+jan-olaveide@users.noreply.github.com> Date: Mon, 17 May 2021 08:53:24 +0200 Subject: [PATCH] Token x (#808) --- .../pdl/OnBehalfOfJerseyPdlKlient.java | 9 ++- .../integrasjon/pdl/TestJerseyPdlClient.java | 12 ++- integrasjon/rest-klient/pom.xml | 17 +++- .../integrasjon/graphql/GraphQLException.java | 3 +- .../jersey/OnBehalfOfTokenRequestFilter.java | 27 ------- .../jersey/StsAccessTokenJerseyClient.java | 5 +- .../tokenx/TokenXAssertionGenerator.java | 57 +++++++++++++ .../rest/jersey/tokenx/TokenXAudience.java | 23 ++++++ .../tokenx/TokenXAudienceGenerator.java | 37 +++++++++ .../rest/jersey/tokenx/TokenXClient.java | 7 ++ .../rest/jersey/tokenx/TokenXConfig.java | 32 ++++++++ .../jersey/tokenx/TokenXConfigMetadata.java | 8 ++ .../jersey/tokenx/TokenXJerseyClient.java | 61 ++++++++++++++ .../jersey/tokenx/TokenXRequestFilter.java | 81 +++++++++++++++++++ .../rest/jersey/tokenx/TokenXResponse.java | 10 +++ .../rest/jersey/tokenx/TestTokenX.java | 19 +++++ 16 files changed, 371 insertions(+), 37 deletions(-) delete mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java create mode 100644 integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java create mode 100644 integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java diff --git a/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java b/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java index 689f7bbd0..939587cf9 100644 --- a/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java +++ b/integrasjon/pdl-klient/src/main/java/no/nav/vedtak/felles/integrasjon/pdl/OnBehalfOfJerseyPdlKlient.java @@ -7,7 +7,7 @@ import no.nav.foreldrepenger.konfig.KonfigVerdi; import no.nav.vedtak.felles.integrasjon.rest.jersey.Jersey; -import no.nav.vedtak.felles.integrasjon.rest.jersey.OnBehalfOfTokenRequestFilter; +import no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx.TokenXRequestFilter; @Dependent @Jersey("onbehalf") @@ -17,6 +17,11 @@ public class OnBehalfOfJerseyPdlKlient extends AbstractJerseyPdlKlient { public OnBehalfOfJerseyPdlKlient( @KonfigVerdi(value = "pdl.base.url", defaultVerdi = HTTP_PDL_API_DEFAULT_GRAPHQL) URI endpoint, @KonfigVerdi(value = "pdl.tema", defaultVerdi = FOR) String tema) { - super(endpoint, new OnBehalfOfTokenRequestFilter(tema)); + this(endpoint, new TokenXRequestFilter(tema)); } + + OnBehalfOfJerseyPdlKlient(URI endpoint, TokenXRequestFilter tokenXTokenRequestFilter) { + super(endpoint, tokenXTokenRequestFilter); + } + } diff --git a/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java b/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java index 0013705c3..ac19e5716 100644 --- a/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java +++ b/integrasjon/pdl-klient/src/test/java/no/nav/vedtak/felles/integrasjon/pdl/TestJerseyPdlClient.java @@ -46,6 +46,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; @@ -68,6 +69,8 @@ import no.nav.vedtak.exception.TekniskException; import no.nav.vedtak.felles.integrasjon.rest.jersey.StsAccessTokenClientRequestFilter; import no.nav.vedtak.felles.integrasjon.rest.jersey.StsAccessTokenJerseyClient; +import no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx.TokenXClient; +import no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx.TokenXRequestFilter; import no.nav.vedtak.sikkerhet.context.SubjectHandler; import no.nav.vedtak.sikkerhet.domene.SAMLAssertionCredential; @@ -84,6 +87,9 @@ class TestJerseyPdlClient { private Pdl legacyClient; private Pdl tokenXClient; + @Mock + TokenXClient client; + @Mock private StsAccessTokenJerseyClient sts; @Mock @@ -112,9 +118,8 @@ static void stopServer() { @BeforeEach void beforeEach() throws Exception { - when(sts.getUsername()).thenReturn(USERNAME); legacyClient = new JerseyPdlKlient(URI, new StsAccessTokenClientRequestFilter(sts, FOR, cache)); - tokenXClient = new OnBehalfOfJerseyPdlKlient(URI, FOR); + tokenXClient = new OnBehalfOfJerseyPdlKlient(URI, new TokenXRequestFilter(FOR, client)); } @Test @@ -135,9 +140,9 @@ void testErrorHandler() throws Exception { @Test @DisplayName("Test at kun Authorization og Tema blir satt for tokenX token") void testPersonAuthWithUserToken() throws Exception { - when(sts.accessToken()).thenReturn(SYSTEMTOKEN); doReturn(TOKENXTOKEN).when(subjectHandler).getInternSsoToken(); try (var s = mockStatic(SubjectHandler.class)) { + when(client.exchange(Mockito.any(), Mockito.any())).thenReturn(TOKENXTOKEN); s.when(SubjectHandler::getSubjectHandler).thenReturn(subjectHandler); stubFor(post(urlPathEqualTo(GRAPHQL)) .withHeader(ACCEPT, equalTo(APPLICATION_JSON)) @@ -152,7 +157,6 @@ void testPersonAuthWithUserToken() throws Exception { res = legacyClient.hentPerson(pq(), pp()); assertNotNull(res.getNavn()); assertNotNull(res.getNavn().get(0).getFornavn()); - verify(sts).accessToken(); } } diff --git a/integrasjon/rest-klient/pom.xml b/integrasjon/rest-klient/pom.xml index e3074880e..3fe6dbf47 100644 --- a/integrasjon/rest-klient/pom.xml +++ b/integrasjon/rest-klient/pom.xml @@ -18,10 +18,15 @@ ${jersey.version} pom import - + + + no.nav.foreldrepenger + konfig + 1.1.2 + io.github.kobylynskyi graphql-java-codegen @@ -103,5 +108,15 @@ org.slf4j jul-to-slf4j + + no.nav.security + token-client-core + 1.3.7 + + + com.nimbusds + nimbus-jose-jwt + 9.9.3 + diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java index 1586fe74e..a1ca0e79c 100644 --- a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/graphql/GraphQLException.java @@ -1,5 +1,6 @@ package no.nav.vedtak.felles.integrasjon.graphql; +import static java.lang.String.format; import static java.util.stream.Collectors.joining; import java.net.URI; @@ -12,7 +13,7 @@ public class GraphQLException extends IntegrasjonException { public GraphQLException(String kode, List errors, URI uri) { - super(kode, String.format("Feil %s ved GraphQL oppslag mot %s", errors.stream() + super(kode, format("Feil %s ved GraphQL oppslag mot %s", errors.stream() .map(GraphQLError::getMessage) .collect(joining(",")), uri)); } diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java deleted file mode 100644 index bfd17ed60..000000000 --- a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/OnBehalfOfTokenRequestFilter.java +++ /dev/null @@ -1,27 +0,0 @@ -package no.nav.vedtak.felles.integrasjon.rest.jersey; - -import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION; -import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.OIDC_AUTH_HEADER_PREFIX; -import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.TEMA; - -import javax.ws.rs.client.ClientRequestContext; - -/** - * Dette filteret skal brukes når man vet man mottar et token som støtter - * on-behalf-of, og når mottakende system krever kun dette - * - */ -public class OnBehalfOfTokenRequestFilter extends OidcTokenRequestFilter { - - private final String tema; - - public OnBehalfOfTokenRequestFilter(String tema) { - this.tema = tema; - } - - @Override - public void filter(ClientRequestContext ctx) { - ctx.getHeaders().add(TEMA, tema); - ctx.getHeaders().add(AUTHORIZATION, OIDC_AUTH_HEADER_PREFIX + accessToken()); - } -} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java index a8fb76a42..01ab0782b 100644 --- a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/StsAccessTokenJerseyClient.java @@ -35,7 +35,8 @@ public String accessToken() { }).get("access_token"); } - public String getUsername() { - return cfg.getUsername(); + @Override + public String toString() { + return getClass().getSimpleName() + " [cfg=" + cfg + "]"; } } diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java new file mode 100644 index 000000000..25e8ebf35 --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAssertionGenerator.java @@ -0,0 +1,57 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import static com.nimbusds.jose.JOSEObjectType.JWT; +import static com.nimbusds.jose.JWSAlgorithm.RS256; + +import java.time.Instant; +import java.util.Date; +import java.util.UUID; + +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.jwk.RSAKey; +import com.nimbusds.jwt.JWTClaimsSet; +import com.nimbusds.jwt.SignedJWT; + +class TokenXAssertionGenerator { + + private final TokenXConfig cfg; + private final TokenXConfigMetadata metadata; + + TokenXAssertionGenerator(TokenXConfig cfg, TokenXConfigMetadata metadata) { + this.cfg = cfg; + this.metadata = metadata; + } + + public String assertion() { + var now = Date.from(Instant.now()); + try { + return sign(new JWTClaimsSet.Builder() + .subject(cfg.clientId()) + .issuer(cfg.clientId()) + .audience(metadata.tokenEndpoint().toString()) + .issueTime(now) + .notBeforeTime(now) + .expirationTime(Date.from(Instant.now().plusSeconds(60))) + .jwtID(UUID.randomUUID().toString()) + .build(), cfg.rsaKey()) + .serialize(); + } catch (JOSEException e) { + throw new IllegalArgumentException(e); + } + } + + private static SignedJWT sign(JWTClaimsSet claimsSet, RSAKey rsaKey) throws JOSEException { + var signedJWT = new SignedJWT(new JWSHeader.Builder(RS256) + .keyID(rsaKey.getKeyID()) + .type(JWT).build(), claimsSet); + signedJWT.sign(new RSASSASigner(rsaKey.toPrivateKey())); + return signedJWT; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [cfg=" + cfg + ", metadata=" + metadata + "]"; + } +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java new file mode 100644 index 000000000..9635576ac --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudience.java @@ -0,0 +1,23 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import java.util.StringJoiner; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import no.nav.foreldrepenger.konfig.Cluster; +import no.nav.foreldrepenger.konfig.Namespace; + +public record TokenXAudience(Cluster cluster, Namespace namespace, String app) { + + private static final Logger LOG = LoggerFactory.getLogger(TokenXAudience.class); + public String asAudience() { + var joiner = new StringJoiner(":"); + joiner.add(cluster.clusterName()); + joiner.add(namespace.getName()); + joiner.add(app); + var audience = joiner.toString(); + LOG.trace("Audience er {}", audience); + return audience; + } +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java new file mode 100644 index 000000000..f840269ed --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXAudienceGenerator.java @@ -0,0 +1,37 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import java.net.URI; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import lombok.val; +import no.nav.foreldrepenger.konfig.Cluster; +import no.nav.foreldrepenger.konfig.Environment; +import no.nav.foreldrepenger.konfig.Namespace; + +public class TokenXAudienceGenerator { + private static final Logger LOG = LoggerFactory.getLogger(TokenXAudienceGenerator.class); + private static final Environment ENV = Environment.current(); + + public TokenXAudience audience(URI uri) { + LOG.trace("Utleder audience for {}", uri); + String host = uri.getHost(); + val elems = host.split("\\."); + + if (elems.length == 1) { + return new TokenXAudience(cluster(host), ENV.getNamespace(), elems[0]); + } + if (elems.length == 2) { + return new TokenXAudience(cluster(host), Namespace.of(elems[1]), elems[0]); + } + throw new IllegalArgumentException("Kan ikke analysere " + host + "(" + elems.length + ")"); + } + + private Cluster cluster(String key) { + return Optional.ofNullable(ENV.getProperty(key)) + .map(Cluster::of) + .orElseGet(() -> ENV.getCluster()); + } +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java new file mode 100644 index 000000000..6c61780ac --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXClient.java @@ -0,0 +1,7 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +public interface TokenXClient { + + String exchange(String token, TokenXAudience audience); + +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java new file mode 100644 index 000000000..5fab180a8 --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfig.java @@ -0,0 +1,32 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import java.net.URI; +import java.text.ParseException; + +import com.nimbusds.jose.jwk.RSAKey; + +import no.nav.foreldrepenger.konfig.Environment; + +public record TokenXConfig(URI wellKnownUrl, String clientId, String privateJwk) { + + private static final Environment ENV = Environment.current(); + + static TokenXConfig fraEnv() { + return new TokenXConfig(ENV.getRequiredProperty("token.x.well.known.url", URI.class), + ENV.getRequiredProperty("token.x.client.id"), + ENV.getRequiredProperty("token.x.private.jwk")); + } + + public RSAKey rsaKey() { + try { + return RSAKey.parse(privateJwk); + } catch (ParseException e) { + throw new IllegalArgumentException(e); + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [wellKnownUrl=" + wellKnownUrl + ",clientId=" + clientId + "]"; + } +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java new file mode 100644 index 000000000..d01595eea --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXConfigMetadata.java @@ -0,0 +1,8 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import com.fasterxml.jackson.annotation.JsonProperty; + +record TokenXConfigMetadata(String issuer, + @JsonProperty("token_endpoint") String tokenEndpoint, + @JsonProperty("jwks_uri") String jwksUri) { +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java new file mode 100644 index 000000000..86a16825c --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXJerseyClient.java @@ -0,0 +1,61 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import static com.nimbusds.oauth2.sdk.auth.JWTAuthentication.CLIENT_ASSERTION_TYPE; +import static javax.ws.rs.client.Entity.form; +import static javax.ws.rs.core.MediaType.APPLICATION_FORM_URLENCODED_TYPE; +import static javax.ws.rs.core.MediaType.APPLICATION_JSON_TYPE; + +import java.net.URI; + +import javax.ws.rs.core.Form; + +import no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient; + +public class TokenXJerseyClient extends AbstractJerseyRestClient implements TokenXClient { + + private final TokenXAssertionGenerator assertionGenerator; + private final TokenXConfigMetadata metadata; + + public TokenXJerseyClient() { + this(TokenXConfig.fraEnv()); + } + + public TokenXJerseyClient(TokenXConfig cfg) { + this.metadata = metadataFra(cfg.wellKnownUrl()); + this.assertionGenerator = new TokenXAssertionGenerator(cfg, metadata); + } + + TokenXJerseyClient(TokenXConfigMetadata metdata, TokenXAssertionGenerator assertionGenerator) { + this.metadata = metdata; + this.assertionGenerator = assertionGenerator; + } + + @Override + public String exchange(String token, TokenXAudience audience) { + var form = new Form() + .param("subject_token", token) + .param("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange") + .param("client_assertion_type", CLIENT_ASSERTION_TYPE) + .param("client_assertion", assertionGenerator.assertion()) + .param("subject_token_type", "urn:ietf:params:oauth:token-type:jwt") + .param("audience", audience.asAudience()); + + return client + .target(metadata.tokenEndpoint()) + .request(APPLICATION_FORM_URLENCODED_TYPE) + .post(form(form), TokenXResponse.class).accessToken(); + } + + private TokenXConfigMetadata metadataFra(URI uri) { + return client + .target(uri) + .request(APPLICATION_JSON_TYPE) + .get(TokenXConfigMetadata.class); + + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [assertionGenerator=" + assertionGenerator + ", metadata=" + metadata + "]"; + } +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java new file mode 100644 index 000000000..b999687fc --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXRequestFilter.java @@ -0,0 +1,81 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import static javax.ws.rs.core.HttpHeaders.AUTHORIZATION; +import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.OIDC_AUTH_HEADER_PREFIX; +import static no.nav.vedtak.felles.integrasjon.rest.jersey.AbstractJerseyRestClient.TEMA; +import static no.nav.vedtak.sikkerhet.context.SubjectHandler.getSubjectHandler; + +import java.net.URI; +import java.util.Optional; + +import javax.ws.rs.client.ClientRequestContext; +import javax.ws.rs.client.ClientRequestFilter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.nimbusds.jwt.SignedJWT; + +/** + * Dette filteret skal brukes når man vet man mottar et token som støtter + * on-behalf-of, og når mottakende system krever kun dette + * + */ +public class TokenXRequestFilter implements ClientRequestFilter { + private static final Logger LOG = LoggerFactory.getLogger(TokenXRequestFilter.class); + private final String tema; + private final TokenXClient client; + private final TokenXAudienceGenerator audienceGenerator; + + public TokenXRequestFilter() { + this("FOR"); + } + + public TokenXRequestFilter(String tema) { + this(tema, new TokenXJerseyClient()); + } + + public TokenXRequestFilter(String tema, TokenXClient client) { + this(tema, client, new TokenXAudienceGenerator()); + } + + public TokenXRequestFilter(String tema, TokenXClient client, TokenXAudienceGenerator audienceGenerator) { + this.tema = tema; + this.client = client; + this.audienceGenerator = audienceGenerator; + } + + @Override + public void filter(ClientRequestContext ctx) { + String token = originalToken(); + ctx.getHeaders().add(TEMA, tema); + if (isTokenXToken(token)) { + LOG.trace("Veksler tokenX token for {}", ctx.getUri()); + ctx.getHeaders().add(AUTHORIZATION, OIDC_AUTH_HEADER_PREFIX + client.exchange(token, audienceGenerator.audience(ctx.getUri()))); + } else { + LOG.warn("Dette er intet tokenX token, sender originalt token videre til {}", ctx.getUri()); + ctx.getHeaders().add(AUTHORIZATION, OIDC_AUTH_HEADER_PREFIX + token); + + } + } + + private String originalToken() { + return Optional.ofNullable(getSubjectHandler().getInternSsoToken()).orElseThrow(); + } + + private boolean isTokenXToken(String token) { + try { + return URI.create(SignedJWT.parse(token) + .getJWTClaimsSet().getIssuer()).getHost().contains("tokendings"); + + } catch (Exception e) { + LOG.warn("Kunne ikke sjekke token type", e); + return false; + } + } + + @Override + public String toString() { + return getClass().getSimpleName() + " [tema=" + tema + ", client=" + client + ", audienceGenerator=" + audienceGenerator + "]"; + } +} diff --git a/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java new file mode 100644 index 000000000..a8609b6e0 --- /dev/null +++ b/integrasjon/rest-klient/src/main/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TokenXResponse.java @@ -0,0 +1,10 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import com.fasterxml.jackson.annotation.JsonProperty; + +record TokenXResponse( + @JsonProperty("access_token") String accessToken, + @JsonProperty("issued_token_type") String issuedTokenType, + @JsonProperty("token_type") String tokenType, + @JsonProperty("expires_in") String expiresIn) { +} diff --git a/integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java b/integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java new file mode 100644 index 000000000..f3672a37f --- /dev/null +++ b/integrasjon/rest-klient/src/test/java/no/nav/vedtak/felles/integrasjon/rest/jersey/tokenx/TestTokenX.java @@ -0,0 +1,19 @@ +package no.nav.vedtak.felles.integrasjon.rest.jersey.tokenx; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.net.URI; + +import org.junit.jupiter.api.Test; + +class TestTokenX { + + @Test + void testAudience() { + System.setProperty("pdl-api.default", "dev-fss"); + var uri = URI.create("http://pdl-api.default/graphql"); + var aud = new TokenXAudienceGenerator().audience(uri); + assertEquals("dev-fss:default:pdl-api", aud.asAudience()); + } + +}