You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by rz...@apache.org on 2017/02/17 22:57:25 UTC
[47/51] [abbrv] ambari git commit: AMBARI-19822. Add
infra-solr-plugin for authorization (with Kerberos) (oleewere)
AMBARI-19822. Add infra-solr-plugin for authorization (with Kerberos) (oleewere)
(cherry picked from commit 79e676c2a86f63becc6dba0041ba11102f11cd9a)
Change-Id: I2863264c1ba8332b2398af29d291616668746454
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/0446e4b1
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/0446e4b1
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/0446e4b1
Branch: refs/heads/branch-feature-BUG-74026
Commit: 0446e4b19aaf2b71f71fb197885c079f61252a70
Parents: 47d94bf
Author: oleewere <ol...@gmail.com>
Authored: Wed Feb 1 18:24:10 2017 +0100
Committer: Zuul <re...@hortonworks.com>
Committed: Tue Feb 7 12:26:55 2017 -0800
----------------------------------------------------------------------
.../ambari-infra-solr-plugin/pom.xml | 56 ++
.../InfraKerberosHostValidator.java | 54 ++
.../InfraRuleBasedAuthorizationPlugin.java | 542 +++++++++++++++++++
.../InfraUserRolesLookupStrategy.java | 49 ++
.../InfraKerberosHostValidatorTest.java | 114 ++++
.../InfraRuleBasedAuthorizationPluginTest.java | 247 +++++++++
.../InfraUserRolesLookupStrategyTest.java | 83 +++
.../ambari-logsearch-assembly/pom.xml | 15 +-
ambari-logsearch/pom.xml | 1 +
.../configuration/infra-solr-security-json.xml | 62 ++-
.../0.1.0/package/scripts/params.py | 29 +-
.../properties/infra-solr-security.json.j2 | 46 ++
.../test/python/stacks/2.4/configs/default.json | 1 +
13 files changed, 1296 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/pom.xml b/ambari-logsearch/ambari-infra-solr-plugin/pom.xml
new file mode 100644
index 0000000..3d0cf00
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/pom.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>ambari-logsearch</artifactId>
+ <groupId>org.apache.ambari</groupId>
+ <version>2.5.0.0.0</version>
+ </parent>
+ <name>Ambari Infra Solr Plugin</name>
+ <url>http://maven.apache.org</url>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>ambari-infra-solr-plugin</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.solr</groupId>
+ <artifactId>solr-core</artifactId>
+ <version>${solr.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.solr</groupId>
+ <artifactId>solr-test-framework</artifactId>
+ <version>${solr.version}</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.3</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java b/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
new file mode 100644
index 0000000..4a47a89
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraKerberosHostValidator.java
@@ -0,0 +1,54 @@
+/*
+ * 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.infra.security;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Validate that the user has the right access based on the hostname in the kerberos principal
+ */
+public class InfraKerberosHostValidator {
+
+ public boolean validate(Principal principal, Map<String, Set<String>> userVsHosts, Map<String, String> userVsHostRegex) {
+ if (principal instanceof AuthenticationToken) {
+ AuthenticationToken authenticationToken = (AuthenticationToken) principal;
+ KerberosName kerberosName = new KerberosName(authenticationToken.getName());
+ String hostname = kerberosName.getHostName();
+ String serviceUserName = kerberosName.getServiceName();
+ if (MapUtils.isNotEmpty(userVsHostRegex)) {
+ String regex = userVsHostRegex.get(serviceUserName);
+ return hostname.matches(regex);
+ }
+ if (MapUtils.isNotEmpty(userVsHosts)) {
+ Set<String> hosts = userVsHosts.get(serviceUserName);
+ if (CollectionUtils.isNotEmpty(hosts)) {
+ return hosts.contains(hostname);
+ }
+ }
+ }
+ return true;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java b/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
new file mode 100644
index 0000000..2f1a558
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraRuleBasedAuthorizationPlugin.java
@@ -0,0 +1,542 @@
+/*
+ * 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.infra.security;
+
+import com.google.common.collect.ImmutableSet;
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.security.Principal;
+import java.util.ArrayList;
+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.Objects;
+import java.util.Set;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.AuthorizationPlugin;
+import org.apache.solr.security.AuthorizationResponse;
+import org.apache.solr.security.ConfigEditablePlugin;
+import org.apache.solr.util.CommandOperation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static java.util.Collections.singleton;
+import static org.apache.solr.common.params.CommonParams.NAME;
+import static org.apache.solr.common.util.Utils.getDeepCopy;
+import static org.apache.solr.handler.admin.SecurityConfHandler.getListValue;
+import static org.apache.solr.handler.admin.SecurityConfHandler.getMapValue;
+
+/**
+ * Modified copy of solr.RuleBasedAuthorizationPlugin to handle role - permission mappings with KereberosPlugin
+ * Added 2 new JSON map: (precedence: user-host-regex > user-host)
+ * 1. "user-host": user host mappings (array) for hostname validation
+ * 2. "user-host-regex": user host regex mapping (string) for hostname validation
+ */
+public class InfraRuleBasedAuthorizationPlugin implements AuthorizationPlugin, ConfigEditablePlugin {
+
+ private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+ private final Map<String, Set<String>> usersVsRoles = new HashMap<>();
+ private final Map<String, WildCardSupportMap> mapping = new HashMap<>();
+ private final List<Permission> permissions = new ArrayList<>();
+ private final Map<String, Set<String>> userVsHosts = new HashMap<>();
+ private final Map<String, String> userVsHostRegex = new HashMap<>();
+
+ private final InfraUserRolesLookupStrategy infraUserRolesLookupStrategy = new InfraUserRolesLookupStrategy();
+ private final InfraKerberosHostValidator infraKerberosDomainValidator = new InfraKerberosHostValidator();
+
+ private static class WildCardSupportMap extends HashMap<String, List<Permission>> {
+ final Set<String> wildcardPrefixes = new HashSet<>();
+
+ @Override
+ public List<Permission> put(String key, List<Permission> value) {
+ if (key != null && key.endsWith("/*")) {
+ key = key.substring(0, key.length() - 2);
+ wildcardPrefixes.add(key);
+ }
+ return super.put(key, value);
+ }
+
+ @Override
+ public List<Permission> get(Object key) {
+ List<Permission> result = super.get(key);
+ if (key == null || result != null) return result;
+ if (!wildcardPrefixes.isEmpty()) {
+ for (String s : wildcardPrefixes) {
+ if (key.toString().startsWith(s)) {
+ List<Permission> l = super.get(s);
+ if (l != null) {
+ result = result == null ? new ArrayList<Permission>() : new ArrayList<Permission>(result);
+ result.addAll(l);
+ }
+ }
+ }
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public AuthorizationResponse authorize(AuthorizationContext context) {
+ List<AuthorizationContext.CollectionRequest> collectionRequests = context.getCollectionRequests();
+ if (context.getRequestType() == AuthorizationContext.RequestType.ADMIN) {
+ MatchStatus flag = checkCollPerm(mapping.get(null), context);
+ return flag.rsp;
+ }
+
+ for (AuthorizationContext.CollectionRequest collreq : collectionRequests) {
+ //check permissions for each collection
+ MatchStatus flag = checkCollPerm(mapping.get(collreq.collectionName), context);
+ if (flag != MatchStatus.NO_PERMISSIONS_FOUND) return flag.rsp;
+ }
+ //check wildcard (all=*) permissions.
+ MatchStatus flag = checkCollPerm(mapping.get("*"), context);
+ return flag.rsp;
+ }
+
+ private MatchStatus checkCollPerm(Map<String, List<Permission>> pathVsPerms,
+ AuthorizationContext context) {
+ if (pathVsPerms == null) return MatchStatus.NO_PERMISSIONS_FOUND;
+
+ String path = context.getResource();
+ MatchStatus flag = checkPathPerm(pathVsPerms.get(path), context);
+ if (flag != MatchStatus.NO_PERMISSIONS_FOUND) return flag;
+ return checkPathPerm(pathVsPerms.get(null), context);
+ }
+
+ private MatchStatus checkPathPerm(List<Permission> permissions, AuthorizationContext context) {
+ if (permissions == null || permissions.isEmpty()) return MatchStatus.NO_PERMISSIONS_FOUND;
+ Principal principal = context.getUserPrincipal();
+ loopPermissions:
+ for (int i = 0; i < permissions.size(); i++) {
+ Permission permission = permissions.get(i);
+ if (permission.method != null && !permission.method.contains(context.getHttpMethod())) {
+ //this permissions HTTP method does not match this rule. try other rules
+ continue;
+ }
+ if(permission.predicate != null){
+ if(!permission.predicate.test(context)) continue ;
+ }
+
+ if (permission.params != null) {
+ for (Map.Entry<String, Object> e : permission.params.entrySet()) {
+ String paramVal = context.getParams().get(e.getKey());
+ Object val = e.getValue();
+ if (val instanceof List) {
+ if (!((List) val).contains(paramVal)) continue loopPermissions;
+ } else if (!Objects.equals(val, paramVal)) continue loopPermissions;
+ }
+ }
+
+ if (permission.role == null) {
+ //no role is assigned permission.That means everybody is allowed to access
+ return MatchStatus.PERMITTED;
+ }
+ if (principal == null) {
+ log.info("request has come without principal. failed permission {} ",permission);
+ //this resource needs a principal but the request has come without
+ //any credential.
+ return MatchStatus.USER_REQUIRED;
+ } else if (permission.role.contains("*")) {
+ return MatchStatus.PERMITTED;
+ }
+
+ for (String role : permission.role) {
+ Set<String> userRoles = infraUserRolesLookupStrategy.getUserRolesFromPrincipal(usersVsRoles, principal);
+ boolean validHostname = infraKerberosDomainValidator.validate(principal, userVsHosts, userVsHostRegex);
+ if (!validHostname) {
+ log.warn("Hostname is not valid for principal {}", principal);
+ return MatchStatus.FORBIDDEN;
+ }
+ if (userRoles != null && userRoles.contains(role)) return MatchStatus.PERMITTED;
+ }
+ log.info("This resource is configured to have a permission {}, The principal {} does not have the right role ", permission, principal);
+ return MatchStatus.FORBIDDEN;
+ }
+ log.debug("No permissions configured for the resource {} . So allowed to access", context.getResource());
+ return MatchStatus.NO_PERMISSIONS_FOUND;
+ }
+
+ @Override
+ public void init(Map<String, Object> initInfo) {
+ mapping.put(null, new WildCardSupportMap());
+ Map<String, Object> map = getMapValue(initInfo, "user-role");
+ for (Object o : map.entrySet()) {
+ Map.Entry e = (Map.Entry) o;
+ String roleName = (String) e.getKey();
+ usersVsRoles.put(roleName, readValueAsSet(map, roleName));
+ }
+ List<Map> perms = getListValue(initInfo, "permissions");
+ for (Map o : perms) {
+ Permission p;
+ try {
+ p = Permission.load(o);
+ } catch (Exception exp) {
+ log.error("Invalid permission ", exp);
+ continue;
+ }
+ permissions.add(p);
+ add2Mapping(p);
+ }
+ // adding user-host
+ Map<String, Object> userHostsMap = getMapValue(initInfo, "user-host");
+ for (Object userHost : userHostsMap.entrySet()) {
+ Map.Entry e = (Map.Entry) userHost;
+ String roleName = (String) e.getKey();
+ userVsHosts.put(roleName, readValueAsSet(userHostsMap, roleName));
+ }
+ // adding user-host-regex
+ Map<String, Object> userHostRegexMap = getMapValue(initInfo, "user-host-regex");
+ for (Map.Entry<String, Object> entry : userHostRegexMap.entrySet()) {
+ userVsHostRegex.put(entry.getKey(), entry.getValue().toString());
+ }
+
+ }
+
+ //this is to do optimized lookup of permissions for a given collection/path
+ private void add2Mapping(Permission permission) {
+ for (String c : permission.collections) {
+ WildCardSupportMap m = mapping.get(c);
+ if (m == null) mapping.put(c, m = new WildCardSupportMap());
+ for (String path : permission.path) {
+ List<Permission> perms = m.get(path);
+ if (perms == null) m.put(path, perms = new ArrayList<>());
+ perms.add(permission);
+ }
+ }
+ }
+
+ /**
+ * read a key value as a set. if the value is a single string ,
+ * return a singleton set
+ *
+ * @param m the map from which to lookup
+ * @param key the key with which to do lookup
+ */
+ static Set<String> readValueAsSet(Map m, String key) {
+ Set<String> result = new HashSet<>();
+ Object val = m.get(key);
+ if (val == null) {
+ if("collection".equals(key)){
+ //for collection collection: null means a core admin/ collection admin request
+ // otherwise it means a request where collection name is ignored
+ return m.containsKey(key) ? singleton((String) null) : singleton("*");
+ }
+ return null;
+ }
+ if (val instanceof Collection) {
+ Collection list = (Collection) val;
+ for (Object o : list) result.add(String.valueOf(o));
+ } else if (val instanceof String) {
+ result.add((String) val);
+ } else {
+ throw new RuntimeException("Bad value for : " + key);
+ }
+ return result.isEmpty() ? null : Collections.unmodifiableSet(result);
+ }
+
+ @Override
+ public void close() throws IOException { }
+
+ static class Permission {
+ String name;
+ Set<String> path, role, collections, method;
+ Map<String, Object> params;
+ Predicate<AuthorizationContext> predicate;
+ Map originalConfig;
+
+ private Permission() {
+ }
+
+ static Permission load(Map m) {
+ Permission p = new Permission();
+ p.originalConfig = new LinkedHashMap<>(m);
+ String name = (String) m.get(NAME);
+ if (!m.containsKey("role")) throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "role not specified");
+ p.role = readValueAsSet(m, "role");
+ if (well_known_permissions.containsKey(name)) {
+ HashSet<String> disAllowed = new HashSet<>(knownKeys);
+ disAllowed.remove("role");//these are the only
+ disAllowed.remove(NAME);//allowed keys for well-known permissions
+ for (String s : disAllowed) {
+ if (m.containsKey(s))
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, s + " is not a valid key for the permission : " + name);
+ }
+ p.predicate = (Predicate<AuthorizationContext>) ((Map) well_known_permissions.get(name)).get(Predicate.class.getName());
+ m = well_known_permissions.get(name);
+ }
+ p.name = name;
+ p.path = readSetSmart(name, m, "path");
+ p.collections = readSetSmart(name, m, "collection");
+ p.method = readSetSmart(name, m, "method");
+ p.params = (Map<String, Object>) m.get("params");
+ return p;
+ }
+
+ @Override
+ public String toString() {
+ return Utils.toJSONString(originalConfig);
+ }
+
+ static final Set<String> knownKeys = ImmutableSet.of("collection", "role", "params", "path", "method", NAME);
+ }
+
+ enum MatchStatus {
+ USER_REQUIRED(AuthorizationResponse.PROMPT),
+ NO_PERMISSIONS_FOUND(AuthorizationResponse.OK),
+ PERMITTED(AuthorizationResponse.OK),
+ FORBIDDEN(AuthorizationResponse.FORBIDDEN);
+
+ final AuthorizationResponse rsp;
+
+ MatchStatus(AuthorizationResponse rsp) {
+ this.rsp = rsp;
+ }
+ }
+
+ /**
+ * This checks for the defaults available other rules for the keys
+ */
+ private static Set<String> readSetSmart(String permissionName, Map m, String key) {
+ Set<String> set = readValueAsSet(m, key);
+ if (set == null && well_known_permissions.containsKey(permissionName)) {
+ set = readValueAsSet((Map) well_known_permissions.get(permissionName), key);
+ }
+ if ("method".equals(key)) {
+ if (set != null) {
+ for (String s : set) if (!HTTP_METHODS.contains(s)) return null;
+ }
+ return set;
+ }
+ return set == null ? singleton((String)null) : set;
+ }
+
+ @Override
+ public Map<String, Object> edit(Map<String, Object> latestConf, List<CommandOperation> commands) {
+ for (CommandOperation op : commands) {
+ OPERATION operation = null;
+ for (OPERATION o : OPERATION.values()) {
+ if (o.name.equals(op.name)) {
+ operation = o;
+ break;
+ }
+ }
+ if (operation == null) {
+ op.unknownOperation();
+ return null;
+ }
+ latestConf = operation.edit(latestConf, op);
+ if (latestConf == null) return null;
+
+ }
+ return latestConf;
+ }
+
+ enum OPERATION {
+ SET_USER_ROLE("set-user-role") {
+ @Override
+ public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+ Map<String, Object> roleMap = getMapValue(latestConf, "user-role");
+ Map<String, Object> map = op.getDataMap();
+ if (op.hasError()) return null;
+ for (Map.Entry<String, Object> e : map.entrySet()) {
+ if (e.getValue() == null) {
+ roleMap.remove(e.getKey());
+ continue;
+ }
+ if (e.getValue() instanceof String || e.getValue() instanceof List) {
+ roleMap.put(e.getKey(), e.getValue());
+ } else {
+ op.addError("Unexpected value ");
+ return null;
+ }
+ }
+ return latestConf;
+ }
+ },
+ SET_PERMISSION("set-permission") {
+ @Override
+ public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+ String name = op.getStr(NAME);
+ Map<String, Object> dataMap = op.getDataMap();
+ if (op.hasError()) return null;
+ dataMap = getDeepCopy(dataMap, 3);
+ String before = (String) dataMap.remove("before");
+ for (String key : dataMap.keySet()) {
+ if (!Permission.knownKeys.contains(key)) op.addError("Unknown key, " + key);
+ }
+ try {
+ Permission.load(dataMap);
+ } catch (Exception e) {
+ op.addError(e.getMessage());
+ return null;
+ }
+ List<Map> permissions = getListValue(latestConf, "permissions");
+ List<Map> permissionsCopy = new ArrayList<>();
+ boolean added = false;
+ for (Map e : permissions) {
+ Object n = e.get(NAME);
+ if (n.equals(before) || n.equals(name)) {
+ added = true;
+ permissionsCopy.add(dataMap);
+ }
+ if (!n.equals(name)) permissionsCopy.add(e);
+ }
+ if (!added && before != null) {
+ op.addError("Invalid 'before' :" + before);
+ return null;
+ }
+ if (!added) permissionsCopy.add(dataMap);
+ latestConf.put("permissions", permissionsCopy);
+ return latestConf;
+ }
+ },
+ UPDATE_PERMISSION("update-permission") {
+ @Override
+ public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+ String name = op.getStr(NAME);
+ if (op.hasError()) return null;
+ for (Map permission : (List<Map>) getListValue(latestConf, "permissions")) {
+ if (name.equals(permission.get(NAME))) {
+ LinkedHashMap copy = new LinkedHashMap<>(permission);
+ copy.putAll(op.getDataMap());
+ op.setCommandData(copy);
+ return SET_PERMISSION.edit(latestConf, op);
+ }
+ }
+ op.addError("No such permission " + name);
+ return null;
+ }
+ },
+ DELETE_PERMISSION("delete-permission") {
+ @Override
+ public Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op) {
+ List<String> names = op.getStrs("");
+ if (names == null || names.isEmpty()) {
+ op.addError("Invalid command");
+ return null;
+ }
+ names = new ArrayList<>(names);
+ List<Map> copy = new ArrayList<>();
+ List<Map> p = getListValue(latestConf, "permissions");
+ for (Map map : p) {
+ Object n = map.get(NAME);
+ if (names.contains(n)) {
+ names.remove(n);
+ continue;
+ } else {
+ copy.add(map);
+ }
+ }
+ if (!names.isEmpty()) {
+ op.addError("Unknown permission name(s) " + names);
+ return null;
+ }
+ latestConf.put("permissions", copy);
+ return latestConf;
+ }
+ };
+
+ public abstract Map<String, Object> edit(Map<String, Object> latestConf, CommandOperation op);
+
+ public final String name;
+
+ OPERATION(String s) {
+ this.name = s;
+ }
+
+ public static OPERATION get(String name) {
+ for (OPERATION o : values()) if (o.name.equals(name)) return o;
+ return null;
+ }
+ }
+
+ public static final Set<String> HTTP_METHODS = ImmutableSet.of("GET", "POST", "DELETE", "PUT", "HEAD");
+
+ private static final Map<String, Map<String,Object>> well_known_permissions = (Map) Utils.fromJSONString(
+ " { " +
+ " security-edit :{" +
+ " path:['/admin/authentication','/admin/authorization']," +
+ " collection:null," +
+ " method:POST }," +
+ " security-read :{" +
+ " path:['/admin/authentication','/admin/authorization']," +
+ " collection:null," +
+ " method:GET}," +
+ " schema-edit :{" +
+ " method:POST," +
+ " path:'/schema/*'}," +
+ " collection-admin-edit :{" +
+ " collection:null," +
+ " path:'/admin/collections'}," +
+ " collection-admin-read :{" +
+ " collection:null," +
+ " path:'/admin/collections'}," +
+ " schema-read :{" +
+ " method:GET," +
+ " path:'/schema/*'}," +
+ " config-read :{" +
+ " method:GET," +
+ " path:'/config/*'}," +
+ " update :{" +
+ " path:'/update/*'}," +
+ " read :{" +
+ " path:['/select', '/get','/browse','/tvrh','/terms','/clustering','/elevate', '/export','/spell','/clustering']}," +
+ " config-edit:{" +
+ " method:POST," +
+ " path:'/config/*'}," +
+ " all:{collection:['*', null]}" +
+ "}");
+
+ static {
+ ((Map) well_known_permissions.get("collection-admin-edit")).put(Predicate.class.getName(), getCollectionActionPredicate(true));
+ ((Map) well_known_permissions.get("collection-admin-read")).put(Predicate.class.getName(), getCollectionActionPredicate(false));
+ }
+
+ private static Predicate<AuthorizationContext> getCollectionActionPredicate(final boolean isEdit) {
+ return new Predicate<AuthorizationContext>() {
+ @Override
+ public boolean test(AuthorizationContext context) {
+ String action = context.getParams().get("action");
+ if (action == null) return false;
+ CollectionParams.CollectionAction collectionAction = CollectionParams.CollectionAction.get(action);
+ if (collectionAction == null) return false;
+ return isEdit ? collectionAction.isWrite : !collectionAction.isWrite;
+ }
+ };
+ }
+
+
+ public static void main(String[] args) {
+ System.out.println(Utils.toJSONString(well_known_permissions));
+
+ }
+
+ public interface Predicate<T> {
+
+ boolean test(T t);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java b/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
new file mode 100644
index 0000000..a54e4ad
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/src/main/java/org.apache.ambari.infra.security/InfraUserRolesLookupStrategy.java
@@ -0,0 +1,49 @@
+/*
+ * 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.infra.security;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.hadoop.security.authentication.util.KerberosName;
+
+import java.security.Principal;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Strategy class to get roles with the principal name (in a specific format e.g.: 'name@DOMAIN')
+ * in case of KerberosPlugin is used for authentication
+ */
+public class InfraUserRolesLookupStrategy {
+
+ public Set<String> getUserRolesFromPrincipal(Map<String, Set<String>> usersVsRoles, Principal principal) {
+ if (principal instanceof AuthenticationToken) {
+ AuthenticationToken authenticationToken = (AuthenticationToken) principal;
+ KerberosName kerberosName = new KerberosName(authenticationToken.getName());
+ Set<String> rolesResult = usersVsRoles.get(String.format("%s@%s", kerberosName.getServiceName(), kerberosName.getRealm()));
+ if (CollectionUtils.isEmpty(rolesResult)) {
+ rolesResult = usersVsRoles.get(principal.getName());
+ }
+ return rolesResult;
+ } else {
+ return usersVsRoles.get(principal.getName());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java b/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
new file mode 100644
index 0000000..828f09a
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraKerberosHostValidatorTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.infra.security;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class InfraKerberosHostValidatorTest {
+
+ private static final String DEFAULT_SERVICE_USER = "logsearch";
+
+ private InfraKerberosHostValidator underTest = new InfraKerberosHostValidator();
+ private AuthenticationToken principal;
+
+
+ @Before
+ public void setUp() {
+ principal = new AuthenticationToken(DEFAULT_SERVICE_USER, DEFAULT_SERVICE_USER + "/c6401.ambari.apache.org@EXAMPLE.COM", "kerberos");
+ }
+
+ @Test
+ public void testValidateHosts() {
+ // GIVEN
+ Map<String, Set<String>> userHostsMap = generateUserHostMap("c6401.ambari.apache.org");
+ // WHEN
+ boolean result = underTest.validate(principal, userHostsMap, new HashMap<String, String>());
+ // THEN
+ assertTrue(result);
+ }
+
+ @Test
+ public void testValidateHostsValid() {
+ // GIVEN
+ Map<String, Set<String>> userHostsMap = generateUserHostMap("c6402.ambari.apache.org");
+ // WHEN
+ boolean result = underTest.validate(principal, userHostsMap, new HashMap<String, String>());
+ // THEN
+ assertFalse(result);
+
+ }
+
+ @Test
+ public void testValidateHostRegex() {
+ // GIVEN
+ Map<String, String> userHostRegex = generateRegexMap("c\\d+.*.apache.org");
+ // WHEN
+ boolean result = underTest.validate(principal, new HashMap<String, Set<String>>(), userHostRegex);
+ // THEN
+ assertTrue(result);
+
+ }
+
+ @Test
+ public void testValidateHostRegexInvalid() {
+ // GIVEN
+ Map<String, String> userHostRegex = generateRegexMap("c\\d+.*.org.apache");
+ // WHEN
+ boolean result = underTest.validate(principal, new HashMap<String, Set<String>>(), userHostRegex);
+ // THEN
+ assertFalse(result);
+ }
+
+ @Test
+ public void testPrecedence() {
+ // GIVEN
+ Map<String, Set<String>> userHostsMap = generateUserHostMap("c6402.ambari.apache.org");
+ Map<String, String> userHostRegex = generateRegexMap("c\\d+.*.apache.org");
+ // WHEN
+ boolean result = underTest.validate(principal, userHostsMap, userHostRegex);
+ // THEN
+ assertTrue(result);
+ }
+
+ private Map<String, Set<String>> generateUserHostMap(String... hosts) {
+ Map<String, Set<String>> map = new HashMap<>();
+ Set<String> hostSet = new HashSet<>();
+ for (String host : hosts) {
+ hostSet.add(host);
+ }
+ map.put(DEFAULT_SERVICE_USER, hostSet);
+ return map;
+ }
+
+ private Map<String, String> generateRegexMap(String regex) {
+ Map<String, String> map = new HashMap<>();
+ map.put(DEFAULT_SERVICE_USER, regex);
+ return map;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java b/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
new file mode 100644
index 0000000..ee84969
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraRuleBasedAuthorizationPluginTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.infra.security;
+
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.solr.common.params.MapSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.Utils;
+import org.apache.solr.security.AuthorizationContext;
+import org.apache.solr.security.AuthorizationContext.RequestType;
+import org.apache.solr.security.AuthorizationResponse;
+import org.junit.Test;
+
+import static java.util.Collections.singletonList;
+import static java.util.Collections.singletonMap;
+import static org.apache.solr.common.util.Utils.makeMap;
+import static org.junit.Assert.assertEquals;
+
+public class InfraRuleBasedAuthorizationPluginTest {
+
+ private static final String PERMISSIONS = "{" +
+ " user-host : {" +
+ " 'infra-solr@EXAMPLE.COM': [hostname, hostname2]" +
+ " }," +
+ " user-role : {" +
+ " 'infra-solr@EXAMPLE.COM': [admin]," +
+ " 'logsearch@EXAMPLE.COM': [logsearch_role,dev]," +
+ " 'logfeeder@EXAMPLE.COM': [logsearch_role,dev]," +
+ " 'atlas@EXAMPLE.COM': [atlas_role, audit_role, dev]," +
+ " 'knox@EXAMPLE.COM': [audit_role,dev]," +
+ " 'hdfs@EXAMPLE.COM': [audit_role,dev]," +
+ " 'hbase@EXAMPLE.COM': [audit_role,dev]," +
+ " 'yarn@EXAMPLE.COM': [audit_role,dev]," +
+ " 'knox@EXAMPLE.COM': [audit_role,dev]," +
+ " 'kafka@EXAMPLE.COM': [audit_role,dev]," +
+ " 'kms@EXAMPLE.COM': [audit_role,dev]," +
+ " 'storm@EXAMPLE.COM': [audit_role,dev]," +
+ " 'rangeradmin@EXAMPLE.COM':[ranger_role, audit_role, dev]" +
+ " }," +
+ " permissions : [" +
+ " {name:'collection-admin-read'," +
+ " role:null}," +
+ " {name:collection-admin-edit ," +
+ " role:[logsearch_role, atlas_role, ranger_role, admin]}," +
+ " {name:mycoll_update," +
+ " collection:mycoll," +
+ " path:'/*'," +
+ " role:[logsearch_role,admin]" +
+ " }," +
+ " {name:mycoll2_update," +
+ " collection:mycoll2," +
+ " path:'/*'," +
+ " role:[ranger_role, audit_role, admin]" +
+ " }," +
+ "{name:read , role:dev }]}";
+
+ @Test
+ public void testPermissions() {
+ int STATUS_OK = 200;
+ int FORBIDDEN = 403;
+ int PROMPT_FOR_CREDENTIALS = 401;
+
+ checkRules(makeMap("resource", "/#",
+ "httpMethod", "POST",
+ "userPrincipal", "unknownuser",
+ "collectionRequests", "freeforall" )
+ , STATUS_OK);
+
+ checkRules(makeMap("resource", "/update/json/docs",
+ "httpMethod", "POST",
+ "userPrincipal", "tim",
+ "collectionRequests", "mycoll")
+ , FORBIDDEN);
+
+ checkRules(makeMap("resource", "/update/json/docs",
+ "httpMethod", "POST",
+ "userPrincipal", "logsearch",
+ "collectionRequests", "mycoll")
+ , STATUS_OK);
+
+ checkRules(makeMap("resource", "/update/json/docs",
+ "httpMethod", "GET",
+ "userPrincipal", "rangeradmin",
+ "collectionRequests", "mycoll")
+ , FORBIDDEN);
+
+ checkRules(makeMap("resource", "/update/json/docs",
+ "httpMethod", "GET",
+ "userPrincipal", "rangeradmin",
+ "collectionRequests", "mycoll2")
+ , STATUS_OK);
+
+ checkRules(makeMap("resource", "/update/json/docs",
+ "httpMethod", "GET",
+ "userPrincipal", "logsearch",
+ "collectionRequests", "mycoll2")
+ , FORBIDDEN);
+
+ checkRules(makeMap("resource", "/update/json/docs",
+ "httpMethod", "POST",
+ "userPrincipal", "kms",
+ "collectionRequests", "mycoll2")
+ , STATUS_OK);
+
+ checkRules(makeMap("resource", "/admin/collections",
+ "userPrincipal", "tim",
+ "requestType", RequestType.ADMIN,
+ "collectionRequests", null,
+ "params", new MapSolrParams(singletonMap("action", "CREATE")))
+ , FORBIDDEN);
+
+ checkRules(makeMap("resource", "/admin/collections",
+ "userPrincipal", null,
+ "requestType", RequestType.ADMIN,
+ "collectionRequests", null,
+ "params", new MapSolrParams(singletonMap("action", "CREATE")))
+ , PROMPT_FOR_CREDENTIALS);
+
+ checkRules(makeMap("resource", "/admin/collections",
+ "userPrincipal", "rangeradmin",
+ "requestType", RequestType.ADMIN,
+ "collectionRequests", null,
+ "params", new MapSolrParams(singletonMap("action", "CREATE")))
+ , STATUS_OK);
+
+ checkRules(makeMap("resource", "/admin/collections",
+ "userPrincipal", "kms",
+ "requestType", RequestType.ADMIN,
+ "collectionRequests", null,
+ "params", new MapSolrParams(singletonMap("action", "CREATE")))
+ , FORBIDDEN);
+
+ checkRules(makeMap("resource", "/admin/collections",
+ "userPrincipal", "kms",
+ "requestType", RequestType.ADMIN,
+ "collectionRequests", null,
+ "params", new MapSolrParams(singletonMap("action", "LIST")))
+ , STATUS_OK);
+
+ checkRules(makeMap("resource", "/admin/collections",
+ "userPrincipal", "rangeradmin",
+ "requestType", RequestType.ADMIN,
+ "collectionRequests", null,
+ "params", new MapSolrParams(singletonMap("action", "LIST")))
+ , STATUS_OK);
+ }
+
+ private void checkRules(Map<String, Object> values, int expected) {
+ checkRules(values,expected,(Map) Utils.fromJSONString(PERMISSIONS));
+ }
+
+ private void checkRules(Map<String, Object> values, int expected, Map<String ,Object> permissions) {
+ AuthorizationContext context = new MockAuthorizationContext(values);
+ InfraRuleBasedAuthorizationPlugin plugin = new InfraRuleBasedAuthorizationPlugin();
+ plugin.init(permissions);
+ AuthorizationResponse authResp = plugin.authorize(context);
+ assertEquals(expected, authResp.statusCode);
+ }
+
+ private static class MockAuthorizationContext extends AuthorizationContext {
+ private final Map<String,Object> values;
+
+ private MockAuthorizationContext(Map<String, Object> values) {
+ this.values = values;
+ }
+
+ @Override
+ public SolrParams getParams() {
+ SolrParams params = (SolrParams) values.get("params");
+ return params == null ? new MapSolrParams(new HashMap<String, String>()) : params;
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ Object userPrincipal = values.get("userPrincipal");
+ return userPrincipal == null ? null :
+ new AuthenticationToken(String.valueOf(userPrincipal), String.format("%s%s", String.valueOf(userPrincipal), "/hostname@EXAMPLE.COM"), "kerberos");
+ }
+
+ @Override
+ public String getHttpHeader(String header) {
+ return null;
+ }
+
+ @Override
+ public Enumeration getHeaderNames() {
+ return null;
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return null;
+ }
+
+ @Override
+ public String getRemoteHost() {
+ return null;
+ }
+
+ @Override
+ public List<CollectionRequest> getCollectionRequests() {
+ Object collectionRequests = values.get("collectionRequests");
+ if (collectionRequests instanceof String) {
+ return singletonList(new CollectionRequest((String)collectionRequests));
+ }
+ return (List<CollectionRequest>) collectionRequests;
+ }
+
+ @Override
+ public RequestType getRequestType() {
+ return (RequestType) values.get("requestType");
+ }
+
+ @Override
+ public String getHttpMethod() {
+ return (String) values.get("httpMethod");
+ }
+
+ @Override
+ public String getResource() {
+ return (String) values.get("resource");
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java b/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
new file mode 100644
index 0000000..c1a47d1
--- /dev/null
+++ b/ambari-logsearch/ambari-infra-solr-plugin/src/test/java/org/apache/ambari/infra/security/InfraUserRolesLookupStrategyTest.java
@@ -0,0 +1,83 @@
+/*
+ * 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.infra.security;
+
+import com.google.common.collect.Sets;
+import org.apache.hadoop.security.authentication.server.AuthenticationToken;
+import org.apache.http.auth.BasicUserPrincipal;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class InfraUserRolesLookupStrategyTest {
+
+ private InfraUserRolesLookupStrategy underTest = new InfraUserRolesLookupStrategy();
+
+ @Test
+ public void testLookupRolesForPrincipalName() {
+ // GIVEN
+ Map<String, Set<String>> usersVsRoles = generateUserRolesMap();
+ AuthenticationToken principal = new AuthenticationToken(
+ "logsearch", "logsearch/c6401.ambari.apache.org@EXAMPLE.COM", "kerberos");
+ // WHEN
+ Set<String> result = underTest.getUserRolesFromPrincipal(usersVsRoles, principal);
+ // THEN
+ assertTrue(result.contains("logsearch_user"));
+ assertTrue(result.contains("ranger_user"));
+ assertFalse(result.contains("admin"));
+ }
+
+ @Test
+ public void testLookupRolesForNonKerberosPrincipalName() {
+ // GIVEN
+ Map<String, Set<String>> usersVsRoles = generateUserRolesMap();
+ BasicUserPrincipal principal = new BasicUserPrincipal("infra-solr");
+ // WHEN
+ Set<String> result = underTest.getUserRolesFromPrincipal(usersVsRoles, principal);
+ // THEN
+ assertTrue(result.contains("admin"));
+ assertTrue(result.contains("logsearch_user"));
+ }
+
+ @Test
+ public void testLookupRolesWithNonKerberosPrincipalWithoutRoles() {
+ // GIVEN
+ Map<String, Set<String>> usersVsRoles = generateUserRolesMap();
+ BasicUserPrincipal principal = new BasicUserPrincipal("unknownuser");
+ // WHEN
+ Set<String> result = underTest.getUserRolesFromPrincipal(usersVsRoles, principal);
+ // THEN
+ assertTrue(result.isEmpty());
+ }
+
+ private Map<String, Set<String>> generateUserRolesMap() {
+ Map<String, Set<String>> usersVsRoles = new HashMap<>();
+ usersVsRoles.put("logsearch@EXAMPLE.COM", Sets.newHashSet("logsearch_user", "ranger_user"));
+ usersVsRoles.put("infra-solr@EXAMPLE.COM", Sets.newHashSet("admin"));
+ usersVsRoles.put("infra-solr", Sets.newHashSet("admin", "logsearch_user"));
+ usersVsRoles.put("unknownuser", new HashSet<String>());
+ return usersVsRoles;
+ }
+}
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/ambari-logsearch-assembly/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-logsearch/ambari-logsearch-assembly/pom.xml b/ambari-logsearch/ambari-logsearch-assembly/pom.xml
index 0777e17..2e68060 100644
--- a/ambari-logsearch/ambari-logsearch-assembly/pom.xml
+++ b/ambari-logsearch/ambari-logsearch-assembly/pom.xml
@@ -43,6 +43,7 @@
<logsearch.portal.conf.mapping.path>/etc/${logsearch.portal.package.name}/conf</logsearch.portal.conf.mapping.path>
<logsearch.logfeeder.conf.mapping.path>/etc/${logsearch.logfeeder.package.name}/conf
</logsearch.logfeeder.conf.mapping.path>
+ <infra.solr.plugin.dir>${project.basedir}/../ambari-infra-solr-plugin</infra.solr.plugin.dir>
</properties>
<artifactId>ambari-logsearch-assembly</artifactId>
<profiles>
@@ -69,7 +70,8 @@
<description>Maven Recipe: RPM Package.</description>
<autoRequires>false</autoRequires>
<prefix>/</prefix>
- <needarch>x86_64</needarch>
+ <needarch>noarch</needarch>
+ <targetOS>linux</targetOS>
<version>${package-version}</version>
<release>${package-release}</release>
@@ -108,6 +110,8 @@
<configuration>
<group>Development</group>
<name>${solr.client.package.name}</name>
+ <needarch>noarch</needarch>
+ <targetOS>linux</targetOS>
<mappings>
<mapping>
<directory>${solr.client.mapping.path}</directory>
@@ -236,6 +240,8 @@
<fileset dir="${project.build.directory}/solr-${solr.version}"/>
</move>
<copy file="${project.basedir}/src/main/resources/solr" toDir="${project.build.directory}/solr/bin/" />
+ <copy file="${infra.solr.plugin.dir}/target/ambari-infra-solr-plugin-${project.version}.jar"
+ toDir="${project.build.directory}/solr/server/solr-webapp/webapp/WEB-INF/lib/"/>
<chmod file="${project.build.directory}/solr/bin/**" perm="755"/>
<chmod file="${project.build.directory}/solr/server/scripts/**" perm="755"/>
</target>
@@ -495,6 +501,8 @@
<fileset dir="${project.build.directory}/solr-${solr.version}"/>
</move>
<copy file="${project.basedir}/src/main/resources/solr" toDir="${project.build.directory}/solr/bin/" />
+ <copy file="${infra.solr.plugin.dir}/target/ambari-infra-solr-plugin-${project.version}.jar"
+ toDir="${project.build.directory}/solr/server/solr-webapp/webapp/WEB-INF/lib/"/>
<chmod file="${project.build.directory}/solr/bin/**" perm="755"/>
<chmod file="${project.build.directory}/solr/server/scripts/**" perm="755"/>
</target>
@@ -523,6 +531,11 @@
<artifactId>ambari-logsearch-solr-client</artifactId>
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.ambari</groupId>
+ <artifactId>ambari-infra-solr-plugin</artifactId>
+ <version>${project.version}</version>
+ </dependency>
</dependencies>
</project>
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-logsearch/pom.xml
----------------------------------------------------------------------
diff --git a/ambari-logsearch/pom.xml b/ambari-logsearch/pom.xml
index cabb84f..6df489f 100644
--- a/ambari-logsearch/pom.xml
+++ b/ambari-logsearch/pom.xml
@@ -35,6 +35,7 @@
<module>ambari-logsearch-logfeeder</module>
<module>ambari-logsearch-solr-client</module>
<module>ambari-logsearch-it</module>
+ <module>ambari-infra-solr-plugin</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/configuration/infra-solr-security-json.xml
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/configuration/infra-solr-security-json.xml b/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/configuration/infra-solr-security-json.xml
index ed623df..e193a8c 100644
--- a/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/configuration/infra-solr-security-json.xml
+++ b/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/configuration/infra-solr-security-json.xml
@@ -22,8 +22,58 @@
<!-- This is a special config file for properties used to monitor status of the service -->
<configuration supports_adding_forbidden="true">
<property>
+ <name>infra_solr_ranger_audit_service_users</name>
+ <display-name>Ranger audit service users</display-name>
+ <value>{default_ranger_audit_users}</value>
+ <description>
+ List of comma separated kerberos service users who can write into ranger audit collections if the cluster is secure. (atlas and rangeradmin supported by default)
+ Change values in that case of custom values are used for kerberos principals. (default_ranger_audit_users is resolved ranger-*-audit/xasecure.audit.jaas.Client.option.principal,
+ by default namenode, hbase, hive knox, kafka, ranger kms and nifi are supported, to change it you can edit the security content,
+ or add a new username next to the default value, e.g.: {default_ranger_audit_users},customuser)
+ </description>
+ <depends-on>
+ <property>
+ <type>ranger-hdfs-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-hbase-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-hive-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-knox-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-kafka-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-kms-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-storm-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-yarn-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ <property>
+ <type>ranger-nifi-audit</type>
+ <name>xasecure.audit.jaas.Client.option.principal</name>
+ </property>
+ </depends-on>
+ <on-ambari-upgrade add="true"/>
+ </property>
+ <property>
<name>content</name>
- <display-name>infra-solr security.json template</display-name>
+ <display-name>security.json template</display-name>
<description>This is the jinja template for security.json file on the solr znode (only used if the cluster is secure)</description>
<value/>
<property-type>VALUE_FROM_PROPERTY_FILE</property-type>
@@ -31,6 +81,16 @@
<property-file-name>infra-solr-security.json.j2</property-file-name>
<property-file-type>text</property-file-type>
</value-attributes>
+ <depends-on>
+ <property>
+ <type>application-properties</type>
+ <name>atlas.authentication.principal</name>
+ </property>
+ <property>
+ <type>ranger-admin-site</type>
+ <name>ranger.admin.kerberos.principal</name>
+ </property>
+ </depends-on>
<on-ambari-upgrade add="true"/>
</property>
</configuration>
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/package/scripts/params.py
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/package/scripts/params.py b/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/package/scripts/params.py
index 526baea..ab9aa61 100644
--- a/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/package/scripts/params.py
+++ b/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/package/scripts/params.py
@@ -108,6 +108,7 @@ for host in config['clusterHostInfo']['zookeeper_hosts']:
if index < len(config['clusterHostInfo']['zookeeper_hosts']):
zookeeper_quorum += ","
+default_ranger_audit_users = 'nn,hbase,hive,knox,kafka,kms,storm,yarn,nifi'
if security_enabled:
kinit_path_local = status_params.kinit_path_local
@@ -119,7 +120,23 @@ if security_enabled:
infra_solr_web_kerberos_principal = config['configurations']['infra-solr-env']['infra_solr_web_kerberos_principal'].replace('_HOST',_hostname_lowercase)
infra_solr_kerberos_name_rules = config['configurations']['infra-solr-env']['infra_solr_kerberos_name_rules'].replace('$', '\$')
infra_solr_sasl_user = get_name_from_principal(infra_solr_kerberos_principal)
-
+ kerberos_realm = config['configurations']['kerberos-env']['realm']
+
+ ranger_audit_principal_conf_key = "xasecure.audit.jaas.Client.option.principal"
+ ranger_audit_principals = []
+ ranger_audit_principals.append(default('configurations/ranger-hdfs-audit/' + ranger_audit_principal_conf_key, 'nn'))
+ ranger_audit_principals.append(default('configurations/ranger-hbase-audit/' + ranger_audit_principal_conf_key, 'hbase'))
+ ranger_audit_principals.append(default('configurations/ranger-hive-audit/' + ranger_audit_principal_conf_key, 'hive'))
+ ranger_audit_principals.append(default('configurations/ranger-knox-audit/' + ranger_audit_principal_conf_key, 'knox'))
+ ranger_audit_principals.append(default('configurations/ranger-kafka-audit/' + ranger_audit_principal_conf_key, 'kafka'))
+ ranger_audit_principals.append(default('configurations/ranger-kms-audit/' + ranger_audit_principal_conf_key, 'kms'))
+ ranger_audit_principals.append(default('configurations/ranger-storm-audit/' + ranger_audit_principal_conf_key, 'storm'))
+ ranger_audit_principals.append(default('configurations/ranger-yarn-audit/' + ranger_audit_principal_conf_key, 'yarn'))
+ ranger_audit_principals.append(default('configurations/ranger-nifi-audit/' + ranger_audit_principal_conf_key, 'nifi'))
+ ranger_audit_names_from_principals = [ get_name_from_principal(x) for x in ranger_audit_principals ]
+ default_ranger_audit_users = ','.join(ranger_audit_names_from_principals)
+
+infra_solr_ranger_audit_service_users = format(config['configurations']['infra-solr-security-json']['infra_solr_ranger_audit_service_users']).split(',')
infra_solr_security_json_content = config['configurations']['infra-solr-security-json']['content']
#Solr log4j
@@ -133,3 +150,13 @@ smokeuser = config['configurations']['cluster-env']['smokeuser']
smoke_user_keytab = config['configurations']['cluster-env']['smokeuser_keytab']
smokeuser_principal = config['configurations']['cluster-env']['smokeuser_principal_name']
+ranger_solr_collection_name = default('configurations/ranger-env/ranger_solr_collection_name', 'ranger_audits')
+logsearch_service_logs_collection = default('configurations/logsearch-properties/logsearch.solr.collection.service.logs', 'hadoop_logs')
+logsearch_audit_logs_collection = default('configurations/logsearch-properties/logsearch.solr.collection.audit.logs', 'audit_logs')
+
+ranger_admin_kerberos_service_user = get_name_from_principal(default('configurations/ranger-admin-site/ranger.admin.kerberos.principal', 'rangeradmin'))
+atlas_kerberos_service_user = get_name_from_principal(default('configurations/application-properties/atlas.authentication.principal', 'atlas'))
+logsearch_kerberos_service_user = get_name_from_principal(default('configurations/logsearch-env/logsearch_kerberos_principal', 'logsearch'))
+logfeeder_kerberos_service_user = get_name_from_principal(default('configurations/logfeeder-env/logfeeder_kerberos_principal', 'logfeeder'))
+infra_solr_kerberos_service_user = get_name_from_principal(default('configurations/infra-solr-env/infra_solr_kerberos_principal', 'infra-solr'))
+
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/properties/infra-solr-security.json.j2
----------------------------------------------------------------------
diff --git a/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/properties/infra-solr-security.json.j2 b/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/properties/infra-solr-security.json.j2
index d8aea24..ed764f0 100644
--- a/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/properties/infra-solr-security.json.j2
+++ b/ambari-server/src/main/resources/common-services/AMBARI_INFRA/0.1.0/properties/infra-solr-security.json.j2
@@ -18,5 +18,51 @@
{
"authentication": {
"class": "org.apache.solr.security.KerberosPlugin"
+ },
+ "authorization": {
+ "class": "org.apache.ambari.infra.security.InfraRuleBasedAuthorizationPlugin",
+ "user-role": {
+ "{{infra_solr_kerberos_service_user}}@{{kerberos_realm}}": "admin",
+ "{{logsearch_kerberos_service_user}}@{{kerberos_realm}}": ["logsearch_user", "ranger_user", "dev"],
+ "{{logfeeder_kerberos_service_user}}@{{kerberos_realm}}": ["logfeeder_user", "dev"],
+ "{{atlas_kerberos_service_user}}@{{kerberos_realm}}": ["atlas_user", "ranger_audit_user", "dev"],
+{% if infra_solr_ranger_audit_service_users %}
+{% for ranger_audit_service_user in infra_solr_ranger_audit_service_users %}
+ "{{ranger_audit_service_user}}@{{kerberos_realm}}": ["ranger_audit_user", "dev"],
+{% endfor %}
+{% endif %}
+ "{{ranger_admin_kerberos_service_user}}@{{kerberos_realm}}": ["ranger_user", "ranger_audit_user", "dev"]
+ },
+ "permissions": [
+ {
+ "name" : "collection-admin-read",
+ "role" :null
+ },
+ {
+ "name" : "collection-admin-edit",
+ "role" : ["admin", "logsearch_user", "logfeeder_user", "atlas_user", "ranger_user"]
+ },
+ {
+ "name":"read",
+ "role": "dev"
+ },
+ {
+ "collection": ["{{logsearch_service_logs_collection}}", "{{logsearch_audit_logs_collection}}", "history"],
+ "role": ["admin", "logsearch_user", "logfeeder_user"],
+ "name": "logsearch-manager",
+ "path": "/*"
+ },
+ {
+ "collection": ["vertex_index", "edge_index", "fulltext_index"],
+ "role": ["admin", "atlas_user"],
+ "name": "atlas-manager",
+ "path": "/*"
+ },
+ {
+ "collection": "{{ranger_solr_collection_name}}",
+ "role": ["admin", "ranger_user", "ranger_audit_user"],
+ "name": "ranger-manager",
+ "path": "/*"
+ }]
}
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/0446e4b1/ambari-server/src/test/python/stacks/2.4/configs/default.json
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/python/stacks/2.4/configs/default.json b/ambari-server/src/test/python/stacks/2.4/configs/default.json
index 7a940d3..d4e6064 100644
--- a/ambari-server/src/test/python/stacks/2.4/configs/default.json
+++ b/ambari-server/src/test/python/stacks/2.4/configs/default.json
@@ -240,6 +240,7 @@
"content": "admin json content"
},
"infra-solr-security-json" : {
+ "infra_solr_ranger_audit_service_users" : "hbase,hdfs",
"content" : "{\"authentication\": \"org.apache.solr.security.KerberosPlugin\"}"
},
"infra-solr-client-log4j" : {