AMBARI-13431. Blueprints Configuration to select Kerberos. (Sandor Magyari and Laszlo Puskas via rnettleton)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/6e67b5e5 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/6e67b5e5 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/6e67b5e5 Branch: refs/heads/branch-2.1 Commit: 6e67b5e547b48787ff1596a180d5b647e60b366e Parents: 614893c Author: Bob Nettleton <rnettle...@hortonworks.com> Authored: Thu Oct 29 11:15:55 2015 -0400 Committer: Bob Nettleton <rnettle...@hortonworks.com> Committed: Thu Oct 29 11:19:02 2015 -0400 ---------------------------------------------------------------------- .../AmbariManagementControllerImpl.java | 25 +- .../ambari/server/controller/AmbariServer.java | 103 +++++---- .../server/controller/ControllerModule.java | 4 +- .../controller/internal/BaseClusterRequest.java | 21 +- .../BlueprintConfigurationProcessor.java | 5 + .../internal/BlueprintResourceProvider.java | 60 +++-- .../internal/ClusterResourceProvider.java | 74 ++++-- .../internal/ExportBlueprintRequest.java | 2 +- .../KerberosDescriptorResourceProvider.java | 6 + .../internal/ProvisionClusterRequest.java | 53 ++++- .../server/orm/entities/BlueprintEntity.java | 34 ++- .../apache/ambari/server/state/Clusters.java | 18 +- .../server/state/cluster/ClustersImpl.java | 42 ++-- .../ambari/server/topology/AmbariContext.java | 26 +-- .../ambari/server/topology/Blueprint.java | 2 + .../server/topology/BlueprintFactory.java | 6 +- .../ambari/server/topology/BlueprintImpl.java | 33 ++- .../ambari/server/topology/Credential.java | 71 ++++++ .../server/topology/SecurityConfiguration.java | 67 ++++++ .../topology/SecurityConfigurationFactory.java | 175 ++++++++++++++ .../ambari/server/topology/TopologyManager.java | 226 +++++++++++++++---- .../server/topology/TopologyRequestFactory.java | 3 +- .../topology/TopologyRequestFactoryImpl.java | 5 +- .../server/upgrade/UpgradeCatalog213.java | 18 +- .../main/resources/Ambari-DDL-MySQL-CREATE.sql | 2 + .../main/resources/Ambari-DDL-Oracle-CREATE.sql | 2 + .../resources/Ambari-DDL-Postgres-CREATE.sql | 2 + .../Ambari-DDL-Postgres-EMBEDDED-CREATE.sql | 2 + .../resources/Ambari-DDL-SQLAnywhere-CREATE.sql | 2 + .../resources/Ambari-DDL-SQLServer-CREATE.sql | 2 + .../src/main/resources/properties.json | 1 + .../internal/AlertResourceProviderTest.java | 54 ++--- .../internal/BlueprintResourceProviderTest.java | 145 ++++++++---- .../internal/ClusterResourceProviderTest.java | 128 ++++++++--- .../internal/ProvisionClusterRequestTest.java | 68 ++++-- .../internal/WidgetResourceProviderTest.java | 6 +- .../ambari/server/state/UpgradeHelperTest.java | 63 +++--- .../server/state/cluster/ClustersTest.java | 45 ++-- .../server/topology/AmbariContextTest.java | 22 +- .../server/topology/BlueprintFactoryTest.java | 12 +- .../server/topology/BlueprintImplTest.java | 16 +- .../SecurityConfigurationFactoryTest.java | 163 +++++++++++++ .../server/topology/TopologyManagerTest.java | 29 ++- .../server/upgrade/UpgradeCatalog213Test.java | 15 ++ .../ambari/server/utils/StageUtilsTest.java | 66 +++--- 45 files changed, 1489 insertions(+), 435 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java index 615b46d..9b53a6a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java @@ -60,6 +60,18 @@ import java.util.TreeMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Singleton; +import com.google.inject.persist.Transactional; + import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.ClusterNotFoundException; import org.apache.ambari.server.DuplicateResourceException; @@ -171,17 +183,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Singleton; -import com.google.inject.persist.Transactional; - @Singleton public class AmbariManagementControllerImpl implements AmbariManagementController { @@ -418,7 +419,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle throw new HostNotFoundException(invalidHostsStr.toString()); } - clusters.addCluster(request.getClusterName(), stackId); + clusters.addCluster(request.getClusterName(), stackId, request.getSecurityType()); Cluster c = clusters.getCluster(request.getClusterName()); if (request.getHostNames() != null) { http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java index 021f4c8..1db86e5 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariServer.java @@ -7,7 +7,7 @@ * "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 + * 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, @@ -19,19 +19,16 @@ package org.apache.ambari.server.controller; -import java.io.File; -import java.io.IOException; -import java.net.Authenticator; -import java.net.BindException; -import java.net.PasswordAuthentication; -import java.net.URL; -import java.util.EnumSet; -import java.util.Enumeration; -import java.util.Map; - -import javax.crypto.BadPaddingException; -import javax.servlet.DispatcherType; - +import com.google.common.util.concurrent.ServiceManager; +import com.google.gson.Gson; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.Scopes; +import com.google.inject.Singleton; +import com.google.inject.name.Named; +import com.google.inject.persist.Transactional; +import com.sun.jersey.spi.container.servlet.ServletContainer; import org.apache.ambari.eventdb.webservice.WorkflowJsonService; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.StateRecoveryManager; @@ -99,6 +96,7 @@ import org.apache.ambari.server.security.unsecured.rest.ConnectionInfo; import org.apache.ambari.server.state.Clusters; import org.apache.ambari.server.topology.AmbariContext; import org.apache.ambari.server.topology.BlueprintFactory; +import org.apache.ambari.server.topology.SecurityConfigurationFactory; import org.apache.ambari.server.topology.TopologyManager; import org.apache.ambari.server.topology.TopologyRequestFactoryImpl; import org.apache.ambari.server.utils.StageUtils; @@ -113,8 +111,8 @@ import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlets.GzipFilter; import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlets.GzipFilter; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.slf4j.Logger; @@ -128,16 +126,17 @@ import org.springframework.web.context.request.RequestContextListener; import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.filter.DelegatingFilterProxy; -import com.google.common.util.concurrent.ServiceManager; -import com.google.gson.Gson; -import com.google.inject.Guice; -import com.google.inject.Inject; -import com.google.inject.Injector; -import com.google.inject.Scopes; -import com.google.inject.Singleton; -import com.google.inject.name.Named; -import com.google.inject.persist.Transactional; -import com.sun.jersey.spi.container.servlet.ServletContainer; +import javax.crypto.BadPaddingException; +import javax.servlet.DispatcherType; +import java.io.File; +import java.io.IOException; +import java.net.Authenticator; +import java.net.BindException; +import java.net.PasswordAuthentication; +import java.net.URL; +import java.util.EnumSet; +import java.util.Enumeration; +import java.util.Map; @Singleton public class AmbariServer { @@ -154,27 +153,27 @@ public class AmbariServer { static { Velocity.setProperty("runtime.log.logsystem.log4j.logger", VELOCITY_LOG_CATEGORY); } - + private static final String CLASSPATH_CHECK_CLASS = "org/apache/ambari/server/controller/AmbariServer.class"; private static final String CLASSPATH_SANITY_CHECK_FAILURE_MESSAGE = "%s class is found in multiple jar files. Possible reasons include multiple ambari server jar files in the ambari classpath.\n" + - "Check for additional ambari server jar files and check that /usr/lib/ambari-server/ambari-server*.jar matches only one file."; - + "Check for additional ambari server jar files and check that /usr/lib/ambari-server/ambari-server*.jar matches only one file."; + static { Enumeration<URL> ambariServerClassUrls; try { ambariServerClassUrls = AmbariServer.class.getClassLoader().getResources(CLASSPATH_CHECK_CLASS); - + int ambariServerClassUrlsSize = 0; - while(ambariServerClassUrls.hasMoreElements()){ + while (ambariServerClassUrls.hasMoreElements()) { ambariServerClassUrlsSize++; URL url = ambariServerClassUrls.nextElement(); LOG.info(String.format("Found %s class in %s", CLASSPATH_CHECK_CLASS, url.getPath())); } - if(ambariServerClassUrlsSize>1) { + if (ambariServerClassUrlsSize > 1) { throw new RuntimeException(String.format(CLASSPATH_SANITY_CHECK_FAILURE_MESSAGE, CLASSPATH_CHECK_CLASS)); } } catch (IOException e) { - e.printStackTrace(); + e.printStackTrace(); } } @@ -332,7 +331,7 @@ public class AmbariServer { if (configs.getApiAuthentication()) { root.addFilter(new FilterHolder(springSecurityFilter), "/api/*", DISPATCHER_TYPES); - // root.addFilter(new FilterHolder(springSecurityFilter), "/proxy/*", DISPATCHER_TYPES); + // root.addFilter(new FilterHolder(springSecurityFilter), "/proxy/*", DISPATCHER_TYPES); } @@ -442,7 +441,7 @@ public class AmbariServer { if (configs.csrfProtectionEnabled()) { sh.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", - "org.apache.ambari.server.api.AmbariCsrfProtectionFilter"); + "org.apache.ambari.server.api.AmbariCsrfProtectionFilter"); /* proxy.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", "org.apache.ambari.server.api.AmbariCsrfProtectionFilter"); */ } @@ -461,7 +460,7 @@ public class AmbariServer { if (configs.getApiSSLAuthentication()) { String httpsKeystore = configsMap.get(Configuration.CLIENT_API_SSL_KSTR_DIR_NAME_KEY) + - File.separator + configsMap.get(Configuration.CLIENT_API_SSL_KSTR_NAME_KEY); + File.separator + configsMap.get(Configuration.CLIENT_API_SSL_KSTR_NAME_KEY); String httpsTruststore = configsMap.get(Configuration.CLIENT_API_SSL_KSTR_DIR_NAME_KEY) + File.separator + configsMap.get(Configuration.CLIENT_API_SSL_TSTR_NAME_KEY); LOG.info("API SSL Authentication is turned on. Keystore - " + httpsKeystore); @@ -481,8 +480,7 @@ public class AmbariServer { sapiConnector.setTruststoreType(configsMap.get(Configuration.CLIENT_API_SSL_KSTR_TYPE_KEY)); sapiConnector.setMaxIdleTime(configs.getConnectionMaxIdleTime()); apiConnector = sapiConnector; - } - else { + } else { apiConnector = new SelectChannelConnector(); apiConnector.setPort(configs.getClientApiPort()); apiConnector.setMaxIdleTime(configs.getConnectionMaxIdleTime()); @@ -519,13 +517,13 @@ public class AmbariServer { LOG.info("********* Initializing Scheduled Request Manager **********"); ExecutionScheduleManager executionScheduleManager = injector - .getInstance(ExecutionScheduleManager.class); + .getInstance(ExecutionScheduleManager.class); clusterController = controller; StateRecoveryManager recoveryManager = injector.getInstance( - StateRecoveryManager.class); + StateRecoveryManager.class); recoveryManager.doWork(); /* @@ -547,11 +545,11 @@ public class AmbariServer { server.join(); LOG.info("Joined the Server"); - } catch (BadPaddingException bpe){ + } catch (BadPaddingException bpe) { LOG.error("Bad keystore or private key password. " + - "HTTPS certificate re-importing may be required."); + "HTTPS certificate re-importing may be required."); throw bpe; - } catch(BindException bindException) { + } catch (BindException bindException) { LOG.error("Could not bind to server port - instance may already be running. " + "Terminating this instance.", bindException); throw bindException; @@ -563,12 +561,12 @@ public class AmbariServer { * at server properties) */ private void disableInsecureProtocols(SslContextFactory factory) { - if (! configs.getSrvrDisabledCiphers().isEmpty()) { - String [] masks = configs.getSrvrDisabledCiphers().split(DISABLED_ENTRIES_SPLITTER); + if (!configs.getSrvrDisabledCiphers().isEmpty()) { + String[] masks = configs.getSrvrDisabledCiphers().split(DISABLED_ENTRIES_SPLITTER); factory.setExcludeCipherSuites(masks); } - if (! configs.getSrvrDisabledProtocols().isEmpty()) { - String [] masks = configs.getSrvrDisabledProtocols().split(DISABLED_ENTRIES_SPLITTER); + if (!configs.getSrvrDisabledProtocols().isEmpty()) { + String[] masks = configs.getSrvrDisabledProtocols().split(DISABLED_ENTRIES_SPLITTER); factory.setExcludeProtocols(masks); } } @@ -600,11 +598,11 @@ public class AmbariServer { FilterHolder gzipFilter = context.addFilter(GzipFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); - gzipFilter.setInitParameter("methods","GET,POST,PUT,DELETE"); + gzipFilter.setInitParameter("methods", "GET,POST,PUT,DELETE"); gzipFilter.setInitParameter("mimeTypes", "text/html,text/plain,text/xml,text/css,application/x-javascript," + - "application/xml,application/x-www-form-urlencoded," + - "application/javascript,application/json"); + "application/xml,application/x-www-form-urlencoded," + + "application/javascript,application/json"); gzipFilter.setInitParameter("minGzipSize", configs.getApiGzipMinSize()); } } @@ -682,15 +680,16 @@ public class AmbariServer { StageUtils.setGson(injector.getInstance(Gson.class)); StageUtils.setTopologyManager(injector.getInstance(TopologyManager.class)); WorkflowJsonService.setDBProperties( - injector.getInstance(Configuration.class)); + injector.getInstance(Configuration.class)); SecurityFilter.init(injector.getInstance(Configuration.class)); StackDefinedPropertyProvider.init(injector); AbstractControllerResourceProvider.init(injector.getInstance(ResourceProviderFactory.class)); BlueprintResourceProvider.init(injector.getInstance(BlueprintFactory.class), - injector.getInstance(BlueprintDAO.class), injector.getInstance(Gson.class)); + injector.getInstance(BlueprintDAO.class), injector.getInstance(SecurityConfigurationFactory.class), injector.getInstance(Gson.class)); StackDependencyResourceProvider.init(ambariMetaInfo); ClusterResourceProvider.init(injector.getInstance(TopologyManager.class), - injector.getInstance(TopologyRequestFactoryImpl.class)); + injector.getInstance(TopologyRequestFactoryImpl.class), injector.getInstance(SecurityConfigurationFactory + .class), injector.getInstance(Gson.class)); HostResourceProvider.setTopologyManager(injector.getInstance(TopologyManager.class)); BlueprintFactory.init(injector.getInstance(BlueprintDAO.class)); BaseClusterRequest.init(injector.getInstance(BlueprintFactory.class)); http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java index 0426625..60217c0 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/ControllerModule.java @@ -103,6 +103,7 @@ import org.apache.ambari.server.state.scheduler.RequestExecutionImpl; import org.apache.ambari.server.state.stack.OsFamily; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostImpl; import org.apache.ambari.server.topology.BlueprintFactory; +import org.apache.ambari.server.topology.SecurityConfigurationFactory; import org.apache.ambari.server.view.ViewInstanceHandlerList; import org.eclipse.jetty.server.SessionIdManager; import org.eclipse.jetty.server.SessionManager; @@ -331,13 +332,14 @@ public class ControllerModule extends AbstractModule { to(configuration.getExecutionCommandsCacheSize()); bind(AmbariManagementController.class).to( - AmbariManagementControllerImpl.class); + AmbariManagementControllerImpl.class); bind(AbstractRootServiceResponseFactory.class).to(RootServiceResponseFactory.class); bind(ExecutionScheduler.class).to(ExecutionSchedulerImpl.class); bind(DBAccessor.class).to(DBAccessorImpl.class); bind(ViewInstanceHandlerList.class).to(AmbariHandlerList.class); bind(TimelineMetricCacheProvider.class); bind(TimelineMetricCacheEntryFactory.class); + bind(SecurityConfigurationFactory.class).in(Scopes.SINGLETON); requestStaticInjection(ExecutionCommandWrapper.class); requestStaticInjection(DatabaseChecker.class); http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java index 22bffe6..a67317a 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BaseClusterRequest.java @@ -18,11 +18,6 @@ package org.apache.ambari.server.controller.internal; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - import org.apache.ambari.server.api.predicate.InvalidQueryException; import org.apache.ambari.server.api.predicate.QueryLexer; import org.apache.ambari.server.api.predicate.Token; @@ -34,8 +29,14 @@ import org.apache.ambari.server.topology.BlueprintFactory; import org.apache.ambari.server.topology.Configuration; import org.apache.ambari.server.topology.HostGroupInfo; import org.apache.ambari.server.topology.InvalidTopologyTemplateException; +import org.apache.ambari.server.topology.SecurityConfiguration; import org.apache.ambari.server.topology.TopologyRequest; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + /** * Provides common cluster request functionality. */ @@ -62,6 +63,11 @@ public abstract class BaseClusterRequest implements TopologyRequest { protected Configuration configuration; /** + * security configuration + */ + protected SecurityConfiguration securityConfiguration; + + /** * blueprint factory */ protected static BlueprintFactory blueprintFactory; @@ -162,6 +168,11 @@ public abstract class BaseClusterRequest implements TopologyRequest { return blueprintFactory; } + + public SecurityConfiguration getSecurityConfiguration() { + return securityConfiguration; + } + /** * Get the host resource provider instance. * http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintConfigurationProcessor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintConfigurationProcessor.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintConfigurationProcessor.java index 223db51..d1d7e64 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintConfigurationProcessor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintConfigurationProcessor.java @@ -2114,6 +2114,11 @@ public class BlueprintConfigurationProcessor { } } + + if(clusterTopology.isClusterKerberosEnabled()) { + configuration.setProperty(CLUSTER_ENV_CONFIG_TYPE_NAME, "security_enabled", "true"); + configTypesUpdated.add(CLUSTER_ENV_CONFIG_TYPE_NAME); + } } /** http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java index 6cb6a74..fa355fa 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/BlueprintResourceProvider.java @@ -20,16 +20,7 @@ package org.apache.ambari.server.controller.internal; import com.google.common.base.Preconditions; import com.google.common.base.Strings; -import java.util.ArrayList; -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 com.google.gson.Gson; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.DuplicateResourceException; import org.apache.ambari.server.controller.AmbariManagementController; @@ -51,11 +42,23 @@ import org.apache.ambari.server.orm.entities.HostGroupComponentEntity; import org.apache.ambari.server.orm.entities.HostGroupEntity; import org.apache.ambari.server.orm.entities.StackEntity; import org.apache.ambari.server.stack.NoSuchStackException; +import org.apache.ambari.server.state.SecurityType; import org.apache.ambari.server.topology.Blueprint; import org.apache.ambari.server.topology.BlueprintFactory; import org.apache.ambari.server.topology.InvalidTopologyException; +import org.apache.ambari.server.topology.SecurityConfiguration; +import org.apache.ambari.server.topology.SecurityConfigurationFactory; -import com.google.gson.Gson; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; /** @@ -73,6 +76,11 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide public static final String STACK_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("Blueprints", "stack_version"); + public static final String BLUEPRINT_SECURITY_PROPERTY_ID = + PropertyHelper.getPropertyId("Blueprints", "security"); + + public static final String BLUEPRINTS_PROPERTY_ID = "Blueprints"; + // Host Groups public static final String HOST_GROUP_PROPERTY_ID = "host_groups"; public static final String HOST_GROUP_NAME_PROPERTY_ID = "name"; @@ -107,6 +115,11 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide private static BlueprintFactory blueprintFactory; /** + * Used to create SecurityConfiguration instances + */ + private static SecurityConfigurationFactory securityConfigurationFactory; + + /** * Blueprint Data Access Object */ private static BlueprintDAO blueprintDAO; @@ -116,7 +129,6 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide */ private static Gson jsonSerializer; - // ----- Constructors ---------------------------------------------------- /** @@ -140,9 +152,11 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide * @param dao blueprint data access object * @param gson json serializer */ - public static void init(BlueprintFactory factory, BlueprintDAO dao, Gson gson) { + public static void init(BlueprintFactory factory, BlueprintDAO dao, SecurityConfigurationFactory + securityFactory, Gson gson) { blueprintFactory = factory; blueprintDAO = dao; + securityConfigurationFactory = securityFactory; jsonSerializer = gson; } @@ -242,8 +256,8 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide modifyResources(new Command<Void>() { @Override public Void invoke() throws AmbariException { - blueprintDAO.removeByName(blueprintName); - return null; + blueprintDAO.removeByName(blueprintName); + return null; } }); } @@ -291,7 +305,16 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide } setResourceProperty(resource, HOST_GROUP_PROPERTY_ID, listGroupProps, requestedIds); setResourceProperty(resource, CONFIGURATION_PROPERTY_ID, - populateConfigurationList(entity.getConfigurations()), requestedIds); + populateConfigurationList(entity.getConfigurations()), requestedIds); + + if (entity.getSecurityType() != null) { + Map<String, String> securityConfigMap = new LinkedHashMap<>(); + securityConfigMap.put(SecurityConfigurationFactory.TYPE_PROPERTY_ID, entity.getSecurityType().name()); + if(entity.getSecurityType() == SecurityType.KERBEROS) { + securityConfigMap.put(SecurityConfigurationFactory.KERBEROS_DESCRIPTOR_REFERENCE_PROPERTY_ID, entity.getSecurityDescriptorReference()); + } + setResourceProperty(resource, BLUEPRINT_SECURITY_PROPERTY_ID, securityConfigMap, requestedIds); + } return resource; } @@ -405,9 +428,12 @@ public class BlueprintResourceProvider extends AbstractControllerResourceProvide Preconditions.checkArgument(((Map) map).size() <= 1, CONFIGURATION_MAP_SIZE_CHECK_ERROR_MESSAGE); } } + SecurityConfiguration securityConfiguration = securityConfigurationFactory + .createSecurityConfigurationFromRequest((Map<String, Object>) rawBodyMap.get(BLUEPRINTS_PROPERTY_ID), true); + Blueprint blueprint; try { - blueprint = blueprintFactory.createBlueprint(properties); + blueprint = blueprintFactory.createBlueprint(properties, securityConfiguration); } catch (NoSuchStackException e) { throw new IllegalArgumentException("Specified stack doesn't exist: " + e, e); } http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java index e2f132e..cd28aac 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ClusterResourceProvider.java @@ -17,6 +17,7 @@ */ package org.apache.ambari.server.controller.internal; +import com.google.gson.Gson; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.controller.AmbariManagementController; import org.apache.ambari.server.controller.ClusterRequest; @@ -25,15 +26,6 @@ import org.apache.ambari.server.controller.ConfigurationRequest; import org.apache.ambari.server.controller.RequestStatusResponse; import org.apache.ambari.server.controller.ServiceConfigVersionRequest; import org.apache.ambari.server.controller.ServiceConfigVersionResponse; -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.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; @@ -45,7 +37,21 @@ import org.apache.ambari.server.controller.spi.SystemException; import org.apache.ambari.server.controller.spi.UnsupportedPropertyException; import org.apache.ambari.server.controller.utilities.PropertyHelper; import org.apache.ambari.server.state.SecurityType; -import org.apache.ambari.server.topology.*; +import org.apache.ambari.server.topology.InvalidTopologyException; +import org.apache.ambari.server.topology.InvalidTopologyTemplateException; +import org.apache.ambari.server.topology.SecurityConfiguration; +import org.apache.ambari.server.topology.SecurityConfigurationFactory; +import org.apache.ambari.server.topology.TopologyManager; +import org.apache.ambari.server.topology.TopologyRequestFactory; + +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; /** @@ -56,8 +62,8 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider // ----- Property ID constants --------------------------------------------- // Clusters - public static final String CLUSTER_ID_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "cluster_id"); - public static final String CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "cluster_name"); + public static final String CLUSTER_ID_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "cluster_id"); + public static final String CLUSTER_NAME_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "cluster_name"); public static final String CLUSTER_VERSION_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "version"); public static final String CLUSTER_PROVISIONING_STATE_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "provisioning_state"); public static final String CLUSTER_SECURITY_TYPE_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "security_type"); @@ -67,6 +73,8 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider public static final String CLUSTER_HEALTH_REPORT_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "health_report"); public static final String CLUSTER_CREDENTIAL_STORE_PROPERTIES_PROPERTY_ID = PropertyHelper.getPropertyId("Clusters", "credential_store_properties"); public static final String BLUEPRINT_PROPERTY_ID = PropertyHelper.getPropertyId(null, "blueprint"); + public static final String SECURITY_PROPERTY_ID = PropertyHelper.getPropertyId(null, "security"); + public static final String CREDENTIALS_PROPERTY_ID = PropertyHelper.getPropertyId(null, "credentials"); public static final String SESSION_ATTRIBUTES_PROPERTY_ID = "session_attributes"; /** @@ -90,6 +98,11 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider private static TopologyRequestFactory topologyRequestFactory; /** + * Used to create SecurityConfiguration instances + */ + private static SecurityConfigurationFactory securityConfigurationFactory; + + /** * The cluster primary key properties. */ private static Set<String> pkPropertyIds = @@ -108,6 +121,11 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider */ private static Set<String> propertyIds = new HashSet<String>(); + /** + * Used to serialize to/from json. + */ + private static Gson jsonSerializer; + static { propertyIds.add(CLUSTER_ID_PROPERTY_ID); @@ -122,6 +140,8 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider propertyIds.add(CLUSTER_CREDENTIAL_STORE_PROPERTIES_PROPERTY_ID); propertyIds.add(BLUEPRINT_PROPERTY_ID); propertyIds.add(SESSION_ATTRIBUTES_PROPERTY_ID); + propertyIds.add(SECURITY_PROPERTY_ID); + propertyIds.add(CREDENTIALS_PROPERTY_ID); } @@ -149,7 +169,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider RequestStatusResponse createResponse = null; for (final Map<String, Object> properties : request.getProperties()) { if (isCreateFromBlueprint(properties)) { - createResponse = processBlueprintCreate(properties); + createResponse = processBlueprintCreate(properties, request.getRequestInfoProperties()); } else { createClusterResource(properties); } @@ -320,6 +340,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider baseUnsupported.remove("host_groups"); baseUnsupported.remove("default_password"); baseUnsupported.remove("configurations"); + baseUnsupported.remove("credentials"); return checkConfigPropertyIds(baseUnsupported, "Clusters"); } @@ -329,14 +350,17 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider /** * Inject the blueprint data access object which is used to obtain blueprint entities. - * - * @param manager topology manager + * @param manager topology manager * @param requestFactory request factory + * @param instance */ //todo: proper static injection mechanism - public static void init(TopologyManager manager, TopologyRequestFactory requestFactory) { + public static void init(TopologyManager manager, TopologyRequestFactory requestFactory, + SecurityConfigurationFactory securityFactory, Gson instance) { topologyManager = manager; topologyRequestFactory = requestFactory; + securityConfigurationFactory = securityFactory; + jsonSerializer = instance; } @@ -451,6 +475,7 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider * * @param properties request body properties * + * @param requestInfoProperties raw request body * @return asynchronous response information * * @throws ResourceAlreadyExistsException if cluster already exists @@ -459,25 +484,36 @@ public class ClusterResourceProvider extends AbstractControllerResourceProvider * @throws NoSuchParentResourceException if a necessary parent resource doesn't exist */ @SuppressWarnings("unchecked") - private RequestStatusResponse processBlueprintCreate(Map<String, Object> properties) + private RequestStatusResponse processBlueprintCreate(Map<String, Object> properties, Map<String, String> requestInfoProperties) throws ResourceAlreadyExistsException, SystemException, UnsupportedPropertyException, NoSuchParentResourceException { LOG.info("Creating Cluster '" + properties.get(CLUSTER_NAME_PROPERTY_ID) + "' based on blueprint '" + String.valueOf(properties.get(BLUEPRINT_PROPERTY_ID)) + "'."); + String rawRequestBody = requestInfoProperties.get(Request.REQUEST_INFO_BODY_PROPERTY); + Map<String, Object> rawBodyMap = jsonSerializer.<Map<String, Object>>fromJson(rawRequestBody, Map.class); + + SecurityConfiguration securityConfiguration = securityConfigurationFactory.createSecurityConfigurationFromRequest + (rawBodyMap, false); ProvisionClusterRequest createClusterRequest; try { - createClusterRequest = topologyRequestFactory.createProvisionClusterRequest(properties); + createClusterRequest = topologyRequestFactory.createProvisionClusterRequest(properties, securityConfiguration); } catch (InvalidTopologyTemplateException e) { throw new IllegalArgumentException("Invalid Cluster Creation Template: " + e, e); } + if (securityConfiguration != null && securityConfiguration.getType() == SecurityType.NONE && + createClusterRequest.getBlueprint().getSecurity() != null && createClusterRequest.getBlueprint().getSecurity() + .getType() == SecurityType.KERBEROS) { + throw new IllegalArgumentException("Setting security to NONE is not allowed as security type in blueprint is set to KERBEROS!"); + } + try { return topologyManager.provisionCluster(createClusterRequest); } catch (InvalidTopologyException e) { throw new IllegalArgumentException("Topology validation failed: " + e, e); - } catch (AmbariException e) { + } catch (AmbariException e) { e.printStackTrace(); throw new SystemException("Unknown exception when asking TopologyManager to provision cluster", e); } http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExportBlueprintRequest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExportBlueprintRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExportBlueprintRequest.java index c80ae2c..8c8b89d 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExportBlueprintRequest.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ExportBlueprintRequest.java @@ -133,7 +133,7 @@ public class ExportBlueprintRequest implements TopologyRequest { hostGroups.add(new HostGroupImpl(exportedHostGroup.getName(), bpName, stack, exportedHostGroup.getComponents(), exportedHostGroup.getConfiguration(), String.valueOf(exportedHostGroup.getCardinality()))); } - blueprint = new BlueprintImpl(bpName, hostGroups, stack, configuration); + blueprint = new BlueprintImpl(bpName, hostGroups, stack, configuration, null); } private void createHostGroupInfo(Collection<ExportedHostGroup> exportedHostGroups) { http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/KerberosDescriptorResourceProvider.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/KerberosDescriptorResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/KerberosDescriptorResourceProvider.java index cc02119..3cece5d 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/KerberosDescriptorResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/KerberosDescriptorResourceProvider.java @@ -70,6 +70,12 @@ public class KerberosDescriptorResourceProvider extends AbstractControllerResour } @Override + public Set<String> checkPropertyIds(Set<String> propertyIds) { + LOGGER.debug("Skipping property id validation for kerberos descriptor resources"); + return Collections.emptySet(); + } + + @Override public RequestStatus createResources(Request request) throws SystemException, UnsupportedPropertyException, ResourceAlreadyExistsException, NoSuchParentResourceException { http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java index 4a906b1..9716abe 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/ProvisionClusterRequest.java @@ -17,22 +17,29 @@ */ package org.apache.ambari.server.controller.internal; +import com.google.common.base.Enums; +import com.google.common.base.Strings; import org.apache.ambari.server.api.predicate.InvalidQueryException; +import org.apache.ambari.server.security.encryption.CredentialStoreType; import org.apache.ambari.server.stack.NoSuchStackException; import org.apache.ambari.server.topology.Configuration; import org.apache.ambari.server.topology.ConfigurationFactory; +import org.apache.ambari.server.topology.Credential; import org.apache.ambari.server.topology.HostGroupInfo; import org.apache.ambari.server.topology.InvalidTopologyTemplateException; import org.apache.ambari.server.topology.NoSuchBlueprintException; import org.apache.ambari.server.topology.RequiredPasswordValidator; +import org.apache.ambari.server.topology.SecurityConfiguration; import org.apache.ambari.server.topology.TopologyValidator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; /** * Request for provisioning a cluster. @@ -94,14 +101,18 @@ public class ProvisionClusterRequest extends BaseClusterRequest { */ private String defaultPassword; + private Map<String, Credential> credentialsMap; + private final static Logger LOG = LoggerFactory.getLogger(ProvisionClusterRequest.class); /** * Constructor. * * @param properties request properties + * @param securityConfiguration security config related properties */ - public ProvisionClusterRequest(Map<String, Object> properties) throws InvalidTopologyTemplateException { + public ProvisionClusterRequest(Map<String, Object> properties, SecurityConfiguration securityConfiguration) throws + InvalidTopologyTemplateException { setClusterName(String.valueOf(properties.get( ClusterResourceProvider.CLUSTER_NAME_PROPERTY_ID))); @@ -117,12 +128,52 @@ public class ProvisionClusterRequest extends BaseClusterRequest { throw new InvalidTopologyTemplateException("The specified blueprint doesn't exist: " + e, e); } + this.securityConfiguration = securityConfiguration; + Configuration configuration = configurationFactory.getConfiguration( (Collection<Map<String, String>>) properties.get(CONFIGURATIONS_PROPERTY)); configuration.setParentConfiguration(blueprint.getConfiguration()); setConfiguration(configuration); parseHostGroupInfo(properties); + + this.credentialsMap = parseCredentials(properties); + } + + private Map<String, Credential> parseCredentials(Map<String, Object> properties) throws + InvalidTopologyTemplateException { + HashMap<String, Credential> credentialHashMap = new HashMap<>(); + Set<Map<String, String>> credentialsSet = (Set<Map<String, String>>) properties.get(ClusterResourceProvider.CREDENTIALS_PROPERTY_ID); + if (credentialsSet != null) { + for (Map<String, String> credentialMap : credentialsSet) { + String alias = Strings.emptyToNull(credentialMap.get("alias")); + if (alias == null) { + throw new InvalidTopologyTemplateException("credential.alias property is missing."); + } + String principal = Strings.emptyToNull(credentialMap.get("principal")); + if (principal == null) { + throw new InvalidTopologyTemplateException("credential.principal property is missing."); + } + String key = Strings.emptyToNull(credentialMap.get("key")); + if (key == null) { + throw new InvalidTopologyTemplateException("credential.key is missing."); + } + String typeString = Strings.emptyToNull(credentialMap.get("type")); + if (typeString == null) { + throw new InvalidTopologyTemplateException("credential.type is missing."); + } + CredentialStoreType type = Enums.getIfPresent(CredentialStoreType.class, typeString.toUpperCase()).orNull(); + if (type == null) { + throw new InvalidTopologyTemplateException("credential.type is invalid."); + } + credentialHashMap.put(alias, new Credential(alias, principal, key, type)); + } + } + return credentialHashMap; + } + + public Map<String, Credential> getCredentialsMap() { + return credentialsMap; } public String getClusterName() { http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java index ada924a..8578d6b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/BlueprintEntity.java @@ -18,21 +18,21 @@ package org.apache.ambari.server.orm.entities; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import com.google.gson.Gson; +import org.apache.ambari.server.state.SecurityType; +import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.NamedQuery; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Table; -import javax.persistence.Transient; +import java.util.Collection; /** @@ -49,6 +49,14 @@ public class BlueprintEntity { updatable = false, unique = true, length = 100) private String blueprintName; + @Basic + @Enumerated(value = EnumType.STRING) + @Column(name = "security_type", nullable = false, insertable = true, updatable = true) + private SecurityType securityType = SecurityType.NONE; + + @Basic + @Column(name = "security_descriptor_reference", nullable = true, insertable = true, updatable = true) + private String securityDescriptorReference; /** * Unidirectional one-to-one association to {@link StackEntity} @@ -136,4 +144,20 @@ public class BlueprintEntity { public void setConfigurations(Collection<BlueprintConfigEntity> configurations) { this.configurations = configurations; } + + public SecurityType getSecurityType() { + return securityType; + } + + public void setSecurityType(SecurityType securityType) { + this.securityType = securityType; + } + + public String getSecurityDescriptorReference() { + return securityDescriptorReference; + } + + public void setSecurityDescriptorReference(String securityDescriptorReference) { + this.securityDescriptorReference = securityDescriptorReference; + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java index 3aa27dd..a1ebaba 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Clusters.java @@ -18,12 +18,12 @@ package org.apache.ambari.server.state; +import org.apache.ambari.server.AmbariException; + import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.ambari.server.AmbariException; - /** * Single entity that tracks all clusters and hosts that are managed * by the Ambari server @@ -32,7 +32,6 @@ public interface Clusters { /** * Add a new Cluster - * * @param clusterName * the cluster name (not {@code null}). * @param stackId @@ -42,6 +41,19 @@ public interface Clusters { throws AmbariException; /** + * Add a new cluster + * @param clusterName + * the cluster name (not {@code null}). + * @param stackId + * the stack for the cluster (not {@code null}). + * @param securityType + * the cluster will be created with this security type. + * @throws AmbariException + */ + public void addCluster(String clusterName, StackId stackId, SecurityType securityType) + throws AmbariException; + + /** * Gets the Cluster given the cluster name * @param clusterName Name of the Cluster to retrieve * @return <code>Cluster</code> identified by the given name http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java index 4040c5f..a89fb91 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClustersImpl.java @@ -18,20 +18,9 @@ package org.apache.ambari.server.state.cluster; -import java.util.ArrayList; -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 java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import javax.persistence.RollbackException; - +import com.google.inject.Inject; +import com.google.inject.Singleton; +import com.google.inject.persist.Transactional; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.ClusterNotFoundException; import org.apache.ambari.server.DuplicateResourceException; @@ -73,6 +62,7 @@ import org.apache.ambari.server.state.HostHealthStatus; import org.apache.ambari.server.state.HostHealthStatus.HealthStatus; import org.apache.ambari.server.state.HostState; import org.apache.ambari.server.state.RepositoryInfo; +import org.apache.ambari.server.state.SecurityType; import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.configgroup.ConfigGroup; import org.apache.ambari.server.state.host.HostFactory; @@ -80,9 +70,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.GrantedAuthority; -import com.google.inject.Inject; -import com.google.inject.Singleton; -import com.google.inject.persist.Transactional; +import javax.persistence.RollbackException; +import java.util.ArrayList; +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 java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; @Singleton public class ClustersImpl implements Clusters { @@ -199,6 +198,12 @@ public class ClustersImpl implements Clusters { @Override public void addCluster(String clusterName, StackId stackId) + throws AmbariException { + addCluster(clusterName, stackId, null); + } + + @Override + public void addCluster(String clusterName, StackId stackId, SecurityType securityType) throws AmbariException { checkLoaded(); @@ -232,6 +237,9 @@ public class ClustersImpl implements Clusters { clusterEntity.setClusterName(clusterName); clusterEntity.setDesiredStack(stackEntity); clusterEntity.setResource(resourceEntity); + if (securityType != null) { + clusterEntity.setSecurityType(securityType); + } try { clusterDAO.create(clusterEntity); http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java index 0cf3bf2..5e93aeb 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/AmbariContext.java @@ -18,15 +18,6 @@ package org.apache.ambari.server.topology; -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 java.util.concurrent.atomic.AtomicLong; - import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.ClusterNotFoundException; import org.apache.ambari.server.Role; @@ -68,6 +59,15 @@ import org.apache.ambari.server.state.configgroup.ConfigGroup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +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 java.util.concurrent.atomic.AtomicLong; + /** * Provides topology related information as well as access to the core Ambari functionality. */ @@ -136,15 +136,15 @@ public class AmbariContext { return getController().getActionManager().getTaskById(id); } - public void createAmbariResources(ClusterTopology topology, String clusterName) { + public void createAmbariResources(ClusterTopology topology, String clusterName, SecurityType securityType) { Stack stack = topology.getBlueprint().getStack(); - createAmbariClusterResource(clusterName, stack.getName(), stack.getVersion()); + createAmbariClusterResource(clusterName, stack.getName(), stack.getVersion(), securityType); createAmbariServiceAndComponentResources(topology, clusterName); } - public void createAmbariClusterResource(String clusterName, String stackName, String stackVersion) { + public void createAmbariClusterResource(String clusterName, String stackName, String stackVersion, SecurityType securityType) { String stackInfo = String.format("%s-%s", stackName, stackVersion); - ClusterRequest clusterRequest = new ClusterRequest(null, clusterName, stackInfo, null); + ClusterRequest clusterRequest = new ClusterRequest(null, clusterName, null, securityType, stackInfo, null); try { getController().createCluster(clusterRequest); } catch (AmbariException e) { http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/Blueprint.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/Blueprint.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/Blueprint.java index fa65022..11311db 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/Blueprint.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/Blueprint.java @@ -103,6 +103,8 @@ public interface Blueprint { */ public Collection<HostGroup> getHostGroupsForComponent(String component); + public SecurityConfiguration getSecurity(); + /** * Validate the blueprint topology. * http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintFactory.java index 210504d..b8ce749 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintFactory.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintFactory.java @@ -22,7 +22,6 @@ package org.apache.ambari.server.topology; import com.google.inject.Inject; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.ObjectNotFoundException; -import org.apache.ambari.server.StackAccessException; import org.apache.ambari.server.controller.AmbariManagementController; import org.apache.ambari.server.controller.AmbariServer; import org.apache.ambari.server.controller.internal.Stack; @@ -88,10 +87,11 @@ public class BlueprintFactory { * Convert a map of properties to a blueprint entity. * * @param properties property map + * @param securityConfiguration security related properties * @return new blueprint entity */ @SuppressWarnings("unchecked") - public Blueprint createBlueprint(Map<String, Object> properties) throws NoSuchStackException { + public Blueprint createBlueprint(Map<String, Object> properties, SecurityConfiguration securityConfiguration) throws NoSuchStackException { String name = String.valueOf(properties.get(BLUEPRINT_NAME_PROPERTY_ID)); // String.valueOf() will return "null" if value is null if (name.equals("null") || name.isEmpty()) { @@ -104,7 +104,7 @@ public class BlueprintFactory { Configuration configuration = configFactory.getConfiguration((Collection<Map<String, String>>) properties.get(CONFIGURATION_PROPERTY_ID)); - return new BlueprintImpl(name, hostGroups, stack, configuration); + return new BlueprintImpl(name, hostGroups, stack, configuration, securityConfiguration); } protected Stack createStack(Map<String, Object> properties) throws NoSuchStackException { http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintImpl.java index 481d217..88052b0 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/BlueprintImpl.java @@ -19,12 +19,6 @@ package org.apache.ambari.server.topology; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; - import com.google.gson.Gson; import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.StackAccessException; @@ -39,6 +33,12 @@ import org.apache.ambari.server.orm.entities.HostGroupEntity; import org.apache.ambari.server.orm.entities.StackEntity; import org.apache.ambari.server.stack.NoSuchStackException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + /** * Blueprint implementation. */ @@ -49,10 +49,14 @@ public class BlueprintImpl implements Blueprint { private Stack stack; private Configuration configuration; private BlueprintValidator validator; - + private SecurityConfiguration security; public BlueprintImpl(BlueprintEntity entity) throws NoSuchStackException { this.name = entity.getBlueprintName(); + if (entity.getSecurityType() != null) { + this.security = new SecurityConfiguration(entity.getSecurityType(), entity.getSecurityDescriptorReference(), + null); + } parseStack(entity.getStack()); @@ -63,9 +67,10 @@ public class BlueprintImpl implements Blueprint { validator = new BlueprintValidatorImpl(this); } - public BlueprintImpl(String name, Collection<HostGroup> groups, Stack stack, Configuration configuration) { + public BlueprintImpl(String name, Collection<HostGroup> groups, Stack stack, Configuration configuration, SecurityConfiguration security) { this.name = name; this.stack = stack; + this.security = security; // caller should set host group configs for (HostGroup hostGroup : groups) { @@ -91,6 +96,10 @@ public class BlueprintImpl implements Blueprint { return stack.getVersion(); } + public SecurityConfiguration getSecurity() { + return security; + } + //todo: safe copy? @Override public Map<String, HostGroup> getHostGroups() { @@ -182,6 +191,14 @@ public class BlueprintImpl implements Blueprint { BlueprintEntity entity = new BlueprintEntity(); entity.setBlueprintName(name); + if (security != null) { + if (security.getType() != null) { + entity.setSecurityType(security.getType()); + } + if (security.getDescriptorReference() != null) { + entity.setSecurityDescriptorReference(security.getDescriptorReference()); + } + } //todo: not using stackDAO so stackEntity.id is not set //todo: this is now being set in BlueprintDAO http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/Credential.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/Credential.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/Credential.java new file mode 100644 index 0000000..2651074 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/Credential.java @@ -0,0 +1,71 @@ +/** + * 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 distribut + * ed 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.topology; + +import org.apache.ambari.server.security.encryption.CredentialStoreType; + +/** + * Holds credential info submitted in a cluster create template. + */ +public class Credential { + + /** + * Credential alias like kdc.admin.credential. + */ + private String alias; + + /** + * Name of a principal. + */ + private String principal; + + /** + * Key of credential. + */ + private String key; + + /** + * Type of credential store. + */ + private CredentialStoreType type; + + public Credential(String alias, String principal, String key, CredentialStoreType type) { + this.alias = alias; + this.principal = principal; + this.key = key; + this.type = type; + } + + public String getAlias() { + return alias; + } + + public String getPrincipal() { + return principal; + } + + public String getKey() { + return key; + } + + public CredentialStoreType getType() { + return type; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfiguration.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfiguration.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfiguration.java new file mode 100644 index 0000000..b35f7b3 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfiguration.java @@ -0,0 +1,67 @@ +/** + * 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 distribut + * ed 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.topology; + +import org.apache.ambari.server.state.SecurityType; + +/** + * Holds security related properties, the securityType and security descriptor (in case of KERBEROS + * kerberos_descriptor) either contains the whole descriptor or just the reference to it. + * + */ +public class SecurityConfiguration { + + /** + * Security Type + */ + private SecurityType type; + + /** + * Holds a reference to a kerberos_descriptor resource. + */ + private String descriptorReference; + + /** + * Content of a kerberos_descriptor as String. + */ + private String descriptor; + + public SecurityConfiguration(SecurityType type) { + this.type = type; + } + + public SecurityConfiguration(SecurityType type, String descriptorReference, String descriptor) { + this.type = type; + this.descriptorReference = descriptorReference; + this.descriptor = descriptor; + } + + public SecurityType getType() { + return type; + } + + public String getDescriptor() { + return descriptor; + } + + public String getDescriptorReference() { + return descriptorReference; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/6e67b5e5/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfigurationFactory.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfigurationFactory.java b/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfigurationFactory.java new file mode 100644 index 0000000..5f8cde1 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/topology/SecurityConfigurationFactory.java @@ -0,0 +1,175 @@ +/** + * 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 distribut + * ed 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.topology; + +import com.google.common.base.Enums; +import com.google.common.base.Strings; +import com.google.gson.Gson; +import com.google.inject.Inject; +import org.apache.ambari.server.orm.dao.KerberosDescriptorDAO; +import org.apache.ambari.server.orm.entities.KerberosDescriptorEntity; +import org.apache.ambari.server.state.SecurityType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.UUID; + +public class SecurityConfigurationFactory { + + private static final Logger LOGGER = LoggerFactory.getLogger(SecurityConfigurationFactory.class); + + public static final String SECURITY_PROPERTY_ID = "security"; + public static final String TYPE_PROPERTY_ID = "type"; + public static final String KERBEROS_DESCRIPTOR_PROPERTY_ID = "kerberos_descriptor"; + public static final String KERBEROS_DESCRIPTOR_REFERENCE_PROPERTY_ID = "kerberos_descriptor_reference"; + + @Inject + protected Gson jsonSerializer; + + @Inject + private KerberosDescriptorDAO kerberosDescriptorDAO; + + @Inject + private KerberosDescriptorFactory kerberosDescriptorFactory; + + public SecurityConfigurationFactory() { + } + + protected SecurityConfigurationFactory(Gson jsonSerializer, KerberosDescriptorDAO kerberosDescriptorDAO, KerberosDescriptorFactory kerberosDescriptorFactory) { + this.jsonSerializer = jsonSerializer; + this.kerberosDescriptorDAO = kerberosDescriptorDAO; + this.kerberosDescriptorFactory = kerberosDescriptorFactory; + } + + /** + * Creates and also validates SecurityConfiguration based on properties parsed from request Json. + * + * @param properties Security properties from Json parsed into a Map + * @param persistEmbeddedDescriptor whether to save embedded descriptor or not + * @return + */ + public SecurityConfiguration createSecurityConfigurationFromRequest(Map<String, Object> properties, boolean + persistEmbeddedDescriptor) { + + SecurityConfiguration securityConfiguration = null; + + LOGGER.debug("Creating security configuration from properties: {}", properties); + Map<String, Object> securityProperties = (Map<String, Object>) properties.get(SECURITY_PROPERTY_ID); + + if (securityProperties == null) { + LOGGER.debug("No security information properties provided, returning null"); + return securityConfiguration; + } + + String securityTypeString = Strings.emptyToNull((String) securityProperties.get(TYPE_PROPERTY_ID)); + if (securityTypeString == null) { + LOGGER.error("Type is missing from security block."); + throw new IllegalArgumentException("Type missing from security block."); + } + + SecurityType securityType = Enums.getIfPresent(SecurityType.class, securityTypeString).orNull(); + if (securityType == null) { + LOGGER.error("Unsupported security type specified: {}", securityType); + throw new IllegalArgumentException("Invalid security type specified: " + securityTypeString); + } + + if (securityType == SecurityType.KERBEROS) { + + // get security information from the request propertie if any + String descriptorReference = Strings.emptyToNull((String) + securityProperties.get(KERBEROS_DESCRIPTOR_REFERENCE_PROPERTY_ID)); + + Object descriptorJsonMap = securityProperties.get(KERBEROS_DESCRIPTOR_PROPERTY_ID); + + if (descriptorReference == null && descriptorJsonMap == null) { + LOGGER.error("Both kerberos descriptor and kerberos descriptor reference are null in the security configuration!"); + throw new IllegalArgumentException(KERBEROS_DESCRIPTOR_PROPERTY_ID + " or " + + KERBEROS_DESCRIPTOR_REFERENCE_PROPERTY_ID + " is required for KERBEROS security setup."); + } + + if (descriptorReference != null && descriptorJsonMap != null) { + LOGGER.error("Both kerberos descriptor and kerberos descriptor reference are set in the security configuration!"); + throw new IllegalArgumentException("Usage of properties : " + KERBEROS_DESCRIPTOR_PROPERTY_ID + " and " + + KERBEROS_DESCRIPTOR_REFERENCE_PROPERTY_ID + " at the same time, is not allowed."); + } + + String descriptorText = null; + + if (descriptorJsonMap != null) { // this means the reference is null + LOGGER.debug("Found embedded descriptor: {}", descriptorJsonMap); + descriptorText = jsonSerializer.<Map<String, Object>>toJson(descriptorJsonMap, Map.class); + if (persistEmbeddedDescriptor) { + descriptorReference = persistKerberosDescriptor(descriptorText); + } + securityConfiguration = new SecurityConfiguration(SecurityType.KERBEROS, descriptorReference, descriptorText); + } else { // this means the reference is not null + LOGGER.debug("Found descriptor reference: {}", descriptorReference); + securityConfiguration = loadSecurityConfigurationByReference(descriptorReference); + } + } else { + LOGGER.debug("There is no security configuration found in the request"); + securityConfiguration = new SecurityConfiguration(SecurityType.NONE); + } + return securityConfiguration; + } + + public SecurityConfiguration loadSecurityConfigurationByReference(String reference) { + SecurityConfiguration securityConfiguration = null; + LOGGER.debug("Loading security configuration by reference: {}", reference); + + if (reference == null) { + LOGGER.error("No security configuration reference provided!"); + throw new IllegalArgumentException("No security configuration reference provided!"); + } + + KerberosDescriptorEntity descriptorEntity = kerberosDescriptorDAO.findByName(reference); + + if (descriptorEntity == null) { + LOGGER.error("No security configuration found for the reference: {}", reference); + throw new IllegalArgumentException("No security configuration found for the reference: " + reference); + } + + securityConfiguration = new SecurityConfiguration(SecurityType.KERBEROS, reference, descriptorEntity.getKerberosDescriptorText()); + + return securityConfiguration; + + } + + private String persistKerberosDescriptor(String descriptor) { + LOGGER.debug("Generating new kerberos descriptor reference ..."); + String kdReference = generateKerberosDescriptorReference(); + + KerberosDescriptor kerberosDescriptor = kerberosDescriptorFactory.createKerberosDescriptor(kdReference, descriptor); + + LOGGER.debug("Persisting kerberos descriptor ..."); + kerberosDescriptorDAO.create(kerberosDescriptor.toEntity()); + return kdReference; + } + + // generates a unique name for the kerberos descriptor for further reference + private String generateKerberosDescriptorReference() { + String kdReference = UUID.randomUUID().toString(); + LOGGER.debug("Generated new kerberos descriptor reference: {}", kdReference); + return kdReference; + } + + +}