You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by lq...@apache.org on 2016/02/16 17:57:43 UTC

svn commit: r1730713 - in /qpid/java/trunk/broker-core/src/main/java/org/apache/qpid/server/security/group/cloudfoundry: ./ CloudFoundryDashboardManagementGroupProvider.java CloudFoundryDashboardManagementGroupProviderImpl.java

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<X extends CloudFoundryDashboardManagementGroupProvider<X>> extends GroupProvider<X>
+{
+    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<String, String> getServiceToManagementGroupMapping();
+
+    @DerivedAttribute
+    List<String> getTlsProtocolWhiteList();
+    @DerivedAttribute
+    List<String> getTlsProtocolBlackList();
+    @DerivedAttribute
+    List<String> getTlsCipherSuiteWhiteList();
+    @DerivedAttribute
+    List<String> 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<CloudFoundryDashboardManagementGroupProviderImpl>
+        implements CloudFoundryDashboardManagementGroupProvider<CloudFoundryDashboardManagementGroupProviderImpl>
+{
+    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<String, String> _serviceToManagementGroupMapping;
+
+    private List<String> _tlsProtocolWhiteList;
+    private List<String> _tlsProtocolBlackList;
+    private List<String> _tlsCipherSuiteWhiteList;
+    private List<String> _tlsCipherSuiteBlackList;
+    private int _connectTimeout;
+    private int _readTimeout;
+
+    @ManagedObjectFactoryConstructor
+    public CloudFoundryDashboardManagementGroupProviderImpl(Map<String, Object> 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<String> 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<Principal> 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<Principal> groupPrincipals = new HashSet<>();
+
+        for (Map.Entry<String, String> 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<String, Object> 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<String, String> getServiceToManagementGroupMapping()
+    {
+        return _serviceToManagementGroupMapping;
+    }
+
+    @Override
+    public List<String> getTlsProtocolWhiteList()
+    {
+        return _tlsProtocolWhiteList;
+    }
+
+    @Override
+    public List<String> getTlsProtocolBlackList()
+    {
+        return _tlsProtocolBlackList;
+    }
+
+    @Override
+    public List<String> getTlsCipherSuiteWhiteList()
+    {
+        return _tlsCipherSuiteWhiteList;
+    }
+
+    @Override
+    public List<String> getTlsCipherSuiteBlackList()
+    {
+        return _tlsCipherSuiteBlackList;
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org