blob: c994b02c26a1c2ea5952d93bf26961b898c3f3f4 [file] [log] [blame]
/*
* 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.authorization.solr.authorizer;
import java.io.IOException;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.security.auth.login.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.ranger.audit.provider.MiscUtil;
import org.apache.ranger.authorization.hadoop.config.RangerConfiguration;
import org.apache.ranger.plugin.audit.RangerMultiResourceAuditHandler;
import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
import org.apache.ranger.plugin.policyengine.RangerAccessResult;
import org.apache.ranger.plugin.service.RangerBasePlugin;
import org.apache.solr.security.AuthorizationContext.RequestType;
import org.apache.solr.security.AuthorizationPlugin;
import org.apache.solr.security.AuthorizationResponse;
import org.apache.solr.security.AuthorizationContext;
import org.apache.solr.security.AuthorizationContext.CollectionRequest;
public class RangerSolrAuthorizer implements AuthorizationPlugin {
private static final Log logger = LogFactory
.getLog(RangerSolrAuthorizer.class);
public static final String PROP_USE_PROXY_IP = "xasecure.solr.use_proxy_ip";
public static final String PROP_PROXY_IP_HEADER = "xasecure.solr.proxy_ip_header";
public static final String PROP_SOLR_APP_NAME = "xasecure.solr.app.name";
public static final String KEY_COLLECTION = "collection";
public static final String ACCESS_TYPE_CREATE = "create";
public static final String ACCESS_TYPE_UPDATE = "update";
public static final String ACCESS_TYPE_QUERY = "query";
public static final String ACCESS_TYPE_OTHERS = "others";
public static final String ACCESS_TYPE_ADMIN = "solr_admin";
private static volatile RangerBasePlugin solrPlugin = null;
boolean useProxyIP = false;
String proxyIPHeader = "HTTP_X_FORWARDED_FOR";
String solrAppName = "Client";
public RangerSolrAuthorizer() {
logger.info("RangerSolrAuthorizer()");
}
/*
* (non-Javadoc)
*
* @see org.apache.solr.security.SolrAuthorizationPlugin#init(java.util.Map)
*/
@Override
public void init(Map<String, Object> initInfo) {
logger.info("init()");
try {
useProxyIP = RangerConfiguration.getInstance().getBoolean(
PROP_USE_PROXY_IP, useProxyIP);
proxyIPHeader = RangerConfiguration.getInstance().get(
PROP_PROXY_IP_HEADER, proxyIPHeader);
// First get from the -D property
solrAppName = System.getProperty("solr.kerberos.jaas.appname",
solrAppName);
// Override if required from Ranger properties
solrAppName = RangerConfiguration.getInstance().get(
PROP_SOLR_APP_NAME, solrAppName);
logger.info("init(): useProxyIP=" + useProxyIP);
logger.info("init(): proxyIPHeader=" + proxyIPHeader);
logger.info("init(): solrAppName=" + solrAppName);
logger.info("init(): KerberosName.rules="
+ MiscUtil.getKerberosNamesRules());
authToJAASFile();
} catch (Throwable t) {
logger.fatal("Error init", t);
}
try {
RangerBasePlugin me = solrPlugin;
if (me == null) {
synchronized(RangerSolrAuthorizer.class) {
me = solrPlugin;
logger.info("RangerSolrAuthorizer(): init called");
if (me == null) {
me = solrPlugin = new RangerBasePlugin("solr", "solr");
}
}
}
solrPlugin.init();
} catch (Throwable t) {
logger.fatal("Error creating and initializing RangerBasePlugin()");
}
}
private void authToJAASFile() {
try {
// logger.info("DEFAULT UGI=" +
// UserGroupInformation.getLoginUser());
Configuration config = Configuration.getConfiguration();
MiscUtil.authWithConfig(solrAppName, config);
logger.info("POST AUTH UGI=" + UserGroupInformation.getLoginUser());
} catch (Throwable t) {
logger.error("Error authenticating for appName=" + solrAppName, t);
}
}
/*
* (non-Javadoc)
*
* @see java.io.Closeable#close()
*/
@Override
public void close() throws IOException {
logger.info("close() called");
try {
solrPlugin.cleanup();
} catch (Throwable t) {
logger.error("Error cleaning up Ranger plugin. Ignoring error", t);
}
}
/*
* (non-Javadoc)
*
* @see
* org.apache.solr.security.SolrAuthorizationPlugin#authorize(org.apache
* .solr.security.SolrRequestContext)
*/
@Override
public AuthorizationResponse authorize(AuthorizationContext context) {
boolean isDenied = true;
try {
if (logger.isDebugEnabled()) {
logAuthorizationConext(context);
}
RangerMultiResourceAuditHandler auditHandler = new RangerMultiResourceAuditHandler();
String userName = getUserName(context);
Set<String> userGroups = getGroupsForUser(userName);
String ip = null;
Date eventTime = new Date();
// // Set the IP
if (useProxyIP) {
ip = context.getHttpHeader(proxyIPHeader);
}
if (ip == null) {
ip = context.getHttpHeader("REMOTE_ADDR");
}
// Create the list of requests for access check. Each field is
// broken
// into a request
List<RangerAccessRequestImpl> rangerRequests = new ArrayList<RangerAccessRequestImpl>();
for (CollectionRequest collectionRequest : context
.getCollectionRequests()) {
List<RangerAccessRequestImpl> requestsForCollection = createRequests(
userName, userGroups, ip, eventTime, context,
collectionRequest);
rangerRequests.addAll(requestsForCollection);
}
if (logger.isDebugEnabled()) {
logger.debug("rangerRequests.size()=" + rangerRequests.size());
}
try {
// Let's check the access for each request/resource
for (RangerAccessRequestImpl rangerRequest : rangerRequests) {
RangerAccessResult result = solrPlugin.isAccessAllowed(
rangerRequest, auditHandler);
if (logger.isDebugEnabled()) {
logger.debug("rangerRequest=" + result);
}
if (result == null || !result.getIsAllowed()) {
isDenied = true;
// rejecting on first failure
break;
} else {
isDenied = false;
}
}
} finally {
auditHandler.flushAudit();
}
} catch (Throwable t) {
MiscUtil.logErrorMessageByInterval(logger, t.getMessage(), t);
}
AuthorizationResponse response = null;
if (isDenied) {
response = new AuthorizationResponse(403);
} else {
response = new AuthorizationResponse(200);
}
if (logger.isDebugEnabled()) {
logger.debug("context=" + context + ": returning: " + isDenied);
}
return response;
}
/**
* @param context
*/
private void logAuthorizationConext(AuthorizationContext context) {
try {
// Note: This method should be called with isDebugEnabled() or
// isInfoEnabled() scope
String collections = "";
int i = -1;
for (CollectionRequest collectionRequest : context
.getCollectionRequests()) {
i++;
if (i > 0) {
collections += ",";
}
collections += collectionRequest.collectionName;
}
String headers = "";
i = -1;
@SuppressWarnings("unchecked")
Enumeration<String> eList = context.getHeaderNames();
while (eList.hasMoreElements()) {
i++;
if (i > 0) {
headers += ",";
}
String header = eList.nextElement();
String value = context.getHttpHeader(header);
headers += header + "=" + value;
}
String ipAddress = context.getHttpHeader("HTTP_X_FORWARDED_FOR");
if (ipAddress == null) {
ipAddress = context.getHttpHeader("REMOTE_HOST");
}
if (ipAddress == null) {
ipAddress = context.getHttpHeader("REMOTE_ADDR");
}
String userName = getUserName(context);
Set<String> groups = getGroupsForUser(userName);
logger.info("AuthorizationContext: context.getResource()="
+ context.getResource() + ", solarParams="
+ context.getParams() + ", requestType="
+ context.getRequestType() + ", ranger.requestType="
+ mapToRangerAccessType(context) + ", userPrincipal="
+ context.getUserPrincipal() + ", userName=" + userName
+ ", groups=" + groups + ", ipAddress=" + ipAddress
+ ", collections=" + collections + ", headers=" + headers);
} catch (Throwable t) {
logger.error("Error getting request context!!!", t);
}
}
/**
* @param userName
* @param userGroups
* @param ip
* @param eventTime
* @param context
* @param collectionRequest
* @return
*/
private List<RangerAccessRequestImpl> createRequests(String userName,
Set<String> userGroups, String ip, Date eventTime,
AuthorizationContext context, CollectionRequest collectionRequest) {
List<RangerAccessRequestImpl> requests = new ArrayList<RangerAccessRequestImpl>();
String accessType = mapToRangerAccessType(context);
String action = accessType;
if (collectionRequest.collectionName != null) {
RangerAccessRequestImpl rangerRequest = createBaseRequest(userName,
userGroups, ip, eventTime);
RangerAccessResourceImpl rangerResource = new RangerAccessResourceImpl();
rangerResource.setValue(KEY_COLLECTION,
collectionRequest.collectionName);
rangerRequest.setResource(rangerResource);
rangerRequest.setAccessType(accessType);
rangerRequest.setAction(action);
requests.add(rangerRequest);
} else {
logger.fatal("Can't create RangerRequest oject. userName="
+ userName + ", accessType=" + accessType + ", ip=" + ip
+ ", collectionRequest=" + collectionRequest);
}
return requests;
}
private RangerAccessRequestImpl createBaseRequest(String userName,
Set<String> userGroups, String ip, Date eventTime) {
RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl();
if (userName != null && !userName.isEmpty()) {
rangerRequest.setUser(userName);
}
if (userGroups != null && userGroups.size() > 0) {
rangerRequest.setUserGroups(userGroups);
}
if (ip != null && !ip.isEmpty()) {
rangerRequest.setClientIPAddress(ip);
}
rangerRequest.setAccessTime(eventTime);
return rangerRequest;
}
private String getUserName(AuthorizationContext context) {
Principal principal = context.getUserPrincipal();
if (principal != null) {
return MiscUtil.getShortNameFromPrincipalName(principal.getName());
}
return null;
}
/**
* @param name
* @return
*/
private Set<String> getGroupsForUser(String name) {
return MiscUtil.getGroupsForRequestUser(name);
}
String mapToRangerAccessType(AuthorizationContext context) {
String accessType = ACCESS_TYPE_OTHERS;
RequestType requestType = context.getRequestType();
if (RequestType.ADMIN.equals(requestType)) {
accessType = ACCESS_TYPE_ADMIN;
} else if (RequestType.READ.equals(requestType)) {
accessType = ACCESS_TYPE_QUERY;
} else if (RequestType.WRITE.equals(requestType)) {
accessType = ACCESS_TYPE_UPDATE;
} else if (RequestType.UNKNOWN.equals(requestType)) {
logger.info("UNKNOWN request type. Mapping it to " + accessType
+ ". Resource=" + context.getResource());
accessType = ACCESS_TYPE_OTHERS;
} else {
logger.info("Request type is not supported. requestType="
+ requestType + ". Mapping it to " + accessType
+ ". Resource=" + context.getResource());
}
return accessType;
}
}