http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java ---------------------------------------------------------------------- diff --git a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java index c999f86..19a1509 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/ServiceREST.java @@ -52,12 +52,14 @@ import org.apache.ranger.biz.ServiceDBStore; import org.apache.ranger.biz.ServiceMgr; import org.apache.ranger.biz.TagDBStore; import org.apache.ranger.biz.XUserMgr; +import org.apache.ranger.common.ContextUtil; import org.apache.ranger.common.GUIDUtil; import org.apache.ranger.common.MessageEnums; import org.apache.ranger.common.RESTErrorUtil; import org.apache.ranger.common.RangerSearchUtil; import org.apache.ranger.common.RangerValidatorFactory; import org.apache.ranger.common.ServiceUtil; +import org.apache.ranger.common.UserSessionBase; import org.apache.ranger.db.RangerDaoManager; import org.apache.ranger.entity.XXPolicyExportAudit; import org.apache.ranger.entity.XXService; @@ -108,6 +110,8 @@ public class ServiceREST { final static public String PARAM_SERVICE_NAME = "serviceName"; final static public String PARAM_POLICY_NAME = "policyName"; final static public String PARAM_UPDATE_IF_EXISTS = "updateIfExists"; + private static final String Allowed_User_List_For_Download = "policy.download.auth.users"; + private static final String Allowed_User_List_For_Grant_Revoke = "policy.grantrevoke.auth.users"; @Autowired RESTErrorUtil restErrorUtil; @@ -441,14 +445,16 @@ public class ServiceREST { RangerServiceValidator validator = validatorFactory.getServiceValidator(svcStore); validator.validate(service, Action.CREATE); - bizUtil.hasAdminPermissions("Services"); + UserSessionBase session = ContextUtil.getCurrentUserSession(); + if(session != null && !session.isSpnegoEnabled()){ + bizUtil.hasAdminPermissions("Services"); - // TODO: As of now we are allowing SYS_ADMIN to create all the - // services including KMS - - XXServiceDef xxServiceDef = daoManager.getXXServiceDef().findByName(service.getType()); - bizUtil.hasKMSPermissions("Service", xxServiceDef.getImplclassname()); + // TODO: As of now we are allowing SYS_ADMIN to create all the + // services including KMS + XXServiceDef xxServiceDef = daoManager.getXXServiceDef().findByName(service.getType()); + bizUtil.hasKMSPermissions("Service", xxServiceDef.getImplclassname()); + } ret = svcStore.createService(service); } catch(WebApplicationException excp) { throw excp; @@ -911,6 +917,119 @@ public class ServiceREST { return ret; } + + @POST + @Path("/secure/services/grant/{serviceName}") + @Produces({ "application/json", "application/xml" }) + public RESTResponse secureGrantAccess(@PathParam("serviceName") String serviceName, GrantRevokeRequest grantRequest, @Context HttpServletRequest request) throws Exception { + if(LOG.isDebugEnabled()) { + LOG.debug("==> ServiceREST.secureGrantAccess(" + serviceName + ", " + grantRequest + ")"); + } + RESTResponse ret = new RESTResponse(); + RangerPerfTracer perf = null; + boolean isAllowed = false; + boolean isKeyAdmin = bizUtil.isKeyAdmin(); + if (serviceUtil.isValidateHttpsAuthentication(serviceName, request)) { + try { + if(RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "ServiceREST.scureGrantAccess(serviceName=" + serviceName + ")"); + } + String userName = grantRequest.getGrantor(); + Set<String> userGroups = userMgr.getGroupsForUser(userName); + RangerAccessResource resource = new RangerAccessResourceImpl(grantRequest.getResource()); + boolean isAdmin = hasAdminAccess(serviceName, userName, userGroups, resource); + + if(!isAdmin) { + throw restErrorUtil.createRESTException(HttpServletResponse.SC_UNAUTHORIZED, "", true); + } + // New Code + XXService xService = daoManager.getXXService().findByName(serviceName); + XXServiceDef xServiceDef = daoManager.getXXServiceDef().getById(xService.getType()); + RangerService rangerService = svcStore.getServiceByName(serviceName); + + if (StringUtils.equals(xServiceDef.getImplclassname(), EmbeddedServiceDefsUtil.KMS_IMPL_CLASS_NAME)) { + if (isKeyAdmin) { + isAllowed = true; + }else { + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Grant_Revoke); + } + }else{ + if (isAdmin) { + isAllowed = true; + } + else{ + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Grant_Revoke); + } + } + // New Code + if (isAllowed) { + RangerPolicy policy = getExactMatchPolicyForResource(serviceName, resource); + + if(policy != null) { + boolean policyUpdated = false; + policyUpdated = ServiceRESTUtil.processGrantRequest(policy, grantRequest); + + if(policyUpdated) { + svcStore.updatePolicy(policy); + } else { + LOG.error("processSecureGrantRequest processing failed"); + throw new Exception("processSecureGrantRequest processing failed"); + } + } else { + policy = new RangerPolicy(); + policy.setService(serviceName); + policy.setName("grant-" + System.currentTimeMillis()); // TODO: better policy name + policy.setDescription("created by grant"); + policy.setIsAuditEnabled(grantRequest.getEnableAudit()); + policy.setCreatedBy(userName); + + Map<String, RangerPolicyResource> policyResources = new HashMap<String, RangerPolicyResource>(); + Set<String> resourceNames = resource.getKeys(); + + if(! CollectionUtils.isEmpty(resourceNames)) { + for(String resourceName : resourceNames) { + RangerPolicyResource policyResource = new RangerPolicyResource(resource.getValue(resourceName)); + policyResource.setIsRecursive(grantRequest.getIsRecursive()); + + policyResources.put(resourceName, policyResource); + } + } + policy.setResources(policyResources); + + RangerPolicyItem policyItem = new RangerPolicyItem(); + + policyItem.setDelegateAdmin(grantRequest.getDelegateAdmin()); + policyItem.getUsers().addAll(grantRequest.getUsers()); + policyItem.getGroups().addAll(grantRequest.getGroups()); + + for(String accessType : grantRequest.getAccessTypes()) { + policyItem.getAccesses().add(new RangerPolicyItemAccess(accessType, Boolean.TRUE)); + } + + policy.getPolicyItems().add(policyItem); + + svcStore.createPolicy(policy); + } + }else{ + LOG.error("secureGrantAccess(" + serviceName + ", " + grantRequest + ") failed as User doesn't have permission to grant Policy"); + } + } catch(WebApplicationException excp) { + throw excp; + } catch(Throwable excp) { + LOG.error("secureGrantAccess(" + serviceName + ", " + grantRequest + ") failed", excp); + + throw restErrorUtil.createRESTException(excp.getMessage()); + } finally { + RangerPerfTracer.log(perf); + } + + ret.setStatusCode(RESTResponse.STATUS_SUCCESS); + } + if(LOG.isDebugEnabled()) { + LOG.debug("<== ServiceREST.secureGrantAccess(" + serviceName + ", " + grantRequest + "): " + ret); + } + return ret; + } @POST @Path("/services/revoke/{serviceName}") @@ -976,6 +1095,86 @@ public class ServiceREST { } @POST + @Path("/secure/services/revoke/{serviceName}") + @Produces({ "application/json", "application/xml" }) + public RESTResponse secureRevokeAccess(@PathParam("serviceName") String serviceName, GrantRevokeRequest revokeRequest, @Context HttpServletRequest request) throws Exception { + if(LOG.isDebugEnabled()) { + LOG.debug("==> ServiceREST.secureRevokeAccess(" + serviceName + ", " + revokeRequest + ")"); + } + RESTResponse ret = new RESTResponse(); + RangerPerfTracer perf = null; + if (serviceUtil.isValidateHttpsAuthentication(serviceName,request)) { + try { + if(RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_LOG, "ServiceREST.secureRevokeAccess(serviceName=" + serviceName + ")"); + } + String userName = revokeRequest.getGrantor(); + Set<String> userGroups = userMgr.getGroupsForUser(userName); + RangerAccessResource resource = new RangerAccessResourceImpl(revokeRequest.getResource()); + boolean isAdmin = hasAdminAccess(serviceName, userName, userGroups, resource); + boolean isAllowed = false; + boolean isKeyAdmin = bizUtil.isKeyAdmin(); + if(!isAdmin) { + throw restErrorUtil.createRESTException(HttpServletResponse.SC_UNAUTHORIZED, "", true); + } + + XXService xService = daoManager.getXXService().findByName(serviceName); + XXServiceDef xServiceDef = daoManager.getXXServiceDef().getById(xService.getType()); + RangerService rangerService = svcStore.getServiceByName(serviceName); + + if (StringUtils.equals(xServiceDef.getImplclassname(), EmbeddedServiceDefsUtil.KMS_IMPL_CLASS_NAME)) { + if (isKeyAdmin) { + isAllowed = true; + }else { + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Grant_Revoke); + } + }else{ + if (isAdmin) { + isAllowed = true; + } + else{ + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Grant_Revoke); + } + } + // New Code + if (isAllowed) { + RangerPolicy policy = getExactMatchPolicyForResource(serviceName, resource); + + if(policy != null) { + boolean policyUpdated = false; + policyUpdated = ServiceRESTUtil.processRevokeRequest(policy, revokeRequest); + + if(policyUpdated) { + svcStore.updatePolicy(policy); + } else { + LOG.error("processSecureRevokeRequest processing failed"); + throw new Exception("processSecureRevokeRequest processing failed"); + } + } else { + // nothing to revoke! + } + }else{ + LOG.error("secureRevokeAccess(" + serviceName + ", " + revokeRequest + ") failed as User doesn't have permission to revoke Policy"); + } + } catch(WebApplicationException excp) { + throw excp; + } catch(Throwable excp) { + LOG.error("secureRevokeAccess(" + serviceName + ", " + revokeRequest + ") failed", excp); + + throw restErrorUtil.createRESTException(excp.getMessage()); + } finally { + RangerPerfTracer.log(perf); + } + + ret.setStatusCode(RESTResponse.STATUS_SUCCESS); + } + if(LOG.isDebugEnabled()) { + LOG.debug("<== ServiceREST.secureRevokeAccess(" + serviceName + ", " + revokeRequest + "): " + ret); + } + return ret; + } + + @POST @Path("/policies") @Produces({ "application/json", "application/xml" }) public RangerPolicy createPolicy(RangerPolicy policy, @Context HttpServletRequest request) { @@ -1558,7 +1757,6 @@ public class ServiceREST { logMsg = excp.getMessage(); } finally { createPolicyDownloadAudit(serviceName, lastKnownVersion, pluginId, httpCode, request); - RangerPerfTracer.log(perf); } @@ -1574,6 +1772,86 @@ public class ServiceREST { return ret; } + + @GET + @Path("/secure/policies/download/{serviceName}") + @Produces({ "application/json", "application/xml" }) + public ServicePolicies getSecureServicePoliciesIfUpdated(@PathParam("serviceName") String serviceName,@QueryParam("lastKnownVersion") Long lastKnownVersion,@QueryParam("pluginId") String pluginId,@Context HttpServletRequest request) throws Exception { + if (LOG.isDebugEnabled()) { + LOG.debug("==> ServiceREST.getSecureServicePoliciesIfUpdated("+ serviceName + ", " + lastKnownVersion + ")"); + } + ServicePolicies ret = null; + int httpCode = HttpServletResponse.SC_OK; + String logMsg = null; + RangerPerfTracer perf = null; + boolean isAllowed = false; + boolean isAdmin = bizUtil.isAdmin(); + boolean isKeyAdmin = bizUtil.isKeyAdmin(); + + if (serviceUtil.isValidateHttpsAuthentication(serviceName, request)) { + if (lastKnownVersion == null) { + lastKnownVersion = Long.valueOf(-1); + } + try { + if (RangerPerfTracer.isPerfTraceEnabled(PERF_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_LOG,"ServiceREST.getSecureServicePoliciesIfUpdated(serviceName="+ serviceName + ",lastKnownVersion="+ lastKnownVersion + ")"); + } + XXService xService = daoManager.getXXService().findByName(serviceName); + XXServiceDef xServiceDef = daoManager.getXXServiceDef().getById(xService.getType()); + RangerService rangerService = svcStore.getServiceByName(serviceName); + + if (StringUtils.equals(xServiceDef.getImplclassname(), EmbeddedServiceDefsUtil.KMS_IMPL_CLASS_NAME)) { + if (isKeyAdmin) { + isAllowed = true; + }else { + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Download); + if(!isAllowed){ + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Grant_Revoke); + } + } + }else{ + if (isAdmin) { + isAllowed = true; + } + else{ + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Download); + if(!isAllowed){ + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Grant_Revoke); + } + } + } + if (isAllowed) { + ret = svcStore.getServicePoliciesIfUpdated(serviceName,lastKnownVersion); + if (ret == null) { + httpCode = HttpServletResponse.SC_NOT_MODIFIED; + logMsg = "No change since last update"; + } else { + httpCode = HttpServletResponse.SC_OK; + logMsg = "Returning " + (ret.getPolicies() != null ? ret.getPolicies().size() : 0) + " policies. Policy version=" + ret.getPolicyVersion(); + } + } else { + LOG.error("getSecureServicePoliciesIfUpdated(" + serviceName + ", " + lastKnownVersion + ") failed as User doesn't have permission to download Policy"); + httpCode = HttpServletResponse.SC_UNAUTHORIZED; + logMsg = "User doesn't have permission to download policy"; + } + } catch (Throwable excp) { + LOG.error("getSecureServicePoliciesIfUpdated(" + serviceName + ", " + lastKnownVersion + ") failed", excp); + httpCode = HttpServletResponse.SC_BAD_REQUEST; + logMsg = excp.getMessage(); + } finally { + createPolicyDownloadAudit(serviceName, lastKnownVersion, pluginId, httpCode, request); + RangerPerfTracer.log(perf); + } + if (httpCode != HttpServletResponse.SC_OK) { + boolean logError = httpCode != HttpServletResponse.SC_NOT_MODIFIED; + throw restErrorUtil.createRESTException(httpCode, logMsg, logError); + } + } + if (LOG.isDebugEnabled()) { + LOG.debug("<== ServiceREST.getSecureServicePoliciesIfUpdated(" + serviceName + ", " + lastKnownVersion + "): count=" + ((ret == null || ret.getPolicies() == null) ? 0 : ret.getPolicies().size())); + } + return ret; + } private void createPolicyDownloadAudit(String serviceName, Long lastKnownVersion, String pluginId, int httpRespCode, HttpServletRequest request) { try {
http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/java/org/apache/ranger/rest/TagREST.java ---------------------------------------------------------------------- diff --git a/security-admin/src/main/java/org/apache/ranger/rest/TagREST.java b/security-admin/src/main/java/org/apache/ranger/rest/TagREST.java index c69ceed..be70cfe 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/TagREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/TagREST.java @@ -20,15 +20,22 @@ package org.apache.ranger.rest; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.ranger.biz.RangerBizUtil; import org.apache.ranger.biz.ServiceDBStore; import org.apache.ranger.biz.TagDBStore; import org.apache.ranger.common.RESTErrorUtil; +import org.apache.ranger.db.RangerDaoManager; +import org.apache.ranger.entity.XXService; +import org.apache.ranger.entity.XXServiceDef; +import org.apache.ranger.plugin.model.RangerService; import org.apache.ranger.plugin.model.RangerServiceResource; import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerTagResourceMap; import org.apache.ranger.plugin.model.RangerTagDef; +import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.store.TagStore; import org.apache.ranger.plugin.store.TagValidator; import org.apache.ranger.plugin.util.SearchFilter; @@ -53,6 +60,7 @@ import java.util.List; public class TagREST { private static final Log LOG = LogFactory.getLog(TagREST.class); + private static final String Allowed_User_List_For_Tag_Download = "tag.download.auth.users"; @Autowired RESTErrorUtil restErrorUtil; @@ -62,6 +70,12 @@ public class TagREST { @Autowired TagDBStore tagStore; + + @Autowired + RangerDaoManager daoManager; + + @Autowired + RangerBizUtil bizUtil; TagValidator validator; @@ -1109,4 +1123,70 @@ public class TagREST { return ret; } + + @GET + @Path(TagRESTConstants.TAGS_SECURE_DOWNLOAD + "{serviceName}") + @Produces({ "application/json", "application/xml" }) + public ServiceTags getSecureServiceTagsIfUpdated(@PathParam("serviceName") String serviceName, + @QueryParam(TagRESTConstants.LAST_KNOWN_TAG_VERSION_PARAM) Long lastKnownVersion, @QueryParam("pluginId") String pluginId) { + if(LOG.isDebugEnabled()) { + LOG.debug("==> TagREST.getSecureServiceTagsIfUpdated(" + serviceName + ", " + lastKnownVersion + ", " + pluginId + ")"); + } + + ServiceTags ret = null; + int httpCode = HttpServletResponse.SC_OK; + String logMsg = null; + boolean isAllowed = false; + boolean isAdmin = bizUtil.isAdmin(); + boolean isKeyAdmin = bizUtil.isKeyAdmin(); + + try { + XXService xService = daoManager.getXXService().findByName(serviceName); + XXServiceDef xServiceDef = daoManager.getXXServiceDef().getById(xService.getType()); + RangerService rangerService = svcStore.getServiceByName(serviceName); + + if (StringUtils.equals(xServiceDef.getImplclassname(), EmbeddedServiceDefsUtil.KMS_IMPL_CLASS_NAME)) { + if (isKeyAdmin) { + isAllowed = true; + }else { + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Tag_Download); + } + }else{ + if (isAdmin) { + isAllowed = true; + }else{ + isAllowed = bizUtil.isUserAllowed(rangerService, Allowed_User_List_For_Tag_Download); + } + } + if (isAllowed) { + ret = tagStore.getServiceTagsIfUpdated(serviceName, lastKnownVersion); + + if(ret == null) { + httpCode = HttpServletResponse.SC_NOT_MODIFIED; + logMsg = "No change since last update"; + } else { + httpCode = HttpServletResponse.SC_OK; + logMsg = "Returning " + (ret.getTags() != null ? ret.getTags().size() : 0) + " tags. Tag version=" + ret.getTagVersion(); + } + }else{ + // do nothing + } + } catch(Exception excp) { + LOG.error("getSecureServiceTagsIfUpdated(" + serviceName + ") failed", excp); + + httpCode = HttpServletResponse.SC_BAD_REQUEST; + logMsg = excp.getMessage(); + } + + if(httpCode != HttpServletResponse.SC_OK) { + boolean logError = httpCode != HttpServletResponse.SC_NOT_MODIFIED; + throw restErrorUtil.createRESTException(httpCode, logMsg, logError); + } + + if(LOG.isDebugEnabled()) { + LOG.debug("<==> TagREST.getSecureServiceTagsIfUpdated(" + serviceName + ", " + lastKnownVersion + ", " + pluginId + ")"); + } + + return ret; + } } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/java/org/apache/ranger/rest/TagRESTConstants.java ---------------------------------------------------------------------- diff --git a/security-admin/src/main/java/org/apache/ranger/rest/TagRESTConstants.java b/security-admin/src/main/java/org/apache/ranger/rest/TagRESTConstants.java index 919f814..7f836bc 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/TagRESTConstants.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/TagRESTConstants.java @@ -34,6 +34,7 @@ public class TagRESTConstants { static final String TAGTYPES_RESOURCE = "/types/"; static final String TAGTYPES_LOOKUP_RESOURCE = "/types/lookup/"; static final String TAGS_DOWNLOAD = "/download/"; + static final String TAGS_SECURE_DOWNLOAD = "/secure/download/"; public static final String SERVICE_NAME_PARAM = "serviceName"; public static final String LAST_KNOWN_TAG_VERSION_PARAM = "lastKnownVersion"; http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/java/org/apache/ranger/security/context/RangerPreAuthSecurityHandler.java ---------------------------------------------------------------------- diff --git a/security-admin/src/main/java/org/apache/ranger/security/context/RangerPreAuthSecurityHandler.java b/security-admin/src/main/java/org/apache/ranger/security/context/RangerPreAuthSecurityHandler.java index daf732e..899d866 100644 --- a/security-admin/src/main/java/org/apache/ranger/security/context/RangerPreAuthSecurityHandler.java +++ b/security-admin/src/main/java/org/apache/ranger/security/context/RangerPreAuthSecurityHandler.java @@ -93,4 +93,13 @@ public class RangerPreAuthSecurityHandler { throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, "User is not allowed to access the API", true); } + public boolean isAPISpnegoAccessible(){ + UserSessionBase userSession = ContextUtil.getCurrentUserSession(); + if (userSession != null && userSession.isSpnegoEnabled()) { + return true; + }else if(userSession != null && userSession.isUserAdmin()){ + return true; + } + throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, "User is not allowed to access the API", true); + } } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKRBAuthenticationFilter.java ---------------------------------------------------------------------- diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKRBAuthenticationFilter.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKRBAuthenticationFilter.java new file mode 100644 index 0000000..c58c987 --- /dev/null +++ b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKRBAuthenticationFilter.java @@ -0,0 +1,569 @@ +/* + * 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.ranger.security.web.filter; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.EventListener; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.FilterRegistration; +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRegistration; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.SessionCookieConfig; +import javax.servlet.SessionTrackingMode; +import javax.servlet.FilterRegistration.Dynamic; +import javax.servlet.descriptor.JspConfigDescriptor; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.commons.collections.iterators.IteratorEnumeration; +import org.apache.ranger.biz.UserMgr; +import org.apache.ranger.common.PropertiesUtil; +import org.apache.ranger.security.handler.RangerAuthenticationProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.security.SecureClientLogin; +import org.springframework.security.web.authentication.WebAuthenticationDetails; + +public class RangerKRBAuthenticationFilter extends RangerKrbFilter { + Logger LOG = LoggerFactory.getLogger(RangerKRBAuthenticationFilter.class); + + @Autowired + UserMgr userMgr; + + static final String NAME_RULES = "hadoop.security.auth_to_local"; + static final String TOKEN_VALID = "ranger.admin.kerberos.token.valid.seconds"; + static final String COOKIE_DOMAIN = "ranger.admin.kerberos.cookie.domain"; + static final String COOKIE_PATH = "ranger.admin.kerberos.cookie.path"; + static final String PRINCIPAL = "ranger.spnego.kerberos.principal"; + static final String KEYTAB = "ranger.spnego.kerberos.keytab"; + static final String NAME_RULES_PARAM = "kerberos.name.rules"; + static final String TOKEN_VALID_PARAM = "token.validity"; + static final String COOKIE_DOMAIN_PARAM = "cookie.domain"; + static final String COOKIE_PATH_PARAM = "cookie.path"; + static final String PRINCIPAL_PARAM = "kerberos.principal"; + static final String KEYTAB_PARAM = "kerberos.keytab"; + static final String AUTH_TYPE = "type"; + static final String RANGER_AUTH_TYPE = "hadoop.security.authentication"; + static final String AUTH_COOKIE_NAME = "hadoop.auth"; + static final String HOST_NAME = "ranger.service.host"; + + private static final String KERBEROS_TYPE = "kerberos"; + + public RangerKRBAuthenticationFilter() { + try { + init(null); + } catch (ServletException e) { + LOG.error("Error while initializing Filter : "+e.getMessage()); + } + } + + @Override + public void init(FilterConfig conf) throws ServletException { + final FilterConfig globalConf = conf; + final Map<String, String> params = new HashMap<String, String>(); + params.put(AUTH_TYPE, PropertiesUtil.getProperty(RANGER_AUTH_TYPE, "simple")); + params.put(NAME_RULES_PARAM, PropertiesUtil.getProperty(NAME_RULES, "DEFAULT")); + params.put(TOKEN_VALID_PARAM, PropertiesUtil.getProperty(TOKEN_VALID,"30")); + params.put(COOKIE_DOMAIN_PARAM, PropertiesUtil.getProperty(COOKIE_DOMAIN, PropertiesUtil.getProperty(HOST_NAME, "localhost"))); + params.put(COOKIE_PATH_PARAM, PropertiesUtil.getProperty(COOKIE_PATH, "/")); + try { + params.put(PRINCIPAL_PARAM, SecureClientLogin.getPrincipal(PropertiesUtil.getProperty(PRINCIPAL,""), PropertiesUtil.getProperty(HOST_NAME))); + } catch (IOException ignored) { + // do nothing + } + params.put(KEYTAB_PARAM, PropertiesUtil.getProperty(KEYTAB,"")); + + FilterConfig myConf = new FilterConfig() { + @Override + public ServletContext getServletContext() { + if (globalConf != null) { + return globalConf.getServletContext(); + } else { + return noContext; + } + } + + @SuppressWarnings("unchecked") + @Override + public Enumeration<String> getInitParameterNames() { + return new IteratorEnumeration(params.keySet().iterator()); + } + + @Override + public String getInitParameter(String param) { + return params.get(param); + } + + @Override + public String getFilterName() { + return "KerberosFilter"; + } + }; + super.init(myConf); + } + + @Override + protected void doFilter(FilterChain filterChain, + HttpServletRequest request, HttpServletResponse response) + throws IOException, ServletException { + String authType = PropertiesUtil.getProperty(RANGER_AUTH_TYPE); + String userName = null; + boolean checkCookie = response.containsHeader("Set-Cookie"); + if(checkCookie){ + Collection<String> authUserName = response.getHeaders("Set-Cookie"); + if(authUserName != null){ + Iterator<String> i = authUserName.iterator(); + while(i.hasNext()){ + String cookie = i.next(); + if(!StringUtils.isEmpty(cookie)){ + if(cookie.toLowerCase().startsWith(AUTH_COOKIE_NAME.toLowerCase()) && cookie.contains("u=")){ + String[] split = cookie.split(";"); + if(split != null){ + for(String s : split){ + if(!StringUtils.isEmpty(s) && s.toLowerCase().startsWith(AUTH_COOKIE_NAME.toLowerCase())){ + int ustr = s.indexOf("u="); + if(ustr != -1){ + int andStr = s.indexOf("&", ustr); + if(andStr != -1){ + try{ + userName = s.substring(ustr+2, andStr); + }catch(Exception e){ + userName = null; + } + } + } + } + } + } + } + } + } + } + } + if((isSpnegoEnable(authType) && (!StringUtils.isEmpty(userName)))){ + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + if(existingAuth == null || !existingAuth.isAuthenticated()){ + //--------------------------- To Create Ranger Session -------------------------------------- + String rangerLdapDefaultRole = PropertiesUtil.getProperty("ranger.ldap.default.role", "ROLE_USER"); + //if we get the userName from the token then log into ranger using the same user + final List<GrantedAuthority> grantedAuths = new ArrayList<>(); + grantedAuths.add(new SimpleGrantedAuthority(rangerLdapDefaultRole)); + final UserDetails principal = new User(userName, "",grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); + WebAuthenticationDetails webDetails = new WebAuthenticationDetails(request); + ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); + RangerAuthenticationProvider authenticationProvider = new RangerAuthenticationProvider(); + Authentication authentication = authenticationProvider.authenticate(finalAuthentication); + authentication = getGrantedAuthority(authentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + request.setAttribute("spnegoEnabled", true); + LOG.info("Logged into Ranger as = "+userName); + filterChain.doFilter(request, response); + }else{ + try{ + super.doFilter(filterChain, request, response); + }catch(Exception e){ + LOG.error("Error RangerKRBAuthenticationFilter : "+e.getMessage()); + } + } + }else{ + filterChain.doFilter(request, response); + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, + FilterChain filterChain) throws IOException, ServletException { + String authtype = PropertiesUtil.getProperty(RANGER_AUTH_TYPE); + HttpServletRequest httpRequest = (HttpServletRequest)request; + if(isSpnegoEnable(authtype)){ + Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); + String userName = null; + Cookie[] cookie = httpRequest.getCookies(); + if(cookie != null){ + for(Cookie c : cookie){ + String cname = c.getName(); + if(cname != null && cname.equalsIgnoreCase("u")) + { + int ustr = cname.indexOf("u="); + if(ustr != -1){ + int andStr = cname.indexOf("&", ustr); + if(andStr != -1){ + userName = cname.substring(ustr+2, andStr); + } + } + }else if(cname != null && cname.equalsIgnoreCase(AUTH_COOKIE_NAME)){ + int ustr = cname.indexOf("u="); + if(ustr != -1){ + int andStr = cname.indexOf("&", ustr); + if(andStr != -1){ + userName = cname.substring(ustr+2, andStr); + } + } + } + } + } + if((existingAuth == null || !existingAuth.isAuthenticated()) && (!StringUtils.isEmpty(userName))){ + //--------------------------- To Create Ranger Session -------------------------------------- + String rangerLdapDefaultRole = PropertiesUtil.getProperty("ranger.ldap.default.role", "ROLE_USER"); + //if we get the userName from the token then log into ranger using the same user + final List<GrantedAuthority> grantedAuths = new ArrayList<>(); + grantedAuths.add(new SimpleGrantedAuthority(rangerLdapDefaultRole)); + final UserDetails principal = new User(userName, "",grantedAuths); + final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths); + WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest); + ((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails); + RangerAuthenticationProvider authenticationProvider = new RangerAuthenticationProvider(); + Authentication authentication = authenticationProvider.authenticate(finalAuthentication); + authentication = getGrantedAuthority(authentication); + SecurityContextHolder.getContext().setAuthentication(authentication); + request.setAttribute("spnegoEnabled", true); + LOG.info("Logged into Ranger as = "+userName); + }else{ + try{ + super.doFilter(request, response, filterChain); + }catch(Exception e){ + LOG.error("Error RangerKRBAuthenticationFilter : "+e.getMessage()); + } + } + }else{ + filterChain.doFilter(request, response); + } + } + + private boolean isSpnegoEnable(String authType){ + String principal = PropertiesUtil.getProperty(PRINCIPAL); + String keytabPath = PropertiesUtil.getProperty(KEYTAB); + return ((!StringUtils.isEmpty(authType)) && authType.equalsIgnoreCase(KERBEROS_TYPE) && SecureClientLogin.isKerberosCredentialExists(principal, keytabPath)); + } + + private Authentication getGrantedAuthority(Authentication authentication) { + UsernamePasswordAuthenticationToken result=null; + if(authentication!=null && authentication.isAuthenticated()){ + final List<GrantedAuthority> grantedAuths=getAuthorities(authentication.getName().toString()); + final UserDetails userDetails = new User(authentication.getName().toString(), authentication.getCredentials().toString(),grantedAuths); + result = new UsernamePasswordAuthenticationToken(userDetails,authentication.getCredentials(),grantedAuths); + result.setDetails(authentication.getDetails()); + return result; + } + return authentication; + } + + private List<GrantedAuthority> getAuthorities(String username) { + Collection<String> roleList=userMgr.getRolesByLoginId(username); + final List<GrantedAuthority> grantedAuths = new ArrayList<>(); + for(String role:roleList){ + grantedAuths.add(new SimpleGrantedAuthority(role)); + } + return grantedAuths; + } + + protected static ServletContext noContext = new ServletContext() { + + @Override + public void setSessionTrackingModes( + Set<SessionTrackingMode> sessionTrackingModes) { + } + + @Override + public boolean setInitParameter(String name, String value) { + return false; + } + + @Override + public void setAttribute(String name, Object object) { + } + + @Override + public void removeAttribute(String name) { + } + + @Override + public void log(String message, Throwable throwable) { + } + + @Override + public void log(Exception exception, String msg) { + } + + @Override + public void log(String msg) { + } + + @Override + public String getVirtualServerName() { + return null; + } + + @Override + public SessionCookieConfig getSessionCookieConfig() { + return null; + } + + @Override + public Enumeration<Servlet> getServlets() { + return null; + } + + @Override + public Map<String, ? extends ServletRegistration> getServletRegistrations() { + return null; + } + + @Override + public ServletRegistration getServletRegistration(String servletName) { + return null; + } + + @Override + public Enumeration<String> getServletNames() { + return null; + } + + @Override + public String getServletContextName() { + return null; + } + + @Override + public Servlet getServlet(String name) throws ServletException { + return null; + } + + @Override + public String getServerInfo() { + return null; + } + + @Override + public Set<String> getResourcePaths(String path) { + return null; + } + + @Override + public InputStream getResourceAsStream(String path) { + return null; + } + + @Override + public URL getResource(String path) throws MalformedURLException { + return null; + } + + @Override + public RequestDispatcher getRequestDispatcher(String path) { + return null; + } + + @Override + public String getRealPath(String path) { + return null; + } + + @Override + public RequestDispatcher getNamedDispatcher(String name) { + return null; + } + + @Override + public int getMinorVersion() { + return 0; + } + + @Override + public String getMimeType(String file) { + return null; + } + + @Override + public int getMajorVersion() { + return 0; + } + + @Override + public JspConfigDescriptor getJspConfigDescriptor() { + return null; + } + + @Override + public Enumeration<String> getInitParameterNames() { + return null; + } + + @Override + public String getInitParameter(String name) { + return null; + } + + @Override + public Map<String, ? extends FilterRegistration> getFilterRegistrations() { + return null; + } + + @Override + public FilterRegistration getFilterRegistration(String filterName) { + return null; + } + + @Override + public Set<SessionTrackingMode> getEffectiveSessionTrackingModes() { + return null; + } + + @Override + public int getEffectiveMinorVersion() { + return 0; + } + + @Override + public int getEffectiveMajorVersion() { + return 0; + } + + @Override + public Set<SessionTrackingMode> getDefaultSessionTrackingModes() { + return null; + } + + @Override + public String getContextPath() { + return null; + } + + @Override + public ServletContext getContext(String uripath) { + return null; + } + + @Override + public ClassLoader getClassLoader() { + return null; + } + + @Override + public Enumeration<String> getAttributeNames() { + return null; + } + + @Override + public Object getAttribute(String name) { + return null; + } + + @Override + public void declareRoles(String... roleNames) { + } + + @Override + public <T extends Servlet> T createServlet(Class<T> clazz) + throws ServletException { + return null; + } + + @Override + public <T extends EventListener> T createListener(Class<T> clazz) + throws ServletException { + return null; + } + + @Override + public <T extends Filter> T createFilter(Class<T> clazz) + throws ServletException { + return null; + } + + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet( + String servletName, Class<? extends Servlet> servletClass) { + return null; + } + + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet( + String servletName, Servlet servlet) { + return null; + } + + @Override + public javax.servlet.ServletRegistration.Dynamic addServlet( + String servletName, String className) { + return null; + } + + @Override + public void addListener(Class<? extends EventListener> listenerClass) { + } + + @Override + public <T extends EventListener> void addListener(T t) { + } + + @Override + public void addListener(String className) { + } + + @Override + public Dynamic addFilter(String filterName, + Class<? extends Filter> filterClass) { + return null; + } + + @Override + public Dynamic addFilter(String filterName, Filter filter) { + return null; + } + + @Override + public Dynamic addFilter(String filterName, String className) { + return null; + } + }; + +} http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKrbFilter.java ---------------------------------------------------------------------- diff --git a/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKrbFilter.java b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKrbFilter.java new file mode 100644 index 0000000..88ab020 --- /dev/null +++ b/security-admin/src/main/java/org/apache/ranger/security/web/filter/RangerKrbFilter.java @@ -0,0 +1,576 @@ +/** + * Licensed 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. See accompanying LICENSE file. + */ +package org.apache.ranger.security.web.filter; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.classification.InterfaceAudience; +import org.apache.hadoop.classification.InterfaceStability; +import org.apache.hadoop.security.authentication.client.AuthenticatedURL; +import org.apache.hadoop.security.authentication.client.AuthenticationException; +import org.apache.hadoop.security.authentication.client.KerberosAuthenticator; +import org.apache.hadoop.security.authentication.server.AuthenticationHandler; +import org.apache.hadoop.security.authentication.server.AuthenticationToken; +import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler; +import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler; +import org.apache.hadoop.security.authentication.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.security.Principal; +import java.text.SimpleDateFormat; +import java.util.*; + [email protected] [email protected] +public class RangerKrbFilter implements Filter { + + private static Logger LOG = LoggerFactory.getLogger(RangerKrbFilter.class); + + /** + * Constant for the property that specifies the configuration prefix. + */ + public static final String CONFIG_PREFIX = "config.prefix"; + + /** + * Constant for the property that specifies the authentication handler to use. + */ + public static final String AUTH_TYPE = "type"; + + /** + * Constant for the property that specifies the secret to use for signing the HTTP Cookies. + */ + public static final String SIGNATURE_SECRET = "signature.secret"; + + public static final String SIGNATURE_SECRET_FILE = SIGNATURE_SECRET + ".file"; + + /** + * Constant for the configuration property that indicates the validity of the generated token. + */ + public static final String AUTH_TOKEN_VALIDITY = "token.validity"; + + /** + * Constant for the configuration property that indicates the domain to use in the HTTP cookie. + */ + public static final String COOKIE_DOMAIN = "cookie.domain"; + + /** + * Constant for the configuration property that indicates the path to use in the HTTP cookie. + */ + public static final String COOKIE_PATH = "cookie.path"; + + /** + * Constant for the configuration property that indicates the name of the + * SignerSecretProvider class to use. + * Possible values are: "string", "random", "zookeeper", or a classname. + * If not specified, the "string" implementation will be used with + * SIGNATURE_SECRET; and if that's not specified, the "random" implementation + * will be used. + */ + public static final String SIGNER_SECRET_PROVIDER = + "signer.secret.provider"; + + /** + * Constant for the ServletContext attribute that can be used for providing a + * custom implementation of the SignerSecretProvider. Note that the class + * should already be initialized. If not specified, SIGNER_SECRET_PROVIDER + * will be used. + */ + public static final String SIGNER_SECRET_PROVIDER_ATTRIBUTE = + "signer.secret.provider.object"; + + private Properties config; + private Signer signer; + private SignerSecretProvider secretProvider; + private AuthenticationHandler authHandler; + private long validity; + private String cookieDomain; + private String cookiePath; + + /** + * <p>Initializes the authentication filter and signer secret provider.</p> + * It instantiates and initializes the specified {@link + * AuthenticationHandler}. + * + * @param filterConfig filter configuration. + * + * @throws ServletException thrown if the filter or the authentication handler could not be initialized properly. + */ + @Override + public void init(FilterConfig filterConfig) throws ServletException { + String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX); + configPrefix = (configPrefix != null) ? configPrefix + "." : ""; + config = getConfiguration(configPrefix, filterConfig); + String authHandlerName = config.getProperty(AUTH_TYPE, null); + String authHandlerClassName; + if (authHandlerName == null) { + throw new ServletException("Authentication type must be specified: " + + PseudoAuthenticationHandler.TYPE + "|" + + KerberosAuthenticationHandler.TYPE + "|<class>"); + } + if(StringUtils.equalsIgnoreCase(authHandlerName, PseudoAuthenticationHandler.TYPE)){ + authHandlerClassName = PseudoAuthenticationHandler.class.getName(); + }else if(StringUtils.equalsIgnoreCase(authHandlerName, KerberosAuthenticationHandler.TYPE)){ + authHandlerClassName = KerberosAuthenticationHandler.class.getName(); + } else { + authHandlerClassName = authHandlerName; + } + + validity = Long.parseLong(config.getProperty(AUTH_TOKEN_VALIDITY, "36000")) + * 1000; //10 hours + initializeSecretProvider(filterConfig); + + initializeAuthHandler(authHandlerClassName, filterConfig); + + cookieDomain = config.getProperty(COOKIE_DOMAIN, null); + cookiePath = config.getProperty(COOKIE_PATH, null); + } + + protected void initializeAuthHandler(String authHandlerClassName, FilterConfig filterConfig) + throws ServletException { + try { + Class<?> klass = Thread.currentThread().getContextClassLoader().loadClass(authHandlerClassName); + authHandler = (AuthenticationHandler) klass.newInstance(); + authHandler.init(config); + } catch (ClassNotFoundException | InstantiationException | + IllegalAccessException ex) { + throw new ServletException(ex); + } + } + + protected void initializeSecretProvider(FilterConfig filterConfig) + throws ServletException { + secretProvider = (SignerSecretProvider) filterConfig.getServletContext(). + getAttribute(SIGNER_SECRET_PROVIDER_ATTRIBUTE); + if (secretProvider == null) { + // As tomcat cannot specify the provider object in the configuration. + // It'll go into this path + try { + secretProvider = constructSecretProvider( + filterConfig.getServletContext(), + config, false); + } catch (Exception ex) { + throw new ServletException(ex); + } + } + signer = new Signer(secretProvider); + } + + public static SignerSecretProvider constructSecretProvider( + ServletContext ctx, Properties config, + boolean disallowFallbackToRandomSecretProvider) throws Exception { + long validity = Long.parseLong(config.getProperty(AUTH_TOKEN_VALIDITY, + "36000")) * 1000; + + String name = config.getProperty(SIGNER_SECRET_PROVIDER); + if (StringUtils.isEmpty(name)) { + if (!disallowFallbackToRandomSecretProvider) { + name = "random"; + } else { + name = "file"; + } + } + + SignerSecretProvider provider; + if ("file".equals(name)) { + provider = new FileSignerSecretProvider(); + try { + provider.init(config, ctx, validity); + } catch (Exception e) { + if (!disallowFallbackToRandomSecretProvider) { + LOG.info("Unable to initialize FileSignerSecretProvider, " + + "falling back to use random secrets."); + provider = new RandomSignerSecretProvider(); + provider.init(config, ctx, validity); + } else { + throw e; + } + } + } else if ("random".equals(name)) { + provider = new RandomSignerSecretProvider(); + provider.init(config, ctx, validity); + } else if ("zookeeper".equals(name)) { + provider = new ZKSignerSecretProvider(); + provider.init(config, ctx, validity); + } else { + provider = (SignerSecretProvider) Thread.currentThread(). + getContextClassLoader().loadClass(name).newInstance(); + provider.init(config, ctx, validity); + } + return provider; + } + + /** + * Returns the configuration properties of the {@link RangerKrbFilter} + * without the prefix. The returned properties are the same that the + * {@link #getConfiguration(String, FilterConfig)} method returned. + * + * @return the configuration properties. + */ + protected Properties getConfiguration() { + return config; + } + + /** + * Returns the authentication handler being used. + * + * @return the authentication handler being used. + */ + protected AuthenticationHandler getAuthenticationHandler() { + return authHandler; + } + + /** + * Returns if a random secret is being used. + * + * @return if a random secret is being used. + */ + protected boolean isRandomSecret() { + return secretProvider != null && secretProvider.getClass() == RandomSignerSecretProvider.class; + } + + /** + * Returns if a custom implementation of a SignerSecretProvider is being used. + * + * @return if a custom implementation of a SignerSecretProvider is being used. + */ + protected boolean isCustomSignerSecretProvider() { + Class<?> clazz = secretProvider != null ? secretProvider.getClass() : null; + return clazz != FileSignerSecretProvider.class && clazz != + RandomSignerSecretProvider.class && clazz != ZKSignerSecretProvider + .class; + } + + /** + * Returns the validity time of the generated tokens. + * + * @return the validity time of the generated tokens, in seconds. + */ + protected long getValidity() { + return validity / 1000; + } + + /** + * Returns the cookie domain to use for the HTTP cookie. + * + * @return the cookie domain to use for the HTTP cookie. + */ + protected String getCookieDomain() { + return cookieDomain; + } + + /** + * Returns the cookie path to use for the HTTP cookie. + * + * @return the cookie path to use for the HTTP cookie. + */ + protected String getCookiePath() { + return cookiePath; + } + + /** + * Destroys the filter. + * <p> + * It invokes the {@link AuthenticationHandler#destroy()} method to release any resources it may hold. + */ + @Override + public void destroy() { + if (authHandler != null) { + authHandler.destroy(); + authHandler = null; + } + } + + /** + * Returns the filtered configuration (only properties starting with the specified prefix). The property keys + * are also trimmed from the prefix. The returned {@link Properties} object is used to initialized the + * {@link AuthenticationHandler}. + * <p> + * This method can be overriden by subclasses to obtain the configuration from other configuration source than + * the web.xml file. + * + * @param configPrefix configuration prefix to use for extracting configuration properties. + * @param filterConfig filter configuration object + * + * @return the configuration to be used with the {@link AuthenticationHandler} instance. + * + * @throws ServletException thrown if the configuration could not be created. + */ + protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { + Properties props = new Properties(); + if(filterConfig != null){ + Enumeration<?> names = filterConfig.getInitParameterNames(); + if(names != null){ + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + if (name != null && configPrefix != null && name.startsWith(configPrefix)) { + String value = filterConfig.getInitParameter(name); + props.put(name.substring(configPrefix.length()), value); + } + } + } + } + return props; + } + + /** + * Returns the full URL of the request including the query string. + * <p> + * Used as a convenience method for logging purposes. + * + * @param request the request object. + * + * @return the full URL of the request including the query string. + */ + protected String getRequestURL(HttpServletRequest request) { + StringBuffer sb = request.getRequestURL(); + if (request.getQueryString() != null) { + sb.append("?").append(request.getQueryString()); + } + return sb.toString(); + } + + /** + * Returns the {@link AuthenticationToken} for the request. + * <p> + * It looks at the received HTTP cookies and extracts the value of the {@link AuthenticatedURL#AUTH_COOKIE} + * if present. It verifies the signature and if correct it creates the {@link AuthenticationToken} and returns + * it. + * <p> + * If this method returns <code>null</code> the filter will invoke the configured {@link AuthenticationHandler} + * to perform user authentication. + * + * @param request request object. + * + * @return the Authentication token if the request is authenticated, <code>null</code> otherwise. + * + * @throws IOException thrown if an IO error occurred. + * @throws AuthenticationException thrown if the token is invalid or if it has expired. + */ + protected AuthenticationToken getToken(HttpServletRequest request) throws IOException, AuthenticationException { + AuthenticationToken token = null; + String tokenStr = null; + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + for (Cookie cookie : cookies) { + if (AuthenticatedURL.AUTH_COOKIE.equals(cookie.getName())) { + tokenStr = cookie.getValue(); + try { + tokenStr = signer.verifyAndExtract(tokenStr); + } catch (SignerException ex) { + throw new AuthenticationException(ex); + } + break; + } + } + } + if (tokenStr != null) { + token = AuthenticationToken.parse(tokenStr); + if(token != null){ + if (!token.getType().equals(authHandler.getType())) { + throw new AuthenticationException("Invalid AuthenticationToken type"); + } + if (token.isExpired()) { + throw new AuthenticationException("AuthenticationToken expired"); + } + } + } + return token; + } + + /** + * If the request has a valid authentication token it allows the request to continue to the target resource, + * otherwise it triggers an authentication sequence using the configured {@link AuthenticationHandler}. + * + * @param request the request object. + * @param response the response object. + * @param filterChain the filter chain object. + * + * @throws IOException thrown if an IO error occurred. + * @throws ServletException thrown if a processing error occurred. + */ + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) + throws IOException, ServletException { + boolean unauthorizedResponse = true; + int errCode = HttpServletResponse.SC_UNAUTHORIZED; + AuthenticationException authenticationEx = null; + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + boolean isHttps = "https".equals(httpRequest.getScheme()); + try { + boolean newToken = false; + AuthenticationToken token; + try { + token = getToken(httpRequest); + } + catch (AuthenticationException ex) { + ex.printStackTrace(); + LOG.warn("AuthenticationToken ignored: " + ex.getMessage()); + // will be sent back in a 401 unless filter authenticates + authenticationEx = ex; + token = null; + } + if (authHandler.managementOperation(token, httpRequest, httpResponse)) { + if (token == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest)); + } + token = authHandler.authenticate(httpRequest, httpResponse); + if (token != null && token.getExpires() != 0 && + token != AuthenticationToken.ANONYMOUS) { + token.setExpires(System.currentTimeMillis() + getValidity() * 1000); + } + newToken = true; + } + if (token != null) { + unauthorizedResponse = false; + if (LOG.isDebugEnabled()) { + LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName()); + } + final AuthenticationToken authToken = token; + httpRequest = new HttpServletRequestWrapper(httpRequest) { + + @Override + public String getAuthType() { + return authToken.getType(); + } + + @Override + public String getRemoteUser() { + return authToken.getUserName(); + } + + @Override + public Principal getUserPrincipal() { + return (authToken != AuthenticationToken.ANONYMOUS) ? authToken : null; + } + }; + if (newToken && !token.isExpired() && token != AuthenticationToken.ANONYMOUS) { + String signedToken = signer.sign(token.toString()); + createAuthCookie(httpResponse, signedToken, getCookieDomain(), + getCookiePath(), token.getExpires(), isHttps); + } + doFilter(filterChain, httpRequest, httpResponse); + } + } else { + unauthorizedResponse = false; + } + } catch (AuthenticationException ex) { + // exception from the filter itself is fatal + ex.printStackTrace(); + errCode = HttpServletResponse.SC_FORBIDDEN; + authenticationEx = ex; + LOG.warn("Authentication exception: " + ex.getMessage(), ex); + } + if (unauthorizedResponse) { + if (!httpResponse.isCommitted()) { + createAuthCookie(httpResponse, "", getCookieDomain(), + getCookiePath(), 0, isHttps); + // If response code is 401. Then WWW-Authenticate Header should be + // present.. reset to 403 if not found.. + if ((errCode == HttpServletResponse.SC_UNAUTHORIZED) + && (!httpResponse.containsHeader( + KerberosAuthenticator.WWW_AUTHENTICATE))) { + errCode = HttpServletResponse.SC_FORBIDDEN; + } + if (authenticationEx == null) { + boolean chk = true; + Collection<String> headerNames = httpResponse.getHeaderNames(); + for(String headerName : headerNames){ + String value = httpResponse.getHeader(headerName); + if(headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("JSESSIONID")){ + chk = false; + break; + } + } + String authHeader = httpRequest.getHeader("Authorization"); + if(authHeader == null && chk){ + filterChain.doFilter(request, response); + }else if(authHeader != null && authHeader.startsWith("Basic")){ + filterChain.doFilter(request, response); + } + } else { + httpResponse.sendError(errCode, authenticationEx.getMessage()); + } + } + } + } + + /** + * Delegates call to the servlet filter chain. Sub-classes my override this + * method to perform pre and post tasks. + */ + protected void doFilter(FilterChain filterChain, HttpServletRequest request, + HttpServletResponse response) throws IOException, ServletException { + filterChain.doFilter(request, response); + } + + /** + * Creates the Hadoop authentication HTTP cookie. + * + * @param token authentication token for the cookie. + * @param expires UNIX timestamp that indicates the expire date of the + * cookie. It has no effect if its value < 0. + * + * XXX the following code duplicate some logic in Jetty / Servlet API, + * because of the fact that Hadoop is stuck at servlet 2.5 and jetty 6 + * right now. + */ + public static void createAuthCookie(HttpServletResponse resp, String token, + String domain, String path, long expires, + boolean isSecure) { + StringBuilder sb = new StringBuilder(AuthenticatedURL.AUTH_COOKIE) + .append("="); + if (token != null && token.length() > 0) { + sb.append("\"").append(token).append("\""); + } + + if (path != null) { + sb.append("; Path=").append(path); + } + + if (domain != null) { + sb.append("; Domain=").append(domain); + } + + if (expires >= 0) { + Date date = new Date(expires); + SimpleDateFormat df = new SimpleDateFormat("EEE, " + + "dd-MMM-yyyy HH:mm:ss zzz"); + df.setTimeZone(TimeZone.getTimeZone("GMT")); + sb.append("; Expires=").append(df.format(date)); + } + + if (isSecure) { + sb.append("; Secure"); + } + + sb.append("; HttpOnly"); + resp.addHeader("Set-Cookie", sb.toString()); + } +} + http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml ---------------------------------------------------------------------- diff --git a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml index b2ec9de..7d748c5 100644 --- a/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml +++ b/security-admin/src/main/resources/conf.dist/ranger-admin-site.xml @@ -233,4 +233,42 @@ <value>Mozilla,chrome</value> </property> <!-- SSO Properties Ends--> + <!-- Kerberos Properties starts--> + <property> + <name>ranger.admin.kerberos.token.valid.seconds</name> + <value>30</value> + </property> + <property> + <name>ranger.admin.kerberos.cookie.domain</name> + <value></value> + </property> + <property> + <name>ranger.admin.kerberos.cookie.path</name> + <value>/</value> + </property> + <property> + <name>ranger.admin.kerberos.principal</name> + <value>rangeradmin/_HOST@REALM</value> + </property> + <property> + <name>ranger.admin.kerberos.keytab</name> + <value></value> + </property> + <property> + <name>ranger.spnego.kerberos.principal</name> + <value>HTTP/_HOST@REALM</value> + </property> + <property> + <name>ranger.spnego.kerberos.keytab</name> + <value></value> + </property> + <property> + <name>ranger.lookup.kerberos.principal</name> + <value>rangerlookup/_HOST@REALM</value> + </property> + <property> + <name>ranger.lookup.kerberos.keytab</name> + <value></value> + </property> + <!-- Kerberos Properties ENDs--> </configuration> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/resources/conf.dist/security-applicationContext.xml ---------------------------------------------------------------------- diff --git a/security-admin/src/main/resources/conf.dist/security-applicationContext.xml b/security-admin/src/main/resources/conf.dist/security-applicationContext.xml index 2f711ad..6becfcd 100644 --- a/security-admin/src/main/resources/conf.dist/security-applicationContext.xml +++ b/security-admin/src/main/resources/conf.dist/security-applicationContext.xml @@ -36,6 +36,7 @@ http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd"> <security:http pattern="/scripts/**" security="none" /> <security:http pattern="/libs/**" security="none" /> <security:http pattern="/images/**" security="none" /> + <security:http pattern="/templates/**" security="none" /> <security:http pattern="/service/assets/policyList/*" security="none"/> <security:http pattern="/service/assets/resources/grant" security="none"/> <security:http pattern="/service/assets/resources/revoke" security="none"/> @@ -48,7 +49,7 @@ http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd"> <security:session-management session-fixation-protection="newSession" /> <intercept-url pattern="/**" access="isAuthenticated()"/> <custom-filter ref="ssoAuthenticationFilter" after="BASIC_AUTH_FILTER" /> - + <security:custom-filter ref="krbAuthenticationFilter" after="SERVLET_API_SUPPORT_FILTER" /> <security:custom-filter position="FORM_LOGIN_FILTER" ref="customUsernamePasswordAuthenticationFilter"/> <security:custom-filter position="LAST" ref="userContextFormationFilter"/> @@ -89,7 +90,10 @@ http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd"> <beans:bean id="customLogoutSuccessHandler" class="org.apache.ranger.security.web.authentication.CustomLogoutSuccessHandler"> </beans:bean> - <beans:bean id="ssoAuthenticationFilter" class="org.apache.ranger.security.web.filter.RangerSSOAuthenticationFilter"> + <beans:bean id="krbAuthenticationFilter" class="org.apache.ranger.security.web.filter.RangerKRBAuthenticationFilter"> + </beans:bean> + + <beans:bean id="ssoAuthenticationFilter" class="org.apache.ranger.security.web.filter.RangerSSOAuthenticationFilter"> </beans:bean> <beans:bean id="userContextFormationFilter" class="org.apache.ranger.security.web.filter.RangerSecurityContextFormationFilter"/> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/resources/resourcenamemap.properties ---------------------------------------------------------------------- diff --git a/security-admin/src/main/resources/resourcenamemap.properties b/security-admin/src/main/resources/resourcenamemap.properties index 201c0fa..16bf704 100644 --- a/security-admin/src/main/resources/resourcenamemap.properties +++ b/security-admin/src/main/resources/resourcenamemap.properties @@ -16,3 +16,5 @@ username=xalogin.xml keytabfile=xalogin.xml password=xalogin.xml +lookupprincipal=xalogin.xml +lookupkeytab=xalogin.xml \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/main/webapp/META-INF/applicationContext.xml ---------------------------------------------------------------------- diff --git a/security-admin/src/main/webapp/META-INF/applicationContext.xml b/security-admin/src/main/webapp/META-INF/applicationContext.xml index c1a9387..17f35a2 100644 --- a/security-admin/src/main/webapp/META-INF/applicationContext.xml +++ b/security-admin/src/main/webapp/META-INF/applicationContext.xml @@ -91,8 +91,9 @@ http://www.springframework.org/schema/util/spring-util.xsd"> <!-- <value>classpath:xa_system.properties</value> --> <!-- <value>classpath:xa_custom.properties</value> --> <!-- <value>classpath:xa_ldap.properties</value> --> + <value>classpath:core-site.xml</value> <value>classpath:ranger-admin-default-site.xml</value> - <value>classpath:ranger-admin-site.xml</value> + <value>classpath:ranger-admin-site.xml</value> </list> </property> <property name="propertiesPersister" ref="xmlPropertyConfigurer" /> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java ---------------------------------------------------------------------- diff --git a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java index 083c777..48acaa0 100644 --- a/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java +++ b/security-admin/src/test/java/org/apache/ranger/rest/TestServiceREST.java @@ -499,7 +499,6 @@ public class TestServiceREST { dbRangerService.getUpdatedBy()); Mockito.verify(validatorFactory).getServiceValidator(svcStore); - Mockito.verify(daoManager).getXXServiceDef(); Mockito.verify(svcStore).createService(rangerService); } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/src/main/assembly/admin-web.xml ---------------------------------------------------------------------- diff --git a/src/main/assembly/admin-web.xml b/src/main/assembly/admin-web.xml index ca68ac6..ea24bf4 100644 --- a/src/main/assembly/admin-web.xml +++ b/src/main/assembly/admin-web.xml @@ -200,6 +200,12 @@ <includes> <include>org.apache.tomcat.embed:tomcat-embed*</include> <include>org.eclipse.jdt.core.compiler:ecj:jar:P20140317-1600</include> + <include>log4j:log4j</include> + <include>org.apache.hadoop:hadoop-auth:jar:${hadoop-common.version}</include> + <include>org.apache.ranger:ranger-plugins-common</include> + <include>org.slf4j:slf4j-api</include> + <include>org.apache.hadoop:hadoop-common</include> + <include>commons-logging:commons-logging</include> </includes> <unpack>false</unpack> </dependencySet> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/src/main/assembly/usersync.xml ---------------------------------------------------------------------- diff --git a/src/main/assembly/usersync.xml b/src/main/assembly/usersync.xml index b032a1d..e60aae0 100644 --- a/src/main/assembly/usersync.xml +++ b/src/main/assembly/usersync.xml @@ -53,6 +53,7 @@ <include>org.apache.htrace:htrace-core</include> <include>commons-httpclient:commons-httpclient</include> <include>commons-codec:commons-codec</include> + <include>org.apache.ranger:ranger-plugins-common</include> </includes> <unpack>false</unpack> </dependencySet> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormClient.java ---------------------------------------------------------------------- diff --git a/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormClient.java b/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormClient.java index 74170fe..432ed78 100644 --- a/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormClient.java +++ b/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormClient.java @@ -33,7 +33,9 @@ import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang.StringUtils; import org.apache.hadoop.security.KrbPasswordSaverLoginModule; +import org.apache.hadoop.security.SecureClientLogin; import org.apache.hadoop.security.authentication.util.KerberosUtil; import org.apache.log4j.Logger; import org.apache.ranger.plugin.client.BaseClient; @@ -62,12 +64,18 @@ public class StormClient { String stormUIUrl; String userName; String password; + String lookupPrincipal; + String lookupKeytab; + String nameRules; - public StormClient(String aStormUIUrl, String aUserName, String aPassword) { + public StormClient(String aStormUIUrl, String aUserName, String aPassword, String lookupPrincipal, String lookupKeytab, String nameRules) { this.stormUIUrl = aStormUIUrl; this.userName = aUserName ; this.password = aPassword; + this.lookupPrincipal = lookupPrincipal; + this.lookupKeytab = lookupKeytab; + this.nameRules = nameRules; if (LOG.isDebugEnabled()) { LOG.debug("Storm Client is build with url [" + aStormUIUrl + "] user: [" + aUserName + "], password: [" + "" + "]"); @@ -173,7 +181,7 @@ public class StormClient { } ; try { - ret = executeUnderKerberos(this.userName, this.password, topologyListGetter) ; + ret = executeUnderKerberos(this.userName, this.password, this.lookupPrincipal, this.lookupKeytab, this.nameRules, topologyListGetter) ; } catch (IOException e) { LOG.error("Unable to get Topology list from [" + stormUIUrl + "]", e) ; } @@ -181,7 +189,7 @@ public class StormClient { return ret; } - public static <T> T executeUnderKerberos(String userName, String password, + public static <T> T executeUnderKerberos(String userName, String password, String lookupPrincipal, String lookupKeytab, String nameRules, PrivilegedAction<T> action) throws IOException { final String errMsg = errMessage; @@ -247,20 +255,28 @@ public class StormClient { LoginContext loginContext = null; try { - subject = new Subject(); - LOG.debug("executeUnderKerberos():user=" + userName + ",pass="); - LOG.debug("executeUnderKerberos():Creating config.."); - MySecureClientLoginConfiguration loginConf = new MySecureClientLoginConfiguration( - userName, password); - LOG.debug("executeUnderKerberos():Creating Context.."); - loginContext = new LoginContext("hadoop-keytab-kerberos", subject, - null, loginConf); - - LOG.debug("executeUnderKerberos():Logging in.."); - loginContext.login(); - - Subject loginSubj = loginContext.getSubject(); - + Subject loginSubj = null; + if(!StringUtils.isEmpty(lookupPrincipal) && !StringUtils.isEmpty(lookupKeytab)){ + LOG.info("Init Lookup Login: security enabled, using lookupPrincipal/lookupKeytab"); + if(StringUtils.isEmpty(nameRules)){ + nameRules = "DEFAULT"; + } + loginSubj = SecureClientLogin.loginUserFromKeytab(lookupPrincipal, lookupKeytab, nameRules); + }else{ + subject = new Subject(); + LOG.debug("executeUnderKerberos():user=" + userName + ",pass="); + LOG.debug("executeUnderKerberos():Creating config.."); + MySecureClientLoginConfiguration loginConf = new MySecureClientLoginConfiguration( + userName, password); + LOG.debug("executeUnderKerberos():Creating Context.."); + loginContext = new LoginContext("hadoop-keytab-kerberos", subject, + null, loginConf); + + LOG.debug("executeUnderKerberos():Logging in.."); + loginContext.login(); + LOG.info("Init Login: using username/password"); + loginSubj = loginContext.getSubject(); + } if (loginSubj != null) { ret = Subject.doAs(loginSubj, action); } @@ -344,8 +360,11 @@ public class StormClient { String stormUrl = configs.get("nimbus.url"); String stormAdminUser = configs.get("username"); String stormAdminPassword = configs.get("password"); + String lookupPrincipal = configs.get("lookupprincipal"); + String lookupKeytab = configs.get("lookupkeytab"); + String nameRules = configs.get("namerules"); stormClient = new StormClient(stormUrl, stormAdminUser, - stormAdminPassword); + stormAdminPassword, lookupPrincipal, lookupKeytab, nameRules); } return stormClient; } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormConnectionMgr.java ---------------------------------------------------------------------- diff --git a/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormConnectionMgr.java b/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormConnectionMgr.java index 5d008e7..55e52e2 100644 --- a/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormConnectionMgr.java +++ b/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormConnectionMgr.java @@ -19,6 +19,7 @@ package org.apache.ranger.services.storm.client; +import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; @@ -26,16 +27,18 @@ public class StormConnectionMgr { public static final Logger LOG = Logger.getLogger(StormConnectionMgr.class); - public static StormClient getStormClient(final String stormUIURL, String userName, String password) { + public static StormClient getStormClient(final String stormUIURL, String userName, String password, String lookupPrincipal, String lookupKeytab, String nameRules) { StormClient stormClient = null; if (stormUIURL == null || stormUIURL.isEmpty()) { LOG.error("Can not create StormClient: stormUIURL is empty"); - } else if (userName == null || userName.isEmpty()) { - LOG.error("Can not create StormClient: stormAdminUser is empty"); - } else if (password == null || password.isEmpty()) { - LOG.error("Can not create StormClient: stormAdminPassword is empty"); - } else { - stormClient = new StormClient(stormUIURL, userName, password); + } else if(StringUtils.isEmpty(lookupPrincipal) || StringUtils.isEmpty(lookupKeytab)){ + if (userName == null || userName.isEmpty()) { + LOG.error("Can not create StormClient: stormAdminUser is empty"); + } else if (password == null || password.isEmpty()) { + LOG.error("Can not create StormClient: stormAdminPassword is empty"); + } + }else { + stormClient = new StormClient(stormUIURL, userName, password, lookupPrincipal, lookupKeytab, nameRules); } return stormClient; } http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormResourceMgr.java ---------------------------------------------------------------------- diff --git a/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormResourceMgr.java b/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormResourceMgr.java index a16fce1..bf9fea3 100644 --- a/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormResourceMgr.java +++ b/storm-agent/src/main/java/org/apache/ranger/services/storm/client/StormResourceMgr.java @@ -75,14 +75,17 @@ public class StormResourceMgr { String url = configs.get("nimbus.url"); String username = configs.get("username"); String password = configs.get("password"); - resultList = getStormResources(url, username, password,StromTopologyName,StormTopologyList) ; + String lookupPrincipal = configs.get("lookupprincipal"); + String lookupKeytab = configs.get("lookupkeytab"); + String nameRules = configs.get("namerules"); + resultList = getStormResources(url, username, password,lookupPrincipal, lookupKeytab, nameRules, StromTopologyName,StormTopologyList) ; } return resultList ; } - public static List<String> getStormResources(String url, String username, String password,String topologyName, List<String> StormTopologyList) { + public static List<String> getStormResources(String url, String username, String password, String lookupPrincipal, String lookupKeytab, String nameRules, String topologyName, List<String> StormTopologyList) { List<String> topologyList = null; - final StormClient stormClient = StormConnectionMgr.getStormClient(url, username, password); + final StormClient stormClient = StormConnectionMgr.getStormClient(url, username, password, lookupPrincipal, lookupKeytab, nameRules); if (stormClient == null) { LOG.error("Storm Client is null"); return new ArrayList<String>(); http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/tagsync/conf/templates/installprop2xml.properties ---------------------------------------------------------------------- diff --git a/tagsync/conf/templates/installprop2xml.properties b/tagsync/conf/templates/installprop2xml.properties index a6840b0..8f0ea75 100644 --- a/tagsync/conf/templates/installprop2xml.properties +++ b/tagsync/conf/templates/installprop2xml.properties @@ -40,4 +40,7 @@ TAGSYNC_ATLAS_ZOOKEEPER_ENDPOINT = atlas.kafka.zookeeper.connect TAGSYNC_ATLAS_CONSUMER_GROUP = atlas.kafka.entities.group.id TAGSYNC_ATLAS_TO_RANGER_SERVICE_MAPPING = ranger.tagsync.atlas.to.service.mapping -TAGSYNC_SOURCE_ATLAS_CUSTOM_RESOURCE_MAPPERS = ranger.tagsync.source.atlas.custom.resource.mappers \ No newline at end of file +TAGSYNC_SOURCE_ATLAS_CUSTOM_RESOURCE_MAPPERS = ranger.tagsync.source.atlas.custom.resource.mappers + +tagsync_principal = ranger.tagsync.kerberos.principal +tagsync_keytab = ranger.tagsync.kerberos.keytab http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/tagsync/conf/templates/ranger-tagsync-template.xml ---------------------------------------------------------------------- diff --git a/tagsync/conf/templates/ranger-tagsync-template.xml b/tagsync/conf/templates/ranger-tagsync-template.xml index bad71bd..d82b6d1 100644 --- a/tagsync/conf/templates/ranger-tagsync-template.xml +++ b/tagsync/conf/templates/ranger-tagsync-template.xml @@ -70,4 +70,13 @@ <property> <name>ranger.tagsync.source.atlas.custom.resource.mappers</name> <value></value> - </property></configuration> + </property> + <property> + <name>ranger.tagsync.kerberos.keytab</name> + <value></value> + </property> + <property> + <name>ranger.tagsync.kerberos.principal</name> + <value></value> + </property> +</configuration> http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/tagsync/scripts/install.properties ---------------------------------------------------------------------- diff --git a/tagsync/scripts/install.properties b/tagsync/scripts/install.properties index b6665d1..695371d 100644 --- a/tagsync/scripts/install.properties +++ b/tagsync/scripts/install.properties @@ -34,6 +34,11 @@ logdir = log # URL for TagAdmin TAGADMIN_ENDPOINT = http://localhost:6080 +#Set to run in kerberos environment +tagsync_principal= +tagsync_keytab= +hadoop_conf=/etc/hadoop/conf + # SSL config file name for TagAdmin TAGADMIN_SSL_CONFIG_FILENAME = @@ -82,4 +87,4 @@ TAGSYNC_ATLAS_TO_RANGER_SERVICE_MAPPING= # RangerServiceResource structures are specified here. If there are no custom mappers, # then it can be left blank -TAGSYNC_SOURCE_ATLAS_CUSTOM_RESOURCE_MAPPERS= \ No newline at end of file +TAGSYNC_SOURCE_ATLAS_CUSTOM_RESOURCE_MAPPERS= http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/tagsync/scripts/ranger-tagsync-services.sh ---------------------------------------------------------------------- diff --git a/tagsync/scripts/ranger-tagsync-services.sh b/tagsync/scripts/ranger-tagsync-services.sh index add42ee..1235e86 100755 --- a/tagsync/scripts/ranger-tagsync-services.sh +++ b/tagsync/scripts/ranger-tagsync-services.sh @@ -59,7 +59,7 @@ if [ "${action}" == "START" ]; then chmod 777 $logdir fi - cp="${cdir}/conf:${cdir}/dist/*:${cdir}/lib/*" + cp="${cdir}/conf:${cdir}/dist/*:${cdir}/lib/*:${RANGER_TAGSYNC_HADOOP_CONF_DIR}/*" if [ -f $pidf ]; then PID=`cat $pidf` http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/8614032c/tagsync/scripts/setup.py ---------------------------------------------------------------------- diff --git a/tagsync/scripts/setup.py b/tagsync/scripts/setup.py index 59cb5c8..1abb1c8 100755 --- a/tagsync/scripts/setup.py +++ b/tagsync/scripts/setup.py @@ -80,6 +80,25 @@ TAG_SOURCE_ATLAS = 'atlas' TAG_SOURCE_ATLASREST = 'atlasrest' TAG_SOURCE_FILE = 'file' +hadoopConfFileName = 'core-site.xml' +ENV_HADOOP_CONF_FILE = "ranger-tagsync-env-hadoopconfdir.sh" +globalDict = {} + +RANGER_TAGSYNC_HOME = os.getenv("RANGER_TAGSYNC_HOME") +if RANGER_TAGSYNC_HOME is None: + RANGER_TAGSYNC_HOME = os.getcwd() + +def populate_global_dict(): + global globalDict + read_config_file = open(os.path.join(RANGER_TAGSYNC_HOME,'install.properties')) + for each_line in read_config_file.read().split('\n') : + if len(each_line) == 0 : continue + if re.search('=', each_line): + key , value = each_line.strip().split("=",1) + key = key.strip() + value = value.strip() + globalDict[key] = value + def archiveFile(originalFileName): archiveDir = dirname(originalFileName) archiveFileName = "." + basename(originalFileName) + "." + (strftime("%d%m%Y%H%M%S", localtime())) @@ -258,11 +277,32 @@ def initializeInitD(): os.remove(ubinScriptName) os.symlink(localScriptName,ubinScriptName) +def write_env_files(exp_var_name, log_path, file_name): + final_path = "{0}/{1}".format(confBaseDirName,file_name) + if not os.path.isfile(final_path): + print "Creating %s file" % file_name + f = open(final_path, "w") + f.write("export {0}={1}".format(exp_var_name,log_path)) + f.close() def main(): print "\nINFO: Installing ranger-tagsync .....\n" + populate_global_dict() + hadoop_conf = globalDict['hadoop_conf'] + + hadoop_conf_full_path = os.path.join(hadoop_conf, hadoopConfFileName) + tagsync_conf_full_path = os.path.join(tagsyncBaseDirFullName,confBaseDirName,hadoopConfFileName) + if not isfile(hadoop_conf_full_path): + print "WARN: core-site.xml file not found in provided hadoop conf path..." + f = open(tagsync_conf_full_path, "w") + f.write("<configuration></configuration>") + f.close() + else: + if os.path.islink(tagsync_conf_full_path): + os.remove(tagsync_conf_full_path) + dirList = [ rangerBaseDirName, tagsyncBaseDirFullName, confFolderName ] for dir in dirList: if (not os.path.isdir(dir)): @@ -389,6 +429,13 @@ def main(): os.chown(fn, ownerId, groupId) os.chmod(fn, 0755) + write_env_files("RANGER_TAGSYNC_HADOOP_CONF_DIR", hadoop_conf, ENV_HADOOP_CONF_FILE); + os.chown(os.path.join(confBaseDirName, ENV_HADOOP_CONF_FILE),ownerId,groupId) + os.chmod(os.path.join(confBaseDirName, ENV_HADOOP_CONF_FILE),0755) + + if isfile(hadoop_conf_full_path): + os.symlink(hadoop_conf_full_path, tagsync_conf_full_path) + print "\nINFO: Completed ranger-tagsync installation.....\n" main()
