org.cloudfoundry.identity.uaa.oauth.ClientAdminBootstrap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cloudfoundry-identity-common Show documentation
Show all versions of cloudfoundry-identity-common Show documentation
Cloud Foundry User Account and Authentication
/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2014] Pivotal Software, Inc. All Rights Reserved.
*
* This product is licensed to you under the Apache License, Version 2.0 (the "License").
* You may not use this product except in compliance with the License.
*
* This product includes a number of subcomponents with
* separate copyright notices and license terms. Your use of these
* subcomponents is subject to the terms and conditions of the
* subcomponent's license, as noted in the LICENSE file.
*******************************************************************************/
package org.cloudfoundry.identity.uaa.oauth;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.client.ClientConstants;
import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientRegistrationService;
import org.springframework.util.StringUtils;
/**
* @author Dave Syer
*
*/
public class ClientAdminBootstrap implements InitializingBean {
private static Log logger = LogFactory.getLog(ClientAdminBootstrap.class);
private Map> clients = new HashMap>();
private Collection autoApproveClients = Collections.emptySet();
private ClientRegistrationService clientRegistrationService;
private String domain = "cloudfoundry\\.com";
private boolean defaultOverride = true;
private final PasswordEncoder passwordEncoder;
public ClientAdminBootstrap(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
/**
* Flag to indicate that client details should override existing values by
* default. If true and the override flag is
* not set in the client details input then the details will override any
* existing details with the same id.
*
* @param defaultOverride the default override flag to set (default true, so
* flag does not have to be provided
* explicitly)
*/
public void setDefaultOverride(boolean defaultOverride) {
this.defaultOverride = defaultOverride;
}
/**
* The domain suffix (default "cloudfoundry.com") used to detect http
* redirects. If an http callback in this domain
* is found in a client registration and there is no corresponding value
* with https as well, then the https value
* will be added.
*
* @param domain the domain to set
*/
public void setDomain(String domain) {
this.domain = domain.replace(".", "\\.");
}
public PasswordEncoder getPasswordEncoder() {
return passwordEncoder;
}
/**
* @param clients the clients to set
*/
public void setClients(Map> clients) {
if (clients == null) {
this.clients = Collections.emptyMap();
} else {
this.clients = new HashMap<>(clients);
}
}
/**
* A set of client ids that are unconditionally to be autoapproved
* (independent of the settings in the client
* details map). These clients will have autoapprove=true
when
* they are inserted into the client
* details store.
*
* @param autoApproveClients the auto approve clients
*/
public void setAutoApproveClients(Collection autoApproveClients) {
this.autoApproveClients = autoApproveClients;
}
/**
* @param clientRegistrationService the clientRegistrationService to set
*/
public void setClientRegistrationService(ClientRegistrationService clientRegistrationService) {
this.clientRegistrationService = clientRegistrationService;
}
@Override
public void afterPropertiesSet() throws Exception {
addHttpsCallbacks();
addNewClients();
updateAutoApprovClients();
}
/**
* Explicitly override autoapprove in all clients that were provided in the
* whitelist.
*/
private void updateAutoApprovClients() {
List clients = clientRegistrationService.listClientDetails();
for (ClientDetails client : clients) {
if (!autoApproveClients.contains(client.getClientId())) {
continue;
}
BaseClientDetails base = new BaseClientDetails(client);
Map info = new HashMap(client.getAdditionalInformation());
info.put(ClientConstants.AUTO_APPROVE, true);
base.setAdditionalInformation(info);
logger.debug("Adding autoapprove flag: " + base);
clientRegistrationService.updateClientDetails(base);
}
}
/**
* Make sure all cloudfoundry.com callbacks are https
*/
private void addHttpsCallbacks() {
List clients = clientRegistrationService.listClientDetails();
for (ClientDetails client : clients) {
Set registeredRedirectUri = client.getRegisteredRedirectUri();
if (registeredRedirectUri == null || registeredRedirectUri.isEmpty()) {
continue;
}
Set uris = new HashSet(registeredRedirectUri);
boolean newItems = false;
for (String uri : registeredRedirectUri) {
if (uri.matches("^http://[^/]*\\." + domain + ".*")) {
newItems = true;
uris.remove(uri);
uris.add("https" + uri.substring("http".length()));
}
}
if (!newItems) {
continue;
}
BaseClientDetails newClient = new BaseClientDetails(client);
newClient.setRegisteredRedirectUri(uris);
logger.debug("Adding https callback: " + newClient);
clientRegistrationService.updateClientDetails(newClient);
}
}
private String getRedirectUris(Map map) {
Set redirectUris = new HashSet<>();
if (map.get("redirect-uri") != null) {
redirectUris.add((String) map.get("redirect-uri"));
}
if (map.get("signup_redirect_url") != null) {
redirectUris.add((String) map.get("signup_redirect_url"));
}
if (map.get("change_email_redirect_url") != null) {
redirectUris.add((String) map.get("change_email_redirect_url"));
}
return StringUtils.arrayToCommaDelimitedString(redirectUris.toArray(new String[] {}));
}
private void addNewClients() throws Exception {
for (String clientId : clients.keySet()) {
Map map = clients.get(clientId);
BaseClientDetails client = new BaseClientDetails(clientId, (String) map.get("resource-ids"),
(String) map.get("scope"), (String) map.get("authorized-grant-types"),
(String) map.get("authorities"), getRedirectUris(map));
client.setClientSecret((String) map.get("secret"));
Integer validity = (Integer) map.get("access-token-validity");
Boolean override = (Boolean) map.get("override");
if (override == null) {
override = defaultOverride;
}
Map info = new HashMap(map);
if (validity != null) {
client.setAccessTokenValiditySeconds(validity);
}
validity = (Integer) map.get("refresh-token-validity");
if (validity != null) {
client.setRefreshTokenValiditySeconds(validity);
}
// UAA does not use the resource ids in client registrations
client.setResourceIds(Collections.singleton("none"));
if (client.getScope().isEmpty()) {
client.setScope(Collections.singleton("uaa.none"));
}
if (client.getAuthorities().isEmpty()) {
client.setAuthorities(Collections.singleton(UaaAuthority.UAA_NONE));
}
if (client.getAuthorizedGrantTypes().contains("authorization_code")) {
client.getAuthorizedGrantTypes().add("refresh_token");
}
for (String key : Arrays.asList("resource-ids", "scope", "authorized-grant-types", "authorities",
"redirect-uri", "secret", "id", "override", "access-token-validity",
"refresh-token-validity")) {
info.remove(key);
}
client.setAdditionalInformation(info);
try {
clientRegistrationService.addClientDetails(client);
} catch (ClientAlreadyExistsException e) {
if (override == null || override) {
logger.debug("Overriding client details for " + clientId);
clientRegistrationService.updateClientDetails(client);
if (StringUtils.hasText(client.getClientSecret()) && didPasswordChange(clientId, client.getClientSecret())) {
clientRegistrationService.updateClientSecret(clientId, client.getClientSecret());
}
} else {
// ignore it
logger.debug(e.getMessage());
}
}
}
}
protected boolean didPasswordChange(String clientId, String rawPassword) {
if (getPasswordEncoder()!=null && clientRegistrationService instanceof ClientDetailsService) {
ClientDetails existing = ((ClientDetailsService)clientRegistrationService).loadClientByClientId(clientId);
String existingPasswordHash = existing.getClientSecret();
return !getPasswordEncoder().matches(rawPassword, existingPasswordHash);
} else {
return true;
}
}
}