ConfigureOAuth2.java revision 8b6198b1170e3128c5df83267597f0058d07c70d
608N/A/*
839N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
608N/A *
608N/A * Copyright 2012-2015 ForgeRock AS.
608N/A *
608N/A * The contents of this file are subject to the terms
608N/A * of the Common Development and Distribution License
608N/A * (the License). You may not use this file except in
608N/A * compliance with the License.
608N/A *
608N/A * You can obtain a copy of the License at
608N/A * http://forgerock.org/license/CDDLv1.0.html
608N/A * See the License for the specific language governing
608N/A * permission and limitations under the License.
608N/A *
608N/A * When distributing Covered Code, include this CDDL
608N/A * Header Notice in each file and include the License file
608N/A * at http://forgerock.org/license/CDDLv1.0.html
608N/A * If applicable, add the following below the CDDL Header,
608N/A * with the fields enclosed by brackets [] replaced by
608N/A * your own identifying information:
608N/A * "Portions copyright [year] [name of copyright owner]".
608N/A */
608N/Apackage com.sun.identity.workflow;
608N/A
839N/Aimport static java.util.Collections.*;
608N/Aimport static org.forgerock.oauth2.core.OAuth2Constants.OAuth2ProviderService.*;
608N/Aimport static org.forgerock.oauth2.core.OAuth2Constants.AuthorizationEndpoint.*;
608N/Aimport static org.forgerock.openam.utils.CollectionUtils.asSet;
608N/A
608N/Aimport java.security.AccessController;
608N/Aimport java.text.MessageFormat;
608N/Aimport java.util.Collections;
608N/Aimport java.util.HashMap;
608N/Aimport java.util.HashSet;
608N/Aimport java.util.Locale;
608N/Aimport java.util.Map;
608N/Aimport java.util.ResourceBundle;
608N/Aimport java.util.Set;
608N/A
608N/Aimport javax.security.auth.Subject;
608N/A
608N/Aimport org.forgerock.guava.common.collect.ImmutableMap;
608N/Aimport org.forgerock.guava.common.collect.ImmutableSet;
608N/Aimport org.forgerock.guice.core.InjectorHolder;
608N/Aimport org.forgerock.oauth2.core.AuthorizationCodeResponseTypeHandler;
608N/Aimport org.forgerock.oauth2.core.TokenResponseTypeHandler;
608N/Aimport org.forgerock.openam.entitlement.ResourceType;
608N/Aimport org.forgerock.openam.entitlement.conditions.subject.AuthenticatedUsers;
608N/Aimport org.forgerock.openam.entitlement.rest.PolicyStore;
839N/Aimport org.forgerock.openam.entitlement.rest.PolicyStoreProvider;
839N/Aimport org.forgerock.openam.entitlement.rest.PrivilegePolicyStoreProvider;
839N/Aimport org.forgerock.openam.entitlement.rest.query.QueryAttribute;
608N/Aimport org.forgerock.openam.entitlement.service.ResourceTypeService;
608N/Aimport org.forgerock.openam.utils.StringUtils;
608N/Aimport org.forgerock.openidconnect.IdTokenResponseTypeHandler;
608N/A
608N/Aimport com.iplanet.sso.SSOException;
608N/Aimport com.iplanet.sso.SSOToken;
608N/Aimport com.sun.identity.common.ISLocaleContext;
608N/Aimport com.sun.identity.entitlement.Application;
608N/Aimport com.sun.identity.entitlement.Entitlement;
608N/Aimport com.sun.identity.entitlement.EntitlementException;
608N/Aimport com.sun.identity.entitlement.Privilege;
608N/Aimport com.sun.identity.entitlement.opensso.SubjectUtils;
608N/Aimport com.sun.identity.policy.PolicyManager;
608N/Aimport com.sun.identity.security.AdminTokenAction;
608N/Aimport com.sun.identity.shared.debug.Debug;
608N/Aimport com.sun.identity.sm.SMSException;
608N/Aimport com.sun.identity.sm.SMSUtils;
839N/Aimport com.sun.identity.sm.ServiceConfigManager;
608N/Aimport com.sun.identity.sm.ServiceSchema;
608N/Aimport com.sun.identity.sm.ServiceSchemaManager;
608N/A
608N/Apublic class ConfigureOAuth2 extends Task {
608N/A
608N/A private static final Map<String, String> OIDC_SCOPES = new ImmutableMap.Builder<String, String>()
608N/A .put("openid", "")
608N/A .put("email", "openidconnect.scopes.email")
608N/A .put("address", "openidconnect.scopes.address")
608N/A .put("phone", "openidconnect.scopes.phone")
.put("profile", "openidconnect.scopes.profile")
.build();
private static final Map<String, String> OIDC_CLAIMS = new ImmutableMap.Builder<String, String>()
.put("email", "openidconnect.claims.email")
.put("address", "openidconnect.claims.address")
.put("phone_number", "openidconnect.claims.phonenumber")
.put("given_name", "openidconnect.claims.givenname")
.put("zoneinfo", "openidconnect.claims.zoneinfo")
.put("family_name", "openidconnect.claims.familyname")
.put("locale", "openidconnect.claims.locale")
.put("name", "openidconnect.claims.name")
.put("profile", "openidconnect.scopes.profile")
.build();
private static final Set<String> OIDC_DEFAULT_SCOPES = asSet("openid", "profile", "email", "address", "phone");
private static final Map<String, Set<String>> COMMON_OIDC_UMA_ATTRIBUTES =
new ImmutableMap.Builder<String, Set<String>>()
.put(SUBJECT_TYPES_SUPPORTED, singleton("public"))
.put(ID_TOKEN_SIGNING_ALGORITHMS, asSet("HS256", "HS384", "HS512", "RS256"))
.put(RESPONSE_TYPE_LIST, asSet(
TOKEN + "|" + TokenResponseTypeHandler.class.getName(),
CODE + "|" + AuthorizationCodeResponseTypeHandler.class.getName(),
ID_TOKEN + "|" + IdTokenResponseTypeHandler.class.getName()
))
.build();
private static final Map<String, Set<String>> OIDC_ATTRIBUTES = new ImmutableMap.Builder<String, Set<String>>()
.putAll(COMMON_OIDC_UMA_ATTRIBUTES)
.put(DEFAULT_SCOPES, OIDC_DEFAULT_SCOPES)
.build();
private static final Map<String, Set<String>> MOBILE_CONNECT_ATTRIBUTES =
new ImmutableMap.Builder<String, Set<String>>()
.putAll(OIDC_ATTRIBUTES)
.put(CREATED_TIMESTAMP_ATTRIBUTE_NAME, singleton("createTimestamp"))
.put(MODIFIED_TIMESTAMP_ATTRIBUTE_NAME, singleton("modifyTimestamp"))
.build();
private static final Map<String, Set<String>> UMA_ATTRIBUTES =
new ImmutableMap.Builder<String, Set<String>>()
.putAll(COMMON_OIDC_UMA_ATTRIBUTES)
.put(DEFAULT_SCOPES, new ImmutableSet.Builder<String>()
.addAll(OIDC_DEFAULT_SCOPES).add("uma_protection").add("uma_authorization").build())
.build();
private static final Map<String, Map<String, Set<String>>> PROFILE_SETTINGS =
new ImmutableMap.Builder<String, Map<String, Set<String>>>()
.put("oauth2", Collections.<String, Set<String>>emptyMap())
.put("oidc", OIDC_ATTRIBUTES)
.put("mobileconnect", MOBILE_CONNECT_ATTRIBUTES)
.put("uma", UMA_ATTRIBUTES)
.build();
private static final Map<String, Map<String, String>> SUPPORTED_SCOPE_KEYS =
new ImmutableMap.Builder<String, Map<String, String>>()
.put("oauth2", Collections.<String, String>emptyMap())
.put("oidc", OIDC_SCOPES)
.put("mobileconnect", OIDC_SCOPES)
.put("uma", new ImmutableMap.Builder<String, String>()
.putAll(OIDC_SCOPES)
.put("uma_protection", "uma.scopes.umaprotection")
.put("uma_authorization", "uma.scopes.umaauthorization")
.build())
.build();
private static final Map<String, Map<String, String>> SUPPORTED_CLAIM_KEYS =
new ImmutableMap.Builder<String, Map<String, String>>()
.put("oauth2", Collections.<String, String>emptyMap())
.put("oidc", OIDC_CLAIMS)
.put("mobileconnect", OIDC_CLAIMS)
.put("uma", OIDC_CLAIMS)
.build();
private static final Debug DEBUG = Debug.getInstance("workflow");
private static final String SERVICE_NAME = "OAuth2Provider";
//params
private static final String TYPE = "type";
private static final String REALM = "realm";
private static final String ROOT = "/";
//service params
private static final String RTL = "rtl";
private static final String ACL = "acl";
private static final String ATL = "atl";
private static final String IRT = "irt";
private static final String IRTR = "irtr";
private static final String SIC = "sic";
//policy params
private static final String POLICY_NAME = "OAuth2ProviderPolicy";
private static final String OAUTH2_AUTHORIZE_ENDPOINT = "/oauth2/authorize?*";
public static final String MESSAGE = "oauth2.provider.configured";
public static final String POLICY_CREATED = "oauth2.provider.policy.created";
public static final String POLICY_EXISTS = "oauth2.provider.policy.exists";
private final PolicyStoreProvider storeProvider;
public ConfigureOAuth2(){
storeProvider = new PrivilegePolicyStoreProvider(Collections.<String, QueryAttribute>emptyMap());
}
public String execute(Locale locale, Map params) throws WorkflowException {
final String type = getString(params, TYPE);
final String realm = getString(params, REALM);
final SSOToken token = AccessController.doPrivileged(AdminTokenAction.getInstance());
if (StringUtils.isEmpty(type)) {
throw new WorkflowException("type parameter is required");
}
//replace service attributes
final Map<String, Set<String>> attrValues = getDefaultOAuth2ProviderAttributes(token);
attrValues.putAll(PROFILE_SETTINGS.get(type));
attrValues.put(SUPPORTED_SCOPES, translate(realm, SUPPORTED_SCOPE_KEYS.get(type)));
attrValues.put(SUPPORTED_CLAIMS, translate(realm, SUPPORTED_CLAIM_KEYS.get(type)));
attrValues.put(REFRESH_TOKEN_LIFETIME_NAME, singleton(getString(params, RTL)));
attrValues.put(AUTHZ_CODE_LIFETIME_NAME, singleton(getString(params, ACL)));
attrValues.put(ACCESS_TOKEN_LIFETIME_NAME, singleton(getString(params, ATL)));
attrValues.put(ISSUE_REFRESH_TOKEN, singleton(getString(params, IRT)));
attrValues.put(ISSUE_REFRESH_TOKEN_ON_REFRESHING_TOKEN, singleton(getString(params, IRTR)));
attrValues.put(SCOPE_PLUGIN_CLASS, singleton(getString(params, SIC)));
createOAuth2Provider(token, realm, attrValues);
String policyURL = getRequestURL(params) + OAUTH2_AUTHORIZE_ENDPOINT;
//check if policy exists
PolicyManager mgr;
boolean createPolicy = false;
try {
mgr = new PolicyManager(token, ROOT);
if (mgr.getPolicy(POLICY_NAME) == null) {
createPolicy = true;
}
} catch (Exception e){
createPolicy = true;
}
if (createPolicy){
try {
Privilege toStore = Privilege.getNewInstance();
Map<String, Boolean> actions = new HashMap<>();
actions.put("POST", true);
actions.put("GET", true);
Entitlement entitlement = new Entitlement();
entitlement.setActionValues(actions);
entitlement.setResourceName(policyURL);
Subject adminSubject = SubjectUtils.createSuperAdminSubject();
toStore.setResourceTypeUuid(getUrlResourceTypeId(entitlement, adminSubject, realm));
toStore.setSubject(new AuthenticatedUsers());
toStore.setName(POLICY_NAME);
toStore.setEntitlement(entitlement);
PolicyStore policyStore = storeProvider.getPolicyStore(adminSubject, ROOT);
policyStore.create(toStore);
} catch (EntitlementException e) {
DEBUG.error("ConfigureOAuth2.execute() : Unable to create policy", e);
throw new WorkflowException("oauth2.provider.policy.failed");
}
}
String messageTemplate = getMessage(MESSAGE, locale);
return MessageFormat.format(messageTemplate, realm,
MessageFormat.format(getMessage(createPolicy ? POLICY_CREATED : POLICY_EXISTS, locale), POLICY_NAME));
}
private Set<String> translate(String realm, Map<String, String> values) {
Set<String> result = new HashSet<>();
ISLocaleContext localeContext = new ISLocaleContext(realm);
ResourceBundle bundle = ResourceBundle.getBundle("oauth2-default-user-descriptions", localeContext.getLocale());
for (Map.Entry<String, String> value : values.entrySet()) {
if (StringUtils.isNotEmpty(value.getValue())) {
result.add(value.getKey() + "|" + bundle.getString(value.getValue()));
} else {
result.add(value.getKey() + "|");
}
}
return result;
}
private String getUrlResourceTypeId(Entitlement entitlement, Subject adminSubject, String realm)
throws EntitlementException, WorkflowException {
ResourceTypeService resourceTypeService = InjectorHolder.getInstance(ResourceTypeService.class);
Application application = entitlement.getApplication(adminSubject, realm);
Set<String> resourceTypeIds = application.getResourceTypeUuids();
for (String id : resourceTypeIds) {
ResourceType resourceType = resourceTypeService.getResourceType(adminSubject, realm, id);
if ("URL".equalsIgnoreCase(resourceType.getName())) {
return id;
}
}
DEBUG.error("Could not find URL resource type on {} application. Found: {}", entitlement.getApplicationName(),
resourceTypeIds.toString());
throw new WorkflowException("oauth2.provider.resourceType.error", entitlement.getApplicationName());
}
private Map<String, Set<String>> getDefaultOAuth2ProviderAttributes(SSOToken token) throws WorkflowException {
try {
final ServiceSchema serviceSchema = new ServiceSchemaManager(SERVICE_NAME, token).getOrganizationSchema();
return SMSUtils.removeValidators(serviceSchema.getReadOnlyAttributeDefaults(), serviceSchema);
} catch (SMSException e) {
DEBUG.error("An error occurred while trying to read the default OAuth2 Provider settings.", e);
throw new WorkflowException("oauth2.provider.read.error", null);
} catch (SSOException e) {
DEBUG.error("An error occurred while trying to read the default OAuth2 Provider settings.", e);
throw new WorkflowException("oauth2.provider.read.error", null);
}
}
private void createOAuth2Provider(SSOToken token, String realm, Map<String, Set<String>> attrValues)
throws WorkflowException {
try {
new ServiceConfigManager(SERVICE_NAME, token).createOrganizationConfig(realm, attrValues);
} catch (SMSException e) {
DEBUG.error("An error occurred while trying to create the OAuth2 Provider.", e);
throw new WorkflowException("oauth2.provider.create.error", null);
} catch (SSOException e) {
DEBUG.error("An error occurred while trying to create the OAuth2 Provider.", e);
throw new WorkflowException("oauth2.provider.create.error", null);
}
}
}