Return-Path: X-Original-To: apmail-qpid-commits-archive@www.apache.org Delivered-To: apmail-qpid-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 3BB10181AE for ; Tue, 16 Feb 2016 16:57:48 +0000 (UTC) Received: (qmail 40803 invoked by uid 500); 16 Feb 2016 16:57:48 -0000 Delivered-To: apmail-qpid-commits-archive@qpid.apache.org Received: (qmail 40774 invoked by uid 500); 16 Feb 2016 16:57:48 -0000 Mailing-List: contact commits-help@qpid.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@qpid.apache.org Delivered-To: mailing list commits@qpid.apache.org Received: (qmail 40765 invoked by uid 99); 16 Feb 2016 16:57:48 -0000 Received: from Unknown (HELO spamd2-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 16 Feb 2016 16:57:48 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd2-us-west.apache.org (ASF Mail Server at spamd2-us-west.apache.org) with ESMTP id A73791A0698 for ; Tue, 16 Feb 2016 16:57:47 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd2-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 1.471 X-Spam-Level: * X-Spam-Status: No, score=1.471 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-0.329] autolearn=disabled Received: from mx1-lw-us.apache.org ([10.40.0.8]) by localhost (spamd2-us-west.apache.org [10.40.0.9]) (amavisd-new, port 10024) with ESMTP id jLBsnnMui0dL for ; Tue, 16 Feb 2016 16:57:44 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-us.apache.org (ASF Mail Server at mx1-lw-us.apache.org) with ESMTP id 45CA55FAD9 for ; Tue, 16 Feb 2016 16:57:44 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 8ACD1E019D for ; Tue, 16 Feb 2016 16:57:43 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 6EDD33A09F7 for ; Tue, 16 Feb 2016 16:57:43 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1730713 - in /qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry: ./ CloudFoundryDashboardManagementGroupProvider.java CloudFoundryDashboardManagementGroupProviderImpl.java Date: Tue, 16 Feb 2016 16:57:43 -0000 To: commits@qpid.apache.org From: lquack@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20160216165743.6EDD33A09F7@svn01-us-west.apache.org> Author: lquack Date: Tue Feb 16 16:57:43 2016 New Revision: 1730713 URL: http://svn.apache.org/viewvc?rev=1730713&view=rev Log: QPID-7069: [Java Broker] Add CloudFoundry specific GroupProvider Added: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProvider.java qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProviderImpl.java Added: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProvider.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProvider.java?rev=1730713&view=auto ============================================================================== --- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProvider.java (added) +++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProvider.java Tue Feb 16 16:57:43 2016 @@ -0,0 +1,62 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.qpid.server.security.group.cloudfoundry; + +import java.net.URI; +import java.util.List; +import java.util.Map; + +import org.apache.qpid.server.model.DerivedAttribute; +import org.apache.qpid.server.model.GroupProvider; +import org.apache.qpid.server.model.ManagedAttribute; +import org.apache.qpid.server.model.ManagedContextDefault; +import org.apache.qpid.server.model.ManagedObject; +import org.apache.qpid.server.model.TrustStore; + +@ManagedObject( category = false, type = "CloudFoundryDashboardManagement" ) +public interface CloudFoundryDashboardManagementGroupProvider> extends GroupProvider +{ + String QPID_GROUPPROVIDER_CLOUDFOUNDRY_CONNECT_TIMEOUT = "qpid.groupprovider.cloudfoundry.connectTimeout"; + @ManagedContextDefault(name = QPID_GROUPPROVIDER_CLOUDFOUNDRY_CONNECT_TIMEOUT) + int DEFAULT_QPID_GROUPPROVIDER_CLOUDFOUNDRY_CONNECT_TIMEOUT = 60000; + + String QPID_GROUPPROVIDER_CLOUDFOUNDRY_READ_TIMEOUT = "qpid.groupprovider.cloudfoundry.readTimeout"; + @ManagedContextDefault(name = QPID_GROUPPROVIDER_CLOUDFOUNDRY_READ_TIMEOUT) + int DEFAULT_QPID_GROUPPROVIDER_CLOUDFOUNDRY_READ_TIMEOUT = 60000; + + @ManagedAttribute( mandatory = true ) + URI getCloudFoundryEndpointURI(); + + @ManagedAttribute() + TrustStore getTrustStore(); + + @ManagedAttribute( mandatory = true ) + Map getServiceToManagementGroupMapping(); + + @DerivedAttribute + List getTlsProtocolWhiteList(); + @DerivedAttribute + List getTlsProtocolBlackList(); + @DerivedAttribute + List getTlsCipherSuiteWhiteList(); + @DerivedAttribute + List getTlsCipherSuiteBlackList(); +} Added: qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProviderImpl.java URL: http://svn.apache.org/viewvc/qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProviderImpl.java?rev=1730713&view=auto ============================================================================== --- qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProviderImpl.java (added) +++ qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry/CloudFoundryDashboardManagementGroupProviderImpl.java Tue Feb 16 16:57:43 2016 @@ -0,0 +1,272 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.qpid.server.security.group.cloudfoundry; + +import static org.apache.qpid.configuration.CommonProperties.QPID_SECURITY_TLS_CIPHER_SUITE_BLACK_LIST; +import static org.apache.qpid.configuration.CommonProperties.QPID_SECURITY_TLS_CIPHER_SUITE_WHITE_LIST; +import static org.apache.qpid.configuration.CommonProperties.QPID_SECURITY_TLS_PROTOCOL_BLACK_LIST; +import static org.apache.qpid.configuration.CommonProperties.QPID_SECURITY_TLS_PROTOCOL_WHITE_LIST; +import static org.apache.qpid.server.util.ParameterizedTypes.LIST_OF_STRINGS; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.apache.qpid.server.configuration.IllegalConfigurationException; +import org.apache.qpid.server.model.AbstractConfiguredObject; +import org.apache.qpid.server.model.Broker; +import org.apache.qpid.server.model.ConfiguredObject; +import org.apache.qpid.server.model.ManagedAttributeField; +import org.apache.qpid.server.model.ManagedObjectFactoryConstructor; +import org.apache.qpid.server.model.TrustStore; +import org.apache.qpid.server.security.auth.manager.oauth2.OAuth2UserPrincipal; +import org.apache.qpid.server.security.group.GroupPrincipal; +import org.apache.qpid.server.util.ConnectionBuilder; +import org.apache.qpid.server.util.ConnectionScopedRuntimeException; +import org.apache.qpid.server.util.ServerScopedRuntimeException; + +/* + * This GroupProvider checks a CloudFoundry service dashboard to see whether a certain user (represented by an + * accessToken) has permission to manage a set of service instances and adds corresponding GroupPrincipals. + * See the CloudFoundry docs for more information: + * http://docs.cloudfoundry.org/services/dashboard-sso.html#checking-user-permissions + */ +public class CloudFoundryDashboardManagementGroupProviderImpl extends AbstractConfiguredObject + implements CloudFoundryDashboardManagementGroupProvider +{ + private static final Logger LOGGER = LoggerFactory.getLogger(CloudFoundryDashboardManagementGroupProviderImpl.class); + private static final String UTF8 = StandardCharsets.UTF_8.name(); + + private final ObjectMapper _objectMapper = new ObjectMapper(); + + @ManagedAttributeField + private URI _cloudFoundryEndpointURI; + + @ManagedAttributeField + private TrustStore _trustStore; + + @ManagedAttributeField + private Map _serviceToManagementGroupMapping; + + private List _tlsProtocolWhiteList; + private List _tlsProtocolBlackList; + private List _tlsCipherSuiteWhiteList; + private List _tlsCipherSuiteBlackList; + private int _connectTimeout; + private int _readTimeout; + + @ManagedObjectFactoryConstructor + public CloudFoundryDashboardManagementGroupProviderImpl(Map attributes, Broker broker) + { + super(parentsMap(broker), attributes); + } + + @Override + public void onOpen() + { + super.onOpen(); + _tlsProtocolWhiteList = getContextValue(List.class, LIST_OF_STRINGS, QPID_SECURITY_TLS_PROTOCOL_WHITE_LIST); + _tlsProtocolBlackList = getContextValue(List.class, LIST_OF_STRINGS, QPID_SECURITY_TLS_PROTOCOL_BLACK_LIST); + _tlsCipherSuiteWhiteList = getContextValue(List.class, LIST_OF_STRINGS, QPID_SECURITY_TLS_CIPHER_SUITE_WHITE_LIST); + _tlsCipherSuiteBlackList = getContextValue(List.class, LIST_OF_STRINGS, QPID_SECURITY_TLS_CIPHER_SUITE_BLACK_LIST); + _connectTimeout = getContextValue(Integer.class, QPID_GROUPPROVIDER_CLOUDFOUNDRY_CONNECT_TIMEOUT); + _readTimeout = getContextValue(Integer.class, QPID_GROUPPROVIDER_CLOUDFOUNDRY_READ_TIMEOUT); + } + + @Override + protected void validateChange(final ConfiguredObject proxyForValidation, final Set changedAttributes) + { + super.validateChange(proxyForValidation, changedAttributes); + final CloudFoundryDashboardManagementGroupProvider validationProxy = (CloudFoundryDashboardManagementGroupProvider) proxyForValidation; + validateSecureEndpoint(validationProxy); + } + + @Override + public void onValidate() + { + super.onValidate(); + validateSecureEndpoint(this); + } + + private void validateSecureEndpoint(final CloudFoundryDashboardManagementGroupProvider provider) + { + if (!"https".equals(provider.getCloudFoundryEndpointURI().getScheme())) + { + throw new IllegalConfigurationException(String.format("CloudFoundryDashboardManagementEndpoint is not secure: '%s'", + provider.getCloudFoundryEndpointURI())); + } + } + + @Override + public Set getGroupPrincipalsForUser(Principal userPrincipal) + { + if (!(userPrincipal instanceof OAuth2UserPrincipal)) + { + return Collections.emptySet(); + } + if (_serviceToManagementGroupMapping == null) + { + throw new IllegalConfigurationException("CloudFoundryDashboardManagementGroupProvider serviceToManagementGroupMapping may not be null"); + } + + OAuth2UserPrincipal oauth2UserPrincipal = (OAuth2UserPrincipal) userPrincipal; + String accessToken = oauth2UserPrincipal.getAccessToken(); + Set groupPrincipals = new HashSet<>(); + + for (Map.Entry entry : _serviceToManagementGroupMapping.entrySet()) + { + String serviceInstanceId = entry.getKey(); + String managementGroupName = entry.getValue(); + if (mayManageServiceInstance(serviceInstanceId, accessToken)) + { + LOGGER.debug("Adding group '{}' to the set of Principals", managementGroupName); + groupPrincipals.add(new GroupPrincipal(managementGroupName)); + } + else + { + LOGGER.debug("CloudFoundryDashboardManagementEndpoint denied management permission for service instance '{}'", serviceInstanceId); + } + } + return groupPrincipals; + } + + private boolean mayManageServiceInstance(final String serviceInstanceId, final String accessToken) + { + HttpURLConnection connection; + String cloudFoundryEndpoint = String.format("%s/v2/service_instances/%s/permissions", + getCloudFoundryEndpointURI().toString(), serviceInstanceId); + try + { + ConnectionBuilder connectionBuilder = new ConnectionBuilder(new URL(cloudFoundryEndpoint)); + connectionBuilder.setConnectTimeout(_connectTimeout).setReadTimeout(_readTimeout); + if (_trustStore != null) + { + try + { + connectionBuilder.setTrustMangers(_trustStore.getTrustManagers()); + } + catch (GeneralSecurityException e) + { + throw new ServerScopedRuntimeException("Cannot initialise TLS", e); + } + } + connectionBuilder.setTlsProtocolWhiteList(_tlsProtocolWhiteList) + .setTlsProtocolBlackList(_tlsProtocolBlackList) + .setTlsCipherSuiteWhiteList(_tlsCipherSuiteWhiteList) + .setTlsCipherSuiteBlackList(_tlsCipherSuiteBlackList); + + LOGGER.debug("About to call CloudFoundryDashboardManagementEndpoint '{}'", cloudFoundryEndpoint); + connection = connectionBuilder.build(); + + connection.setRequestProperty("Accept-Charset", UTF8); + connection.setRequestProperty("Accept", "application/json"); + connection.setRequestProperty("Authorization", "Bearer " + accessToken); + + connection.connect(); + } + catch (IOException e) + { + throw new ConnectionScopedRuntimeException(String.format("Could not connect to CloudFoundryDashboardManagementEndpoint '%s'.", + cloudFoundryEndpoint), e); + } + + try (InputStream input = connection.getInputStream()) + { + final int responseCode = connection.getResponseCode(); + LOGGER.debug("Call to CloudFoundryDashboardManagementEndpoint '{}' complete, response code : {}", cloudFoundryEndpoint, responseCode); + + Map responseMap = _objectMapper.readValue(input, Map.class); + Object mayManageObject = responseMap.get("manage"); + if (mayManageObject == null || !(mayManageObject instanceof Boolean)) + { + throw new ConnectionScopedRuntimeException("CloudFoundryDashboardManagementEndpoint response did not contain \"manage\" entry."); + } + return (boolean) mayManageObject; + } + catch (JsonProcessingException e) + { + throw new ConnectionScopedRuntimeException(String.format("CloudFoundryDashboardManagementEndpoint '%s' did not return json", + cloudFoundryEndpoint), e); + } + catch (IOException e) + { + throw new ConnectionScopedRuntimeException(String.format("Connection to CloudFoundryDashboardManagementEndpoint '%s' failed", + cloudFoundryEndpoint), e); + } + } + + @Override + public URI getCloudFoundryEndpointURI() + { + return _cloudFoundryEndpointURI; + } + + @Override + public TrustStore getTrustStore() + { + return _trustStore; + } + + @Override + public Map getServiceToManagementGroupMapping() + { + return _serviceToManagementGroupMapping; + } + + @Override + public List getTlsProtocolWhiteList() + { + return _tlsProtocolWhiteList; + } + + @Override + public List getTlsProtocolBlackList() + { + return _tlsProtocolBlackList; + } + + @Override + public List getTlsCipherSuiteWhiteList() + { + return _tlsCipherSuiteWhiteList; + } + + @Override + public List getTlsCipherSuiteBlackList() + { + return _tlsCipherSuiteBlackList; + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org For additional commands, e-mail: commits-help@qpid.apache.org