All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.cloudfoundry.identity.uaa.client.ClientAdminBootstrap Maven / Gradle / Ivy
/*******************************************************************************
* Cloud Foundry
* Copyright (c) [2009-2016] 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.client;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.cloudfoundry.identity.uaa.audit.event.EntityDeletedEvent;
import org.cloudfoundry.identity.uaa.authentication.SystemAuthentication;
import org.cloudfoundry.identity.uaa.oauth.client.ClientConstants;
import org.cloudfoundry.identity.uaa.user.UaaAuthority;
import org.cloudfoundry.identity.uaa.zone.ClientServicesExtension;
import org.cloudfoundry.identity.uaa.zone.IdentityZone;
import org.cloudfoundry.identity.uaa.zone.IdentityZoneHolder;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientAlreadyExistsException;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import org.springframework.util.StringUtils;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
public class ClientAdminBootstrap implements InitializingBean, ApplicationListener, ApplicationEventPublisherAware {
private static Log logger = LogFactory.getLog(ClientAdminBootstrap.class);
private Map> clients = new HashMap>();
private List clientsToDelete = null;
private Collection autoApproveClients = Collections.emptySet();
private ClientServicesExtension clientRegistrationService;
private ClientMetadataProvisioning clientMetadataProvisioning;
private boolean defaultOverride = true;
private final PasswordEncoder passwordEncoder;
private ApplicationEventPublisher publisher;
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;
}
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);
}
}
public void setClientsToDelete(List clientsToDelete) {
this.clientsToDelete = clientsToDelete;
}
/**
* 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(ClientServicesExtension clientRegistrationService) {
this.clientRegistrationService = clientRegistrationService;
}
@Override
public void afterPropertiesSet() throws Exception {
addNewClients();
updateAutoApproveClients();
}
/**
* Explicitly override autoapprove in all clients that were provided in the
* whitelist.
*/
private void updateAutoApproveClients() {
List slatedForDeletion = ofNullable(clientsToDelete).orElse(emptyList());
Collection autoApproveList = new LinkedList(ofNullable(autoApproveClients).orElse(emptyList()));
autoApproveList.removeIf(s -> slatedForDeletion.contains(s));
for (String clientId : autoApproveList) {
try {
BaseClientDetails base = (BaseClientDetails) clientRegistrationService.loadClientByClientId(clientId, IdentityZone.getUaa().getId());
base.addAdditionalInformation(ClientConstants.AUTO_APPROVE, true);
logger.debug("Adding autoapprove flag to client: " + clientId);
clientRegistrationService.updateClientDetails(base, IdentityZone.getUaa().getId());
} catch (NoSuchClientException n) {
logger.debug("Client not found, unable to set autoapprove: " + clientId);
}
}
}
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 {
List slatedForDeletion = ofNullable(clientsToDelete).orElse(emptyList());
Set>> entries = clients.entrySet();
entries.removeIf(entry -> slatedForDeletion.contains(entry.getKey()));
for (Map.Entry> entry : entries) {
String clientId = entry.getKey();
Map map = entry.getValue();
if(map.get("authorized-grant-types") == null) {
throw new InvalidClientDetailsException("Client must have at least one authorized-grant-type. client ID: " + 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(map.get("secret") == null ? "" : (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","show-on-homepage","app-launch-url","app-icon")) {
info.remove(key);
}
client.setAdditionalInformation(info);
try {
clientRegistrationService.addClientDetails(client, IdentityZone.getUaa().getId());
} catch (ClientAlreadyExistsException e) {
if (override == null || override) {
logger.debug("Overriding client details for " + clientId);
clientRegistrationService.updateClientDetails(client, IdentityZone.getUaa().getId());
if ( didPasswordChange(clientId, client.getClientSecret())) {
clientRegistrationService.updateClientSecret(clientId, client.getClientSecret(), IdentityZone.getUaa().getId());
}
} else {
// ignore it
logger.debug(e.getMessage());
}
}
for (String s : Arrays.asList("authorization_code", "implicit")) {
if (client.getAuthorizedGrantTypes().contains(s) && isMissingRedirectUris(client)) {
throw new InvalidClientDetailsException(s + " grant type requires at least one redirect URL. ClientID: " + client.getClientId());
}
}
ClientMetadata clientMetadata = buildClientMetadata(map, clientId);
clientMetadataProvisioning.update(clientMetadata, IdentityZoneHolder.get().getId());
}
}
private boolean isMissingRedirectUris(BaseClientDetails client) {
return client.getRegisteredRedirectUri() == null || client.getRegisteredRedirectUri().isEmpty();
}
private ClientMetadata buildClientMetadata(Map map, String clientId) {
Boolean showOnHomepage = (Boolean) map.get("show-on-homepage");
String appLaunchUrl = (String) map.get("app-launch-url");
String appIcon = (String) map.get("app-icon");
ClientMetadata clientMetadata = new ClientMetadata();
clientMetadata.setClientId(clientId);
clientMetadata.setAppIcon(appIcon);
clientMetadata.setShowOnHomePage(showOnHomepage != null && showOnHomepage);
if(StringUtils.hasText(appLaunchUrl)) {
try {
clientMetadata.setAppLaunchUrl(new URL(appLaunchUrl));
} catch (MalformedURLException e) {
logger.info(new ClientMetadataException("Invalid app-launch-url for client " + clientId, e, HttpStatus.INTERNAL_SERVER_ERROR));
}
}
return clientMetadata;
}
protected boolean didPasswordChange(String clientId, String rawPassword) {
if (getPasswordEncoder()!=null) {
ClientDetails existing = clientRegistrationService.loadClientByClientId(clientId, IdentityZoneHolder.get().getId());
String existingPasswordHash = existing.getClientSecret();
return !getPasswordEncoder().matches(rawPassword, existingPasswordHash);
} else {
return true;
}
}
public ClientMetadataProvisioning getClientMetadataProvisioning() {
return clientMetadataProvisioning;
}
public void setClientMetadataProvisioning(ClientMetadataProvisioning clientMetadataProvisioning) {
this.clientMetadataProvisioning = clientMetadataProvisioning;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
Authentication auth = SystemAuthentication.SYSTEM_AUTHENTICATION;
for (String clientId : ofNullable(clientsToDelete).orElse(emptyList())) {
try {
ClientDetails client = clientRegistrationService.loadClientByClientId(clientId, IdentityZoneHolder.get().getId());
logger.debug("Deleting client from manifest:"+clientId);
EntityDeletedEvent delete = new EntityDeletedEvent<>(client, auth);
publish(delete);
} catch (NoSuchClientException e) {
logger.debug("Ignoring delete for non existent client:"+clientId);
}
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish(ApplicationEvent event) {
if (publisher!=null) {
publisher.publishEvent(event);
}
}
}