Search in sources :

Example 1 with IdentityProvider

use of org.dcache.gplazma.oidc.IdentityProvider in project dcache by dCache.

the class QueryUserInfoEndpoint method identityProviders.

private Collection<IdentityProvider> identityProviders(String token) throws AuthenticationException {
    if (JsonWebToken.isCompatibleFormat(token)) {
        try {
            JsonWebToken jwt = new JsonWebToken(token);
            Optional<String> iss = jwt.getPayloadString("iss");
            if (iss.isPresent()) {
                try {
                    URI issuer = new URI(iss.get());
                    IdentityProvider ip = providersByIssuer.get(issuer);
                    checkAuthentication(ip != null, "JWT with unknown \"iss\" claim");
                    LOG.debug("Discovered token is JWT issued by {}", ip.getName());
                    return Collections.singleton(ip);
                } catch (URISyntaxException e) {
                    LOG.debug("Bad \"iss\" claim \"{}\": {}", iss.get(), e.toString());
                    throw new AuthenticationException("Bad \"iss\" claim in JWT");
                }
            }
        } catch (IOException e) {
            LOG.debug("Failed to parse JWT: {}", e.toString());
            throw new AuthenticationException("Bad JWT");
        }
    }
    return providersByIssuer.values();
}
Also used : AuthenticationException(org.dcache.gplazma.AuthenticationException) IdentityProvider(org.dcache.gplazma.oidc.IdentityProvider) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) JsonWebToken(org.dcache.gplazma.util.JsonWebToken) URI(java.net.URI)

Example 2 with IdentityProvider

use of org.dcache.gplazma.oidc.IdentityProvider in project dcache by dCache.

the class QueryUserInfoEndpoint method extract.

@Override
public ExtractResult extract(String token) throws AuthenticationException {
    Stopwatch userinfoLookupTiming = Stopwatch.createStarted();
    List<LookupResult> allResults;
    try {
        allResults = userInfoCache.get(token);
    } catch (ExecutionException e) {
        Throwable cause = e.getCause();
        Throwables.throwIfInstanceOf(cause, AuthenticationException.class);
        Throwables.throwIfUnchecked(cause);
        if (cause instanceof InterruptedException) {
            throw new AuthenticationException("Shutting down");
        }
        throw new RuntimeException("Unexpected exception", e);
    }
    if (LOG.isDebugEnabled()) {
        LOG.debug("Doing user-info lookup against {} OPs took {}", allResults.size(), TimeUtils.describe(userinfoLookupTiming.elapsed()).orElse("no time"));
    }
    List<LookupResult> successfulResults = allResults.stream().filter(LookupResult::isSuccess).collect(Collectors.toList());
    if (successfulResults.isEmpty()) {
        if (allResults.size() == 1) {
            LookupResult result = allResults.get(0);
            throw new AuthenticationException("OpenId Validation failed for " + result.getIdentityProvider().getName() + ": " + result.getError());
        } else {
            String randomId = randomId();
            String errors = allResults.stream().map(r -> "[" + r.getIdentityProvider().getName() + ": " + r.getError() + "]").collect(Collectors.joining(", "));
            LOG.warn("OpenId Validation Failure ({}): {}", randomId, errors);
            throw new AuthenticationException("OpenId Validation Failed check [log entry #" + randomId + "]");
        }
    }
    if (successfulResults.size() > 1) {
        String names = successfulResults.stream().map(LookupResult::getIdentityProvider).map(IdentityProvider::getName).collect(Collectors.joining(", "));
        LOG.warn("Multiple OpenID-Connect endpoints accepted access token: {}", names);
        throw new AuthenticationException("Multiple OPs accepted token.");
    }
    var result = successfulResults.get(0);
    return new ExtractResult(result.getIdentityProvider(), result.getClaims());
}
Also used : LoadingCache(com.google.common.cache.LoadingCache) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) Stopwatch(com.google.common.base.Stopwatch) URISyntaxException(java.net.URISyntaxException) LoggerFactory(org.slf4j.LoggerFactory) ArrayList(java.util.ArrayList) ExtractResult(org.dcache.gplazma.oidc.ExtractResult) ListenableFutureTask(com.google.common.util.concurrent.ListenableFutureTask) Collectors.toMap(java.util.stream.Collectors.toMap) Duration(java.time.Duration) Map(java.util.Map) ThreadLocalRandom(java.util.concurrent.ThreadLocalRandom) Objects.requireNonNull(java.util.Objects.requireNonNull) JsonNode(com.fasterxml.jackson.databind.JsonNode) URI(java.net.URI) TimeUtils(org.dcache.util.TimeUtils) JsonHttpClient(org.dcache.gplazma.oidc.helpers.JsonHttpClient) ExecutorService(java.util.concurrent.ExecutorService) JsonNodeType(com.fasterxml.jackson.databind.node.JsonNodeType) Properties(java.util.Properties) Logger(org.slf4j.Logger) Preconditions.checkAuthentication(org.dcache.gplazma.util.Preconditions.checkAuthentication) PropertiesUtils.asInt(org.dcache.gplazma.oidc.PropertiesUtils.asInt) TokenProcessor(org.dcache.gplazma.oidc.TokenProcessor) Collection(java.util.Collection) JsonWebToken(org.dcache.gplazma.util.JsonWebToken) Throwables(com.google.common.base.Throwables) Set(java.util.Set) IOException(java.io.IOException) Streams(com.google.common.collect.Streams) Collectors(java.util.stream.Collectors) AuthenticationException(org.dcache.gplazma.AuthenticationException) CacheLoader(com.google.common.cache.CacheLoader) ExecutionException(java.util.concurrent.ExecutionException) TimeUnit(java.util.concurrent.TimeUnit) Futures(com.google.common.util.concurrent.Futures) Base64(java.util.Base64) List(java.util.List) ChronoUnit(java.time.temporal.ChronoUnit) Entry(java.util.Map.Entry) Optional(java.util.Optional) CacheBuilder(com.google.common.cache.CacheBuilder) Collections(java.util.Collections) IdentityProvider(org.dcache.gplazma.oidc.IdentityProvider) BoundedCachedExecutor(org.dcache.util.BoundedCachedExecutor) AuthenticationException(org.dcache.gplazma.AuthenticationException) Stopwatch(com.google.common.base.Stopwatch) ExecutionException(java.util.concurrent.ExecutionException) ExtractResult(org.dcache.gplazma.oidc.ExtractResult)

Example 3 with IdentityProvider

use of org.dcache.gplazma.oidc.IdentityProvider in project dcache by dCache.

the class IssuerTest method shouldReturnIdentityProvider.

@Test
public void shouldReturnIdentityProvider() throws Exception {
    given(aClient());
    given(anIp("EXAMPLE").withEndpoint("https://oidc.example.org/"));
    given(anIssuer().withoutHistory());
    IdentityProvider ip = issuer.getIdentityProvider();
    assertThat(ip, is(sameInstance(identityProvider)));
}
Also used : IdentityProvider(org.dcache.gplazma.oidc.IdentityProvider) Test(org.junit.Test)

Example 4 with IdentityProvider

use of org.dcache.gplazma.oidc.IdentityProvider in project dcache by dCache.

the class QueryUserInfoEndpointTest method shouldTargetSingleIpForJwt.

@Test
public void shouldTargetSingleIpForJwt() throws Exception {
    given(aClient().onGet("https://oidc.example.org/.well-known/openid-configuration").responds().withEntity("{\"userinfo_endpoint\": \"https://oidc.example.org/oauth\"}").onGet("https://oidc.example.org/oauth").responds().withEntity("{\"sub\": \"ae7fb9688e0683999d864ab5618b92b9\"," + "\"another_claim\": \"another value\"," + "\"null_claim\": null," + "\"number_claim\": 42," + "\"array_claim\": [ \"first\", \"second\"]}").onGet("https://other-oidc.example.com/.well-known/openid-configuration").responds().withEntity("{\"userinfo_endpoint\": \"https://other-oidc.example.com/userinfo\"}").onGet("https://other-oidc.example.com/userinfo").responds().withStatusCode(404).withoutEntity());
    given(aQueryUserInfoEndpoint().withIdentityProvider(new IdentityProvider("EXAMPLE-1", URI.create("https://oidc.example.org/"), IGNORE_ALL, client, CACHE_DURATION)).withIdentityProvider(new IdentityProvider("EXAMPLE-2", URI.create("https://other-oidc.example.com/"), IGNORE_ALL, client, CACHE_DURATION)));
    given(aJwt().withPayloadClaim("iss", "https://oidc.example.org/"));
    var result = processor.extract(jwt);
    assertThat(result.claims(), hasEntry("sub", jsonString("ae7fb9688e0683999d864ab5618b92b9")));
    assertThat(result.claims(), hasEntry("another_claim", jsonString("another value")));
    assertThat(result.claims(), hasEntry("null_claim", jsonNull()));
    assertThat(result.claims(), hasEntry("number_claim", jsonNumber(42)));
    assertThat(result.claims(), hasEntry("array_claim", jsonStringArray("first", "second")));
}
Also used : IdentityProvider(org.dcache.gplazma.oidc.IdentityProvider) Test(org.junit.Test)

Example 5 with IdentityProvider

use of org.dcache.gplazma.oidc.IdentityProvider in project dcache by dCache.

the class QueryUserInfoEndpointTest method shouldRejectTokenIfMultipleIpsReturnInformation.

@Test(expected = AuthenticationException.class)
public void shouldRejectTokenIfMultipleIpsReturnInformation() throws Exception {
    given(aClient().onGet("https://oidc.example.org/.well-known/openid-configuration").responds().withEntity("{\"userinfo_endpoint\": \"https://oidc.example.org/oauth\"}").onGet("https://oidc.example.org/oauth").responds().withEntity("{\"sub\": \"ae7fb9688e0683999d864ab5618b92b9\"}").onGet("https://other-oidc.example.com/.well-known/openid-configuration").responds().withEntity("{\"userinfo_endpoint\": \"https://other-oidc.example.com/userinfo\"}").onGet("https://other-oidc.example.com/userinfo").responds().withEntity("{\"sub\": \"another-identity\"}"));
    given(aQueryUserInfoEndpoint().withIdentityProvider(new IdentityProvider("EXAMPLE-1", URI.create("https://oidc.example.org/"), IGNORE_ALL, client, CACHE_DURATION)).withIdentityProvider(new IdentityProvider("EXAMPLE-2", URI.create("https://other-oidc.example.com/"), IGNORE_ALL, client, CACHE_DURATION)));
    processor.extract("an-access-token");
}
Also used : IdentityProvider(org.dcache.gplazma.oidc.IdentityProvider) Test(org.junit.Test)

Aggregations

IdentityProvider (org.dcache.gplazma.oidc.IdentityProvider)8 Test (org.junit.Test)6 IOException (java.io.IOException)2 URI (java.net.URI)2 URISyntaxException (java.net.URISyntaxException)2 AuthenticationException (org.dcache.gplazma.AuthenticationException)2 JsonWebToken (org.dcache.gplazma.util.JsonWebToken)2 JsonNode (com.fasterxml.jackson.databind.JsonNode)1 JsonNodeType (com.fasterxml.jackson.databind.node.JsonNodeType)1 Stopwatch (com.google.common.base.Stopwatch)1 Throwables (com.google.common.base.Throwables)1 CacheBuilder (com.google.common.cache.CacheBuilder)1 CacheLoader (com.google.common.cache.CacheLoader)1 LoadingCache (com.google.common.cache.LoadingCache)1 Streams (com.google.common.collect.Streams)1 Futures (com.google.common.util.concurrent.Futures)1 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)1 ListenableFutureTask (com.google.common.util.concurrent.ListenableFutureTask)1 Duration (java.time.Duration)1 ChronoUnit (java.time.temporal.ChronoUnit)1