Removed unnecessary modules
parent
c8ddea070e
commit
f963790943
|
@ -1,12 +0,0 @@
|
|||
local-values.conf
|
||||
target
|
||||
*~
|
||||
bin
|
||||
*.idea
|
||||
*.iml
|
||||
*.eml
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
||||
/target
|
||||
.springBeans
|
|
@ -1,12 +0,0 @@
|
|||
# OpenID Connect Client #
|
||||
|
||||
## Overview ##
|
||||
|
||||
This project contains an OpenID Connect Client implemented as a Spring Security AuthenticationFilter. The client facilitates a user's authentication into the secured application to an OpenID Connect Server following the OpenID Connect standard protocol.
|
||||
|
||||
## Configuring ##
|
||||
|
||||
For an example of the Client configuration, see the [Simple Web App](https://github.com/mitreid-connect/simple-web-app) project.
|
||||
|
||||
Full documentation is available on the [project documentation wiki pages](https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server/wiki/Client-configuration).
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2018 The MIT Internet Trust Consortium
|
||||
|
||||
Portions copyright 2011-2013 The MITRE Corporation
|
||||
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>openid-connect-parent</artifactId>
|
||||
<groupId>org.mitre</groupId>
|
||||
<version>1.3.4-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>openid-connect-client</artifactId>
|
||||
<description>OpenID Connect Client filter for Spring Security</description>
|
||||
<name>OpenID Connect Client</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.mitre</groupId>
|
||||
<artifactId>openid-connect-common</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<packaging>jar</packaging>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${java-version}</source>
|
||||
<target>${java-version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- BUILD SOURCE FILES -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- BUILD JavaDoc FILES -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1,3 +0,0 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
|
@ -1,392 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter;
|
||||
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
|
||||
import org.mitre.oauth2.introspectingfilter.service.impl.SimpleIntrospectionAuthorityGranter;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Request;
|
||||
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
|
||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.util.Base64;
|
||||
|
||||
/**
|
||||
* This ResourceServerTokenServices implementation introspects incoming tokens at a
|
||||
* server's introspection endpoint URL and passes an Authentication object along
|
||||
* based on the response from the introspection endpoint.
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class IntrospectingTokenService implements ResourceServerTokenServices {
|
||||
|
||||
private IntrospectionConfigurationService introspectionConfigurationService;
|
||||
private IntrospectionAuthorityGranter introspectionAuthorityGranter = new SimpleIntrospectionAuthorityGranter();
|
||||
|
||||
private int defaultExpireTime = 300000; // 5 minutes in milliseconds
|
||||
private boolean forceCacheExpireTime = false; // force removal of cached tokens based on default expire time
|
||||
private boolean cacheNonExpiringTokens = false;
|
||||
private boolean cacheTokens = true;
|
||||
|
||||
private HttpComponentsClientHttpRequestFactory factory;
|
||||
|
||||
public IntrospectingTokenService() {
|
||||
this(HttpClientBuilder.create().useSystemProperties().build());
|
||||
}
|
||||
|
||||
public IntrospectingTokenService(HttpClient httpClient) {
|
||||
this.factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
|
||||
// Inner class to store in the hash map
|
||||
private class TokenCacheObject {
|
||||
OAuth2AccessToken token;
|
||||
OAuth2Authentication auth;
|
||||
Date cacheExpire;
|
||||
|
||||
private TokenCacheObject(OAuth2AccessToken token, OAuth2Authentication auth) {
|
||||
this.token = token;
|
||||
this.auth = auth;
|
||||
|
||||
// we don't need to check the cacheTokens values, because this won't actually be added to the cache if cacheTokens is false
|
||||
// if the token isn't null we use the token expire time
|
||||
// if forceCacheExpireTime is also true, we also make sure that the token expire time is shorter than the default expire time
|
||||
if ((this.token.getExpiration() != null) && (!forceCacheExpireTime || (forceCacheExpireTime && (this.token.getExpiration().getTime() - System.currentTimeMillis() <= defaultExpireTime)))) {
|
||||
this.cacheExpire = this.token.getExpiration();
|
||||
} else { // if the token doesn't have an expire time, or if the using forceCacheExpireTime the token expire time is longer than the default, then use the default expire time
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.add(Calendar.MILLISECOND, defaultExpireTime);
|
||||
this.cacheExpire = cal.getTime();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, TokenCacheObject> authCache = new HashMap<>();
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(IntrospectingTokenService.class);
|
||||
|
||||
/**
|
||||
* @return the introspectionConfigurationService
|
||||
*/
|
||||
public IntrospectionConfigurationService getIntrospectionConfigurationService() {
|
||||
return introspectionConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param introspectionConfigurationService the introspectionConfigurationService to set
|
||||
*/
|
||||
public void setIntrospectionConfigurationService(IntrospectionConfigurationService introspectionUrlProvider) {
|
||||
this.introspectionConfigurationService = introspectionUrlProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param introspectionAuthorityGranter the introspectionAuthorityGranter to set
|
||||
*/
|
||||
public void setIntrospectionAuthorityGranter(IntrospectionAuthorityGranter introspectionAuthorityGranter) {
|
||||
this.introspectionAuthorityGranter = introspectionAuthorityGranter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the introspectionAuthorityGranter
|
||||
*/
|
||||
public IntrospectionAuthorityGranter getIntrospectionAuthorityGranter() {
|
||||
return introspectionAuthorityGranter;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the default cache expire time in milliseconds
|
||||
* @return
|
||||
*/
|
||||
public int getDefaultExpireTime() {
|
||||
return defaultExpireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the default cache expire time in milliseconds
|
||||
* @param defaultExpireTime
|
||||
*/
|
||||
public void setDefaultExpireTime(int defaultExpireTime) {
|
||||
this.defaultExpireTime = defaultExpireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* check if forcing a cache expire time maximum value
|
||||
* @return the forceCacheExpireTime setting
|
||||
*/
|
||||
public boolean isForceCacheExpireTime() {
|
||||
return forceCacheExpireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* set forcing a cache expire time maximum value
|
||||
* @param forceCacheExpireTime
|
||||
*/
|
||||
public void setForceCacheExpireTime(boolean forceCacheExpireTime) {
|
||||
this.forceCacheExpireTime = forceCacheExpireTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are non-expiring tokens cached using the default cache time
|
||||
* @return state of cacheNonExpiringTokens
|
||||
*/
|
||||
public boolean isCacheNonExpiringTokens() {
|
||||
return cacheNonExpiringTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* should non-expiring tokens be cached using the default cache timeout
|
||||
* @param cacheNonExpiringTokens
|
||||
*/
|
||||
public void setCacheNonExpiringTokens(boolean cacheNonExpiringTokens) {
|
||||
this.cacheNonExpiringTokens = cacheNonExpiringTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the service caching tokens, or is it hitting the introspection end point every time
|
||||
* @return true is caching tokens locally, false hits the introspection end point every time
|
||||
*/
|
||||
public boolean isCacheTokens() {
|
||||
return cacheTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure if the client should cache tokens locally or not
|
||||
* @param cacheTokens
|
||||
*/
|
||||
public void setCacheTokens(boolean cacheTokens) {
|
||||
this.cacheTokens = cacheTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the introspection end point response for a token has been cached locally
|
||||
* This call will return the token if it has been cached and is still valid according to
|
||||
* the cache expire time on the TokenCacheObject. If a cached value has been found but is
|
||||
* expired, either by default expire times or the token's own expire time, then the token is
|
||||
* removed from the cache and null is returned.
|
||||
* @param key is the token to check
|
||||
* @return the cached TokenCacheObject or null
|
||||
*/
|
||||
private TokenCacheObject checkCache(String key) {
|
||||
if (cacheTokens && authCache.containsKey(key)) {
|
||||
TokenCacheObject tco = authCache.get(key);
|
||||
|
||||
if (tco != null && tco.cacheExpire != null && tco.cacheExpire.after(new Date())) {
|
||||
return tco;
|
||||
} else {
|
||||
// if the token is expired, don't keep things around.
|
||||
authCache.remove(key);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private OAuth2Request createStoredRequest(final JsonObject token) {
|
||||
String clientId = token.get("client_id").getAsString();
|
||||
Set<String> scopes = new HashSet<>();
|
||||
if (token.has("scope")) {
|
||||
scopes.addAll(OAuth2Utils.parseParameterList(token.get("scope").getAsString()));
|
||||
}
|
||||
Map<String, String> parameters = new HashMap<>();
|
||||
parameters.put("client_id", clientId);
|
||||
parameters.put("scope", OAuth2Utils.formatParameterList(scopes));
|
||||
OAuth2Request storedRequest = new OAuth2Request(parameters, clientId, null, true, scopes, null, null, null, null);
|
||||
return storedRequest;
|
||||
}
|
||||
|
||||
private Authentication createUserAuthentication(JsonObject token) {
|
||||
JsonElement userId = token.get("user_id");
|
||||
if(userId == null) {
|
||||
userId = token.get("sub");
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return new PreAuthenticatedAuthenticationToken(userId.getAsString(), token, introspectionAuthorityGranter.getAuthorities(token));
|
||||
}
|
||||
|
||||
private OAuth2AccessToken createAccessToken(final JsonObject token, final String tokenString) {
|
||||
OAuth2AccessToken accessToken = new OAuth2AccessTokenImpl(token, tokenString);
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a token string against the introspection endpoint,
|
||||
* then parse it and store it in the local cache if caching is enabled.
|
||||
*
|
||||
* @param accessToken Token to pass to the introspection endpoint
|
||||
* @return TokenCacheObject containing authentication and token if the token was valid, otherwise null
|
||||
*/
|
||||
private TokenCacheObject parseToken(String accessToken) {
|
||||
|
||||
// find out which URL to ask
|
||||
String introspectionUrl;
|
||||
RegisteredClient client;
|
||||
try {
|
||||
introspectionUrl = introspectionConfigurationService.getIntrospectionUrl(accessToken);
|
||||
client = introspectionConfigurationService.getClientConfiguration(accessToken);
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.error("Unable to load introspection URL or client configuration", e);
|
||||
return null;
|
||||
}
|
||||
// Use the SpringFramework RestTemplate to send the request to the
|
||||
// endpoint
|
||||
String validatedToken = null;
|
||||
|
||||
RestTemplate restTemplate;
|
||||
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
||||
|
||||
final String clientId = client.getClientId();
|
||||
final String clientSecret = client.getClientSecret();
|
||||
|
||||
if (SECRET_BASIC.equals(client.getTokenEndpointAuthMethod())){
|
||||
// use BASIC auth if configured to do so
|
||||
restTemplate = new RestTemplate(factory) {
|
||||
|
||||
@Override
|
||||
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
|
||||
ClientHttpRequest httpRequest = super.createRequest(url, method);
|
||||
httpRequest.getHeaders().add("Authorization",
|
||||
String.format("Basic %s", Base64.encode(String.format("%s:%s", clientId, clientSecret)) ));
|
||||
return httpRequest;
|
||||
}
|
||||
};
|
||||
} else { //Alternatively use form based auth
|
||||
restTemplate = new RestTemplate(factory);
|
||||
|
||||
form.add("client_id", clientId);
|
||||
form.add("client_secret", clientSecret);
|
||||
}
|
||||
|
||||
form.add("token", accessToken);
|
||||
|
||||
try {
|
||||
validatedToken = restTemplate.postForObject(introspectionUrl, form, String.class);
|
||||
} catch (RestClientException rce) {
|
||||
logger.error("validateToken", rce);
|
||||
return null;
|
||||
}
|
||||
if (validatedToken != null) {
|
||||
// parse the json
|
||||
JsonElement jsonRoot = new JsonParser().parse(validatedToken);
|
||||
if (!jsonRoot.isJsonObject()) {
|
||||
return null; // didn't get a proper JSON object
|
||||
}
|
||||
|
||||
JsonObject tokenResponse = jsonRoot.getAsJsonObject();
|
||||
|
||||
if (tokenResponse.get("error") != null) {
|
||||
// report an error?
|
||||
logger.error("Got an error back: " + tokenResponse.get("error") + ", " + tokenResponse.get("error_description"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!tokenResponse.get("active").getAsBoolean()) {
|
||||
// non-valid token
|
||||
logger.info("Server returned non-active token");
|
||||
return null;
|
||||
}
|
||||
// create an OAuth2Authentication
|
||||
OAuth2Authentication auth = new OAuth2Authentication(createStoredRequest(tokenResponse), createUserAuthentication(tokenResponse));
|
||||
// create an OAuth2AccessToken
|
||||
OAuth2AccessToken token = createAccessToken(tokenResponse, accessToken);
|
||||
|
||||
if (token.getExpiration() == null || token.getExpiration().after(new Date())) {
|
||||
// Store them in the cache
|
||||
TokenCacheObject tco = new TokenCacheObject(token, auth);
|
||||
if (cacheTokens && (cacheNonExpiringTokens || token.getExpiration() != null)) {
|
||||
authCache.put(accessToken, tco);
|
||||
}
|
||||
return tco;
|
||||
}
|
||||
}
|
||||
|
||||
// when the token is invalid for whatever reason
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException {
|
||||
// First check if the in memory cache has an Authentication object, and
|
||||
// that it is still valid
|
||||
// If Valid, return it
|
||||
TokenCacheObject cacheAuth = checkCache(accessToken);
|
||||
if (cacheAuth != null) {
|
||||
return cacheAuth.auth;
|
||||
} else {
|
||||
cacheAuth = parseToken(accessToken);
|
||||
if (cacheAuth != null) {
|
||||
return cacheAuth.auth;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2AccessToken readAccessToken(String accessToken) {
|
||||
// First check if the in memory cache has a Token object, and that it is
|
||||
// still valid
|
||||
// If Valid, return it
|
||||
TokenCacheObject cacheAuth = checkCache(accessToken);
|
||||
if (cacheAuth != null) {
|
||||
return cacheAuth.token;
|
||||
} else {
|
||||
cacheAuth = parseToken(accessToken);
|
||||
if (cacheAuth != null) {
|
||||
return cacheAuth.token;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.OAuth2RefreshToken;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
|
||||
public class OAuth2AccessTokenImpl implements OAuth2AccessToken {
|
||||
|
||||
private JsonObject introspectionResponse;
|
||||
private String tokenString;
|
||||
private Set<String> scopes = new HashSet<>();
|
||||
private Date expireDate;
|
||||
|
||||
|
||||
public OAuth2AccessTokenImpl(JsonObject introspectionResponse, String tokenString) {
|
||||
this.setIntrospectionResponse(introspectionResponse);
|
||||
this.tokenString = tokenString;
|
||||
if (introspectionResponse.get("scope") != null) {
|
||||
scopes = Sets.newHashSet(Splitter.on(" ").split(introspectionResponse.get("scope").getAsString()));
|
||||
}
|
||||
|
||||
if (introspectionResponse.get("exp") != null) {
|
||||
expireDate = new Date(introspectionResponse.get("exp").getAsLong() * 1000L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAdditionalInformation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getScope() {
|
||||
return scopes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OAuth2RefreshToken getRefreshToken() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTokenType() {
|
||||
return BEARER_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpired() {
|
||||
if (expireDate != null && expireDate.before(new Date())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getExpiration() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getExpiresIn() {
|
||||
if (expireDate != null) {
|
||||
return (int)TimeUnit.MILLISECONDS.toSeconds(expireDate.getTime() - (new Date()).getTime());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue() {
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the token
|
||||
*/
|
||||
public JsonObject getIntrospectionResponse() {
|
||||
return introspectionResponse;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param token the token to set
|
||||
*/
|
||||
public void setIntrospectionResponse(JsonObject token) {
|
||||
this.introspectionResponse = token;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface IntrospectionAuthorityGranter {
|
||||
|
||||
public List<GrantedAuthority> getAuthorities(JsonObject introspectionResponse);
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface IntrospectionConfigurationService {
|
||||
|
||||
/**
|
||||
* Get the introspection URL based on the access token.
|
||||
* @param accessToken
|
||||
* @return
|
||||
*/
|
||||
public String getIntrospectionUrl(String accessToken);
|
||||
|
||||
|
||||
/**
|
||||
* Get the client configuration to use to connect to the
|
||||
* introspection endpoint. In particular, this cares about
|
||||
* the clientId, clientSecret, and tokenEndpointAuthMethod
|
||||
* fields.
|
||||
*/
|
||||
public RegisteredClient getClientConfiguration(String accessToken);
|
||||
|
||||
}
|
|
@ -1,132 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import java.text.ParseException;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* Parses the incoming accesstoken as a JWT and determines the issuer based on
|
||||
* the "iss" field inside the JWT. Uses the ServerConfigurationService to determine
|
||||
* the introspection URL for that issuer.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JWTParsingIntrospectionConfigurationService implements IntrospectionConfigurationService {
|
||||
|
||||
private ServerConfigurationService serverConfigurationService;
|
||||
private ClientConfigurationService clientConfigurationService;
|
||||
|
||||
/**
|
||||
* @return the serverConfigurationService
|
||||
*/
|
||||
public ServerConfigurationService getServerConfigurationService() {
|
||||
return serverConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param serverConfigurationService the serverConfigurationService to set
|
||||
*/
|
||||
public void setServerConfigurationService(ServerConfigurationService serverConfigurationService) {
|
||||
this.serverConfigurationService = serverConfigurationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientConfigurationService the clientConfigurationService to set
|
||||
*/
|
||||
public void setClientConfigurationService(ClientConfigurationService clientConfigurationService) {
|
||||
this.clientConfigurationService = clientConfigurationService;
|
||||
}
|
||||
|
||||
private String getIssuer(String accessToken) {
|
||||
try {
|
||||
JWT jwt = JWTParser.parse(accessToken);
|
||||
|
||||
String issuer = jwt.getJWTClaimsSet().getIssuer();
|
||||
|
||||
return issuer;
|
||||
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException("Unable to parse JWT", e);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionConfigurationService#getIntrospectionUrl(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getIntrospectionUrl(String accessToken) {
|
||||
String issuer = getIssuer(accessToken);
|
||||
if (!Strings.isNullOrEmpty(issuer)) {
|
||||
ServerConfiguration server = serverConfigurationService.getServerConfiguration(issuer);
|
||||
if (server != null) {
|
||||
if (!Strings.isNullOrEmpty(server.getIntrospectionEndpointUri())) {
|
||||
return server.getIntrospectionEndpointUri();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Server does not have Introspection Endpoint defined");
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find server configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("No issuer claim found in JWT");
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService#getClientConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(String accessToken) {
|
||||
|
||||
String issuer = getIssuer(accessToken);
|
||||
if (!Strings.isNullOrEmpty(issuer)) {
|
||||
ServerConfiguration server = serverConfigurationService.getServerConfiguration(issuer);
|
||||
if (server != null) {
|
||||
RegisteredClient client = clientConfigurationService.getClientConfiguration(server);
|
||||
if (client != null) {
|
||||
return client;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find client configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Could not find server configuration for issuer " + issuer);
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("No issuer claim found in JWT");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.oauth2.common.util.OAuth2Utils;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class ScopeBasedIntrospectionAuthoritiesGranter implements IntrospectionAuthorityGranter {
|
||||
|
||||
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_API");
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionAuthorityGranter#getAuthorities(net.minidev.json.JSONObject)
|
||||
*/
|
||||
@Override
|
||||
public List<GrantedAuthority> getAuthorities(JsonObject introspectionResponse) {
|
||||
List<GrantedAuthority> auth = new ArrayList<>(getAuthorities());
|
||||
|
||||
if (introspectionResponse.has("scope") && introspectionResponse.get("scope").isJsonPrimitive()) {
|
||||
String scopeString = introspectionResponse.get("scope").getAsString();
|
||||
Set<String> scopes = OAuth2Utils.parseParameterList(scopeString);
|
||||
for (String scope : scopes) {
|
||||
auth.add(new SimpleGrantedAuthority("OAUTH_SCOPE_" + scope));
|
||||
}
|
||||
}
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the authorities
|
||||
*/
|
||||
public List<GrantedAuthority> getAuthorities() {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authorities the authorities to set
|
||||
*/
|
||||
public void setAuthorities(List<GrantedAuthority> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionAuthorityGranter;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
*
|
||||
* Grants the same set of authorities no matter what's passed in.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class SimpleIntrospectionAuthorityGranter implements IntrospectionAuthorityGranter {
|
||||
|
||||
private List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("ROLE_API");
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionAuthorityGranter#getAuthorities(net.minidev.json.JSONObject)
|
||||
*/
|
||||
@Override
|
||||
public List<GrantedAuthority> getAuthorities(JsonObject introspectionResponse) {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the authorities
|
||||
*/
|
||||
public List<GrantedAuthority> getAuthorities() {
|
||||
return authorities;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authorities the authorities to set
|
||||
*/
|
||||
public void setAuthorities(List<GrantedAuthority> authorities) {
|
||||
this.authorities = authorities;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
|
||||
/**
|
||||
*
|
||||
* Always provides the (configured) IntrospectionURL and RegisteredClient regardless
|
||||
* of token. Useful for talking to a single, trusted authorization server.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticIntrospectionConfigurationService implements IntrospectionConfigurationService {
|
||||
|
||||
private String introspectionUrl;
|
||||
private RegisteredClient clientConfiguration;
|
||||
|
||||
/**
|
||||
* @return the clientConfiguration
|
||||
*/
|
||||
public RegisteredClient getClientConfiguration() {
|
||||
return clientConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clientConfiguration the clientConfiguration to set
|
||||
*/
|
||||
public void setClientConfiguration(RegisteredClient client) {
|
||||
this.clientConfiguration = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the introspectionUrl
|
||||
*/
|
||||
public String getIntrospectionUrl() {
|
||||
return introspectionUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param introspectionUrl the introspectionUrl to set
|
||||
*/
|
||||
public void setIntrospectionUrl(String introspectionUrl) {
|
||||
this.introspectionUrl = introspectionUrl;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.IntrospectionConfigurationService#getIntrospectionUrl(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String getIntrospectionUrl(String accessToken) {
|
||||
return getIntrospectionUrl();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.oauth2.introspectingfilter.service.IntrospectionConfigurationService#getClientConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(String accessToken) {
|
||||
return getClientConfiguration();
|
||||
}
|
||||
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
public class AuthorizationEndpointException extends AuthenticationServiceException {
|
||||
|
||||
private static final long serialVersionUID = 6953119789654778380L;
|
||||
|
||||
private String error;
|
||||
|
||||
private String errorDescription;
|
||||
|
||||
private String errorURI;
|
||||
|
||||
public AuthorizationEndpointException(String error, String errorDescription, String errorURI) {
|
||||
super("Error from Authorization Endpoint: " + error + " " + errorDescription + " " + errorURI);
|
||||
this.error = error;
|
||||
this.errorDescription = errorDescription;
|
||||
this.errorURI = errorURI;
|
||||
}
|
||||
|
||||
public String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
public String getErrorDescription() {
|
||||
return errorDescription;
|
||||
}
|
||||
|
||||
public String getErrorURI() {
|
||||
return errorURI;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AuthorizationEndpointException [error=" + error + ", errorDescription=" + errorDescription + ", errorURI=" + errorURI + "]";
|
||||
}
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mitre.openid.connect.model.UserInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* Simple mapper that adds ROLE_USER to the authorities map for all queries,
|
||||
* plus adds ROLE_ADMIN if the subject and issuer pair are found in the
|
||||
* configurable "admins" set.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class NamedAdminAuthoritiesMapper implements OIDCAuthoritiesMapper {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(NamedAdminAuthoritiesMapper.class);
|
||||
|
||||
private static final SimpleGrantedAuthority ROLE_ADMIN = new SimpleGrantedAuthority("ROLE_ADMIN");
|
||||
private static final SimpleGrantedAuthority ROLE_USER = new SimpleGrantedAuthority("ROLE_USER");
|
||||
|
||||
private Set<SubjectIssuerGrantedAuthority> admins = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public Collection<? extends GrantedAuthority> mapAuthorities(JWT idToken, UserInfo userInfo) {
|
||||
|
||||
Set<GrantedAuthority> out = new HashSet<>();
|
||||
try {
|
||||
JWTClaimsSet claims = idToken.getJWTClaimsSet();
|
||||
|
||||
SubjectIssuerGrantedAuthority authority = new SubjectIssuerGrantedAuthority(claims.getSubject(), claims.getIssuer());
|
||||
out.add(authority);
|
||||
|
||||
if (admins.contains(authority)) {
|
||||
out.add(ROLE_ADMIN);
|
||||
}
|
||||
|
||||
// everybody's a user by default
|
||||
out.add(ROLE_USER);
|
||||
|
||||
} catch (ParseException e) {
|
||||
logger.error("Unable to parse ID Token inside of authorities mapper (huh?)");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the admins
|
||||
*/
|
||||
public Set<SubjectIssuerGrantedAuthority> getAdmins() {
|
||||
return admins;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param admins the admins to set
|
||||
*/
|
||||
public void setAdmins(Set<SubjectIssuerGrantedAuthority> admins) {
|
||||
this.admins = admins;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,902 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.PRIVATE_KEY;
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_BASIC;
|
||||
import static org.mitre.oauth2.model.ClientDetailsEntity.AuthMethod.SECRET_JWT;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
|
||||
import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
|
||||
import org.mitre.jwt.signer.service.impl.SymmetricKeyJWTValidatorCacheService;
|
||||
import org.mitre.oauth2.model.PKCEAlgorithm;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.mitre.openid.connect.client.service.IssuerService;
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.client.service.impl.StaticAuthRequestOptionsService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
|
||||
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.nimbusds.jose.Algorithm;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jose.util.Base64;
|
||||
import com.nimbusds.jose.util.Base64URL;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.JWTParser;
|
||||
import com.nimbusds.jwt.PlainJWT;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
||||
/**
|
||||
* OpenID Connect Authentication Filter class
|
||||
*
|
||||
* @author nemonik, jricher
|
||||
*
|
||||
*/
|
||||
public class OIDCAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
|
||||
|
||||
protected final static String REDIRECT_URI_SESION_VARIABLE = "redirect_uri";
|
||||
protected final static String CODE_VERIFIER_SESSION_VARIABLE = "code_verifier";
|
||||
protected final static String STATE_SESSION_VARIABLE = "state";
|
||||
protected final static String NONCE_SESSION_VARIABLE = "nonce";
|
||||
protected final static String ISSUER_SESSION_VARIABLE = "issuer";
|
||||
protected final static String TARGET_SESSION_VARIABLE = "target";
|
||||
protected final static int HTTP_SOCKET_TIMEOUT = 30000;
|
||||
|
||||
public final static String FILTER_PROCESSES_URL = "/openid_connect_login";
|
||||
|
||||
// Allow for time sync issues by having a window of X seconds.
|
||||
private int timeSkewAllowance = 300;
|
||||
|
||||
// fetches and caches public keys for servers
|
||||
@Autowired(required=false)
|
||||
private JWKSetCacheService validationServices;
|
||||
|
||||
// creates JWT signer/validators for symmetric keys
|
||||
@Autowired(required=false)
|
||||
private SymmetricKeyJWTValidatorCacheService symmetricCacheService;
|
||||
|
||||
// signer based on keypair for this client (for outgoing auth requests)
|
||||
@Autowired(required=false)
|
||||
private JWTSigningAndValidationService authenticationSignerService;
|
||||
|
||||
@Autowired(required=false)
|
||||
private HttpClient httpClient;
|
||||
|
||||
/*
|
||||
* Modular services to build out client filter.
|
||||
*/
|
||||
// looks at the request and determines which issuer to use for lookup on the server
|
||||
private IssuerService issuerService;
|
||||
// holds server information (auth URI, token URI, etc.), indexed by issuer
|
||||
private ServerConfigurationService servers;
|
||||
// holds client information (client ID, redirect URI, etc.), indexed by issuer of the server
|
||||
private ClientConfigurationService clients;
|
||||
// provides extra options to inject into the outbound request
|
||||
private AuthRequestOptionsService authOptions = new StaticAuthRequestOptionsService(); // initialize with an empty set of options
|
||||
// builds the actual request URI based on input from all other services
|
||||
private AuthRequestUrlBuilder authRequestBuilder;
|
||||
|
||||
// private helpers to handle target link URLs
|
||||
private TargetLinkURIAuthenticationSuccessHandler targetSuccessHandler = new TargetLinkURIAuthenticationSuccessHandler();
|
||||
private TargetLinkURIChecker deepLinkFilter;
|
||||
|
||||
protected int httpSocketTimeout = HTTP_SOCKET_TIMEOUT;
|
||||
|
||||
/**
|
||||
* OpenIdConnectAuthenticationFilter constructor
|
||||
*/
|
||||
public OIDCAuthenticationFilter() {
|
||||
super(FILTER_PROCESSES_URL);
|
||||
targetSuccessHandler.passthrough = super.getSuccessHandler();
|
||||
super.setAuthenticationSuccessHandler(targetSuccessHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
super.afterPropertiesSet();
|
||||
|
||||
// if our JOSE validators don't get wired in, drop defaults into place
|
||||
|
||||
if (validationServices == null) {
|
||||
validationServices = new JWKSetCacheService();
|
||||
}
|
||||
|
||||
if (symmetricCacheService == null) {
|
||||
symmetricCacheService = new SymmetricKeyJWTValidatorCacheService();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the main entry point for the filter.
|
||||
*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.security.web.authentication.
|
||||
* AbstractAuthenticationProcessingFilter
|
||||
* #attemptAuthentication(javax.servlet.http.HttpServletRequest,
|
||||
* javax.servlet.http.HttpServletResponse)
|
||||
*/
|
||||
@Override
|
||||
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
|
||||
|
||||
if (!Strings.isNullOrEmpty(request.getParameter("error"))) {
|
||||
|
||||
// there's an error coming back from the server, need to handle this
|
||||
handleError(request, response);
|
||||
return null; // no auth, response is sent to display page or something
|
||||
|
||||
} else if (!Strings.isNullOrEmpty(request.getParameter("code"))) {
|
||||
|
||||
// we got back the code, need to process this to get our tokens
|
||||
Authentication auth = handleAuthorizationCodeResponse(request, response);
|
||||
return auth;
|
||||
|
||||
} else {
|
||||
|
||||
// not an error, not a code, must be an initial login of some type
|
||||
handleAuthorizationRequest(request, response);
|
||||
|
||||
return null; // no auth, response redirected to the server's Auth Endpoint (or possibly to the account chooser)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate an Authorization request
|
||||
*
|
||||
* @param request
|
||||
* The request from which to extract parameters and perform the
|
||||
* authentication
|
||||
* @param response
|
||||
* @throws IOException
|
||||
* If an input or output exception occurs
|
||||
*/
|
||||
protected void handleAuthorizationRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
HttpSession session = request.getSession();
|
||||
|
||||
IssuerServiceResponse issResp = issuerService.getIssuer(request);
|
||||
|
||||
if (issResp == null) {
|
||||
logger.error("Null issuer response returned from service.");
|
||||
throw new AuthenticationServiceException("No issuer found.");
|
||||
}
|
||||
|
||||
if (issResp.shouldRedirect()) {
|
||||
response.sendRedirect(issResp.getRedirectUrl());
|
||||
} else {
|
||||
String issuer = issResp.getIssuer();
|
||||
|
||||
if (!Strings.isNullOrEmpty(issResp.getTargetLinkUri())) {
|
||||
// there's a target URL in the response, we should save this so we can forward to it later
|
||||
session.setAttribute(TARGET_SESSION_VARIABLE, issResp.getTargetLinkUri());
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(issuer)) {
|
||||
logger.error("No issuer found: " + issuer);
|
||||
throw new AuthenticationServiceException("No issuer found: " + issuer);
|
||||
}
|
||||
|
||||
ServerConfiguration serverConfig = servers.getServerConfiguration(issuer);
|
||||
if (serverConfig == null) {
|
||||
logger.error("No server configuration found for issuer: " + issuer);
|
||||
throw new AuthenticationServiceException("No server configuration found for issuer: " + issuer);
|
||||
}
|
||||
|
||||
|
||||
session.setAttribute(ISSUER_SESSION_VARIABLE, serverConfig.getIssuer());
|
||||
|
||||
RegisteredClient clientConfig = clients.getClientConfiguration(serverConfig);
|
||||
if (clientConfig == null) {
|
||||
logger.error("No client configuration found for issuer: " + issuer);
|
||||
throw new AuthenticationServiceException("No client configuration found for issuer: " + issuer);
|
||||
}
|
||||
|
||||
String redirectUri = null;
|
||||
if (clientConfig.getRegisteredRedirectUri() != null && clientConfig.getRegisteredRedirectUri().size() == 1) {
|
||||
// if there's a redirect uri configured (and only one), use that
|
||||
redirectUri = Iterables.getOnlyElement(clientConfig.getRegisteredRedirectUri());
|
||||
} else {
|
||||
// otherwise our redirect URI is this current URL, with no query parameters
|
||||
redirectUri = request.getRequestURL().toString();
|
||||
}
|
||||
session.setAttribute(REDIRECT_URI_SESION_VARIABLE, redirectUri);
|
||||
|
||||
// this value comes back in the id token and is checked there
|
||||
String nonce = createNonce(session);
|
||||
|
||||
// this value comes back in the auth code response
|
||||
String state = createState(session);
|
||||
|
||||
Map<String, String> options = authOptions.getOptions(serverConfig, clientConfig, request);
|
||||
|
||||
// if we're using PKCE, handle the challenge here
|
||||
if (clientConfig.getCodeChallengeMethod() != null) {
|
||||
String codeVerifier = createCodeVerifier(session);
|
||||
options.put("code_challenge_method", clientConfig.getCodeChallengeMethod().getName());
|
||||
if (clientConfig.getCodeChallengeMethod().equals(PKCEAlgorithm.plain)) {
|
||||
options.put("code_challenge", codeVerifier);
|
||||
} else if (clientConfig.getCodeChallengeMethod().equals(PKCEAlgorithm.S256)) {
|
||||
try {
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||
String hash = Base64URL.encode(digest.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII))).toString();
|
||||
options.put("code_challenge", hash);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String authRequest = authRequestBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, issResp.getLoginHint());
|
||||
|
||||
logger.debug("Auth Request: " + authRequest);
|
||||
|
||||
response.sendRedirect(authRequest);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param request
|
||||
* The request from which to extract parameters and perform the
|
||||
* authentication
|
||||
* @return The authenticated user token, or null if authentication is
|
||||
* incomplete.
|
||||
*/
|
||||
protected Authentication handleAuthorizationCodeResponse(HttpServletRequest request, HttpServletResponse response) {
|
||||
|
||||
String authorizationCode = request.getParameter("code");
|
||||
|
||||
HttpSession session = request.getSession();
|
||||
|
||||
// check for state, if it doesn't match we bail early
|
||||
String storedState = getStoredState(session);
|
||||
String requestState = request.getParameter("state");
|
||||
if (storedState == null || !storedState.equals(requestState)) {
|
||||
throw new AuthenticationServiceException("State parameter mismatch on return. Expected " + storedState + " got " + requestState);
|
||||
}
|
||||
|
||||
// look up the issuer that we set out to talk to
|
||||
String issuer = getStoredSessionString(session, ISSUER_SESSION_VARIABLE);
|
||||
|
||||
// pull the configurations based on that issuer
|
||||
ServerConfiguration serverConfig = servers.getServerConfiguration(issuer);
|
||||
final RegisteredClient clientConfig = clients.getClientConfiguration(serverConfig);
|
||||
|
||||
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
||||
form.add("grant_type", "authorization_code");
|
||||
form.add("code", authorizationCode);
|
||||
form.setAll(authOptions.getTokenOptions(serverConfig, clientConfig, request));
|
||||
|
||||
String codeVerifier = getStoredCodeVerifier(session);
|
||||
if (codeVerifier != null) {
|
||||
form.add("code_verifier", codeVerifier);
|
||||
}
|
||||
|
||||
String redirectUri = getStoredSessionString(session, REDIRECT_URI_SESION_VARIABLE);
|
||||
if (redirectUri != null) {
|
||||
form.add("redirect_uri", redirectUri);
|
||||
}
|
||||
|
||||
// Handle Token Endpoint interaction
|
||||
|
||||
if(httpClient == null) {
|
||||
httpClient = HttpClientBuilder.create()
|
||||
.useSystemProperties()
|
||||
.setDefaultRequestConfig(RequestConfig.custom()
|
||||
.setSocketTimeout(httpSocketTimeout)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
|
||||
RestTemplate restTemplate;
|
||||
|
||||
if (SECRET_BASIC.equals(clientConfig.getTokenEndpointAuthMethod())){
|
||||
// use BASIC auth if configured to do so
|
||||
restTemplate = new RestTemplate(factory) {
|
||||
|
||||
@Override
|
||||
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
|
||||
ClientHttpRequest httpRequest = super.createRequest(url, method);
|
||||
httpRequest.getHeaders().add("Authorization",
|
||||
String.format("Basic %s", Base64.encode(String.format("%s:%s",
|
||||
UriUtils.encodePathSegment(clientConfig.getClientId(), "UTF-8"),
|
||||
UriUtils.encodePathSegment(clientConfig.getClientSecret(), "UTF-8")))));
|
||||
|
||||
return httpRequest;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// we're not doing basic auth, figure out what other flavor we have
|
||||
restTemplate = new RestTemplate(factory);
|
||||
|
||||
if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) || PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
|
||||
// do a symmetric secret signed JWT for auth
|
||||
|
||||
|
||||
JWTSigningAndValidationService signer = null;
|
||||
JWSAlgorithm alg = clientConfig.getTokenEndpointAuthSigningAlg();
|
||||
|
||||
if (SECRET_JWT.equals(clientConfig.getTokenEndpointAuthMethod()) &&
|
||||
(JWSAlgorithm.HS256.equals(alg)
|
||||
|| JWSAlgorithm.HS384.equals(alg)
|
||||
|| JWSAlgorithm.HS512.equals(alg))) {
|
||||
|
||||
// generate one based on client secret
|
||||
signer = symmetricCacheService.getSymmetricValidtor(clientConfig.getClient());
|
||||
|
||||
} else if (PRIVATE_KEY.equals(clientConfig.getTokenEndpointAuthMethod())) {
|
||||
|
||||
// needs to be wired in to the bean
|
||||
signer = authenticationSignerService;
|
||||
|
||||
if (alg == null) {
|
||||
alg = authenticationSignerService.getDefaultSigningAlgorithm();
|
||||
}
|
||||
}
|
||||
|
||||
if (signer == null) {
|
||||
throw new AuthenticationServiceException("Couldn't find required signer service for use with private key auth.");
|
||||
}
|
||||
|
||||
JWTClaimsSet.Builder claimsSet = new JWTClaimsSet.Builder();
|
||||
|
||||
claimsSet.issuer(clientConfig.getClientId());
|
||||
claimsSet.subject(clientConfig.getClientId());
|
||||
claimsSet.audience(Lists.newArrayList(serverConfig.getTokenEndpointUri()));
|
||||
claimsSet.jwtID(UUID.randomUUID().toString());
|
||||
|
||||
// TODO: make this configurable
|
||||
Date exp = new Date(System.currentTimeMillis() + (60 * 1000)); // auth good for 60 seconds
|
||||
claimsSet.expirationTime(exp);
|
||||
|
||||
Date now = new Date(System.currentTimeMillis());
|
||||
claimsSet.issueTime(now);
|
||||
claimsSet.notBeforeTime(now);
|
||||
|
||||
JWSHeader header = new JWSHeader(alg, null, null, null, null, null, null, null, null, null,
|
||||
signer.getDefaultSignerKeyId(),
|
||||
null, null);
|
||||
SignedJWT jwt = new SignedJWT(header, claimsSet.build());
|
||||
|
||||
signer.signJwt(jwt, alg);
|
||||
|
||||
form.add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
|
||||
form.add("client_assertion", jwt.serialize());
|
||||
} else {
|
||||
//Alternatively use form based auth
|
||||
form.add("client_id", clientConfig.getClientId());
|
||||
form.add("client_secret", clientConfig.getClientSecret());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
logger.debug("tokenEndpointURI = " + serverConfig.getTokenEndpointUri());
|
||||
logger.debug("form = " + form);
|
||||
|
||||
String jsonString = null;
|
||||
|
||||
try {
|
||||
jsonString = restTemplate.postForObject(serverConfig.getTokenEndpointUri(), form, String.class);
|
||||
} catch (RestClientException e) {
|
||||
|
||||
// Handle error
|
||||
|
||||
logger.error("Token Endpoint error response: " + e.getMessage());
|
||||
|
||||
throw new AuthenticationServiceException("Unable to obtain Access Token: " + e.getMessage());
|
||||
}
|
||||
|
||||
logger.debug("from TokenEndpoint jsonString = " + jsonString);
|
||||
|
||||
JsonElement jsonRoot = new JsonParser().parse(jsonString);
|
||||
if (!jsonRoot.isJsonObject()) {
|
||||
throw new AuthenticationServiceException("Token Endpoint did not return a JSON object: " + jsonRoot);
|
||||
}
|
||||
|
||||
JsonObject tokenResponse = jsonRoot.getAsJsonObject();
|
||||
|
||||
if (tokenResponse.get("error") != null) {
|
||||
|
||||
// Handle error
|
||||
|
||||
String error = tokenResponse.get("error").getAsString();
|
||||
|
||||
logger.error("Token Endpoint returned: " + error);
|
||||
|
||||
throw new AuthenticationServiceException("Unable to obtain Access Token. Token Endpoint returned: " + error);
|
||||
|
||||
} else {
|
||||
|
||||
// Extract the id_token to insert into the
|
||||
// OIDCAuthenticationToken
|
||||
|
||||
// get out all the token strings
|
||||
String accessTokenValue = null;
|
||||
String idTokenValue = null;
|
||||
String refreshTokenValue = null;
|
||||
|
||||
if (tokenResponse.has("access_token")) {
|
||||
accessTokenValue = tokenResponse.get("access_token").getAsString();
|
||||
} else {
|
||||
throw new AuthenticationServiceException("Token Endpoint did not return an access_token: " + jsonString);
|
||||
}
|
||||
|
||||
if (tokenResponse.has("id_token")) {
|
||||
idTokenValue = tokenResponse.get("id_token").getAsString();
|
||||
} else {
|
||||
logger.error("Token Endpoint did not return an id_token");
|
||||
throw new AuthenticationServiceException("Token Endpoint did not return an id_token");
|
||||
}
|
||||
|
||||
if (tokenResponse.has("refresh_token")) {
|
||||
refreshTokenValue = tokenResponse.get("refresh_token").getAsString();
|
||||
}
|
||||
|
||||
try {
|
||||
JWT idToken = JWTParser.parse(idTokenValue);
|
||||
|
||||
// validate our ID Token over a number of tests
|
||||
JWTClaimsSet idClaims = idToken.getJWTClaimsSet();
|
||||
|
||||
// check the signature
|
||||
JWTSigningAndValidationService jwtValidator = null;
|
||||
|
||||
Algorithm tokenAlg = idToken.getHeader().getAlgorithm();
|
||||
|
||||
Algorithm clientAlg = clientConfig.getIdTokenSignedResponseAlg();
|
||||
|
||||
if (clientAlg != null) {
|
||||
if (!clientAlg.equals(tokenAlg)) {
|
||||
throw new AuthenticationServiceException("Token algorithm " + tokenAlg + " does not match expected algorithm " + clientAlg);
|
||||
}
|
||||
}
|
||||
|
||||
if (idToken instanceof PlainJWT) {
|
||||
|
||||
if (clientAlg == null) {
|
||||
throw new AuthenticationServiceException("Unsigned ID tokens can only be used if explicitly configured in client.");
|
||||
}
|
||||
|
||||
if (tokenAlg != null && !tokenAlg.equals(Algorithm.NONE)) {
|
||||
throw new AuthenticationServiceException("Unsigned token received, expected signature with " + tokenAlg);
|
||||
}
|
||||
} else if (idToken instanceof SignedJWT) {
|
||||
|
||||
SignedJWT signedIdToken = (SignedJWT)idToken;
|
||||
|
||||
if (tokenAlg.equals(JWSAlgorithm.HS256)
|
||||
|| tokenAlg.equals(JWSAlgorithm.HS384)
|
||||
|| tokenAlg.equals(JWSAlgorithm.HS512)) {
|
||||
|
||||
// generate one based on client secret
|
||||
jwtValidator = symmetricCacheService.getSymmetricValidtor(clientConfig.getClient());
|
||||
} else {
|
||||
// otherwise load from the server's public key
|
||||
jwtValidator = validationServices.getValidator(serverConfig.getJwksUri());
|
||||
}
|
||||
|
||||
if (jwtValidator != null) {
|
||||
if(!jwtValidator.validateSignature(signedIdToken)) {
|
||||
throw new AuthenticationServiceException("Signature validation failed");
|
||||
}
|
||||
} else {
|
||||
logger.error("No validation service found. Skipping signature validation");
|
||||
throw new AuthenticationServiceException("Unable to find an appropriate signature validator for ID Token.");
|
||||
}
|
||||
} // TODO: encrypted id tokens
|
||||
|
||||
// check the issuer
|
||||
if (idClaims.getIssuer() == null) {
|
||||
throw new AuthenticationServiceException("Id Token Issuer is null");
|
||||
} else if (!idClaims.getIssuer().equals(serverConfig.getIssuer())){
|
||||
throw new AuthenticationServiceException("Issuers do not match, expected " + serverConfig.getIssuer() + " got " + idClaims.getIssuer());
|
||||
}
|
||||
|
||||
// check expiration
|
||||
if (idClaims.getExpirationTime() == null) {
|
||||
throw new AuthenticationServiceException("Id Token does not have required expiration claim");
|
||||
} else {
|
||||
// it's not null, see if it's expired
|
||||
Date now = new Date(System.currentTimeMillis() - (timeSkewAllowance * 1000));
|
||||
if (now.after(idClaims.getExpirationTime())) {
|
||||
throw new AuthenticationServiceException("Id Token is expired: " + idClaims.getExpirationTime());
|
||||
}
|
||||
}
|
||||
|
||||
// check not before
|
||||
if (idClaims.getNotBeforeTime() != null) {
|
||||
Date now = new Date(System.currentTimeMillis() + (timeSkewAllowance * 1000));
|
||||
if (now.before(idClaims.getNotBeforeTime())){
|
||||
throw new AuthenticationServiceException("Id Token not valid untill: " + idClaims.getNotBeforeTime());
|
||||
}
|
||||
}
|
||||
|
||||
// check issued at
|
||||
if (idClaims.getIssueTime() == null) {
|
||||
throw new AuthenticationServiceException("Id Token does not have required issued-at claim");
|
||||
} else {
|
||||
// since it's not null, see if it was issued in the future
|
||||
Date now = new Date(System.currentTimeMillis() + (timeSkewAllowance * 1000));
|
||||
if (now.before(idClaims.getIssueTime())) {
|
||||
throw new AuthenticationServiceException("Id Token was issued in the future: " + idClaims.getIssueTime());
|
||||
}
|
||||
}
|
||||
|
||||
// check audience
|
||||
if (idClaims.getAudience() == null) {
|
||||
throw new AuthenticationServiceException("Id token audience is null");
|
||||
} else if (!idClaims.getAudience().contains(clientConfig.getClientId())) {
|
||||
throw new AuthenticationServiceException("Audience does not match, expected " + clientConfig.getClientId() + " got " + idClaims.getAudience());
|
||||
}
|
||||
|
||||
// compare the nonce to our stored claim
|
||||
String nonce = idClaims.getStringClaim("nonce");
|
||||
if (Strings.isNullOrEmpty(nonce)) {
|
||||
|
||||
logger.error("ID token did not contain a nonce claim.");
|
||||
|
||||
throw new AuthenticationServiceException("ID token did not contain a nonce claim.");
|
||||
}
|
||||
|
||||
String storedNonce = getStoredNonce(session);
|
||||
if (!nonce.equals(storedNonce)) {
|
||||
logger.error("Possible replay attack detected! The comparison of the nonce in the returned "
|
||||
+ "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + ".");
|
||||
|
||||
throw new AuthenticationServiceException(
|
||||
"Possible replay attack detected! The comparison of the nonce in the returned "
|
||||
+ "ID Token to the session " + NONCE_SESSION_VARIABLE + " failed. Expected " + storedNonce + " got " + nonce + ".");
|
||||
}
|
||||
|
||||
// construct an PendingOIDCAuthenticationToken and return a Authentication object w/the userId and the idToken
|
||||
|
||||
PendingOIDCAuthenticationToken token = new PendingOIDCAuthenticationToken(idClaims.getSubject(), idClaims.getIssuer(),
|
||||
serverConfig,
|
||||
idToken, accessTokenValue, refreshTokenValue);
|
||||
|
||||
Authentication authentication = this.getAuthenticationManager().authenticate(token);
|
||||
|
||||
return authentication;
|
||||
} catch (ParseException e) {
|
||||
throw new AuthenticationServiceException("Couldn't parse idToken: ", e);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Authorization Endpoint error
|
||||
*
|
||||
* @param request
|
||||
* The request from which to extract parameters and handle the
|
||||
* error
|
||||
* @param response
|
||||
* The response, needed to do a redirect to display the error
|
||||
* @throws IOException
|
||||
* If an input or output exception occurs
|
||||
*/
|
||||
protected void handleError(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
|
||||
String error = request.getParameter("error");
|
||||
String errorDescription = request.getParameter("error_description");
|
||||
String errorURI = request.getParameter("error_uri");
|
||||
|
||||
throw new AuthorizationEndpointException(error, errorDescription, errorURI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the named stored session variable as a string. Return null if not found or not a string.
|
||||
* @param session
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
private static String getStoredSessionString(HttpSession session, String key) {
|
||||
Object o = session.getAttribute(key);
|
||||
if (o != null && o instanceof String) {
|
||||
return o.toString();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cryptographically random nonce and store it in the session
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
protected static String createNonce(HttpSession session) {
|
||||
String nonce = new BigInteger(50, new SecureRandom()).toString(16);
|
||||
session.setAttribute(NONCE_SESSION_VARIABLE, nonce);
|
||||
|
||||
return nonce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the nonce we stored in the session
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
protected static String getStoredNonce(HttpSession session) {
|
||||
return getStoredSessionString(session, NONCE_SESSION_VARIABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a cryptographically random state and store it in the session
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
protected static String createState(HttpSession session) {
|
||||
String state = new BigInteger(50, new SecureRandom()).toString(16);
|
||||
session.setAttribute(STATE_SESSION_VARIABLE, state);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the state we stored in the session
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
protected static String getStoredState(HttpSession session) {
|
||||
return getStoredSessionString(session, STATE_SESSION_VARIABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a random code challenge and store it in the session
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
protected static String createCodeVerifier(HttpSession session) {
|
||||
String challenge = new BigInteger(50, new SecureRandom()).toString(16);
|
||||
session.setAttribute(CODE_VERIFIER_SESSION_VARIABLE, challenge);
|
||||
return challenge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the stored challenge from our session
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
protected static String getStoredCodeVerifier(HttpSession session) {
|
||||
return getStoredSessionString(session, CODE_VERIFIER_SESSION_VARIABLE);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler successHandler) {
|
||||
targetSuccessHandler.passthrough = successHandler;
|
||||
super.setAuthenticationSuccessHandler(targetSuccessHandler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Handle a successful authentication event. If the issuer service sets
|
||||
* a target URL, we'll go to that. Otherwise we'll let the superclass handle
|
||||
* it for us with the configured behavior.
|
||||
*/
|
||||
protected class TargetLinkURIAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
|
||||
|
||||
private AuthenticationSuccessHandler passthrough;
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSuccess(HttpServletRequest request,
|
||||
HttpServletResponse response, Authentication authentication)
|
||||
throws IOException, ServletException {
|
||||
|
||||
HttpSession session = request.getSession();
|
||||
|
||||
// check to see if we've got a target
|
||||
String target = getStoredSessionString(session, TARGET_SESSION_VARIABLE);
|
||||
|
||||
if (!Strings.isNullOrEmpty(target)) {
|
||||
session.removeAttribute(TARGET_SESSION_VARIABLE);
|
||||
|
||||
if (deepLinkFilter != null) {
|
||||
target = deepLinkFilter.filter(target);
|
||||
}
|
||||
|
||||
response.sendRedirect(target);
|
||||
} else {
|
||||
// if the target was blank, use the default behavior here
|
||||
passthrough.onAuthenticationSuccess(request, response, authentication);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Getters and setters for configuration variables
|
||||
//
|
||||
|
||||
|
||||
public int getTimeSkewAllowance() {
|
||||
return timeSkewAllowance;
|
||||
}
|
||||
|
||||
public void setTimeSkewAllowance(int timeSkewAllowance) {
|
||||
this.timeSkewAllowance = timeSkewAllowance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the validationServices
|
||||
*/
|
||||
public JWKSetCacheService getValidationServices() {
|
||||
return validationServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param validationServices the validationServices to set
|
||||
*/
|
||||
public void setValidationServices(JWKSetCacheService validationServices) {
|
||||
this.validationServices = validationServices;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the servers
|
||||
*/
|
||||
public ServerConfigurationService getServerConfigurationService() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param servers the servers to set
|
||||
*/
|
||||
public void setServerConfigurationService(ServerConfigurationService servers) {
|
||||
this.servers = servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the clients
|
||||
*/
|
||||
public ClientConfigurationService getClientConfigurationService() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clients the clients to set
|
||||
*/
|
||||
public void setClientConfigurationService(ClientConfigurationService clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the issuerService
|
||||
*/
|
||||
public IssuerService getIssuerService() {
|
||||
return issuerService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param issuerService the issuerService to set
|
||||
*/
|
||||
public void setIssuerService(IssuerService issuerService) {
|
||||
this.issuerService = issuerService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the authRequestBuilder
|
||||
*/
|
||||
public AuthRequestUrlBuilder getAuthRequestUrlBuilder() {
|
||||
return authRequestBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authRequestBuilder the authRequestBuilder to set
|
||||
*/
|
||||
public void setAuthRequestUrlBuilder(AuthRequestUrlBuilder authRequestBuilder) {
|
||||
this.authRequestBuilder = authRequestBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the authOptions
|
||||
*/
|
||||
public AuthRequestOptionsService getAuthRequestOptionsService() {
|
||||
return authOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authOptions the authOptions to set
|
||||
*/
|
||||
public void setAuthRequestOptionsService(AuthRequestOptionsService authOptions) {
|
||||
this.authOptions = authOptions;
|
||||
}
|
||||
|
||||
public SymmetricKeyJWTValidatorCacheService getSymmetricCacheService() {
|
||||
return symmetricCacheService;
|
||||
}
|
||||
|
||||
public void setSymmetricCacheService(SymmetricKeyJWTValidatorCacheService symmetricCacheService) {
|
||||
this.symmetricCacheService = symmetricCacheService;
|
||||
}
|
||||
|
||||
public TargetLinkURIAuthenticationSuccessHandler getTargetLinkURIAuthenticationSuccessHandler() {
|
||||
return targetSuccessHandler;
|
||||
}
|
||||
|
||||
public void setTargetLinkURIAuthenticationSuccessHandler(
|
||||
TargetLinkURIAuthenticationSuccessHandler targetSuccessHandler) {
|
||||
this.targetSuccessHandler = targetSuccessHandler;
|
||||
}
|
||||
|
||||
public TargetLinkURIChecker targetLinkURIChecker() {
|
||||
return deepLinkFilter;
|
||||
}
|
||||
|
||||
public void setTargetLinkURIChecker(TargetLinkURIChecker deepLinkFilter) {
|
||||
this.deepLinkFilter = deepLinkFilter;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,128 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mitre.openid.connect.model.OIDCAuthenticationToken;
|
||||
import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken;
|
||||
import org.mitre.openid.connect.model.UserInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.security.authentication.AuthenticationProvider;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jwt.JWT;
|
||||
|
||||
/**
|
||||
* @author nemonik, Justin Richer
|
||||
*
|
||||
*/
|
||||
public class OIDCAuthenticationProvider implements AuthenticationProvider {
|
||||
|
||||
private static Logger logger = LoggerFactory.getLogger(OIDCAuthenticationProvider.class);
|
||||
|
||||
private UserInfoFetcher userInfoFetcher = new UserInfoFetcher();
|
||||
|
||||
private OIDCAuthoritiesMapper authoritiesMapper = new NamedAdminAuthoritiesMapper();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.security.authentication.AuthenticationProvider#
|
||||
* authenticate(org.springframework.security.core.Authentication)
|
||||
*/
|
||||
@Override
|
||||
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
|
||||
|
||||
if (!supports(authentication.getClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (authentication instanceof PendingOIDCAuthenticationToken) {
|
||||
|
||||
PendingOIDCAuthenticationToken token = (PendingOIDCAuthenticationToken) authentication;
|
||||
|
||||
// get the ID Token value out
|
||||
JWT idToken = token.getIdToken();
|
||||
|
||||
// load the user info if we can
|
||||
UserInfo userInfo = userInfoFetcher.loadUserInfo(token);
|
||||
|
||||
if (userInfo == null) {
|
||||
// user info not found -- could be an error, could be fine
|
||||
} else {
|
||||
// if we found userinfo, double check it
|
||||
if (!Strings.isNullOrEmpty(userInfo.getSub()) && !userInfo.getSub().equals(token.getSub())) {
|
||||
// the userinfo came back and the user_id fields don't match what was in the id_token
|
||||
throw new UsernameNotFoundException("user_id mismatch between id_token and user_info call: " + token.getSub() + " / " + userInfo.getSub());
|
||||
}
|
||||
}
|
||||
|
||||
return createAuthenticationToken(token, authoritiesMapper.mapAuthorities(idToken, userInfo), userInfo);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this function to return a different kind of Authentication, processes the authorities differently,
|
||||
* or do post-processing based on the UserInfo object.
|
||||
*
|
||||
* @param token
|
||||
* @param authorities
|
||||
* @param userInfo
|
||||
* @return
|
||||
*/
|
||||
protected Authentication createAuthenticationToken(PendingOIDCAuthenticationToken token, Collection<? extends GrantedAuthority> authorities, UserInfo userInfo) {
|
||||
return new OIDCAuthenticationToken(token.getSub(),
|
||||
token.getIssuer(),
|
||||
userInfo, authorities,
|
||||
token.getIdToken(), token.getAccessTokenValue(), token.getRefreshTokenValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userInfoFetcher
|
||||
*/
|
||||
public void setUserInfoFetcher(UserInfoFetcher userInfoFetcher) {
|
||||
this.userInfoFetcher = userInfoFetcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param authoritiesMapper
|
||||
*/
|
||||
public void setAuthoritiesMapper(OIDCAuthoritiesMapper authoritiesMapper) {
|
||||
this.authoritiesMapper = authoritiesMapper;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.security.authentication.AuthenticationProvider#supports
|
||||
* (java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public boolean supports(Class<?> authentication) {
|
||||
return PendingOIDCAuthenticationToken.class.isAssignableFrom(authentication);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.mitre.openid.connect.model.UserInfo;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import com.nimbusds.jwt.JWT;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface OIDCAuthoritiesMapper {
|
||||
|
||||
/**
|
||||
* @param idToken the ID Token (parsed as a JWT, cannot be @null)
|
||||
* @param userInfo userInfo of the current user (could be @null)
|
||||
* @return the set of authorities to map to this user
|
||||
*/
|
||||
Collection<? extends GrantedAuthority> mapAuthorities(JWT idToken, UserInfo userInfo);
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
/**
|
||||
* Simple target URI checker, checks whether the string in question starts
|
||||
* with a configured prefix. Returns "/" if the match fails.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticPrefixTargetLinkURIChecker implements TargetLinkURIChecker {
|
||||
|
||||
private String prefix = "";
|
||||
|
||||
@Override
|
||||
public String filter(String target) {
|
||||
if (target == null) {
|
||||
return "/";
|
||||
} else if (target.startsWith(prefix)) {
|
||||
return target;
|
||||
} else {
|
||||
return "/";
|
||||
}
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,125 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
*
|
||||
* Simple authority representing a user at an issuer.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class SubjectIssuerGrantedAuthority implements GrantedAuthority {
|
||||
|
||||
private static final long serialVersionUID = 5584978219226664794L;
|
||||
|
||||
private final String subject;
|
||||
private final String issuer;
|
||||
|
||||
/**
|
||||
* @param subject
|
||||
* @param issuer
|
||||
*/
|
||||
public SubjectIssuerGrantedAuthority(String subject, String issuer) {
|
||||
if (Strings.isNullOrEmpty(subject) || Strings.isNullOrEmpty(issuer)) {
|
||||
throw new IllegalArgumentException("Neither subject nor issuer may be null or empty");
|
||||
}
|
||||
this.subject = subject;
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string formed by concatenating the subject with the issuer, separated by _ and prepended with OIDC_
|
||||
*
|
||||
* For example, the user "bob" from issuer "http://id.example.com/" would return the authority string of:
|
||||
*
|
||||
* OIDC_bob_http://id.example.com/
|
||||
*/
|
||||
@Override
|
||||
public String getAuthority() {
|
||||
return "OIDC_" + subject + "_" + issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the subject
|
||||
*/
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the issuer
|
||||
*/
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((issuer == null) ? 0 : issuer.hashCode());
|
||||
result = prime * result + ((subject == null) ? 0 : subject.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (!(obj instanceof SubjectIssuerGrantedAuthority)) {
|
||||
return false;
|
||||
}
|
||||
SubjectIssuerGrantedAuthority other = (SubjectIssuerGrantedAuthority) obj;
|
||||
if (issuer == null) {
|
||||
if (other.issuer != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!issuer.equals(other.issuer)) {
|
||||
return false;
|
||||
}
|
||||
if (subject == null) {
|
||||
if (other.subject != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!subject.equals(other.subject)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getAuthority();
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
public interface TargetLinkURIChecker {
|
||||
|
||||
/**
|
||||
* Check the parameter to make sure that it's a valid deep-link into this application.
|
||||
*
|
||||
* @param target
|
||||
* @return
|
||||
*/
|
||||
public String filter(String target);
|
||||
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration.UserInfoTokenMethod;
|
||||
import org.mitre.openid.connect.model.DefaultUserInfo;
|
||||
import org.mitre.openid.connect.model.PendingOIDCAuthenticationToken;
|
||||
import org.mitre.openid.connect.model.UserInfo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Utility class to fetch userinfo from the userinfo endpoint, if available. Caches the results.
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class UserInfoFetcher {
|
||||
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(UserInfoFetcher.class);
|
||||
|
||||
private LoadingCache<PendingOIDCAuthenticationToken, UserInfo> cache;
|
||||
|
||||
public UserInfoFetcher() {
|
||||
this(HttpClientBuilder.create().useSystemProperties().build());
|
||||
}
|
||||
|
||||
public UserInfoFetcher(HttpClient httpClient) {
|
||||
cache = CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(1, TimeUnit.HOURS) // expires 1 hour after fetch
|
||||
.maximumSize(100)
|
||||
.build(new UserInfoLoader(httpClient));
|
||||
}
|
||||
|
||||
public UserInfo loadUserInfo(final PendingOIDCAuthenticationToken token) {
|
||||
try {
|
||||
return cache.get(token);
|
||||
} catch (UncheckedExecutionException | ExecutionException e) {
|
||||
logger.warn("Couldn't load User Info from token: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class UserInfoLoader extends CacheLoader<PendingOIDCAuthenticationToken, UserInfo> {
|
||||
private HttpComponentsClientHttpRequestFactory factory;
|
||||
|
||||
UserInfoLoader(HttpClient httpClient) {
|
||||
this.factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserInfo load(final PendingOIDCAuthenticationToken token) throws URISyntaxException {
|
||||
|
||||
ServerConfiguration serverConfiguration = token.getServerConfiguration();
|
||||
|
||||
if (serverConfiguration == null) {
|
||||
logger.warn("No server configuration found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(serverConfiguration.getUserInfoUri())) {
|
||||
logger.warn("No userinfo endpoint, not fetching.");
|
||||
return null;
|
||||
}
|
||||
|
||||
String userInfoString = null;
|
||||
|
||||
if (serverConfiguration.getUserInfoTokenMethod() == null || serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.HEADER)) {
|
||||
RestTemplate restTemplate = new RestTemplate(factory) {
|
||||
|
||||
@Override
|
||||
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
|
||||
ClientHttpRequest httpRequest = super.createRequest(url, method);
|
||||
httpRequest.getHeaders().add("Authorization", String.format("Bearer %s", token.getAccessTokenValue()));
|
||||
return httpRequest;
|
||||
}
|
||||
};
|
||||
|
||||
userInfoString = restTemplate.getForObject(serverConfiguration.getUserInfoUri(), String.class);
|
||||
|
||||
} else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.FORM)) {
|
||||
MultiValueMap<String, String> form = new LinkedMultiValueMap<>();
|
||||
form.add("access_token", token.getAccessTokenValue());
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(factory);
|
||||
userInfoString = restTemplate.postForObject(serverConfiguration.getUserInfoUri(), form, String.class);
|
||||
} else if (serverConfiguration.getUserInfoTokenMethod().equals(UserInfoTokenMethod.QUERY)) {
|
||||
URIBuilder builder = new URIBuilder(serverConfiguration.getUserInfoUri());
|
||||
builder.setParameter("access_token", token.getAccessTokenValue());
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(factory);
|
||||
userInfoString = restTemplate.getForObject(builder.toString(), String.class);
|
||||
}
|
||||
|
||||
|
||||
if (!Strings.isNullOrEmpty(userInfoString)) {
|
||||
|
||||
JsonObject userInfoJson = new JsonParser().parse(userInfoString).getAsJsonObject();
|
||||
|
||||
UserInfo userInfo = fromJson(userInfoJson);
|
||||
|
||||
return userInfo;
|
||||
} else {
|
||||
// didn't get anything throw exception
|
||||
throw new IllegalArgumentException("Unable to load user info");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected UserInfo fromJson(JsonObject userInfoJson) {
|
||||
return DefaultUserInfo.fromJson(userInfoJson);
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.keypublisher;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
|
||||
import org.mitre.openid.connect.view.JWKSetView;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class ClientKeyPublisher implements BeanDefinitionRegistryPostProcessor {
|
||||
|
||||
private JWTSigningAndValidationService signingAndValidationService;
|
||||
|
||||
private String jwkPublishUrl;
|
||||
|
||||
private BeanDefinitionRegistry registry;
|
||||
|
||||
private String jwkViewName = JWKSetView.VIEWNAME;
|
||||
|
||||
/**
|
||||
* If the jwkPublishUrl field is set on this bean, set up a listener on that URL to publish keys.
|
||||
*/
|
||||
@Override
|
||||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
if (!Strings.isNullOrEmpty(getJwkPublishUrl())) {
|
||||
|
||||
// add a mapping to this class
|
||||
BeanDefinitionBuilder clientKeyMapping = BeanDefinitionBuilder.rootBeanDefinition(ClientKeyPublisherMapping.class);
|
||||
// custom view resolver
|
||||
BeanDefinitionBuilder viewResolver = BeanDefinitionBuilder.rootBeanDefinition(JwkViewResolver.class);
|
||||
|
||||
if (!Strings.isNullOrEmpty(getJwkPublishUrl())) {
|
||||
clientKeyMapping.addPropertyValue("jwkPublishUrl", getJwkPublishUrl());
|
||||
|
||||
// randomize view name to make sure it doesn't conflict with local views
|
||||
jwkViewName = JWKSetView.VIEWNAME + "-" + UUID.randomUUID().toString();
|
||||
viewResolver.addPropertyValue("jwkViewName", jwkViewName);
|
||||
|
||||
// view bean
|
||||
BeanDefinitionBuilder jwkView = BeanDefinitionBuilder.rootBeanDefinition(JWKSetView.class);
|
||||
registry.registerBeanDefinition(JWKSetView.VIEWNAME, jwkView.getBeanDefinition());
|
||||
viewResolver.addPropertyReference("jwk", JWKSetView.VIEWNAME);
|
||||
}
|
||||
|
||||
registry.registerBeanDefinition("clientKeyMapping", clientKeyMapping.getBeanDefinition());
|
||||
registry.registerBeanDefinition("jwkViewResolver", viewResolver.getBeanDefinition());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry(org.springframework.beans.factory.support.BeanDefinitionRegistry)
|
||||
*/
|
||||
@Override
|
||||
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a view to publish all keys in JWK format. Only used if jwkPublishUrl is set.
|
||||
* @return
|
||||
*/
|
||||
public ModelAndView publishClientJwk() {
|
||||
|
||||
// map from key id to key
|
||||
Map<String, JWK> keys = signingAndValidationService.getAllPublicKeys();
|
||||
|
||||
return new ModelAndView(jwkViewName, "keys", keys);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the jwkPublishUrl
|
||||
*/
|
||||
public String getJwkPublishUrl() {
|
||||
return jwkPublishUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jwkPublishUrl the jwkPublishUrl to set
|
||||
*/
|
||||
public void setJwkPublishUrl(String jwkPublishUrl) {
|
||||
this.jwkPublishUrl = jwkPublishUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the signingAndValidationService
|
||||
*/
|
||||
public JWTSigningAndValidationService getSigningAndValidationService() {
|
||||
return signingAndValidationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param signingAndValidationService the signingAndValidationService to set
|
||||
*/
|
||||
public void setSigningAndValidationService(JWTSigningAndValidationService signingAndValidationService) {
|
||||
this.signingAndValidationService = signingAndValidationService;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.keypublisher;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
|
||||
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
@Component
|
||||
public class ClientKeyPublisherMapping extends RequestMappingInfoHandlerMapping {
|
||||
|
||||
private String jwkPublishUrl;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#isHandler(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
protected boolean isHandler(Class<?> beanType) {
|
||||
return beanType.equals(ClientKeyPublisher.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the "jwkKeyPublish" method to our jwkPublishUrl.
|
||||
*/
|
||||
@Override
|
||||
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
|
||||
|
||||
if (method.getName().equals("publishClientJwk") && getJwkPublishUrl() != null) {
|
||||
return new RequestMappingInfo(
|
||||
new PatternsRequestCondition(new String[] {getJwkPublishUrl()}, getUrlPathHelper(), getPathMatcher(), false, false),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the jwkPublishUrl
|
||||
*/
|
||||
public String getJwkPublishUrl() {
|
||||
return jwkPublishUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jwkPublishUrl the jwkPublishUrl to set
|
||||
*/
|
||||
public void setJwkPublishUrl(String jwkPublishUrl) {
|
||||
this.jwkPublishUrl = jwkPublishUrl;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.keypublisher;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.web.servlet.View;
|
||||
import org.springframework.web.servlet.ViewResolver;
|
||||
|
||||
/**
|
||||
*
|
||||
* Simple view resolver to map JWK view names to appropriate beans
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JwkViewResolver implements ViewResolver, Ordered {
|
||||
|
||||
private String jwkViewName = "jwkKeyList";
|
||||
private View jwk;
|
||||
|
||||
private int order = HIGHEST_PRECEDENCE; // highest precedence, most specific -- avoids hitting the catch-all view resolvers
|
||||
|
||||
/**
|
||||
* Map "jwkKeyList" to the jwk property on this bean.
|
||||
* Everything else returns null
|
||||
*/
|
||||
@Override
|
||||
public View resolveViewName(String viewName, Locale locale) throws Exception {
|
||||
if (viewName != null) {
|
||||
if (viewName.equals(getJwkViewName())) {
|
||||
return getJwk();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the jwk
|
||||
*/
|
||||
public View getJwk() {
|
||||
return jwk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jwk the jwk to set
|
||||
*/
|
||||
public void setJwk(View jwk) {
|
||||
this.jwk = jwk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the order
|
||||
*/
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param order the order to set
|
||||
*/
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the jwkViewName
|
||||
*/
|
||||
public String getJwkViewName() {
|
||||
return jwkViewName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jwkViewName the jwkViewName to set
|
||||
*/
|
||||
public void setJwkViewName(String jwkViewName) {
|
||||
this.jwkViewName = jwkViewName;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.model;
|
||||
|
||||
/**
|
||||
*
|
||||
* Data container to facilitate returns from the IssuerService API.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class IssuerServiceResponse {
|
||||
|
||||
private String issuer;
|
||||
private String loginHint;
|
||||
private String targetLinkUri;
|
||||
private String redirectUrl;
|
||||
|
||||
/**
|
||||
* @param issuer
|
||||
* @param loginHint
|
||||
* @param targetLinkUri
|
||||
*/
|
||||
public IssuerServiceResponse(String issuer, String loginHint, String targetLinkUri) {
|
||||
this.issuer = issuer;
|
||||
this.loginHint = loginHint;
|
||||
this.targetLinkUri = targetLinkUri;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param redirectUrl
|
||||
*/
|
||||
public IssuerServiceResponse(String redirectUrl) {
|
||||
this.redirectUrl = redirectUrl;
|
||||
}
|
||||
/**
|
||||
* @return the issuer
|
||||
*/
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
/**
|
||||
* @param issuer the issuer to set
|
||||
*/
|
||||
public void setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
}
|
||||
/**
|
||||
* @return the loginHint
|
||||
*/
|
||||
public String getLoginHint() {
|
||||
return loginHint;
|
||||
}
|
||||
/**
|
||||
* @param loginHint the loginHint to set
|
||||
*/
|
||||
public void setLoginHint(String loginHint) {
|
||||
this.loginHint = loginHint;
|
||||
}
|
||||
/**
|
||||
* @return the targetLinkUri
|
||||
*/
|
||||
public String getTargetLinkUri() {
|
||||
return targetLinkUri;
|
||||
}
|
||||
/**
|
||||
* @param targetLinkUri the targetLinkUri to set
|
||||
*/
|
||||
public void setTargetLinkUri(String targetLinkUri) {
|
||||
this.targetLinkUri = targetLinkUri;
|
||||
}
|
||||
/**
|
||||
* @return the redirectUrl
|
||||
*/
|
||||
public String getRedirectUrl() {
|
||||
return redirectUrl;
|
||||
}
|
||||
/**
|
||||
* @param redirectUrl the redirectUrl to set
|
||||
*/
|
||||
public void setRedirectUrl(String redirectUrl) {
|
||||
this.redirectUrl = redirectUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the redirect url has been set, then we should send a redirect using it instead of processing things.
|
||||
*/
|
||||
public boolean shouldRedirect() {
|
||||
return this.redirectUrl != null;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
* This service provides any extra options that need to be passed to the authentication request,
|
||||
* either through the authorization endpoint (getOptions) or the token endpoint (getTokenOptions).
|
||||
* These options may depend on the server configuration, client configuration, or HTTP request.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface AuthRequestOptionsService {
|
||||
|
||||
/**
|
||||
* The set of options needed at the authorization endpoint.
|
||||
*
|
||||
* @param server
|
||||
* @param client
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> getOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request);
|
||||
|
||||
/**
|
||||
* The set of options needed at the token endpoint.
|
||||
*
|
||||
* @param server
|
||||
* @param client
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public Map<String, String> getTokenOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request);
|
||||
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* Builds a URL string to the IdP's authorization endpoint.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface AuthRequestUrlBuilder {
|
||||
|
||||
/**
|
||||
* @param serverConfig
|
||||
* @param clientConfig
|
||||
* @param redirectUri
|
||||
* @param nonce
|
||||
* @param state
|
||||
* @param loginHint
|
||||
* @return
|
||||
*/
|
||||
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint);
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface ClientConfigurationService {
|
||||
|
||||
public RegisteredClient getClientConfiguration(ServerConfiguration issuer);
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
|
||||
/**
|
||||
*
|
||||
* Gets an issuer for the given request. Might do dynamic discovery, or might be statically configured.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface IssuerService {
|
||||
|
||||
public IssuerServiceResponse getIssuer(HttpServletRequest request);
|
||||
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface RegisteredClientService {
|
||||
|
||||
/**
|
||||
* Get a remembered client (if one exists) to talk to the given issuer. This
|
||||
* client likely doesn't have its full configuration information but contains
|
||||
* the information needed to fetch it.
|
||||
* @param issuer
|
||||
* @return
|
||||
*/
|
||||
RegisteredClient getByIssuer(String issuer);
|
||||
|
||||
/**
|
||||
* Save this client's information for talking to the given issuer. This will
|
||||
* save only enough information to fetch the client's full configuration from
|
||||
* the server.
|
||||
* @param client
|
||||
*/
|
||||
void save(String issuer, RegisteredClient client);
|
||||
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service;
|
||||
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public interface ServerConfigurationService {
|
||||
|
||||
public ServerConfiguration getServerConfiguration(String issuer);
|
||||
|
||||
}
|
|
@ -1,247 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.mitre.openid.connect.client.service.RegisteredClientService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class DynamicRegistrationClientConfigurationService implements ClientConfigurationService {
|
||||
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(DynamicRegistrationClientConfigurationService.class);
|
||||
|
||||
private LoadingCache<ServerConfiguration, RegisteredClient> clients;
|
||||
|
||||
private RegisteredClientService registeredClientService = new InMemoryRegisteredClientService();
|
||||
|
||||
private RegisteredClient template;
|
||||
|
||||
private Set<String> whitelist = new HashSet<>();
|
||||
private Set<String> blacklist = new HashSet<>();
|
||||
|
||||
public DynamicRegistrationClientConfigurationService() {
|
||||
this(HttpClientBuilder.create().useSystemProperties().build());
|
||||
}
|
||||
|
||||
public DynamicRegistrationClientConfigurationService(HttpClient httpClient) {
|
||||
clients = CacheBuilder.newBuilder().build(new DynamicClientRegistrationLoader(httpClient));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(ServerConfiguration issuer) {
|
||||
try {
|
||||
if (!whitelist.isEmpty() && !whitelist.contains(issuer.getIssuer())) {
|
||||
throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + issuer);
|
||||
}
|
||||
|
||||
if (blacklist.contains(issuer.getIssuer())) {
|
||||
throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer);
|
||||
}
|
||||
|
||||
return clients.get(issuer);
|
||||
} catch (UncheckedExecutionException | ExecutionException e) {
|
||||
logger.warn("Unable to get client configuration", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the template
|
||||
*/
|
||||
public RegisteredClient getTemplate() {
|
||||
return template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param template the template to set
|
||||
*/
|
||||
public void setTemplate(RegisteredClient template) {
|
||||
// make sure the template doesn't have unwanted fields set on it
|
||||
if (template != null) {
|
||||
template.setClientId(null);
|
||||
template.setClientSecret(null);
|
||||
template.setRegistrationClientUri(null);
|
||||
template.setRegistrationAccessToken(null);
|
||||
}
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the registeredClientService
|
||||
*/
|
||||
public RegisteredClientService getRegisteredClientService() {
|
||||
return registeredClientService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registeredClientService the registeredClientService to set
|
||||
*/
|
||||
public void setRegisteredClientService(RegisteredClientService registeredClientService) {
|
||||
this.registeredClientService = registeredClientService;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the whitelist
|
||||
*/
|
||||
public Set<String> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whitelist the whitelist to set
|
||||
*/
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blacklist
|
||||
*/
|
||||
public Set<String> getBlacklist() {
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blacklist the blacklist to set
|
||||
*/
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
this.blacklist = blacklist;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loader class that fetches the client information.
|
||||
*
|
||||
* If a client has been registered (ie, it's known to the RegisteredClientService), then this
|
||||
* will fetch the client's configuration from the server.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class DynamicClientRegistrationLoader extends CacheLoader<ServerConfiguration, RegisteredClient> {
|
||||
private HttpComponentsClientHttpRequestFactory httpFactory;
|
||||
private Gson gson = new Gson(); // note that this doesn't serialize nulls by default
|
||||
|
||||
public DynamicClientRegistrationLoader() {
|
||||
this(HttpClientBuilder.create().useSystemProperties().build());
|
||||
}
|
||||
|
||||
public DynamicClientRegistrationLoader(HttpClient httpClient) {
|
||||
this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegisteredClient load(ServerConfiguration serverConfig) throws Exception {
|
||||
RestTemplate restTemplate = new RestTemplate(httpFactory);
|
||||
|
||||
|
||||
RegisteredClient knownClient = registeredClientService.getByIssuer(serverConfig.getIssuer());
|
||||
if (knownClient == null) {
|
||||
|
||||
// dynamically register this client
|
||||
JsonObject jsonRequest = ClientDetailsEntityJsonProcessor.serialize(template);
|
||||
String serializedClient = gson.toJson(jsonRequest);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_JSON);
|
||||
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(serializedClient, headers);
|
||||
|
||||
try {
|
||||
String registered = restTemplate.postForObject(serverConfig.getRegistrationEndpointUri(), entity, String.class);
|
||||
|
||||
RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
|
||||
|
||||
// save this client for later
|
||||
registeredClientService.save(serverConfig.getIssuer(), client);
|
||||
|
||||
return client;
|
||||
} catch (RestClientException rce) {
|
||||
throw new InvalidClientException("Error registering client with server");
|
||||
}
|
||||
} else {
|
||||
|
||||
if (knownClient.getClientId() == null) {
|
||||
|
||||
// load this client's information from the server
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, knownClient.getRegistrationAccessToken()));
|
||||
headers.setAccept(Lists.newArrayList(MediaType.APPLICATION_JSON));
|
||||
|
||||
HttpEntity<String> entity = new HttpEntity<>(headers);
|
||||
|
||||
try {
|
||||
String registered = restTemplate.exchange(knownClient.getRegistrationClientUri(), HttpMethod.GET, entity, String.class).getBody();
|
||||
// TODO: handle HTTP errors
|
||||
|
||||
RegisteredClient client = ClientDetailsEntityJsonProcessor.parseRegistered(registered);
|
||||
|
||||
return client;
|
||||
} catch (RestClientException rce) {
|
||||
throw new InvalidClientException("Error loading previously registered client information from server");
|
||||
}
|
||||
} else {
|
||||
// it's got a client ID from the store, don't bother trying to load it
|
||||
return knownClient;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import static org.mitre.util.JsonUtils.getAsBoolean;
|
||||
import static org.mitre.util.JsonUtils.getAsEncryptionMethodList;
|
||||
import static org.mitre.util.JsonUtils.getAsJweAlgorithmList;
|
||||
import static org.mitre.util.JsonUtils.getAsJwsAlgorithmList;
|
||||
import static org.mitre.util.JsonUtils.getAsString;
|
||||
import static org.mitre.util.JsonUtils.getAsStringList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
*
|
||||
* Dynamically fetches OpenID Connect server configurations based on the issuer. Caches the server configurations.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class DynamicServerConfigurationService implements ServerConfigurationService {
|
||||
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(DynamicServerConfigurationService.class);
|
||||
|
||||
// map of issuer -> server configuration, loaded dynamically from service discovery
|
||||
private LoadingCache<String, ServerConfiguration> servers;
|
||||
|
||||
private Set<String> whitelist = new HashSet<>();
|
||||
private Set<String> blacklist = new HashSet<>();
|
||||
|
||||
public DynamicServerConfigurationService() {
|
||||
this(HttpClientBuilder.create().useSystemProperties().build());
|
||||
}
|
||||
|
||||
public DynamicServerConfigurationService(HttpClient httpClient) {
|
||||
// initialize the cache
|
||||
servers = CacheBuilder.newBuilder().build(new OpenIDConnectServiceConfigurationFetcher(httpClient));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the whitelist
|
||||
*/
|
||||
public Set<String> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whitelist the whitelist to set
|
||||
*/
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blacklist
|
||||
*/
|
||||
public Set<String> getBlacklist() {
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blacklist the blacklist to set
|
||||
*/
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
this.blacklist = blacklist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerConfiguration getServerConfiguration(String issuer) {
|
||||
try {
|
||||
|
||||
if (!whitelist.isEmpty() && !whitelist.contains(issuer)) {
|
||||
throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + issuer);
|
||||
}
|
||||
|
||||
if (blacklist.contains(issuer)) {
|
||||
throw new AuthenticationServiceException("Issuer was in blacklist: " + issuer);
|
||||
}
|
||||
|
||||
return servers.get(issuer);
|
||||
} catch (UncheckedExecutionException | ExecutionException e) {
|
||||
logger.warn("Couldn't load configuration for " + issuer + ": " + e);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
private class OpenIDConnectServiceConfigurationFetcher extends CacheLoader<String, ServerConfiguration> {
|
||||
private HttpComponentsClientHttpRequestFactory httpFactory;
|
||||
private JsonParser parser = new JsonParser();
|
||||
|
||||
OpenIDConnectServiceConfigurationFetcher(HttpClient httpClient) {
|
||||
this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerConfiguration load(String issuer) throws Exception {
|
||||
RestTemplate restTemplate = new RestTemplate(httpFactory);
|
||||
|
||||
// data holder
|
||||
ServerConfiguration conf = new ServerConfiguration();
|
||||
|
||||
// construct the well-known URI
|
||||
String url = issuer + "/.well-known/openid-configuration";
|
||||
|
||||
// fetch the value
|
||||
String jsonString = restTemplate.getForObject(url, String.class);
|
||||
|
||||
JsonElement parsed = parser.parse(jsonString);
|
||||
if (parsed.isJsonObject()) {
|
||||
|
||||
JsonObject o = parsed.getAsJsonObject();
|
||||
|
||||
// sanity checks
|
||||
if (!o.has("issuer")) {
|
||||
throw new IllegalStateException("Returned object did not have an 'issuer' field");
|
||||
}
|
||||
|
||||
if (!issuer.equals(o.get("issuer").getAsString())) {
|
||||
logger.info("Issuer used for discover was " + issuer + " but final issuer is " + o.get("issuer").getAsString());
|
||||
}
|
||||
|
||||
conf.setIssuer(o.get("issuer").getAsString());
|
||||
|
||||
|
||||
conf.setAuthorizationEndpointUri(getAsString(o, "authorization_endpoint"));
|
||||
conf.setTokenEndpointUri(getAsString(o, "token_endpoint"));
|
||||
conf.setJwksUri(getAsString(o, "jwks_uri"));
|
||||
conf.setUserInfoUri(getAsString(o, "userinfo_endpoint"));
|
||||
conf.setRegistrationEndpointUri(getAsString(o, "registration_endpoint"));
|
||||
conf.setIntrospectionEndpointUri(getAsString(o, "introspection_endpoint"));
|
||||
conf.setAcrValuesSupported(getAsStringList(o, "acr_values_supported"));
|
||||
conf.setCheckSessionIframe(getAsString(o, "check_session_iframe"));
|
||||
conf.setClaimsLocalesSupported(getAsStringList(o, "claims_locales_supported"));
|
||||
conf.setClaimsParameterSupported(getAsBoolean(o, "claims_parameter_supported"));
|
||||
conf.setClaimsSupported(getAsStringList(o, "claims_supported"));
|
||||
conf.setDisplayValuesSupported(getAsStringList(o, "display_values_supported"));
|
||||
conf.setEndSessionEndpoint(getAsString(o, "end_session_endpoint"));
|
||||
conf.setGrantTypesSupported(getAsStringList(o, "grant_types_supported"));
|
||||
conf.setIdTokenSigningAlgValuesSupported(getAsJwsAlgorithmList(o, "id_token_signing_alg_values_supported"));
|
||||
conf.setIdTokenEncryptionAlgValuesSupported(getAsJweAlgorithmList(o, "id_token_encryption_alg_values_supported"));
|
||||
conf.setIdTokenEncryptionEncValuesSupported(getAsEncryptionMethodList(o, "id_token_encryption_enc_values_supported"));
|
||||
conf.setOpPolicyUri(getAsString(o, "op_policy_uri"));
|
||||
conf.setOpTosUri(getAsString(o, "op_tos_uri"));
|
||||
conf.setRequestObjectEncryptionAlgValuesSupported(getAsJweAlgorithmList(o, "request_object_encryption_alg_values_supported"));
|
||||
conf.setRequestObjectEncryptionEncValuesSupported(getAsEncryptionMethodList(o, "request_object_encryption_enc_values_supported"));
|
||||
conf.setRequestObjectSigningAlgValuesSupported(getAsJwsAlgorithmList(o, "request_object_signing_alg_values_supported"));
|
||||
conf.setRequestParameterSupported(getAsBoolean(o, "request_parameter_supported"));
|
||||
conf.setRequestUriParameterSupported(getAsBoolean(o, "request_uri_parameter_supported"));
|
||||
conf.setResponseTypesSupported(getAsStringList(o, "response_types_supported"));
|
||||
conf.setScopesSupported(getAsStringList(o, "scopes_supported"));
|
||||
conf.setSubjectTypesSupported(getAsStringList(o, "subject_types_supported"));
|
||||
conf.setServiceDocumentation(getAsString(o, "service_documentation"));
|
||||
conf.setTokenEndpointAuthMethodsSupported(getAsStringList(o, "token_endpoint_auth_methods"));
|
||||
conf.setTokenEndpointAuthSigningAlgValuesSupported(getAsJwsAlgorithmList(o, "token_endpoint_auth_signing_alg_values_supported"));
|
||||
conf.setUiLocalesSupported(getAsStringList(o, "ui_locales_supported"));
|
||||
conf.setUserinfoEncryptionAlgValuesSupported(getAsJweAlgorithmList(o, "userinfo_encryption_alg_values_supported"));
|
||||
conf.setUserinfoEncryptionEncValuesSupported(getAsEncryptionMethodList(o, "userinfo_encryption_enc_values_supported"));
|
||||
conf.setUserinfoSigningAlgValuesSupported(getAsJwsAlgorithmList(o, "userinfo_signing_alg_values_supported"));
|
||||
|
||||
return conf;
|
||||
} else {
|
||||
throw new IllegalStateException("Couldn't parse server discovery results for " + url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.mitre.jwt.encryption.service.JWTEncryptionAndDecryptionService;
|
||||
import org.mitre.jwt.signer.service.impl.JWKSetCacheService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jose.EncryptionMethod;
|
||||
import com.nimbusds.jose.JWEAlgorithm;
|
||||
import com.nimbusds.jose.JWEHeader;
|
||||
import com.nimbusds.jwt.EncryptedJWT;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class EncryptedAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
|
||||
|
||||
private JWKSetCacheService encrypterService;
|
||||
|
||||
private JWEAlgorithm alg;
|
||||
private EncryptionMethod enc;
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequestUrl(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, java.lang.String, java.lang.String, java.lang.String, java.util.Map)
|
||||
*/
|
||||
@Override
|
||||
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint) {
|
||||
|
||||
// create our signed JWT for the request object
|
||||
JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder();
|
||||
|
||||
//set parameters to JwtClaims
|
||||
claims.claim("response_type", "code");
|
||||
claims.claim("client_id", clientConfig.getClientId());
|
||||
claims.claim("scope", Joiner.on(" ").join(clientConfig.getScope()));
|
||||
|
||||
// build our redirect URI
|
||||
claims.claim("redirect_uri", redirectUri);
|
||||
|
||||
// this comes back in the id token
|
||||
claims.claim("nonce", nonce);
|
||||
|
||||
// this comes back in the auth request return
|
||||
claims.claim("state", state);
|
||||
|
||||
// Optional parameters
|
||||
for (Entry<String, String> option : options.entrySet()) {
|
||||
claims.claim(option.getKey(), option.getValue());
|
||||
}
|
||||
|
||||
// if there's a login hint, send it
|
||||
if (!Strings.isNullOrEmpty(loginHint)) {
|
||||
claims.claim("login_hint", loginHint);
|
||||
}
|
||||
|
||||
EncryptedJWT jwt = new EncryptedJWT(new JWEHeader(alg, enc), claims.build());
|
||||
|
||||
JWTEncryptionAndDecryptionService encryptor = encrypterService.getEncrypter(serverConfig.getJwksUri());
|
||||
|
||||
encryptor.encryptJwt(jwt);
|
||||
|
||||
try {
|
||||
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
|
||||
uriBuilder.addParameter("request", jwt.serialize());
|
||||
|
||||
// build out the URI
|
||||
return uriBuilder.build().toString();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AuthenticationServiceException("Malformed Authorization Endpoint Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the encrypterService
|
||||
*/
|
||||
public JWKSetCacheService getEncrypterService() {
|
||||
return encrypterService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param encrypterService the encrypterService to set
|
||||
*/
|
||||
public void setEncrypterService(JWKSetCacheService encrypterService) {
|
||||
this.encrypterService = encrypterService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the alg
|
||||
*/
|
||||
public JWEAlgorithm getAlg() {
|
||||
return alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alg the alg to set
|
||||
*/
|
||||
public void setAlg(JWEAlgorithm alg) {
|
||||
this.alg = alg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the enc
|
||||
*/
|
||||
public EncryptionMethod getEnc() {
|
||||
return enc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param enc the enc to set
|
||||
*/
|
||||
public void setEnc(EncryptionMethod enc) {
|
||||
this.enc = enc;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.mitre.openid.connect.client.service.RegisteredClientService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* Houses both a static client configuration and a dynamic client configuration
|
||||
* service in one object. Checks the static service first, then falls through to
|
||||
* the dynamic service.
|
||||
*
|
||||
* Provides configuration passthrough for the template, registered client service, whitelist,
|
||||
* and blacklist for the dynamic service, and to the static service's client map.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class HybridClientConfigurationService implements ClientConfigurationService {
|
||||
|
||||
private StaticClientConfigurationService staticClientService = new StaticClientConfigurationService();
|
||||
|
||||
private DynamicRegistrationClientConfigurationService dynamicClientService = new DynamicRegistrationClientConfigurationService();
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.ClientConfigurationService#getClientConfiguration(org.mitre.openid.connect.config.ServerConfiguration)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(ServerConfiguration issuer) {
|
||||
|
||||
RegisteredClient client = staticClientService.getClientConfiguration(issuer);
|
||||
if (client != null) {
|
||||
return client;
|
||||
} else {
|
||||
return dynamicClientService.getClientConfiguration(issuer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.StaticClientConfigurationService#getClients()
|
||||
*/
|
||||
public Map<String, RegisteredClient> getClients() {
|
||||
return staticClientService.getClients();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clients
|
||||
* @see org.mitre.openid.connect.client.service.impl.StaticClientConfigurationService#setClients(java.util.Map)
|
||||
*/
|
||||
public void setClients(Map<String, RegisteredClient> clients) {
|
||||
staticClientService.setClients(clients);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#getTemplate()
|
||||
*/
|
||||
public RegisteredClient getTemplate() {
|
||||
return dynamicClientService.getTemplate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param template
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#setTemplate(org.mitre.oauth2.model.RegisteredClient)
|
||||
*/
|
||||
public void setTemplate(RegisteredClient template) {
|
||||
dynamicClientService.setTemplate(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#getRegisteredClientService()
|
||||
*/
|
||||
public RegisteredClientService getRegisteredClientService() {
|
||||
return dynamicClientService.getRegisteredClientService();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param registeredClientService
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#setRegisteredClientService(org.mitre.openid.connect.client.service.RegisteredClientService)
|
||||
*/
|
||||
public void setRegisteredClientService(RegisteredClientService registeredClientService) {
|
||||
dynamicClientService.setRegisteredClientService(registeredClientService);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#getWhitelist()
|
||||
*/
|
||||
public Set<String> getWhitelist() {
|
||||
return dynamicClientService.getWhitelist();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whitelist
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#setWhitelist(java.util.Set)
|
||||
*/
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
dynamicClientService.setWhitelist(whitelist);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#getBlacklist()
|
||||
*/
|
||||
public Set<String> getBlacklist() {
|
||||
return dynamicClientService.getBlacklist();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blacklist
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicRegistrationClientConfigurationService#setBlacklist(java.util.Set)
|
||||
*/
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
dynamicClientService.setBlacklist(blacklist);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.IssuerService;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
/**
|
||||
*
|
||||
* Issuer service that tries to parse input from the inputs from a third-party
|
||||
* account chooser service (if possible), but falls back to webfinger discovery
|
||||
* if not.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class HybridIssuerService implements IssuerService {
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.ThirdPartyIssuerService#getAccountChooserUrl()
|
||||
*/
|
||||
public String getAccountChooserUrl() {
|
||||
return thirdPartyIssuerService.getAccountChooserUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accountChooserUrl
|
||||
* @see org.mitre.openid.connect.client.service.impl.ThirdPartyIssuerService#setAccountChooserUrl(java.lang.String)
|
||||
*/
|
||||
public void setAccountChooserUrl(String accountChooserUrl) {
|
||||
thirdPartyIssuerService.setAccountChooserUrl(accountChooserUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.WebfingerIssuerService#isForceHttps()
|
||||
*/
|
||||
public boolean isForceHttps() {
|
||||
return webfingerIssuerService.isForceHttps();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceHttps
|
||||
* @see org.mitre.openid.connect.client.service.impl.WebfingerIssuerService#setForceHttps(boolean)
|
||||
*/
|
||||
public void setForceHttps(boolean forceHttps) {
|
||||
webfingerIssuerService.setForceHttps(forceHttps);
|
||||
}
|
||||
|
||||
private ThirdPartyIssuerService thirdPartyIssuerService = new ThirdPartyIssuerService();
|
||||
private WebfingerIssuerService webfingerIssuerService = new WebfingerIssuerService();
|
||||
|
||||
@Override
|
||||
public IssuerServiceResponse getIssuer(HttpServletRequest request) {
|
||||
|
||||
IssuerServiceResponse resp = thirdPartyIssuerService.getIssuer(request);
|
||||
if (resp.shouldRedirect()) {
|
||||
// if it wants us to redirect, try the webfinger approach first
|
||||
return webfingerIssuerService.getIssuer(request);
|
||||
} else {
|
||||
return resp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Set<String> getWhitelist() {
|
||||
return Sets.union(thirdPartyIssuerService.getWhitelist(), webfingerIssuerService.getWhitelist());
|
||||
}
|
||||
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
thirdPartyIssuerService.setWhitelist(whitelist);
|
||||
webfingerIssuerService.setWhitelist(whitelist);
|
||||
}
|
||||
|
||||
public Set<String> getBlacklist() {
|
||||
return Sets.union(thirdPartyIssuerService.getBlacklist(), webfingerIssuerService.getWhitelist());
|
||||
}
|
||||
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
thirdPartyIssuerService.setBlacklist(blacklist);
|
||||
webfingerIssuerService.setBlacklist(blacklist);
|
||||
}
|
||||
|
||||
public String getParameterName() {
|
||||
return webfingerIssuerService.getParameterName();
|
||||
}
|
||||
|
||||
public void setParameterName(String parameterName) {
|
||||
webfingerIssuerService.setParameterName(parameterName);
|
||||
}
|
||||
|
||||
public String getLoginPageUrl() {
|
||||
return webfingerIssuerService.getLoginPageUrl();
|
||||
}
|
||||
|
||||
public void setLoginPageUrl(String loginPageUrl) {
|
||||
webfingerIssuerService.setLoginPageUrl(loginPageUrl);
|
||||
thirdPartyIssuerService.setAccountChooserUrl(loginPageUrl); // set the same URL on both, but this one gets ignored
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,115 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* Houses both a static server configuration and a dynamic server configuration
|
||||
* service in one object. Checks the static service first, then falls through to
|
||||
* the dynamic service.
|
||||
*
|
||||
* Provides configuration passthrough to the dynamic service's whitelist and blacklist,
|
||||
* and to the static service's server map.
|
||||
*
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class HybridServerConfigurationService implements ServerConfigurationService {
|
||||
|
||||
private StaticServerConfigurationService staticServerService = new StaticServerConfigurationService();
|
||||
|
||||
private DynamicServerConfigurationService dynamicServerService = new DynamicServerConfigurationService();
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.ServerConfigurationService#getServerConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ServerConfiguration getServerConfiguration(String issuer) {
|
||||
ServerConfiguration server = staticServerService.getServerConfiguration(issuer);
|
||||
if (server != null) {
|
||||
return server;
|
||||
} else {
|
||||
return dynamicServerService.getServerConfiguration(issuer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.StaticServerConfigurationService#getServers()
|
||||
*/
|
||||
public Map<String, ServerConfiguration> getServers() {
|
||||
return staticServerService.getServers();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param servers
|
||||
* @see org.mitre.openid.connect.client.service.impl.StaticServerConfigurationService#setServers(java.util.Map)
|
||||
*/
|
||||
public void setServers(Map<String, ServerConfiguration> servers) {
|
||||
staticServerService.setServers(servers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicServerConfigurationService#getWhitelist()
|
||||
*/
|
||||
public Set<String> getWhitelist() {
|
||||
return dynamicServerService.getWhitelist();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param whitelist
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicServerConfigurationService#setWhitelist(java.util.Set)
|
||||
*/
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
dynamicServerService.setWhitelist(whitelist);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicServerConfigurationService#getBlacklist()
|
||||
*/
|
||||
public Set<String> getBlacklist() {
|
||||
return dynamicServerService.getBlacklist();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param blacklist
|
||||
* @see org.mitre.openid.connect.client.service.impl.DynamicServerConfigurationService#setBlacklist(java.util.Set)
|
||||
*/
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
dynamicServerService.setBlacklist(blacklist);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.RegisteredClientService;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class InMemoryRegisteredClientService implements RegisteredClientService {
|
||||
|
||||
private Map<String, RegisteredClient> clients = new HashMap<>();
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.RegisteredClientService#getByIssuer(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getByIssuer(String issuer) {
|
||||
return clients.get(issuer);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.RegisteredClientService#save(org.mitre.oauth2.model.RegisteredClient)
|
||||
*/
|
||||
@Override
|
||||
public void save(String issuer, RegisteredClient client) {
|
||||
clients.put(issuer, client);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.ClientDetailsEntityJsonProcessor;
|
||||
import org.mitre.openid.connect.client.service.RegisteredClientService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class JsonFileRegisteredClientService implements RegisteredClientService {
|
||||
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(JsonFileRegisteredClientService.class);
|
||||
|
||||
private Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(RegisteredClient.class, new JsonSerializer<RegisteredClient>() {
|
||||
@Override
|
||||
public JsonElement serialize(RegisteredClient src, Type typeOfSrc, JsonSerializationContext context) {
|
||||
return ClientDetailsEntityJsonProcessor.serialize(src);
|
||||
}
|
||||
})
|
||||
.registerTypeAdapter(RegisteredClient.class, new JsonDeserializer<RegisteredClient>() {
|
||||
@Override
|
||||
public RegisteredClient deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return ClientDetailsEntityJsonProcessor.parseRegistered(json);
|
||||
}
|
||||
})
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
private File file;
|
||||
|
||||
private Map<String, RegisteredClient> clients = new HashMap<>();
|
||||
|
||||
public JsonFileRegisteredClientService(String filename) {
|
||||
this.file = new File(filename);
|
||||
load();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.RegisteredClientService#getByIssuer(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getByIssuer(String issuer) {
|
||||
return clients.get(issuer);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.RegisteredClientService#save(java.lang.String, org.mitre.oauth2.model.RegisteredClient)
|
||||
*/
|
||||
@Override
|
||||
public void save(String issuer, RegisteredClient client) {
|
||||
clients.put(issuer, client);
|
||||
write();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync the map of clients out to disk.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private void write() {
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
// create a new file
|
||||
logger.info("Creating saved clients list in " + file);
|
||||
file.createNewFile();
|
||||
}
|
||||
FileWriter out = new FileWriter(file);
|
||||
|
||||
gson.toJson(clients, new TypeToken<Map<String, RegisteredClient>>(){}.getType(), out);
|
||||
|
||||
out.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not write to output file", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the map in from disk.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private void load() {
|
||||
try {
|
||||
if (!file.exists()) {
|
||||
logger.info("No sved clients file found in " + file);
|
||||
return;
|
||||
}
|
||||
FileReader in = new FileReader(file);
|
||||
|
||||
clients = gson.fromJson(in, new TypeToken<Map<String, RegisteredClient>>(){}.getType());
|
||||
|
||||
in.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Could not read from input file", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
*
|
||||
* Builds an auth request redirect URI with normal query parameters.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class PlainAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequest(javax.servlet.http.HttpServletRequest, org.mitre.openid.connect.config.ServerConfiguration, org.springframework.security.oauth2.provider.ClientDetails)
|
||||
*/
|
||||
@Override
|
||||
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint) {
|
||||
try {
|
||||
|
||||
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
|
||||
uriBuilder.addParameter("response_type", "code");
|
||||
uriBuilder.addParameter("client_id", clientConfig.getClientId());
|
||||
uriBuilder.addParameter("scope", Joiner.on(" ").join(clientConfig.getScope()));
|
||||
|
||||
uriBuilder.addParameter("redirect_uri", redirectUri);
|
||||
|
||||
uriBuilder.addParameter("nonce", nonce);
|
||||
|
||||
uriBuilder.addParameter("state", state);
|
||||
|
||||
// Optional parameters:
|
||||
for (Entry<String, String> option : options.entrySet()) {
|
||||
uriBuilder.addParameter(option.getKey(), option.getValue());
|
||||
}
|
||||
|
||||
// if there's a login hint, send it
|
||||
if (!Strings.isNullOrEmpty(loginHint)) {
|
||||
uriBuilder.addParameter("login_hint", loginHint);
|
||||
}
|
||||
|
||||
return uriBuilder.build().toString();
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AuthenticationServiceException("Malformed Authorization Endpoint Uri", e);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,116 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.mitre.jwt.signer.service.JWTSigningAndValidationService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestUrlBuilder;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Strings;
|
||||
import com.nimbusds.jose.JWSAlgorithm;
|
||||
import com.nimbusds.jose.JWSHeader;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class SignedAuthRequestUrlBuilder implements AuthRequestUrlBuilder {
|
||||
|
||||
private JWTSigningAndValidationService signingAndValidationService;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.AuthRequestUrlBuilder#buildAuthRequestUrl(org.mitre.openid.connect.config.ServerConfiguration, org.springframework.security.oauth2.provider.ClientDetails, java.lang.String, java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public String buildAuthRequestUrl(ServerConfiguration serverConfig, RegisteredClient clientConfig, String redirectUri, String nonce, String state, Map<String, String> options, String loginHint) {
|
||||
|
||||
// create our signed JWT for the request object
|
||||
JWTClaimsSet.Builder claims = new JWTClaimsSet.Builder();
|
||||
|
||||
//set parameters to JwtClaims
|
||||
claims.claim("response_type", "code");
|
||||
claims.claim("client_id", clientConfig.getClientId());
|
||||
claims.claim("scope", Joiner.on(" ").join(clientConfig.getScope()));
|
||||
|
||||
// build our redirect URI
|
||||
claims.claim("redirect_uri", redirectUri);
|
||||
|
||||
// this comes back in the id token
|
||||
claims.claim("nonce", nonce);
|
||||
|
||||
// this comes back in the auth request return
|
||||
claims.claim("state", state);
|
||||
|
||||
// Optional parameters
|
||||
for (Entry<String, String> option : options.entrySet()) {
|
||||
claims.claim(option.getKey(), option.getValue());
|
||||
}
|
||||
|
||||
// if there's a login hint, send it
|
||||
if (!Strings.isNullOrEmpty(loginHint)) {
|
||||
claims.claim("login_hint", loginHint);
|
||||
}
|
||||
|
||||
JWSAlgorithm alg = clientConfig.getRequestObjectSigningAlg();
|
||||
if (alg == null) {
|
||||
alg = signingAndValidationService.getDefaultSigningAlgorithm();
|
||||
}
|
||||
|
||||
SignedJWT jwt = new SignedJWT(new JWSHeader(alg), claims.build());
|
||||
|
||||
signingAndValidationService.signJwt(jwt, alg);
|
||||
|
||||
try {
|
||||
URIBuilder uriBuilder = new URIBuilder(serverConfig.getAuthorizationEndpointUri());
|
||||
uriBuilder.addParameter("request", jwt.serialize());
|
||||
|
||||
// build out the URI
|
||||
return uriBuilder.build().toString();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AuthenticationServiceException("Malformed Authorization Endpoint Uri", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the signingAndValidationService
|
||||
*/
|
||||
public JWTSigningAndValidationService getSigningAndValidationService() {
|
||||
return signingAndValidationService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param signingAndValidationService the signingAndValidationService to set
|
||||
*/
|
||||
public void setSigningAndValidationService(JWTSigningAndValidationService signingAndValidationService) {
|
||||
this.signingAndValidationService = signingAndValidationService;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.AuthRequestOptionsService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
*
|
||||
* Always returns the same set of options.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticAuthRequestOptionsService implements AuthRequestOptionsService {
|
||||
|
||||
private Map<String, String> options = new HashMap<>();
|
||||
private Map<String, String> tokenOptions = new HashMap<>();
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.AuthRequestOptionsService#getOptions(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request) {
|
||||
return options;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.AuthRequestOptionsService#getTokenOptions(org.mitre.openid.connect.config.ServerConfiguration, org.mitre.oauth2.model.RegisteredClient, javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
public Map<String, String> getTokenOptions(ServerConfiguration server, RegisteredClient client, HttpServletRequest request) {
|
||||
return tokenOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the options object directly
|
||||
*/
|
||||
public Map<String, String> getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options the options to set
|
||||
*/
|
||||
public void setOptions(Map<String, String> options) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the tokenOptions
|
||||
*/
|
||||
public Map<String, String> getTokenOptions() {
|
||||
return tokenOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tokenOptions the tokenOptions to set
|
||||
*/
|
||||
public void setTokenOptions(Map<String, String> tokenOptions) {
|
||||
this.tokenOptions = tokenOptions;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.client.service.ClientConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* Client configuration service that holds a static map from issuer URL to a ClientDetails object to use at that issuer.
|
||||
*
|
||||
* Designed to be configured as a bean.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticClientConfigurationService implements ClientConfigurationService {
|
||||
|
||||
// Map of issuer URL -> client configuration information
|
||||
private Map<String, RegisteredClient> clients;
|
||||
|
||||
/**
|
||||
* @return the clients
|
||||
*/
|
||||
public Map<String, RegisteredClient> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clients the clients to set
|
||||
*/
|
||||
public void setClients(Map<String, RegisteredClient> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client configured for this issuer
|
||||
*
|
||||
* @see org.mitre.openid.connect.client.service.ClientConfigurationService#getClientConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public RegisteredClient getClientConfiguration(ServerConfiguration issuer) {
|
||||
|
||||
return clients.get(issuer.getIssuer());
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
if (clients == null || clients.isEmpty()) {
|
||||
throw new IllegalArgumentException("Clients map cannot be null or empty");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,71 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.mitre.openid.connect.client.service.ServerConfigurationService;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
|
||||
/**
|
||||
* Statically configured server configuration service that maps issuer URLs to server configurations to use at that issuer.
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticServerConfigurationService implements ServerConfigurationService {
|
||||
|
||||
// map of issuer url -> server configuration information
|
||||
private Map<String, ServerConfiguration> servers;
|
||||
|
||||
/**
|
||||
* @return the servers
|
||||
*/
|
||||
public Map<String, ServerConfiguration> getServers() {
|
||||
return servers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param servers the servers to set
|
||||
*/
|
||||
public void setServers(Map<String, ServerConfiguration> servers) {
|
||||
this.servers = servers;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.ServerConfigurationService#getServerConfiguration(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public ServerConfiguration getServerConfiguration(String issuer) {
|
||||
return servers.get(issuer);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
if (servers == null || servers.isEmpty()) {
|
||||
throw new IllegalArgumentException("Servers map cannot be null or empty.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.IssuerService;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class StaticSingleIssuerService implements IssuerService {
|
||||
|
||||
private String issuer;
|
||||
|
||||
/**
|
||||
* @return the issuer
|
||||
*/
|
||||
public String getIssuer() {
|
||||
return issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param issuer the issuer to set
|
||||
*/
|
||||
public void setIssuer(String issuer) {
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always returns the configured issuer URL
|
||||
*
|
||||
* @see org.mitre.openid.connect.client.service.IssuerService#getIssuer(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
public IssuerServiceResponse getIssuer(HttpServletRequest request) {
|
||||
return new IssuerServiceResponse(getIssuer(), null, null);
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
if (Strings.isNullOrEmpty(issuer)) {
|
||||
throw new IllegalArgumentException("Issuer must not be null or empty.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.IssuerService;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
|
||||
/**
|
||||
*
|
||||
* Determines the issuer using an account chooser or other third-party-initiated login
|
||||
*
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class ThirdPartyIssuerService implements IssuerService {
|
||||
|
||||
private String accountChooserUrl;
|
||||
|
||||
private Set<String> whitelist = new HashSet<>();
|
||||
private Set<String> blacklist = new HashSet<>();
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.IssuerService#getIssuer(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
public IssuerServiceResponse getIssuer(HttpServletRequest request) {
|
||||
|
||||
// if the issuer is passed in, return that
|
||||
String iss = request.getParameter("iss");
|
||||
if (!Strings.isNullOrEmpty(iss)) {
|
||||
if (!whitelist.isEmpty() && !whitelist.contains(iss)) {
|
||||
throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + iss);
|
||||
}
|
||||
|
||||
if (blacklist.contains(iss)) {
|
||||
throw new AuthenticationServiceException("Issuer was in blacklist: " + iss);
|
||||
}
|
||||
|
||||
return new IssuerServiceResponse(iss, request.getParameter("login_hint"), request.getParameter("target_link_uri"));
|
||||
} else {
|
||||
|
||||
try {
|
||||
// otherwise, need to forward to the account chooser
|
||||
String redirectUri = request.getRequestURL().toString();
|
||||
URIBuilder builder = new URIBuilder(accountChooserUrl);
|
||||
|
||||
builder.addParameter("redirect_uri", redirectUri);
|
||||
|
||||
return new IssuerServiceResponse(builder.build().toString());
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
throw new AuthenticationServiceException("Account Chooser URL is not valid", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the accountChooserUrl
|
||||
*/
|
||||
public String getAccountChooserUrl() {
|
||||
return accountChooserUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param accountChooserUrl the accountChooserUrl to set
|
||||
*/
|
||||
public void setAccountChooserUrl(String accountChooserUrl) {
|
||||
this.accountChooserUrl = accountChooserUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the whitelist
|
||||
*/
|
||||
public Set<String> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whitelist the whitelist to set
|
||||
*/
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blacklist
|
||||
*/
|
||||
public Set<String> getBlacklist() {
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blacklist the blacklist to set
|
||||
*/
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
this.blacklist = blacklist;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void afterPropertiesSet() {
|
||||
if (Strings.isNullOrEmpty(this.accountChooserUrl)) {
|
||||
throw new IllegalArgumentException("Account Chooser URL cannot be null or empty");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,305 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.utils.URIBuilder;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.mitre.discovery.util.WebfingerURLNormalizer;
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mitre.openid.connect.client.service.IssuerService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.web.client.RestClientException;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.util.concurrent.UncheckedExecutionException;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
/**
|
||||
* Use Webfinger to discover the appropriate issuer for a user-given input string.
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class WebfingerIssuerService implements IssuerService {
|
||||
|
||||
/**
|
||||
* Logger for this class
|
||||
*/
|
||||
private static final Logger logger = LoggerFactory.getLogger(WebfingerIssuerService.class);
|
||||
|
||||
// map of user input -> issuer, loaded dynamically from webfinger discover
|
||||
private LoadingCache<String, LoadingResult> issuers;
|
||||
|
||||
// private data shuttle class to get back two bits of info from the cache loader
|
||||
private class LoadingResult {
|
||||
public String loginHint;
|
||||
public String issuer;
|
||||
public LoadingResult(String loginHint, String issuer) {
|
||||
this.loginHint = loginHint;
|
||||
this.issuer = issuer;
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> whitelist = new HashSet<>();
|
||||
private Set<String> blacklist = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Name of the incoming parameter to check for discovery purposes.
|
||||
*/
|
||||
private String parameterName = "identifier";
|
||||
|
||||
/**
|
||||
* URL of the page to forward to if no identifier is given.
|
||||
*/
|
||||
private String loginPageUrl;
|
||||
|
||||
/**
|
||||
* Strict enfocement of "https"
|
||||
*/
|
||||
private boolean forceHttps = true;
|
||||
|
||||
public WebfingerIssuerService() {
|
||||
this(HttpClientBuilder.create().useSystemProperties().build());
|
||||
}
|
||||
|
||||
public WebfingerIssuerService(HttpClient httpClient) {
|
||||
issuers = CacheBuilder.newBuilder().build(new WebfingerIssuerFetcher(httpClient));
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.mitre.openid.connect.client.service.IssuerService#getIssuer(javax.servlet.http.HttpServletRequest)
|
||||
*/
|
||||
@Override
|
||||
public IssuerServiceResponse getIssuer(HttpServletRequest request) {
|
||||
|
||||
String identifier = request.getParameter(parameterName);
|
||||
if (!Strings.isNullOrEmpty(identifier)) {
|
||||
try {
|
||||
LoadingResult lr = issuers.get(identifier);
|
||||
if (!whitelist.isEmpty() && !whitelist.contains(lr.issuer)) {
|
||||
throw new AuthenticationServiceException("Whitelist was nonempty, issuer was not in whitelist: " + lr.issuer);
|
||||
}
|
||||
|
||||
if (blacklist.contains(lr.issuer)) {
|
||||
throw new AuthenticationServiceException("Issuer was in blacklist: " + lr.issuer);
|
||||
}
|
||||
|
||||
return new IssuerServiceResponse(lr.issuer, lr.loginHint, request.getParameter("target_link_uri"));
|
||||
} catch (UncheckedExecutionException | ExecutionException e) {
|
||||
logger.warn("Issue fetching issuer for user input: " + identifier + ": " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
|
||||
} else {
|
||||
logger.warn("No user input given, directing to login page: " + loginPageUrl);
|
||||
return new IssuerServiceResponse(loginPageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the parameterName
|
||||
*/
|
||||
public String getParameterName() {
|
||||
return parameterName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parameterName the parameterName to set
|
||||
*/
|
||||
public void setParameterName(String parameterName) {
|
||||
this.parameterName = parameterName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the loginPageUrl
|
||||
*/
|
||||
public String getLoginPageUrl() {
|
||||
return loginPageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loginPageUrl the loginPageUrl to set
|
||||
*/
|
||||
public void setLoginPageUrl(String loginPageUrl) {
|
||||
this.loginPageUrl = loginPageUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the whitelist
|
||||
*/
|
||||
public Set<String> getWhitelist() {
|
||||
return whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param whitelist the whitelist to set
|
||||
*/
|
||||
public void setWhitelist(Set<String> whitelist) {
|
||||
this.whitelist = whitelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the blacklist
|
||||
*/
|
||||
public Set<String> getBlacklist() {
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param blacklist the blacklist to set
|
||||
*/
|
||||
public void setBlacklist(Set<String> blacklist) {
|
||||
this.blacklist = blacklist;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the forceHttps
|
||||
*/
|
||||
public boolean isForceHttps() {
|
||||
return forceHttps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forceHttps the forceHttps to set
|
||||
*/
|
||||
public void setForceHttps(boolean forceHttps) {
|
||||
this.forceHttps = forceHttps;
|
||||
}
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
private class WebfingerIssuerFetcher extends CacheLoader<String, LoadingResult> {
|
||||
private HttpComponentsClientHttpRequestFactory httpFactory;
|
||||
private JsonParser parser = new JsonParser();
|
||||
|
||||
WebfingerIssuerFetcher(HttpClient httpClient) {
|
||||
this.httpFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadingResult load(String identifier) throws Exception {
|
||||
|
||||
UriComponents key = WebfingerURLNormalizer.normalizeResource(identifier);
|
||||
|
||||
RestTemplate restTemplate = new RestTemplate(httpFactory);
|
||||
// construct the URL to go to
|
||||
|
||||
String scheme = key.getScheme();
|
||||
|
||||
// preserving http scheme is strictly for demo system use only.
|
||||
if (!Strings.isNullOrEmpty(scheme) &&scheme.equals("http")) {
|
||||
if (forceHttps) {
|
||||
throw new IllegalArgumentException("Scheme must not be 'http'");
|
||||
} else {
|
||||
logger.warn("Webfinger endpoint MUST use the https URI scheme, overriding by configuration");
|
||||
scheme = "http://"; // add on colon and slashes.
|
||||
}
|
||||
} else {
|
||||
// otherwise we don't know the scheme, assume HTTPS
|
||||
scheme = "https://";
|
||||
}
|
||||
|
||||
// do a webfinger lookup
|
||||
URIBuilder builder = new URIBuilder(scheme
|
||||
+ key.getHost()
|
||||
+ (key.getPort() >= 0 ? ":" + key.getPort() : "")
|
||||
+ Strings.nullToEmpty(key.getPath())
|
||||
+ "/.well-known/webfinger"
|
||||
+ (Strings.isNullOrEmpty(key.getQuery()) ? "" : "?" + key.getQuery())
|
||||
);
|
||||
builder.addParameter("resource", identifier);
|
||||
builder.addParameter("rel", "http://openid.net/specs/connect/1.0/issuer");
|
||||
|
||||
try {
|
||||
|
||||
// do the fetch
|
||||
logger.info("Loading: " + builder.toString());
|
||||
String webfingerResponse = restTemplate.getForObject(builder.build(), String.class);
|
||||
|
||||
JsonElement json = parser.parse(webfingerResponse);
|
||||
|
||||
if (json != null && json.isJsonObject()) {
|
||||
// find the issuer
|
||||
JsonArray links = json.getAsJsonObject().get("links").getAsJsonArray();
|
||||
for (JsonElement link : links) {
|
||||
if (link.isJsonObject()) {
|
||||
JsonObject linkObj = link.getAsJsonObject();
|
||||
if (linkObj.has("href")
|
||||
&& linkObj.has("rel")
|
||||
&& linkObj.get("rel").getAsString().equals("http://openid.net/specs/connect/1.0/issuer")) {
|
||||
|
||||
// we found the issuer, return it
|
||||
String href = linkObj.get("href").getAsString();
|
||||
|
||||
if (identifier.equals(href)
|
||||
|| identifier.startsWith("http")) {
|
||||
// try to avoid sending a URL as the login hint
|
||||
return new LoadingResult(null, href);
|
||||
} else {
|
||||
// otherwise pass back whatever the user typed as a login hint
|
||||
return new LoadingResult(identifier, href);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JsonParseException | RestClientException e) {
|
||||
logger.warn("Failure in fetching webfinger input", e.getMessage());
|
||||
}
|
||||
|
||||
// we couldn't find it!
|
||||
|
||||
if (key.getScheme().equals("http") || key.getScheme().equals("https")) {
|
||||
// if it looks like HTTP then punt: return the input, hope for the best
|
||||
logger.warn("Returning normalized input string as issuer, hoping for the best: " + identifier);
|
||||
return new LoadingResult(null, identifier);
|
||||
} else {
|
||||
// if it's not HTTP, give up
|
||||
logger.warn("Couldn't find issuer: " + identifier);
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TestOAuth2AccessTokenImpl {
|
||||
|
||||
private static String tokenString = "thisisatokenstring";
|
||||
|
||||
private static Set<String> scopes = ImmutableSet.of("bar", "foo");
|
||||
private static String scopeString = "foo bar";
|
||||
|
||||
private static Date exp = new Date(123 * 1000L);
|
||||
private static Long expVal = 123L;
|
||||
|
||||
@Test
|
||||
public void testFullToken() {
|
||||
|
||||
|
||||
JsonObject tokenObj = new JsonObject();
|
||||
tokenObj.addProperty("active", true);
|
||||
tokenObj.addProperty("scope", scopeString);
|
||||
tokenObj.addProperty("exp", expVal);
|
||||
tokenObj.addProperty("sub", "subject");
|
||||
tokenObj.addProperty("client_id", "123-456-789");
|
||||
|
||||
OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
|
||||
|
||||
assertThat(tok.getScope(), is(equalTo(scopes)));
|
||||
assertThat(tok.getExpiration(), is(equalTo(exp)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullExp() {
|
||||
|
||||
|
||||
JsonObject tokenObj = new JsonObject();
|
||||
tokenObj.addProperty("active", true);
|
||||
tokenObj.addProperty("scope", scopeString);
|
||||
tokenObj.addProperty("sub", "subject");
|
||||
tokenObj.addProperty("client_id", "123-456-789");
|
||||
|
||||
OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
|
||||
|
||||
assertThat(tok.getScope(), is(equalTo(scopes)));
|
||||
assertThat(tok.getExpiration(), is(equalTo(null)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullScopes() {
|
||||
|
||||
|
||||
JsonObject tokenObj = new JsonObject();
|
||||
tokenObj.addProperty("active", true);
|
||||
tokenObj.addProperty("exp", expVal);
|
||||
tokenObj.addProperty("sub", "subject");
|
||||
tokenObj.addProperty("client_id", "123-456-789");
|
||||
|
||||
OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
|
||||
|
||||
assertThat(tok.getScope(), is(equalTo(Collections.EMPTY_SET)));
|
||||
assertThat(tok.getExpiration(), is(equalTo(exp)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullScopesNullExp() {
|
||||
|
||||
|
||||
JsonObject tokenObj = new JsonObject();
|
||||
tokenObj.addProperty("active", true);
|
||||
tokenObj.addProperty("sub", "subject");
|
||||
tokenObj.addProperty("client_id", "123-456-789");
|
||||
|
||||
OAuth2AccessTokenImpl tok = new OAuth2AccessTokenImpl(tokenObj, tokenString);
|
||||
|
||||
assertThat(tok.getScope(), is(equalTo(Collections.EMPTY_SET)));
|
||||
assertThat(tok.getExpiration(), is(equalTo(null)));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.oauth2.introspectingfilter.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author jricher
|
||||
*
|
||||
*/
|
||||
public class TestScopeBasedIntrospectionAuthoritiesGranter {
|
||||
|
||||
private JsonObject introspectionResponse;
|
||||
|
||||
private ScopeBasedIntrospectionAuthoritiesGranter granter = new ScopeBasedIntrospectionAuthoritiesGranter();
|
||||
|
||||
/**
|
||||
* @throws java.lang.Exception
|
||||
*/
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
introspectionResponse = new JsonObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.mitre.oauth2.introspectingfilter.service.impl.ScopeBasedIntrospectionAuthoritiesGranter#getAuthorities(com.google.gson.JsonObject)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAuthoritiesJsonObject_withScopes() {
|
||||
introspectionResponse.addProperty("scope", "foo bar baz batman");
|
||||
|
||||
List<GrantedAuthority> expected = new ArrayList<>();
|
||||
expected.add(new SimpleGrantedAuthority("ROLE_API"));
|
||||
expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_foo"));
|
||||
expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_bar"));
|
||||
expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_baz"));
|
||||
expected.add(new SimpleGrantedAuthority("OAUTH_SCOPE_batman"));
|
||||
|
||||
List<GrantedAuthority> authorities = granter.getAuthorities(introspectionResponse);
|
||||
|
||||
assertTrue(authorities.containsAll(expected));
|
||||
assertTrue(expected.containsAll(authorities));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test method for {@link org.mitre.oauth2.introspectingfilter.service.impl.ScopeBasedIntrospectionAuthoritiesGranter#getAuthorities(com.google.gson.JsonObject)}.
|
||||
*/
|
||||
@Test
|
||||
public void testGetAuthoritiesJsonObject_withoutScopes() {
|
||||
|
||||
List<GrantedAuthority> expected = new ArrayList<>();
|
||||
expected.add(new SimpleGrantedAuthority("ROLE_API"));
|
||||
|
||||
List<GrantedAuthority> authorities = granter.getAuthorities(introspectionResponse);
|
||||
|
||||
assertTrue(authorities.containsAll(expected));
|
||||
assertTrue(expected.containsAll(authorities));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class TestOIDCAuthenticationFilter {
|
||||
|
||||
private OIDCAuthenticationFilter filter = new OIDCAuthenticationFilter();
|
||||
|
||||
@Test
|
||||
public void attemptAuthentication_error() throws Exception {
|
||||
|
||||
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
|
||||
Mockito.when(request.getParameter("error")).thenReturn("Error");
|
||||
Mockito.when(request.getParameter("error_description")).thenReturn("Description");
|
||||
Mockito.when(request.getParameter("error_uri")).thenReturn("http://example.com");
|
||||
|
||||
try {
|
||||
filter.attemptAuthentication(request, mock(HttpServletResponse.class));
|
||||
|
||||
fail("AuthorizationEndpointException expected.");
|
||||
}
|
||||
catch (AuthorizationEndpointException exception) {
|
||||
assertThat(exception.getMessage(),
|
||||
is("Error from Authorization Endpoint: Error Description http://example.com"));
|
||||
|
||||
assertThat(exception.getError(), is("Error"));
|
||||
assertThat(exception.getErrorDescription(), is("Description"));
|
||||
assertThat(exception.getErrorURI(), is("http://example.com"));
|
||||
|
||||
assertThat(exception, is(instanceOf(AuthenticationServiceException.class)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,117 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TestHybridClientConfigurationService {
|
||||
|
||||
@Mock
|
||||
private StaticClientConfigurationService mockStaticService;
|
||||
|
||||
@Mock
|
||||
private DynamicRegistrationClientConfigurationService mockDynamicService;
|
||||
|
||||
@InjectMocks
|
||||
private HybridClientConfigurationService hybridService;
|
||||
|
||||
// test fixture
|
||||
|
||||
@Mock
|
||||
private RegisteredClient mockClient;
|
||||
|
||||
@Mock
|
||||
private ServerConfiguration mockServerConfig;
|
||||
|
||||
private String issuer = "https://www.example.com/";
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
||||
Mockito.reset(mockDynamicService, mockStaticService);
|
||||
|
||||
Mockito.when(mockServerConfig.getIssuer()).thenReturn(issuer);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientConfiguration_useStatic() {
|
||||
|
||||
Mockito.when(mockStaticService.getClientConfiguration(mockServerConfig)).thenReturn(mockClient);
|
||||
|
||||
RegisteredClient result = hybridService.getClientConfiguration(mockServerConfig);
|
||||
|
||||
Mockito.verify(mockStaticService).getClientConfiguration(mockServerConfig);
|
||||
Mockito.verify(mockDynamicService, Mockito.never()).getClientConfiguration(Matchers.any(ServerConfiguration.class));
|
||||
assertEquals(mockClient, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientConfiguration_useDynamic() {
|
||||
|
||||
Mockito.when(mockStaticService.getClientConfiguration(mockServerConfig)).thenReturn(null);
|
||||
Mockito.when(mockDynamicService.getClientConfiguration(mockServerConfig)).thenReturn(mockClient);
|
||||
|
||||
RegisteredClient result = hybridService.getClientConfiguration(mockServerConfig);
|
||||
|
||||
Mockito.verify(mockStaticService).getClientConfiguration(mockServerConfig);
|
||||
Mockito.verify(mockDynamicService).getClientConfiguration(mockServerConfig);
|
||||
assertEquals(mockClient, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the behavior when the issuer is not known.
|
||||
*/
|
||||
@Test
|
||||
public void getClientConfiguration_noIssuer() {
|
||||
|
||||
// The mockServerConfig is known to both services
|
||||
Mockito.when(mockStaticService.getClientConfiguration(mockServerConfig)).thenReturn(mockClient);
|
||||
Mockito.when(mockDynamicService.getClientConfiguration(mockServerConfig)).thenReturn(mockClient);
|
||||
|
||||
// But oh noes! We're going to ask it to find us some other issuer
|
||||
ServerConfiguration badIssuer = Mockito.mock(ServerConfiguration.class);
|
||||
Mockito.when(badIssuer.getIssuer()).thenReturn("www.badexample.com");
|
||||
|
||||
RegisteredClient result = hybridService.getClientConfiguration(badIssuer);
|
||||
|
||||
Mockito.verify(mockStaticService).getClientConfiguration(badIssuer);
|
||||
Mockito.verify(mockDynamicService).getClientConfiguration(badIssuer);
|
||||
assertThat(result, is(nullValue()));
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Matchers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TestHybridServerConfigurationService {
|
||||
|
||||
@Mock
|
||||
private StaticServerConfigurationService mockStaticService;
|
||||
|
||||
@Mock
|
||||
private DynamicServerConfigurationService mockDynamicService;
|
||||
|
||||
@InjectMocks
|
||||
private HybridServerConfigurationService hybridService;
|
||||
|
||||
@Mock
|
||||
private ServerConfiguration mockServerConfig;
|
||||
|
||||
private String issuer = "https://www.example.com/";
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
||||
Mockito.reset(mockDynamicService, mockStaticService);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getServerConfiguration_useStatic() {
|
||||
|
||||
Mockito.when(mockStaticService.getServerConfiguration(issuer)).thenReturn(mockServerConfig);
|
||||
|
||||
ServerConfiguration result = hybridService.getServerConfiguration(issuer);
|
||||
|
||||
Mockito.verify(mockStaticService).getServerConfiguration(issuer);
|
||||
Mockito.verify(mockDynamicService, Mockito.never()).getServerConfiguration(Matchers.anyString());
|
||||
assertEquals(mockServerConfig, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServerConfiguration_useDynamic() {
|
||||
|
||||
Mockito.when(mockStaticService.getServerConfiguration(issuer)).thenReturn(null);
|
||||
Mockito.when(mockDynamicService.getServerConfiguration(issuer)).thenReturn(mockServerConfig);
|
||||
|
||||
ServerConfiguration result = hybridService.getServerConfiguration(issuer);
|
||||
|
||||
Mockito.verify(mockStaticService).getServerConfiguration(issuer);
|
||||
Mockito.verify(mockDynamicService).getServerConfiguration(issuer);
|
||||
assertEquals(mockServerConfig, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the behavior when the issuer is not known.
|
||||
*/
|
||||
@Test
|
||||
public void getServerConfiguration_noIssuer() {
|
||||
|
||||
Mockito.when(mockStaticService.getServerConfiguration(issuer)).thenReturn(mockServerConfig);
|
||||
Mockito.when(mockDynamicService.getServerConfiguration(issuer)).thenReturn(mockServerConfig);
|
||||
|
||||
String badIssuer = "www.badexample.com";
|
||||
|
||||
ServerConfiguration result = hybridService.getServerConfiguration(badIssuer);
|
||||
|
||||
Mockito.verify(mockStaticService).getServerConfiguration(badIssuer);
|
||||
Mockito.verify(mockDynamicService).getServerConfiguration(badIssuer);
|
||||
assertThat(result, is(nullValue()));
|
||||
}
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
public class TestPlainAuthRequestUrlBuilder {
|
||||
|
||||
// Test fixture:
|
||||
ServerConfiguration serverConfig;
|
||||
RegisteredClient clientConfig;
|
||||
|
||||
private PlainAuthRequestUrlBuilder urlBuilder = new PlainAuthRequestUrlBuilder();
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
||||
serverConfig = Mockito.mock(ServerConfiguration.class);
|
||||
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("https://server.example.com/authorize");
|
||||
|
||||
clientConfig = Mockito.mock(RegisteredClient.class);
|
||||
Mockito.when(clientConfig.getClientId()).thenReturn("s6BhdRkqt3");
|
||||
Mockito.when(clientConfig.getScope()).thenReturn(Sets.newHashSet("openid", "profile"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildAuthRequestUrl() {
|
||||
|
||||
String expectedUrl = "https://server.example.com/authorize?" +
|
||||
"response_type=code" +
|
||||
"&client_id=s6BhdRkqt3" +
|
||||
"&scope=openid+profile" + // plus sign used for space per application/x-www-form-encoded standard
|
||||
"&redirect_uri=https%3A%2F%2Fclient.example.org%2F" +
|
||||
"&nonce=34fasf3ds" +
|
||||
"&state=af0ifjsldkj" +
|
||||
"&foo=bar";
|
||||
|
||||
Map<String, String> options = ImmutableMap.of("foo", "bar");
|
||||
|
||||
String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, null);
|
||||
|
||||
assertThat(actualUrl, equalTo(expectedUrl));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildAuthRequestUrl_withLoginHint() {
|
||||
|
||||
String expectedUrl = "https://server.example.com/authorize?" +
|
||||
"response_type=code" +
|
||||
"&client_id=s6BhdRkqt3" +
|
||||
"&scope=openid+profile" + // plus sign used for space per application/x-www-form-encoded standard
|
||||
"&redirect_uri=https%3A%2F%2Fclient.example.org%2F" +
|
||||
"&nonce=34fasf3ds" +
|
||||
"&state=af0ifjsldkj" +
|
||||
"&foo=bar" +
|
||||
"&login_hint=bob";
|
||||
|
||||
Map<String, String> options = ImmutableMap.of("foo", "bar");
|
||||
|
||||
String actualUrl = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "https://client.example.org/", "34fasf3ds", "af0ifjsldkj", options, "bob");
|
||||
|
||||
assertThat(actualUrl, equalTo(expectedUrl));
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationServiceException.class)
|
||||
public void buildAuthRequestUrl_badUri() {
|
||||
|
||||
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2");
|
||||
|
||||
Map<String, String> options = ImmutableMap.of("foo", "bar");
|
||||
|
||||
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,204 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mitre.jwt.signer.service.impl.DefaultJWTSigningAndValidationService;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
import org.springframework.web.util.UriComponents;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.nimbusds.jose.Algorithm;
|
||||
import com.nimbusds.jose.jwk.JWK;
|
||||
import com.nimbusds.jose.jwk.KeyUse;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.nimbusds.jose.util.Base64URL;
|
||||
import com.nimbusds.jwt.JWTClaimsSet;
|
||||
import com.nimbusds.jwt.SignedJWT;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
public class TestSignedAuthRequestUrlBuilder {
|
||||
|
||||
// Test fixture:
|
||||
private ServerConfiguration serverConfig;
|
||||
private RegisteredClient clientConfig;
|
||||
|
||||
private String redirectUri = "https://client.example.org/";
|
||||
private String nonce = "34fasf3ds";
|
||||
private String state = "af0ifjsldkj";
|
||||
private String responseType = "code";
|
||||
private Map<String, String> options = ImmutableMap.of("foo", "bar");
|
||||
|
||||
|
||||
// RSA key properties:
|
||||
// {@link package com.nimbusds.jose.jwk#RSAKey}
|
||||
private String n = "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zw" +
|
||||
"u1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc" +
|
||||
"5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8K" +
|
||||
"JZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh" +
|
||||
"6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw";
|
||||
private String e = "AQAB";
|
||||
private String d = "X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9M7dx5oo7GURknc" +
|
||||
"hnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqijwp3RTzlBaCxWp4doFk5" +
|
||||
"N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d_cPVY3i07a3t8MN6TNwm0dSa" +
|
||||
"wm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBznbJSzFHK66jT8bgkuqsk0GjskDJk1" +
|
||||
"9Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFzme1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q";
|
||||
private String alg = "RS256";
|
||||
private String kid = "2011-04-29";
|
||||
private String loginHint = "bob";
|
||||
|
||||
private DefaultJWTSigningAndValidationService signingAndValidationService;
|
||||
|
||||
private SignedAuthRequestUrlBuilder urlBuilder = new SignedAuthRequestUrlBuilder();
|
||||
|
||||
@Before
|
||||
public void prepare() throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
|
||||
RSAKey key = new RSAKey(new Base64URL(n), new Base64URL(e), new Base64URL(d), KeyUse.SIGNATURE, null, new Algorithm(alg), kid, null, null, null, null, null);
|
||||
Map<String, JWK> keys = Maps.newHashMap();
|
||||
keys.put("client", key);
|
||||
|
||||
signingAndValidationService = new DefaultJWTSigningAndValidationService(keys);
|
||||
signingAndValidationService.setDefaultSignerKeyId("client");
|
||||
signingAndValidationService.setDefaultSigningAlgorithmName(alg);
|
||||
|
||||
urlBuilder.setSigningAndValidationService(signingAndValidationService);
|
||||
|
||||
serverConfig = Mockito.mock(ServerConfiguration.class);
|
||||
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("https://server.example.com/authorize");
|
||||
|
||||
clientConfig = Mockito.mock(RegisteredClient.class);
|
||||
Mockito.when(clientConfig.getClientId()).thenReturn("s6BhdRkqt3");
|
||||
Mockito.when(clientConfig.getScope()).thenReturn(Sets.newHashSet("openid", "profile"));
|
||||
}
|
||||
|
||||
/**
|
||||
* This test takes the URI from the result of building a signed request
|
||||
* and checks that the JWS object parsed from the request URI matches up
|
||||
* with the expected claim values.
|
||||
*/
|
||||
@Test
|
||||
public void buildAuthRequestUrl() {
|
||||
|
||||
String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, null);
|
||||
|
||||
// parsing the result
|
||||
UriComponentsBuilder builder = null;
|
||||
|
||||
try {
|
||||
builder = UriComponentsBuilder.fromUri(new URI(requestUri));
|
||||
} catch (URISyntaxException e1) {
|
||||
fail("URISyntaxException was thrown.");
|
||||
}
|
||||
|
||||
UriComponents components = builder.build();
|
||||
String jwtString = components.getQueryParams().get("request").get(0);
|
||||
JWTClaimsSet claims = null;
|
||||
|
||||
try {
|
||||
SignedJWT jwt = SignedJWT.parse(jwtString);
|
||||
claims = jwt.getJWTClaimsSet();
|
||||
} catch (ParseException e) {
|
||||
fail("ParseException was thrown.");
|
||||
}
|
||||
|
||||
assertEquals(responseType, claims.getClaim("response_type"));
|
||||
assertEquals(clientConfig.getClientId(), claims.getClaim("client_id"));
|
||||
|
||||
List<String> scopeList = Arrays.asList(((String) claims.getClaim("scope")).split(" "));
|
||||
assertTrue(scopeList.containsAll(clientConfig.getScope()));
|
||||
|
||||
assertEquals(redirectUri, claims.getClaim("redirect_uri"));
|
||||
assertEquals(nonce, claims.getClaim("nonce"));
|
||||
assertEquals(state, claims.getClaim("state"));
|
||||
for (String claim : options.keySet()) {
|
||||
assertEquals(options.get(claim), claims.getClaim(claim));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildAuthRequestUrl_withLoginHint() {
|
||||
|
||||
String requestUri = urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, redirectUri, nonce, state, options, loginHint);
|
||||
|
||||
// parsing the result
|
||||
UriComponentsBuilder builder = null;
|
||||
|
||||
try {
|
||||
builder = UriComponentsBuilder.fromUri(new URI(requestUri));
|
||||
} catch (URISyntaxException e1) {
|
||||
fail("URISyntaxException was thrown.");
|
||||
}
|
||||
|
||||
UriComponents components = builder.build();
|
||||
String jwtString = components.getQueryParams().get("request").get(0);
|
||||
JWTClaimsSet claims = null;
|
||||
|
||||
try {
|
||||
SignedJWT jwt = SignedJWT.parse(jwtString);
|
||||
claims = jwt.getJWTClaimsSet();
|
||||
} catch (ParseException e) {
|
||||
fail("ParseException was thrown.");
|
||||
}
|
||||
|
||||
assertEquals(responseType, claims.getClaim("response_type"));
|
||||
assertEquals(clientConfig.getClientId(), claims.getClaim("client_id"));
|
||||
|
||||
List<String> scopeList = Arrays.asList(((String) claims.getClaim("scope")).split(" "));
|
||||
assertTrue(scopeList.containsAll(clientConfig.getScope()));
|
||||
|
||||
assertEquals(redirectUri, claims.getClaim("redirect_uri"));
|
||||
assertEquals(nonce, claims.getClaim("nonce"));
|
||||
assertEquals(state, claims.getClaim("state"));
|
||||
for (String claim : options.keySet()) {
|
||||
assertEquals(options.get(claim), claims.getClaim(claim));
|
||||
}
|
||||
assertEquals(loginHint, claims.getClaim("login_hint"));
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationServiceException.class)
|
||||
public void buildAuthRequestUrl_badUri() {
|
||||
|
||||
Mockito.when(serverConfig.getAuthorizationEndpointUri()).thenReturn("e=mc^2");
|
||||
|
||||
urlBuilder.buildAuthRequestUrl(serverConfig, clientConfig, "example.com", "", "", options, null);
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mitre.oauth2.model.RegisteredClient;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TestStaticClientConfigurationService {
|
||||
|
||||
private StaticClientConfigurationService service;
|
||||
|
||||
private String issuer = "https://www.example.com/";
|
||||
|
||||
@Mock
|
||||
private RegisteredClient mockClient;
|
||||
|
||||
@Mock
|
||||
private ServerConfiguration mockServerConfig;
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
||||
service = new StaticClientConfigurationService();
|
||||
|
||||
Map<String, RegisteredClient> clients = new HashMap<>();
|
||||
clients.put(issuer, mockClient);
|
||||
|
||||
service.setClients(clients);
|
||||
|
||||
Mockito.when(mockServerConfig.getIssuer()).thenReturn(issuer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getClientConfiguration_success() {
|
||||
|
||||
RegisteredClient result = service.getClientConfiguration(mockServerConfig);
|
||||
|
||||
assertThat(mockClient, is(notNullValue()));
|
||||
assertEquals(mockClient, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the behavior when the issuer is not known.
|
||||
*/
|
||||
@Test
|
||||
public void getClientConfiguration_noIssuer() {
|
||||
Mockito.when(mockServerConfig.getIssuer()).thenReturn("www.badexample.net");
|
||||
|
||||
RegisteredClient actualClient = service.getClientConfiguration(mockServerConfig);
|
||||
|
||||
assertThat(actualClient, is(nullValue()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mitre.openid.connect.config.ServerConfiguration;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TestStaticServerConfigurationService {
|
||||
|
||||
|
||||
private StaticServerConfigurationService service;
|
||||
|
||||
private String issuer = "https://www.example.com/";
|
||||
|
||||
@Mock
|
||||
private ServerConfiguration mockServerConfig;
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
||||
service = new StaticServerConfigurationService();
|
||||
|
||||
Map<String, ServerConfiguration> servers = new HashMap<>();
|
||||
servers.put(issuer, mockServerConfig);
|
||||
|
||||
service.setServers(servers);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getServerConfiguration_success() {
|
||||
|
||||
ServerConfiguration result = service.getServerConfiguration(issuer);
|
||||
|
||||
assertThat(mockServerConfig, is(notNullValue()));
|
||||
assertEquals(mockServerConfig, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the behavior when the issuer is not known.
|
||||
*/
|
||||
@Test
|
||||
public void getClientConfiguration_noIssuer() {
|
||||
|
||||
ServerConfiguration result = service.getServerConfiguration("www.badexample.net");
|
||||
|
||||
assertThat(result, is(nullValue()));
|
||||
}
|
||||
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
/*******************************************************************************
|
||||
* Copyright 2018 The MIT Internet Trust Consortium
|
||||
*
|
||||
* Portions copyright 2011-2013 The MITRE Corporation
|
||||
*
|
||||
* 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 org.mitre.openid.connect.client.service.impl;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mitre.openid.connect.client.model.IssuerServiceResponse;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.security.authentication.AuthenticationServiceException;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.equalTo;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author wkim
|
||||
*
|
||||
*/
|
||||
public class TestThirdPartyIssuerService {
|
||||
|
||||
// Test fixture:
|
||||
private HttpServletRequest request;
|
||||
|
||||
private String iss = "https://server.example.org";
|
||||
private String login_hint = "I'm not telling you nothin!";
|
||||
private String target_link_uri = "https://www.example.com";
|
||||
private String redirect_uri = "https://www.example.com";
|
||||
|
||||
private String accountChooserUrl = "https://www.example.com/account";
|
||||
|
||||
private ThirdPartyIssuerService service = new ThirdPartyIssuerService();
|
||||
|
||||
@Before
|
||||
public void prepare() {
|
||||
|
||||
service.setAccountChooserUrl(accountChooserUrl);
|
||||
|
||||
request = Mockito.mock(HttpServletRequest.class);
|
||||
Mockito.when(request.getParameter("iss")).thenReturn(iss);
|
||||
Mockito.when(request.getParameter("login_hint")).thenReturn(login_hint);
|
||||
Mockito.when(request.getParameter("target_link_uri")).thenReturn(target_link_uri);
|
||||
Mockito.when(request.getRequestURL()).thenReturn(new StringBuffer(redirect_uri));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIssuer_hasIssuer() {
|
||||
|
||||
IssuerServiceResponse response = service.getIssuer(request);
|
||||
|
||||
assertThat(response.getIssuer(), equalTo(iss));
|
||||
assertThat(response.getLoginHint(), equalTo(login_hint));
|
||||
assertThat(response.getTargetLinkUri(), equalTo(target_link_uri));
|
||||
|
||||
assertThat(response.getRedirectUrl(), nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIssuer_noIssuer() {
|
||||
|
||||
Mockito.when(request.getParameter("iss")).thenReturn(null);
|
||||
|
||||
IssuerServiceResponse response = service.getIssuer(request);
|
||||
|
||||
assertThat(response.getIssuer(), nullValue());
|
||||
assertThat(response.getLoginHint(), nullValue());
|
||||
assertThat(response.getTargetLinkUri(), nullValue());
|
||||
|
||||
String expectedRedirectUrl = accountChooserUrl + "?redirect_uri=" + "https%3A%2F%2Fwww.example.com"; // url-encoded string of the request url
|
||||
assertThat(response.getRedirectUrl(), equalTo(expectedRedirectUrl));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIssuer_isWhitelisted() {
|
||||
|
||||
service.setWhitelist(Sets.newHashSet(iss));
|
||||
|
||||
IssuerServiceResponse response = service.getIssuer(request);
|
||||
|
||||
assertThat(response.getIssuer(), equalTo(iss));
|
||||
assertThat(response.getLoginHint(), equalTo(login_hint));
|
||||
assertThat(response.getTargetLinkUri(), equalTo(target_link_uri));
|
||||
|
||||
assertThat(response.getRedirectUrl(), nullValue());
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationServiceException.class)
|
||||
public void getIssuer_notWhitelisted() {
|
||||
|
||||
service.setWhitelist(Sets.newHashSet("some.other.site"));
|
||||
|
||||
service.getIssuer(request);
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationServiceException.class)
|
||||
public void getIssuer_blacklisted() {
|
||||
|
||||
service.setBlacklist(Sets.newHashSet(iss));
|
||||
|
||||
service.getIssuer(request);
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationServiceException.class)
|
||||
public void getIssuer_badUri() {
|
||||
|
||||
Mockito.when(request.getParameter("iss")).thenReturn(null);
|
||||
service.setAccountChooserUrl("e=mc^2");
|
||||
|
||||
service.getIssuer(request);
|
||||
}
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{"jwk":
|
||||
[
|
||||
{"alg":"RSA",
|
||||
"mod": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
|
||||
"exp":"AQAB",
|
||||
"kid":"2011-04-29"}
|
||||
]
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
{"jwk":
|
||||
[
|
||||
{"alg":"RSA",
|
||||
"mod": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
|
||||
"exp":"AQAB",
|
||||
"kid":"2011-04-29"}
|
||||
]
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2018 The MIT Internet Trust Consortium
|
||||
|
||||
Portions copyright 2011-2013 The MITRE Corporation
|
||||
|
||||
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.
|
||||
-->
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jwt-signer="http://www.mitre.org/schema/openid-connect/jwt-signer"
|
||||
xsi:schemaLocation=
|
||||
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.mitre.org/schema/openid-connect/jwt-signer http://www.mitre.org/schema/openid-connect/jwt-signer/jwt-signer-1.0.xsd" >
|
||||
|
||||
<!-- Creates an in-memory database populated with test jdbc -->
|
||||
<bean id="dataSource" class="org.mitre.jdbc.datasource.H2DataSourceFactory">
|
||||
<property name="databaseName" value="connect"/>
|
||||
<property name="scriptLocations" >
|
||||
<list>
|
||||
<!-- OpenID Connect Data model -->
|
||||
<value>file:db/tables/accesstoken.sql</value>
|
||||
<value>file:db/tables/address.sql</value>
|
||||
<value>file:db/tables/approvedsite.sql</value>
|
||||
<value>file:db/tables/authorities.sql</value>
|
||||
<value>file:db/tables/clientdetails.sql</value>
|
||||
<value>file:db/tables/event.sql</value>
|
||||
<value>file:db/tables/granttypes.sql</value>
|
||||
<value>file:db/tables/idtoken.sql</value>
|
||||
<value>file:db/tables/idtokenclaims.sql</value>
|
||||
<value>file:db/tables/refreshtoken.sql</value>
|
||||
<value>file:db/tables/scope.sql</value>
|
||||
<value>file:db/tables/userinfo.sql</value>
|
||||
<value>file:db/tables/whitelistedsite.sql</value>
|
||||
<!-- Preloaded data -->
|
||||
<value>classpath:test-data.sql</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="dateConversionPatterns">
|
||||
<map>
|
||||
<entry key="yyyy/mm/dd hh24:mi:ss" value="yyy/MM/dd HH:mm:ss" />
|
||||
<entry key="yyyy-mm-dd" value="yyyy-MM-dd" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
</beans>
|
|
@ -1,15 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU
|
||||
ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh
|
||||
dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV
|
||||
BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0
|
||||
NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo
|
||||
BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt
|
||||
YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz
|
||||
LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ
|
||||
Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2
|
||||
Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq
|
||||
hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8
|
||||
6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg
|
||||
oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg==
|
||||
-----END CERTIFICATE-----
|
|
@ -1,15 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIICxDCCAi0CBECcV/wwDQYJKoZIhvcNAQEEBQAwgagxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIEwVU
|
||||
ZXhhczEPMA0GA1UEBxMGQXVzdGluMSowKAYDVQQKEyFUaGUgVW5pdmVyc2l0eSBvZiBUZXhhcyBh
|
||||
dCBBdXN0aW4xKDAmBgNVBAsTH0luZm9ybWF0aW9uIFRlY2hub2xvZ3kgU2VydmljZXMxIjAgBgNV
|
||||
BAMTGXhtbGdhdGV3YXkuaXRzLnV0ZXhhcy5lZHUwHhcNMDQwNTA4MDM0NjA0WhcNMDQwODA2MDM0
|
||||
NjA0WjCBqDELMAkGA1UEBhMCVVMxDjAMBgNVBAgTBVRleGFzMQ8wDQYDVQQHEwZBdXN0aW4xKjAo
|
||||
BgNVBAoTIVRoZSBVbml2ZXJzaXR5IG9mIFRleGFzIGF0IEF1c3RpbjEoMCYGA1UECxMfSW5mb3Jt
|
||||
YXRpb24gVGVjaG5vbG9neSBTZXJ2aWNlczEiMCAGA1UEAxMZeG1sZ2F0ZXdheS5pdHMudXRleGFz
|
||||
LmVkdTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsmc+6+NjLmanvh+FvBziYdBwTiz+d/DZ
|
||||
Uy2jyvij6f8Xly6zkhHLSsuBzw08wPzr2K+F359bf9T3uiZMuao//FBGtDrTYpvQwkn4PFZwSeY2
|
||||
Ynw4edxp1JEWT2zfOY+QJDfNgpsYQ9hrHDwqnpbMVVqjdBq5RgTKGhFBj9kxEq0CAwEAATANBgkq
|
||||
hkiG9w0BAQQFAAOBgQCPYGXF6oRbnjti3CPtjfwORoO7ab1QzNS9Z2rLMuPnt6POlm1A3UPEwCS8
|
||||
6flTlAqg19Sh47H7+Iq/LuzotKvUE5ugK52QRNMa4c0OSaO5UEM5EfVox1pT9tZV1Z3whYYMhThg
|
||||
oC4y/On0NUVMN5xfF/GpSACga/bVjoNvd8HWEg==
|
||||
-----END CERTIFICATE-----
|
|
@ -1,12 +0,0 @@
|
|||
local-values.conf
|
||||
target
|
||||
*~
|
||||
bin
|
||||
*.idea
|
||||
*.iml
|
||||
*.eml
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
||||
/target
|
||||
.springBeans
|
|
@ -1,132 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2018 The MIT Internet Trust Consortium
|
||||
|
||||
Portions copyright 2011-2013 The MITRE Corporation
|
||||
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>openid-connect-parent</artifactId>
|
||||
<groupId>org.mitre</groupId>
|
||||
<version>1.3.4-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>openid-connect-common</artifactId>
|
||||
<description>OpenID Connect Common modules</description>
|
||||
<name>OpenID Connect Common</name>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>commons-logging</groupId>
|
||||
<artifactId>commons-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security.oauth</groupId>
|
||||
<artifactId>spring-security-oauth2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nimbusds</groupId>
|
||||
<artifactId>nimbus-jose-jwt</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.persistence</groupId>
|
||||
<artifactId>javax.persistence</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${java-version}</source>
|
||||
<target>${java-version}</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<!-- BUILD SOURCE FILES -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- BUILD JavaDoc FILES -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>attach-sources</id>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -1,3 +0,0 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue