-
Notifications
You must be signed in to change notification settings - Fork 824
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Created classes to fetch OIDC metadata
[#157639907] https://www.pivotaltracker.com/story/show/157639907 Signed-off-by: Bruce Ricard <bricard@pivotal.io> Co-authored-by: Bruce Ricard <bricard@pivotal.io>
- Loading branch information
1 parent
715d5f5
commit 80ff310
Showing
3 changed files
with
225 additions
and
0 deletions.
There are no files selected for viewing
63 changes: 63 additions & 0 deletions
63
server/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/OidcMetadata.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
package org.cloudfoundry.identity.uaa.provider.oauth; | ||
|
||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
import java.net.URL; | ||
|
||
@JsonIgnoreProperties(ignoreUnknown = true) | ||
public class OidcMetadata { | ||
@JsonProperty("authorization_endpoint") | ||
private URL authorizationEndpoint; | ||
|
||
@JsonProperty("userinfo_endpoint") | ||
private URL userinfoEndpoint; | ||
|
||
@JsonProperty("token_endpoint") | ||
private URL tokenEndpoint; | ||
|
||
@JsonProperty("jwks_uri") | ||
private URL jsonWebKeysUri; | ||
|
||
private String issuer; | ||
|
||
public URL getAuthorizationEndpoint() { | ||
return authorizationEndpoint; | ||
} | ||
|
||
public void setAuthorizationEndpoint(URL authorizationEndpoint) { | ||
this.authorizationEndpoint = authorizationEndpoint; | ||
} | ||
|
||
public URL getUserinfoEndpoint() { | ||
return userinfoEndpoint; | ||
} | ||
|
||
public void setUserinfoEndpoint(URL userinfoEndpoint) { | ||
this.userinfoEndpoint = userinfoEndpoint; | ||
} | ||
|
||
public URL getTokenEndpoint() { | ||
return tokenEndpoint; | ||
} | ||
|
||
public void setTokenEndpoint(URL tokenEndpoint) { | ||
this.tokenEndpoint = tokenEndpoint; | ||
} | ||
|
||
public URL getJsonWebKeysUri() { | ||
return jsonWebKeysUri; | ||
} | ||
|
||
public void setJsonWebKeysUri(URL jsonWebKeysUri) { | ||
this.jsonWebKeysUri = jsonWebKeysUri; | ||
} | ||
|
||
public String getIssuer() { | ||
return issuer; | ||
} | ||
|
||
public void setIssuer(String issuer) { | ||
this.issuer = issuer; | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
...er/src/main/java/org/cloudfoundry/identity/uaa/provider/oauth/OidcMetadataDiscoverer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
package org.cloudfoundry.identity.uaa.provider.oauth; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.cloudfoundry.identity.uaa.cache.UrlContentCache; | ||
import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.io.IOException; | ||
import java.net.URL; | ||
|
||
public class OidcMetadataDiscoverer { | ||
private final UrlContentCache contentCache; | ||
private final RestTemplate trustingRestTemplate; | ||
private final RestTemplate nonTrustingRestTemplate; | ||
|
||
public OidcMetadataDiscoverer(UrlContentCache contentCache, | ||
RestTemplate trustingRestTemplate, | ||
RestTemplate nonTrustingRestTemplate | ||
) { | ||
this.contentCache = contentCache; | ||
this.trustingRestTemplate = trustingRestTemplate; | ||
this.nonTrustingRestTemplate = nonTrustingRestTemplate; | ||
} | ||
|
||
public void performDiscoveryAndUpdateDefinition(OIDCIdentityProviderDefinition definition) throws IOException { | ||
if (shouldPerformDiscovery(definition)) { | ||
OidcMetadata oidcMetadata = | ||
performDiscovery(definition.getDiscoveryUrl(), definition.isSkipSslValidation()); | ||
|
||
updateIdpDefinition(definition, oidcMetadata); | ||
} | ||
} | ||
|
||
private OidcMetadata performDiscovery(URL discoveryUrl, boolean shouldDoSslValidation) throws IOException { | ||
byte[] rawContents; | ||
if (shouldDoSslValidation) { | ||
rawContents = contentCache.getUrlContent(discoveryUrl.toString(), trustingRestTemplate); | ||
} else { | ||
rawContents = contentCache.getUrlContent(discoveryUrl.toString(), nonTrustingRestTemplate); | ||
} | ||
return new ObjectMapper().readValue(rawContents, OidcMetadata.class); | ||
} | ||
|
||
private void updateIdpDefinition(OIDCIdentityProviderDefinition definition, OidcMetadata oidcMetadata) { | ||
definition.setAuthUrl(oidcMetadata.getAuthorizationEndpoint()); | ||
definition.setTokenUrl(oidcMetadata.getTokenEndpoint()); | ||
definition.setTokenKeyUrl(oidcMetadata.getJsonWebKeysUri()); | ||
definition.setUserInfoUrl(oidcMetadata.getUserinfoEndpoint()); | ||
definition.setIssuer(oidcMetadata.getIssuer()); | ||
} | ||
|
||
private boolean shouldPerformDiscovery(OIDCIdentityProviderDefinition definition) { | ||
return definition.getDiscoveryUrl() != null && !StringUtils.isBlank(definition.getDiscoveryUrl().toString()); | ||
} | ||
} |
106 changes: 106 additions & 0 deletions
106
...rc/test/java/org/cloudfoundry/identity/uaa/provider/oauth/OidcMetadataDiscovererTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package org.cloudfoundry.identity.uaa.provider.oauth; | ||
|
||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import org.cloudfoundry.identity.uaa.cache.ExpiringUrlCache; | ||
import org.cloudfoundry.identity.uaa.cache.UrlContentCache; | ||
import org.cloudfoundry.identity.uaa.provider.OIDCIdentityProviderDefinition; | ||
import org.cloudfoundry.identity.uaa.util.TimeServiceImpl; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.Answers; | ||
import org.springframework.web.client.RestTemplate; | ||
|
||
import java.io.IOException; | ||
import java.net.URI; | ||
import java.net.URL; | ||
import java.time.Duration; | ||
|
||
import static org.hamcrest.CoreMatchers.*; | ||
import static org.junit.Assert.assertThat; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
class OidcMetadataDiscovererTest { | ||
private OidcMetadataDiscoverer metadataDiscoverer; | ||
private UrlContentCache urlContentCache; | ||
private RestTemplate restTemplate; | ||
|
||
private OIDCIdentityProviderDefinition definition; | ||
|
||
private ObjectMapper objectMapper = new ObjectMapper(); | ||
|
||
@BeforeEach | ||
void setUp() { | ||
urlContentCache = new ExpiringUrlCache(Duration.ofMinutes(2), new TimeServiceImpl(), 2); | ||
restTemplate = mock(RestTemplate.class, Answers.RETURNS_DEEP_STUBS); | ||
|
||
metadataDiscoverer = new OidcMetadataDiscoverer(urlContentCache, restTemplate, restTemplate); | ||
} | ||
|
||
@Test | ||
public void withoutDiscoveryUrl_shouldNotPerformDiscovery() throws IOException { | ||
definition = new OIDCIdentityProviderDefinition(); | ||
definition.setAuthUrl(new URL("http://not.updated")); | ||
definition.setTokenUrl(new URL("http://not.updated")); | ||
|
||
metadataDiscoverer.performDiscoveryAndUpdateDefinition(definition); | ||
|
||
assertThat(definition, is(notNullValue())); | ||
assertThat(definition.getDiscoveryUrl(), nullValue()); | ||
assertThat(definition.getAuthUrl().toString(), is("http://not.updated")); | ||
assertThat(definition.getTokenUrl().toString(), is("http://not.updated")); | ||
} | ||
|
||
@Test | ||
public void withDiscoveryUrl_shouldPerformDiscovery() throws IOException { | ||
definition = new OIDCIdentityProviderDefinition(); | ||
definition.setAuthUrl(new URL("http://should.be.updated")); | ||
definition.setTokenUrl(new URL("http://should.be.updated")); | ||
definition.setDiscoveryUrl(new URL("http://discovery.com")); | ||
when(restTemplate.getForObject(any(URI.class), eq(byte[].class))) | ||
.thenReturn(objectMapper.writeValueAsBytes(buildOidcMetadata())); | ||
|
||
metadataDiscoverer.performDiscoveryAndUpdateDefinition(definition); | ||
|
||
assertThat(definition, is(notNullValue())); | ||
assertThat(definition.getAuthUrl().toString(), is("http://authz.endpoint")); | ||
assertThat(definition.getTokenUrl().toString(), is("http://token.endpoint")); | ||
assertThat(definition.getUserInfoUrl().toString(), is("http://userinfo.endpoint")); | ||
assertThat(definition.getTokenKeyUrl().toString(), is("http://jwks.uri")); | ||
assertThat(definition.getIssuer(), is("metadataissuer")); | ||
} | ||
|
||
@Test | ||
public void withDiscoveryUrl_usesCache() throws IOException { | ||
definition = new OIDCIdentityProviderDefinition(); | ||
definition.setAuthUrl(new URL("http://should.be.updated")); | ||
definition.setTokenUrl(new URL("http://should.be.updated")); | ||
definition.setDiscoveryUrl(new URL("http://discovery.com")); | ||
definition.setSkipSslValidation(false); | ||
|
||
when(restTemplate.getForObject(any(URI.class), eq(byte[].class))) | ||
.thenReturn(objectMapper.writeValueAsBytes(buildOidcMetadata())) | ||
.thenThrow(new RuntimeException("shouldn't have been called more than once")); | ||
|
||
metadataDiscoverer.performDiscoveryAndUpdateDefinition(definition); | ||
metadataDiscoverer.performDiscoveryAndUpdateDefinition(definition); | ||
} | ||
|
||
private OidcMetadata buildOidcMetadata() { | ||
try { | ||
OidcMetadata metadata = new OidcMetadata(); | ||
|
||
metadata.setAuthorizationEndpoint(new URL("http://authz.endpoint")); | ||
metadata.setTokenEndpoint(new URL("http://token.endpoint")); | ||
metadata.setUserinfoEndpoint(new URL("http://userinfo.endpoint")); | ||
metadata.setJsonWebKeysUri(new URL("http://jwks.uri")); | ||
metadata.setIssuer("metadataissuer"); | ||
|
||
return metadata; | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |