Repository: ambari
Updated Branches:
  refs/heads/trunk fe0c7ab99 -> e146400e6


AMBARI-15232. Provide details on the JCE policy installed in the JVM used by 
Ambari (rlevas)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e146400e
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e146400e
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e146400e

Branch: refs/heads/trunk
Commit: e146400e661d23f7ee2f08a06b0eadcbf798e476
Parents: fe0c7ab
Author: Robert Levas <[email protected]>
Authored: Fri Mar 4 10:54:14 2016 -0500
Committer: Robert Levas <[email protected]>
Committed: Fri Mar 4 10:54:14 2016 -0500

----------------------------------------------------------------------
 .../internal/AbstractProviderModule.java        |   3 +
 .../RootServiceComponentPropertyProvider.java   | 208 +++++++++++++++++++
 ...ootServiceComponentPropertyProviderTest.java |  92 ++++++++
 3 files changed, 303 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/e146400e/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java
index ff9b4e4..b96addb 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/AbstractProviderModule.java
@@ -772,6 +772,9 @@ public abstract class AbstractProviderModule implements 
ProviderModule,
               gpp));
         }
         break;
+        case RootServiceComponent:
+          providers.add(new RootServiceComponentPropertyProvider());
+          break;
         default:
           break;
       }

http://git-wip-us.apache.org/repos/asf/ambari/blob/e146400e/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProvider.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProvider.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProvider.java
new file mode 100644
index 0000000..8bf389f
--- /dev/null
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProvider.java
@@ -0,0 +1,208 @@
+/*
+ * 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.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.controller.spi.Predicate;
+import org.apache.ambari.server.controller.spi.PropertyProvider;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.SystemException;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.Cipher;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * RootServiceComponentPropertyProvider is a PropertyProvider implementation 
providing additional
+ * properties for RootServiceComponent resources, like AMBARI_SERVER and 
AMBARI_AGENT.
+ * <p/>
+ * This implementation conditionally calculates and returns cipher and JCE 
details upon request.
+ * The Cipher data is cached after the first request for it and held in memory 
until Ambari is
+ * restarted.  The user is required to explicitly query for one or both of the 
following properties
+ * (or fields) to get access to the data:
+ * <ul>
+ * <li>RootServiceComponents/jce_policy</li>
+ * <li>RootServiceComponents/ciphers</li>
+ * </ul>
+ * <p/>
+ * For example:
+ * <ul>
+ * <li><code>GET 
/api/v1/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/ciphers</code></li>
+ * <li><code>GET 
/api/v1/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/jce_policy</code></li>
+ * <li><code>GET 
/api/v1/services/AMBARI/components/AMBARI_SERVER?fields=RootServiceComponents/jce_policy,RootServiceComponents/ciphers</code></li>
+ * </ul>
+ * <p/>
+ * When querying for ciphers the returned data is a listing if installed 
cipher algorithms and their
+ * maximum key lengths. If all maximum key lengths are 
<code>2147483647</code>, then the unlimited
+ * key JCE policy is installed, else each item will have some smaller value 
(in bytes).
+ * <p/>
+ * For example:
+ * <pre>
+ * "ciphers" : {
+ *   "sunjce.aes" : 2147483647,
+ *   "sunjce.aeswrap" : 2147483647,
+ *   "sunjce.aeswrap_128" : 2147483647,
+ *   "sunjce.aeswrap_192" : 2147483647,
+ *   "sunjce.aeswrap_256" : 2147483647,
+ *   "sunjce.arcfour" : 2147483647,
+ *   "sunjce.blowfish" : 2147483647,
+ *   ...
+ * }
+ * </pre>
+ * <p/>
+ * When querying for the JDC policy, the returned data is a structure with 
details about the JCE
+ * policy - namely whether the unlimited key length policy is installed or not.
+ * <p/>
+ * For example:
+ * <pre>
+ * "jce_policy" : {
+ *   "unlimited_key" : true
+ * }
+ * </pre>
+ */
+public class RootServiceComponentPropertyProvider extends BaseProvider 
implements PropertyProvider {
+  public static final String JCE_POLICY_PROPERTY_ID = PropertyHelper
+      .getPropertyId("RootServiceComponents", "jce_policy");
+
+  public static final String CIPHER_PROPERTIES_PROPERTY_ID = PropertyHelper
+      .getPropertyId("RootServiceComponents", "ciphers");
+
+  private static final Set<String> SUPPORTED_PROPERTY_IDS;
+
+  private final static Logger LOG = 
LoggerFactory.getLogger(RootServiceComponentPropertyProvider.class);
+
+  static {
+    Set<String> propertyIds = new HashSet<String>();
+    propertyIds.add(JCE_POLICY_PROPERTY_ID);
+    propertyIds.add(CIPHER_PROPERTIES_PROPERTY_ID);
+    SUPPORTED_PROPERTY_IDS = Collections.unmodifiableSet(propertyIds);
+  }
+
+  /**
+   * Map of cipher algorithm names to maximum key lengths.
+   * <p/>
+   * This is cached in memory after the first time it is needed.
+   */
+  private static final Map<String, Integer> CACHED_CIPHER_MAX_KEY_LENGTHS = 
new HashMap<String, Integer>();
+
+  /**
+   * Constructor
+   */
+  public RootServiceComponentPropertyProvider() {
+    super(SUPPORTED_PROPERTY_IDS);
+  }
+
+  @Override
+  public Set<Resource> populateResources(Set<Resource> resources, Request 
request, Predicate predicate) throws SystemException {
+
+    Set<String> requestedIds = request.getPropertyIds();
+
+    for (Resource resource : resources) {
+      // If this resource represents the AMBARI_SERVER component, handle it's 
specific properties...
+      if 
(RootServiceResponseFactory.Components.AMBARI_SERVER.name().equals(resource.getPropertyValue(RootServiceComponentResourceProvider.COMPONENT_NAME_PROPERTY_ID)))
 {
+        // Attempt to fill in the cipher details only if explicitly asked for.
+        if (requestedIds.contains(JCE_POLICY_PROPERTY_ID) || 
requestedIds.contains(CIPHER_PROPERTIES_PROPERTY_ID)) {
+          setCipherDetails(resource, requestedIds);
+        }
+      }
+    }
+
+    return resources;
+  }
+
+  /**
+   * Retrieve details about the active JCE policy and installed ciphers, then 
set the resource
+   * properties for the relevant resource.
+   * <p/>
+   * The following properties are set:
+   * <dl>
+   * <dt>RootServiceComponents/jce_policy/unlimited_key</dt>
+   * <dd>true if the unlimited key JCE policy is detected; false if it is not 
detected; null if unknown</dd>
+   * <dt>RootServiceComponents/ciphers</dt>
+   * <dd>A list of installed ciphers and their maximum key length values</dd>
+   * </dl>
+   *
+   * @param resource     the resource to update
+   * @param requestedIds the request ids to populate
+   */
+  private void setCipherDetails(Resource resource, Set<String> requestedIds) {
+    // Lazily fill the cache on first request....
+    synchronized (CACHED_CIPHER_MAX_KEY_LENGTHS) {
+      if (CACHED_CIPHER_MAX_KEY_LENGTHS.isEmpty()) {
+        // Get the list of cipher algorithms and determine maximum key 
lengths. Report as a resource property.
+        for (Provider provider : Security.getProviders()) {
+          String providerName = provider.getName();
+
+          for (Provider.Service service : provider.getServices()) {
+            String algorithmName = service.getAlgorithm();
+
+            if ("Cipher".equalsIgnoreCase(service.getType())) {
+              try {
+                CACHED_CIPHER_MAX_KEY_LENGTHS.put(String.format("%s.%s", 
providerName, algorithmName).toLowerCase(),
+                    Cipher.getMaxAllowedKeyLength(algorithmName));
+              } catch (NoSuchAlgorithmException e) {
+                // This is unlikely since we are getting the algorithm names 
from the service providers.
+                // In any case, if a bad algorithm is listed it can be skipped 
since this is only for
+                // informational purposes.
+                LOG.warn(String.format("Failed to get the max key length of 
cipher %s, skipping.", algorithmName), e);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    // Report each cipher as a resource property, if requested
+    if (requestedIds.contains(CIPHER_PROPERTIES_PROPERTY_ID)) {
+      for (Map.Entry<String, Integer> entry : 
CACHED_CIPHER_MAX_KEY_LENGTHS.entrySet()) {
+        setResourceProperty(resource,
+            PropertyHelper.getPropertyId(CIPHER_PROPERTIES_PROPERTY_ID, 
entry.getKey()),
+            entry.getValue(),
+            requestedIds);
+      }
+    }
+
+    // Determine if the unlimited key JCE policy is installed.  This is 
determined by selecting a
+    // valid cipher algorithm and seeing if its max allowed key length value 
is set to the maximum
+    // size of an Integer.
+    if (requestedIds.contains(JCE_POLICY_PROPERTY_ID)) {
+      Boolean unlimitedKeyJCEPolicyInstalled = null;
+
+      Map.Entry<String, Integer> entry = 
CACHED_CIPHER_MAX_KEY_LENGTHS.entrySet().iterator().next();
+      if (entry != null) {
+        unlimitedKeyJCEPolicyInstalled = (Integer.MAX_VALUE == 
entry.getValue());
+      }
+
+      setResourceProperty(resource,
+          PropertyHelper.getPropertyId(JCE_POLICY_PROPERTY_ID, 
"unlimited_key"),
+          unlimitedKeyJCEPolicyInstalled,
+          requestedIds);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/ambari/blob/e146400e/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProviderTest.java
----------------------------------------------------------------------
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProviderTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProviderTest.java
new file mode 100644
index 0000000..3b7b546
--- /dev/null
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/RootServiceComponentPropertyProviderTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.ambari.server.controller.internal;
+
+import org.apache.ambari.server.controller.RootServiceResponseFactory;
+import org.apache.ambari.server.controller.spi.Request;
+import org.apache.ambari.server.controller.spi.Resource;
+import org.apache.ambari.server.controller.spi.TemporalInfo;
+import org.apache.ambari.server.controller.utilities.PropertyHelper;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class RootServiceComponentPropertyProviderTest {
+  @Test
+  public void testPopulateResources_AmbariServer_None() throws Exception {
+    
testPopulateResources(RootServiceResponseFactory.Components.AMBARI_SERVER.name(),
 false, false, false, false);
+  }
+
+  @Test
+  public void testPopulateResources_AmbariServer_CiphersAndJCEPolicy() throws 
Exception {
+    
testPopulateResources(RootServiceResponseFactory.Components.AMBARI_SERVER.name(),
 true, true, true, true);
+  }
+
+  @Test
+  public void testPopulateResources_AmbariServer_JCEPolicy() throws Exception {
+    
testPopulateResources(RootServiceResponseFactory.Components.AMBARI_SERVER.name(),
 false, true, false, true);
+  }
+
+  @Test
+  public void testPopulateResources_AmbariServer_Ciphers() throws Exception {
+    
testPopulateResources(RootServiceResponseFactory.Components.AMBARI_SERVER.name(),
 true, false, true, false);
+  }
+
+  @Test
+  public void testPopulateResources_AmbariAgent_CiphersAndJCEPolicy() throws 
Exception {
+    
testPopulateResources(RootServiceResponseFactory.Components.AMBARI_AGENT.name(),
 true, true, false, false);
+  }
+
+  public void testPopulateResources(String componentName,
+                                    boolean requestCiphers, boolean 
requestJCEPolicy,
+                                    boolean expectCiphers, boolean 
expectJCEPolicy) throws Exception {
+    RootServiceComponentPropertyProvider propertyProvider = new 
RootServiceComponentPropertyProvider();
+    Resource resource = new ResourceImpl(Resource.Type.RootService);
+
+    
resource.setProperty(RootServiceComponentResourceProvider.COMPONENT_NAME_PROPERTY_ID,
 componentName);
+    
resource.setProperty(RootServiceComponentResourceProvider.SERVICE_NAME_PROPERTY_ID,
 RootServiceResponseFactory.Services.AMBARI.name());
+
+    HashSet<String> requestIds = new HashSet<String>();
+
+    if (requestCiphers) {
+      
requestIds.add(RootServiceComponentPropertyProvider.CIPHER_PROPERTIES_PROPERTY_ID);
+    }
+
+    if (requestJCEPolicy) {
+      
requestIds.add(RootServiceComponentPropertyProvider.JCE_POLICY_PROPERTY_ID);
+    }
+
+    Request request = PropertyHelper.getReadRequest(requestIds, new 
HashMap<String, TemporalInfo>());
+
+    Set<Resource> resources = 
propertyProvider.populateResources(Collections.singleton(resource), request, 
null);
+    Assert.assertEquals(1, resources.size());
+
+    resource = resources.iterator().next();
+
+    Map<String, Map<String, Object>> values = resource.getPropertiesMap();
+
+    Assert.assertEquals(expectCiphers, 
values.containsKey(RootServiceComponentPropertyProvider.CIPHER_PROPERTIES_PROPERTY_ID));
+    Assert.assertEquals(expectJCEPolicy, 
values.containsKey(RootServiceComponentPropertyProvider.JCE_POLICY_PROPERTY_ID));
+  }
+}
\ No newline at end of file

Reply via email to