blob: 8bd93ad686c056c11f355f14640ad4fc3129b342 [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.solr.sentry;
import java.net.URL;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.sentry.binding.solr.authz.SentrySolrAuthorizationException;
import org.apache.sentry.binding.solr.authz.SolrAuthzBinding;
import org.apache.sentry.binding.solr.conf.SolrAuthzConf;
import org.apache.sentry.core.common.Subject;
import org.apache.sentry.core.model.search.Collection;
import org.apache.sentry.core.model.search.SearchModelAction;
import org.apache.solr.common.SolrException;
import org.apache.solr.core.SolrCore;
import org.apache.solr.request.LocalSolrQueryRequest;
import org.apache.solr.request.SolrQueryRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SentryIndexAuthorizationSingleton {
private static final Logger LOG =
LoggerFactory.getLogger(SentryIndexAuthorizationSingleton.class);
/**
* Java system property for specifying location of sentry-site.xml
*/
public static final String propertyName = "solr.authorization.sentry.site";
/**
* {@link HttpServletRequest} attribute for requesting user name
*/
public static final String USER_NAME = "solr.user.name";
/**
* {@link HttpServletRequest} attribute for requesting do as user.
*/
public static final String DO_AS_USER_NAME = "solr.do.as.user.name";
private static final SentryIndexAuthorizationSingleton INSTANCE =
new SentryIndexAuthorizationSingleton(System.getProperty(propertyName));
private final SolrAuthzBinding binding;
private final AuditLogger auditLogger = new AuditLogger();
private SentryIndexAuthorizationSingleton(String sentrySiteLocation) {
SolrAuthzBinding tmpBinding = null;
try {
if (sentrySiteLocation != null && sentrySiteLocation.length() > 0) {
tmpBinding =
new SolrAuthzBinding(new SolrAuthzConf(new URL("file://" + sentrySiteLocation)));
LOG.info("SolrAuthzBinding created successfully");
} else {
LOG.info("SolrAuthzBinding not created because " + propertyName
+ " not set, sentry not enabled");
}
} catch (Exception ex) {
LOG.error("Unable to create SolrAuthzBinding", ex);
}
binding = tmpBinding;
}
public static SentryIndexAuthorizationSingleton getInstance() {
return INSTANCE;
}
/**
* Returns true iff Sentry index authorization checking is enabled
*/
public boolean isEnabled() {
return binding != null;
}
/**
* Attempt to authorize an administrative action.
*
* @param req request to check
* @param actions set of actions to check
* @param checkCollection check the collection the action is on, or only "admin"?
* @param collection only relevant if checkCollection==true,
* use collection (if non-null) instead pulling collection name from req (if null)
*/
public void authorizeAdminAction(SolrQueryRequest req,
Set<SearchModelAction> actions, String operation, boolean checkCollection, String collection)
throws SolrException {
authorizeCollectionAction(req, actions, operation, "admin", true);
if (checkCollection) {
// Let's not error out if we can't find the collection associated with an
// admin action, it's pretty complicated to get all the possible administrative
// actions correct. Instead, let's warn in the log and address any issues we
// find.
authorizeCollectionAction(req, actions, operation, collection, false);
}
}
/**
* Attempt to authorize a collection action. The collection
* name will be pulled from the request.
*/
public void authorizeCollectionAction(SolrQueryRequest req,
Set<SearchModelAction> actions, String operation) throws SolrException {
authorizeCollectionAction(req, actions, operation, null, true);
}
/**
* Attempt to authorize a collection action.
*
* @param req request to check
* @param actions set of actions to check
* @param collectionName the collection to check. If null, the collection
* name is pulled from the request
* @param errorIfNoCollection is true, throw an exception if collection
* cannot be located
*/
public void authorizeCollectionAction(SolrQueryRequest req,
Set<SearchModelAction> actions, String operation, String collectionName,
boolean errorIfNoCollection)
throws SolrException {
Subject superUser = new Subject(System.getProperty("solr.authorization.superuser", "solr"));
Subject userName = new Subject(getUserName(req));
long eventTime = req.getStartTime();
String paramString = req.getParamString();
String impersonator = getImpersonatorName(req);
String ipAddress = null;
HttpServletRequest sreq = (HttpServletRequest) req.getContext().get("httpRequest");
if (sreq != null) {
try {
ipAddress = sreq.getRemoteAddr();
} catch (AssertionError e) {
// ignore
// This is a work-around for "Unexpected method call getRemoteAddr()"
// exception during unit test mocking at
// com.sun.proxy.$Proxy28.getRemoteAddr(Unknown Source)
}
}
String newCollectionName = collectionName;
if (newCollectionName == null) {
SolrCore solrCore = req.getCore();
if (solrCore == null) {
String msg = "Unable to locate collection for sentry to authorize because "
+ "no SolrCore attached to request";
if (errorIfNoCollection) {
auditLogger.log(userName.getName(), impersonator, ipAddress,
operation, paramString, eventTime, AuditLogger.UNAUTHORIZED, "");
throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, msg);
} else { // just warn
LOG.warn(msg);
auditLogger.log(userName.getName(), impersonator, ipAddress,
operation, paramString, eventTime, AuditLogger.ALLOWED, "");
return;
}
}
newCollectionName = solrCore.getCoreDescriptor().getCloudDescriptor().getCollectionName();
}
Collection collection = new Collection(newCollectionName);
try {
if (!superUser.getName().equals(userName.getName())) {
binding.authorizeCollection(userName, collection, actions);
}
} catch (SentrySolrAuthorizationException ex) {
auditLogger.log(userName.getName(), impersonator, ipAddress,
operation, paramString, eventTime, AuditLogger.UNAUTHORIZED, newCollectionName);
throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, ex);
}
auditLogger.log(userName.getName(), impersonator, ipAddress,
operation, paramString, eventTime, AuditLogger.ALLOWED, newCollectionName);
}
/**
* Get the roles associated with the user
* @param userName to get roles for
* @return The roles associated with the user
*/
public Set<String> getRoles(String userName) {
if (binding == null) {
return null;
}
return binding.getRoles(userName);
}
/**
* Get the user name associated with the request
*
* @param req the request
* @return the user name associated with the request
*/
public String getUserName(SolrQueryRequest req) throws SolrException {
if (binding == null) {
throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED,
"Solr binding was not created successfully. Defaulting to no access");
}
SolrCore solrCore = req.getCore();
HttpServletRequest httpServletRequest = (HttpServletRequest)req.getContext().get("httpRequest");
// LocalSolrQueryRequests won't have the HttpServletRequest because there is no
// http request associated with it.
if (httpServletRequest == null && !(req instanceof LocalSolrQueryRequest)) {
StringBuilder builder = new StringBuilder("Unable to locate HttpServletRequest");
if (solrCore != null && !solrCore.getSolrConfig().getBool(
"requestDispatcher/requestParsers/@addHttpRequestToContext", true)) {
builder.append(", ensure requestDispatcher/requestParsers/@addHttpRequestToContext is set to true");
}
throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, builder.toString());
}
String superUser = System.getProperty("solr.authorization.superuser", "solr");
// If a local request, treat it like a super user request; i.e. it is equivalent to an
// http request from the same process.
return req instanceof LocalSolrQueryRequest?
superUser : (String)httpServletRequest.getAttribute(USER_NAME);
}
private String getImpersonatorName(SolrQueryRequest req) {
HttpServletRequest httpServletRequest = (HttpServletRequest)req.getContext().get("httpRequest");
if (httpServletRequest != null) {
return (String)httpServletRequest.getAttribute(DO_AS_USER_NAME);
}
return null;
}
/**
* Attempt to notify the Sentry service when deleting collection happened
* @param collection
* @throws SolrException
*/
public void deleteCollection(String collection) throws SolrException {
try {
binding.deleteCollectionPrivilege(collection);
} catch (SentrySolrAuthorizationException ex) {
throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, ex);
}
}
}