pl.edu.icm.unity.oauth.client.config.CustomProviderProperties Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of unity-server-oauth Show documentation
Show all versions of unity-server-oauth Show documentation
Client and server OAuth support
The newest version!
/*
* Copyright (c) 2014 ICM Uniwersytet Warszawski All rights reserved.
* See LICENCE.txt file for licensing information.
*/
package pl.edu.icm.unity.oauth.client.config;
import com.nimbusds.oauth2.sdk.http.HTTPRequest.Method;
import eu.emi.security.authn.x509.X509CertChainValidator;
import eu.unicore.util.configuration.ConfigurationException;
import eu.unicore.util.configuration.DocumentationReferenceMeta;
import eu.unicore.util.configuration.DocumentationReferencePrefix;
import eu.unicore.util.configuration.PropertyMD;
import eu.unicore.util.httpclient.ServerHostnameCheckingMode;
import io.imunity.vaadin.auth.CommonWebAuthnProperties;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.apache.logging.log4j.Logger;
import pl.edu.icm.unity.base.exceptions.EngineException;
import pl.edu.icm.unity.base.utils.Log;
import pl.edu.icm.unity.engine.api.PKIManagement;
import pl.edu.icm.unity.engine.api.config.UnityPropertiesHelper;
import pl.edu.icm.unity.oauth.BaseRemoteASProperties;
import pl.edu.icm.unity.oauth.client.UserProfileFetcher;
import pl.edu.icm.unity.oauth.client.config.OAuthClientProperties.Providers;
import pl.edu.icm.unity.oauth.client.profile.OpenIdProfileFetcher;
import pl.edu.icm.unity.oauth.client.profile.PlainProfileFetcher;
import pl.edu.icm.unity.oauth.oidc.metadata.OIDCMetadataRequest;
import java.util.*;
/**
* Configuration of OAuth client for custom provider.
* @author K. Benedyczak
*/
public class CustomProviderProperties extends UnityPropertiesHelper implements BaseRemoteASProperties
{
private static final Logger log = Log.getLogger(Log.U_SERVER_CFG, CustomProviderProperties.class);
public enum AccessTokenFormat {standard, httpParams};
public enum ClientAuthnMode {secretPost, secretBasic};
public enum ClientHttpMethod {post, get};
@DocumentationReferencePrefix
public static final String P = "unity.oauth2.client.CLIENT_ID.";
public static final String PROVIDER_TYPE = "type";
public static final String PROVIDER_LOCATION = "authEndpoint";
public static final String ACCESS_TOKEN_ENDPOINT = "accessTokenEndpoint";
public static final String PROVIDER_NAME = "name";
public static final String SCOPES = "scopes";
public static final String ACCESS_TOKEN_FORMAT = "accessTokenFormat";
public static final String OPENID_CONNECT = "openIdConnect";
public static final String OPENID_DISCOVERY = "openIdConnectDiscoveryEndpoint";
public static final String ICON_URL = "iconUrl";
public static final String ADDITIONAL_AUTHZ_PARAMS = "extraAuthzParams.";
@DocumentationReferenceMeta
public final static Map META = new HashMap();
static
{
META.put(PROVIDER_TYPE, new PropertyMD(Providers.custom).
setDescription("Type of provider. Either a well known provider type can be specified"
+ " or 'custom'. In the first case only few additional settings are required: "
+ "client id, secret and translation profile. Other settings as scope "
+ "can be additionally set to fine tune the remote authentication. "
+ "In the latter 'custom' case all mandatory options must be set."));
META.put(PROVIDER_LOCATION, new PropertyMD().
setDescription("Location (URL) of OAuth2 provider's authorization endpoint. "
+ "It is mandatory for non OpenID Connect providers, in whose case "
+ "the endopint can be discovered."));
META.put(ACCESS_TOKEN_ENDPOINT, new PropertyMD().
setDescription("Location (URL) of OAuth2 provider's access token endpoint. "
+ "In case of OpenID Connect mode can be discovered, otherwise mandatory."));
META.put(PROFILE_ENDPOINT, new PropertyMD().setCanHaveSubkeys().
setDescription("Location (URL) of OAuth2 provider's user's profile endpoint. "
+ "It is used to obtain additional user's attributes. "
+ "It can be autodiscovered for OpenID Connect mode. Otherwise it should be"
+ " set as otherwise there is bearly no information about the user identity."
+ " If not set then the only information about the user is the one "
+ "extracted from the access token (if any). "
+ "Additionally a subkeys can be added (.1, .2, ...) if user attributes"
+ " should be fetched from more then a single endpoint."));
META.put(PROVIDER_NAME, new PropertyMD().setMandatory().setCanHaveSubkeys().
setDescription("Name of the OAuth provider to be displayed. Can be localized with locale subkeys."));
META.put(ICON_URL, new PropertyMD().setCanHaveSubkeys().
setDescription("URL to provider's logo. Can be http(s), file or data scheme. Can be localized."));
META.put(CLIENT_ID, new PropertyMD().setMandatory().
setDescription("Client identifier, obtained during Unity's "
+ "registration at the provider"));
META.put(CLIENT_SECRET, new PropertyMD().setSecret().setMandatory().
setDescription("Client secret, obtained during Unity's "
+ "registration at the provider"));
META.put(CLIENT_AUTHN_MODE, new PropertyMD(ClientAuthnMode.secretBasic).
setDescription("Defines how the client secret and id should be passed to the provider."));
META.put(CLIENT_AUTHN_MODE_FOR_PROFILE_ACCESS, new PropertyMD().setEnum(ClientAuthnMode.secretBasic).setDescription(
"Defines how the client secret and id should be passed to the provider's user's profile endpoint. If not set the "
+ CLIENT_AUTHN_MODE + " is used"));
META.put(CLIENT_HTTP_METHOD_FOR_PROFILE_ACCESS, new PropertyMD(ClientHttpMethod.get)
.setDescription("Http method used in query to profile endpoint"));
META.put(SCOPES, new PropertyMD().
setDescription("Space separated list of authorization scopes to be requested. "
+ "Most often required if in non OpenID Connect mode, otherwise has a default "
+ "value of 'openid email'"));
META.put(ACCESS_TOKEN_FORMAT, new PropertyMD(AccessTokenFormat.standard).
setDescription("Some providers (Facebook) use legacy format of a response to "
+ "the access token query. Non standard format can be set here."));
META.put(OPENID_CONNECT, new PropertyMD("false").
setDescription("If set to true, then the provider is treated as OpenID "
+ "Connect 1.0 provider. For such providers specifying " +
PROFILE_ENDPOINT + " is not needed as the basic user information "
+ "is retrieved together with access token. However the "
+ "discovery endpoint must be set."));
META.put(OPENID_DISCOVERY, new PropertyMD().
setDescription("OpenID Connect Discovery endpoint address, relevant (and required) "
+ "only when OpenID Connect mode is turned on."));
META.put(CommonWebAuthnProperties.REGISTRATION_FORM, new PropertyMD().
setDescription("Registration form to be shown for the locally unknown users which "
+ "were successfuly authenticated remotely."));
META.put(CommonWebAuthnProperties.TRANSLATION_PROFILE, new PropertyMD().
setDescription("Name of translation profile which will be used to map received user "
+ "information to a local representation."));
META.put(CommonWebAuthnProperties.EMBEDDED_TRANSLATION_PROFILE, new PropertyMD().setHidden().
setDescription("Translation profile in json which will be used to map received user "
+ "information to a local representation."));
META.put(CommonWebAuthnProperties.ENABLE_ASSOCIATION, new PropertyMD().setBoolean().
setDescription("If true then unknown remote user gets an option to associate "
+ "the remote identity with an another local "
+ "(already existing) account. Overrides the global setting."));
META.put(CLIENT_HOSTNAME_CHECKING, new PropertyMD(ServerHostnameCheckingMode.FAIL).
setDescription("Controls how to react on the DNS name mismatch with "
+ "the server's certificate. Unless in testing environment "
+ "should be left on the default setting."));
META.put(CLIENT_TRUSTSTORE, new PropertyMD().setDescription("Name of the truststore which should be used"
+ " to validate TLS peer's certificates. "
+ "If undefined then the system Java tuststore is used."));
META.put(ADDITIONAL_AUTHZ_PARAMS, new PropertyMD().setList(false).
setDescription("Allows to specify non-standard, fixed parameters which shall be "
+ "added to the query string of the authorization redirect request. "
+ "format must be: PARAM=VALUE"));
}
private X509CertChainValidator validator = null;
public CustomProviderProperties(Properties properties, String prefix, PKIManagement pkiManagement)
throws ConfigurationException
{
super(prefix, properties, META, log);
boolean openIdConnect = getBooleanValue(OPENID_CONNECT);
if (openIdConnect)
{
if (!isSet(SCOPES))
setProperty(SCOPES, "openid email");
if (!isSet(OPENID_DISCOVERY))
throw new ConfigurationException(getKeyDescription(OPENID_DISCOVERY) +
" is mandatory in OpenID Connect mode");
} else
{
if (!isSet(PROVIDER_LOCATION))
throw new ConfigurationException(getKeyDescription(PROVIDER_LOCATION) +
" is mandatory in non OpenID Connect mode");
if (!isSet(ACCESS_TOKEN_ENDPOINT))
throw new ConfigurationException(getKeyDescription(ACCESS_TOKEN_ENDPOINT) +
" is mandatory in non OpenID Connect mode");
}
if (!isSet(PROVIDER_NAME))
throw new ConfigurationException(getKeyDescription(PROVIDER_NAME) +
" is mandatory");
if (!isSet(CommonWebAuthnProperties.EMBEDDED_TRANSLATION_PROFILE)
&& !isSet(CommonWebAuthnProperties.TRANSLATION_PROFILE))
{
throw new ConfigurationException(getKeyDescription(CommonWebAuthnProperties.TRANSLATION_PROFILE)
+ " is mandatory");
}
String validatorName = getValue(CLIENT_TRUSTSTORE);
if (validatorName != null)
{
try
{
if (!pkiManagement.getValidatorNames().contains(validatorName))
throw new ConfigurationException("The http client truststore " +
validatorName +
" for the OAuth verification client does not exist");
validator = pkiManagement.getValidator(validatorName);
} catch (EngineException e)
{
throw new ConfigurationException("Can not establish the http client truststore " +
validatorName + " for the OAuth verification client", e);
}
}
}
public UserProfileFetcher getUserAttributesResolver()
{
boolean openIdConnectMode = getBooleanValue(OPENID_CONNECT);
return openIdConnectMode ? new OpenIdProfileFetcher() : new PlainProfileFetcher();
}
public ClientAuthnMode getClientAuthModeForProfileAccess()
{
ClientAuthnMode mode = getEnumValue(CLIENT_AUTHN_MODE_FOR_PROFILE_ACCESS,
ClientAuthnMode.class);
return mode != null ? mode : getEnumValue(CLIENT_AUTHN_MODE, ClientAuthnMode.class);
}
public Method getClientHttpMethodForProfileAccess()
{
return (getEnumValue(CLIENT_HTTP_METHOD_FOR_PROFILE_ACCESS,
ClientHttpMethod.class) == ClientHttpMethod.get) ? Method.GET
: Method.POST;
}
public List getUserInfoEndpoints()
{
List userInfoEndpoints = new ArrayList<>();
String mainUserInfoEndpoint = getValue(CustomProviderProperties.PROFILE_ENDPOINT);
if (mainUserInfoEndpoint != null)
userInfoEndpoints.add(mainUserInfoEndpoint);
userInfoEndpoints.addAll(getListOfValues(CustomProviderProperties.PROFILE_ENDPOINT + "."));
return userInfoEndpoints;
}
public Properties getProperties()
{
return properties;
}
@Override
public X509CertChainValidator getValidator()
{
return validator;
}
public List getAdditionalAuthzParams()
{
List raw = getListOfValues(ADDITIONAL_AUTHZ_PARAMS);
List ret = new ArrayList<>(raw.size());
for (String rawParam: raw)
{
int splitAt = rawParam.indexOf('=');
if (splitAt == -1)
{
log.warn("Specification of extra authz query parameter is invalid, no '=': " +
rawParam + " ignoring it");
continue;
}
if (splitAt == rawParam.length()-1)
{
log.warn("Specification of extra authz query parameter is invalid, no value: " +
rawParam + " ignoring it");
continue;
}
ret.add(new BasicNameValuePair(
rawParam.substring(0, splitAt), rawParam.substring(splitAt+1)));
}
return ret;
}
public static void setIfUnset(Properties properties, String property, String value)
{
if (!properties.containsKey(property))
properties.setProperty(property, value);
}
public static void setDefaultProfileIfUnset(Properties properties, String prefix, String defaultProfile)
{
if (!properties.containsKey(prefix + CommonWebAuthnProperties.EMBEDDED_TRANSLATION_PROFILE) &&
!properties.containsKey(prefix + CommonWebAuthnProperties.TRANSLATION_PROFILE))
properties.setProperty(prefix + CommonWebAuthnProperties.TRANSLATION_PROFILE, defaultProfile);
}
public OIDCMetadataRequest generateMetadataRequest()
{
return OIDCMetadataRequest.builder()
.withHostnameChecking(getHostNameCheckingMode())
.withValidator(validator)
.withValidatorName(getValue(CLIENT_TRUSTSTORE))
.withUrl(getValue(OPENID_DISCOVERY))
.build();
}
public ServerHostnameCheckingMode getHostNameCheckingMode()
{
return getEnumValue(CLIENT_HOSTNAME_CHECKING, ServerHostnameCheckingMode.class);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy