Repository: incubator-sentry Updated Branches: refs/heads/master f1a2efac1 -> aca4d0e9c
SENTRY-987: Move general (non specific handler) solr-sentry code to solr-sentry-core package Project: http://git-wip-us.apache.org/repos/asf/incubator-sentry/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-sentry/commit/aca4d0e9 Tree: http://git-wip-us.apache.org/repos/asf/incubator-sentry/tree/aca4d0e9 Diff: http://git-wip-us.apache.org/repos/asf/incubator-sentry/diff/aca4d0e9 Branch: refs/heads/master Commit: aca4d0e9ca2c5956a8f59ee3885f92a63af2f65e Parents: f1a2efa Author: Gregory Chanan <[email protected]> Authored: Tue Dec 15 15:33:07 2015 -0800 Committer: Gregory Chanan <[email protected]> Committed: Tue Dec 22 13:04:50 2015 -0800 ---------------------------------------------------------------------- pom.xml | 5 + sentry-dist/pom.xml | 4 + sentry-solr/pom.xml | 1 + sentry-solr/solr-sentry-core/pom.xml | 58 +++++ .../org/apache/solr/sentry/AuditLogger.java | 97 ++++++++ .../RollingFileWithoutDeleteAppender.java | 176 ++++++++++++++ .../solr/sentry/SecureRequestHandlerUtil.java | 84 +++++++ .../SentryIndexAuthorizationSingleton.java | 234 +++++++++++++++++++ sentry-solr/solr-sentry-handlers/pom.xml | 10 +- .../SecureDocumentAnalysisRequestHandler.java | 1 + .../SecureFieldAnalysisRequestHandler.java | 1 + .../solr/handler/SecureReplicationHandler.java | 1 + .../solr/handler/SecureRequestHandlerUtil.java | 85 ------- .../solr/handler/admin/SecureAdminHandlers.java | 2 +- .../handler/admin/SecureCollectionsHandler.java | 2 +- .../handler/admin/SecureCoreAdminHandler.java | 2 +- .../org/apache/solr/sentry/AuditLogger.java | 97 -------- .../RollingFileWithoutDeleteAppender.java | 176 -------------- .../SentryIndexAuthorizationSingleton.java | 234 ------------------- .../sentry/SentrySingletonTestInstance.java | 2 +- 20 files changed, 667 insertions(+), 605 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 9495286..6210454 100644 --- a/pom.xml +++ b/pom.xml @@ -253,6 +253,11 @@ limitations under the License. </dependency> <dependency> <groupId>org.apache.sentry</groupId> + <artifactId>solr-sentry-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> <artifactId>solr-sentry-handlers</artifactId> <version>${project.version}</version> </dependency> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-dist/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-dist/pom.xml b/sentry-dist/pom.xml index daa2a5a..4e078f0 100644 --- a/sentry-dist/pom.xml +++ b/sentry-dist/pom.xml @@ -60,6 +60,10 @@ limitations under the License. </dependency> <dependency> <groupId>org.apache.sentry</groupId> + <artifactId>solr-sentry-core</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> <artifactId>solr-sentry-handlers</artifactId> </dependency> <dependency> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-solr/pom.xml b/sentry-solr/pom.xml index c3e22c4..43798c9 100644 --- a/sentry-solr/pom.xml +++ b/sentry-solr/pom.xml @@ -31,6 +31,7 @@ limitations under the License. <modules> <module>solr-sentry-handlers</module> + <module>solr-sentry-core</module> </modules> </project> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-core/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-core/pom.xml b/sentry-solr/solr-sentry-core/pom.xml new file mode 100644 index 0000000..44fbb86 --- /dev/null +++ b/sentry-solr/solr-sentry-core/pom.xml @@ -0,0 +1,58 @@ +<?xml version="1.0"?> +<!-- +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +--> +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-solr</artifactId> + <version>1.7.0-incubating-SNAPSHOT</version> + </parent> + + <artifactId>solr-sentry-core</artifactId> + <name>Solr Sentry Core</name> + + <dependencies> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-common</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-core-model-search</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sentry</groupId> + <artifactId>sentry-binding-solr</artifactId> + </dependency> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-solrj</artifactId> + </dependency> + <dependency> + <groupId>org.apache.solr</groupId> + <artifactId>solr-core</artifactId> + </dependency> + </dependencies> + +</project> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/AuditLogger.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/AuditLogger.java b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/AuditLogger.java new file mode 100644 index 0000000..7f3e391 --- /dev/null +++ b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/AuditLogger.java @@ -0,0 +1,97 @@ +/* + * 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 org.apache.lucene.util.Version; +import org.noggit.CharArr; +import org.noggit.JSONWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * Writes audit events to the audit log. This helps answer questions such as: + * Who did what action when from where, and what values were changed from what + * to what as a result? + */ +final class AuditLogger { + + public static final int ALLOWED = 1; + public static final int UNAUTHORIZED = 0; + + private final Logger logger; + + private static final boolean IS_ENABLED = + Boolean.valueOf( + System.getProperty(AuditLogger.class.getName() + ".isEnabled", "true")); + + private static final String SOLR_VERSION = Version.LATEST.toString(); + + + public AuditLogger() { + this.logger = LoggerFactory.getLogger(getClass()); + } + + public boolean isLogEnabled() { + return IS_ENABLED && logger.isInfoEnabled(); + } + + public void log( + String userName, + String impersonator, + String ipAddress, + String operation, + String operationParams, + long eventTime, + int allowed, + String collectionName) { + + if (!isLogEnabled()) { + return; + } + CharArr chars = new CharArr(512); + JSONWriter writer = new JSONWriter(chars, -1); + writer.startObject(); + writeField("solrVersion", SOLR_VERSION, writer); + writer.writeValueSeparator(); + writeField("eventTime", eventTime, writer); + writer.writeValueSeparator(); + writeField("allowed", allowed, writer); + writer.writeValueSeparator(); + writeField("collectionName", collectionName, writer); + writer.writeValueSeparator(); + writeField("operation", operation, writer); + writer.writeValueSeparator(); + writeField("operationParams", operationParams, writer); + writer.writeValueSeparator(); + writeField("ipAddress", ipAddress, writer); + writer.writeValueSeparator(); + writeField("username", userName, writer); + writer.writeValueSeparator(); + writeField("impersonator", impersonator, writer); + writer.endObject(); + logger.info("{}", chars); + } + + private void writeField(String key, Object value, JSONWriter writer) { + writer.writeString(key); + writer.writeNameSeparator(); + writer.write(value); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java new file mode 100644 index 0000000..ec26ef3 --- /dev/null +++ b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java @@ -0,0 +1,176 @@ +/** + * 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.io.File; +import java.io.IOException; +import java.io.InterruptedIOException; +import java.io.Writer; +import java.nio.file.Files; + +import org.apache.log4j.FileAppender; +import org.apache.log4j.Layout; +import org.apache.log4j.helpers.CountingQuietWriter; +import org.apache.log4j.helpers.LogLog; +import org.apache.log4j.helpers.OptionConverter; +import org.apache.log4j.spi.LoggingEvent; + +public class RollingFileWithoutDeleteAppender extends FileAppender { + /** + * The default maximum file size is 10MB. + */ + protected long maxFileSize = 10 * 1024 * 1024; + + private long nextRollover = 0; + + /** + * The default constructor simply calls its {@link FileAppender#FileAppender + * parents constructor}. + */ + public RollingFileWithoutDeleteAppender() { + super(); + } + + /** + * Instantiate a RollingFileAppender and open the file designated by + * <code>filename</code>. The opened filename will become the ouput + * destination for this appender. + * <p> + * If the <code>append</code> parameter is true, the file will be appended to. + * Otherwise, the file desginated by <code>filename</code> will be truncated + * before being opened. + */ + public RollingFileWithoutDeleteAppender(Layout layout, String filename, + boolean append) throws IOException { + super(layout, getLogFileName(filename), append); + } + + /** + * Instantiate a FileAppender and open the file designated by + * <code>filename</code>. The opened filename will become the output + * destination for this appender. + * <p> + * The file will be appended to. + */ + public RollingFileWithoutDeleteAppender(Layout layout, String filename) + throws IOException { + super(layout, getLogFileName(filename)); + } + + /** + * Get the maximum size that the output file is allowed to reach before being + * rolled over to backup files. + */ + public long getMaximumFileSize() { + return maxFileSize; + } + + /** + * Implements the usual roll over behaviour. + * <p> + * <code>File</code> is renamed <code>File.yyyyMMddHHmmss</code> and closed. A + * new <code>File</code> is created to receive further log output. + */ + // synchronization not necessary since doAppend is alreasy synched + public void rollOver() { + if (qw != null) { + long size = ((CountingQuietWriter) qw).getCount(); + LogLog.debug("rolling over count=" + size); + // if operation fails, do not roll again until + // maxFileSize more bytes are written + nextRollover = size + maxFileSize; + } + + this.closeFile(); // keep windows happy. + + String newFileName = getLogFileName(fileName); + try { + // This will also close the file. This is OK since multiple + // close operations are safe. + this.setFile(newFileName, false, bufferedIO, bufferSize); + nextRollover = 0; + } catch (IOException e) { + if (e instanceof InterruptedIOException) { + Thread.currentThread().interrupt(); + } + LogLog.error("setFile(" + newFileName + ", false) call failed.", e); + } + } + + public synchronized void setFile(String fileName, boolean append, + boolean bufferedIO, int bufferSize) throws IOException { + super.setFile(fileName, append, this.bufferedIO, this.bufferSize); + if (append) { + File f = new File(fileName); + ((CountingQuietWriter) qw).setCount(f.length()); + } + } + + /** + * Set the maximum size that the output file is allowed to reach before being + * rolled over to backup files. + * <p> + * This method is equivalent to {@link #setMaxFileSize} except that it is + * required for differentiating the setter taking a <code>long</code> argument + * from the setter taking a <code>String</code> argument by the JavaBeans + * {@link java.beans.Introspector Introspector}. + * + * @see #setMaxFileSize(String) + */ + public void setMaximumFileSize(long maxFileSize) { + this.maxFileSize = maxFileSize; + } + + /** + * Set the maximum size that the output file is allowed to reach before being + * rolled over to backup files. + * <p> + * In configuration files, the <b>MaxFileSize</b> option takes an long integer + * in the range 0 - 2^63. You can specify the value with the suffixes "KB", + * "MB" or "GB" so that the integer is interpreted being expressed + * respectively in kilobytes, megabytes or gigabytes. For example, the value + * "10KB" will be interpreted as 10240. + */ + public void setMaxFileSize(String value) { + maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1); + } + + protected void setQWForFiles(Writer writer) { + this.qw = new CountingQuietWriter(writer, errorHandler); + } + + /** + * This method differentiates RollingFileAppender from its super class. + */ + protected void subAppend(LoggingEvent event) { + super.subAppend(event); + + if (fileName != null && qw != null) { + long size = ((CountingQuietWriter) qw).getCount(); + if (size >= maxFileSize && size >= nextRollover) { + rollOver(); + } + } + } + + // Mangled file name. Append the current timestamp + private static String getLogFileName(String oldFileName) { + return oldFileName + "." + Long.toString(System.currentTimeMillis()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SecureRequestHandlerUtil.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SecureRequestHandlerUtil.java b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SecureRequestHandlerUtil.java new file mode 100644 index 0000000..1f46835 --- /dev/null +++ b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SecureRequestHandlerUtil.java @@ -0,0 +1,84 @@ +/* + * 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.util.EnumSet; +import java.util.Set; +import org.apache.sentry.core.model.search.SearchModelAction; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.response.SolrQueryResponse; + +/** + * Utility functions for Secure (sentry-aware) versions of RequestHandlers + */ +public class SecureRequestHandlerUtil { + public static final Set<SearchModelAction> QUERY_ONLY = EnumSet.of(SearchModelAction.QUERY); + public static final Set<SearchModelAction> UPDATE_ONLY = EnumSet.of(SearchModelAction.UPDATE); + public static final Set<SearchModelAction> QUERY_AND_UPDATE = EnumSet.of(SearchModelAction.QUERY, SearchModelAction.UPDATE); + + // Hack to provide a test-only version of SentryIndexAuthorizationSingleton + public static SentryIndexAuthorizationSingleton testOverride = null; + + /** + * Attempt to authorize an administrative action. + * + * @param req request to check + * @param andActions 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 static void checkSentryAdmin(SolrQueryRequest req, Set<SearchModelAction> andActions, + String operation, boolean checkCollection, String collection) { + checkSentry(req, andActions, operation, true, checkCollection, collection); + } + + /** + * Attempt to authorize a collection action. The collection + * name will be pulled from the request. + */ + public static void checkSentryCollection(SolrQueryRequest req, Set<SearchModelAction> andActions, String operation) { + checkSentry(req, andActions, operation, false, false, null); + } + + /** + * Attempt to sync collection privileges with Sentry when the metadata has changed. + * ex: When the collection has been deleted, the privileges related to the collection + * were also needed to drop. When the collection has been renamed, the privileges must been + * renamed too. + */ + public static void syncDeleteCollection(String collection) { + final SentryIndexAuthorizationSingleton sentryInstance = + (testOverride == null)?SentryIndexAuthorizationSingleton.getInstance():testOverride; + sentryInstance.deleteCollection(collection); + } + + private static void checkSentry(SolrQueryRequest req, Set<SearchModelAction> andActions, + String operation, boolean admin, boolean checkCollection, String collection) { + // Sentry currently does have AND support for actions; need to check + // actions one at a time + final SentryIndexAuthorizationSingleton sentryInstance = + (testOverride == null)?SentryIndexAuthorizationSingleton.getInstance():testOverride; + for (SearchModelAction action : andActions) { + if (admin) { + sentryInstance.authorizeAdminAction(req, EnumSet.of(action), operation, checkCollection, collection); + } else { + sentryInstance.authorizeCollectionAction(req, EnumSet.of(action), operation); + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java new file mode 100644 index 0000000..185884b --- /dev/null +++ b/sentry-solr/solr-sentry-core/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java @@ -0,0 +1,234 @@ +/* + * 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 Logger log = + LoggerFactory.getLogger(SentryIndexAuthorizationSingleton.class); + + public static final String propertyName = "solr.authorization.sentry.site"; + private static final String USER_NAME = "solr.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 = null; // FIXME + + 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) + } + } + + if (collectionName == 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, collectionName); + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, msg); + } else { // just warn + log.warn(msg); + auditLogger.log(userName.getName(), impersonator, ipAddress, + operation, paramString, eventTime, AuditLogger.ALLOWED, collectionName); + return; + } + } + collectionName = solrCore.getCoreDescriptor().getCloudDescriptor().getCollectionName(); + } + + Collection collection = new Collection(collectionName); + 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, collectionName); + throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, ex); + } + + auditLogger.log(userName.getName(), impersonator, ipAddress, + operation, paramString, eventTime, AuditLogger.ALLOWED, collectionName); + } + + /** + * 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) == false) { + 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); + } + + /** + * 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); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/pom.xml ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/pom.xml b/sentry-solr/solr-sentry-handlers/pom.xml index 61c2da2..07d95fa 100644 --- a/sentry-solr/solr-sentry-handlers/pom.xml +++ b/sentry-solr/solr-sentry-handlers/pom.xml @@ -45,20 +45,12 @@ limitations under the License. <scope>test</scope> </dependency> <dependency> - <groupId>log4j</groupId> - <artifactId>log4j</artifactId> - </dependency> - <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.sentry</groupId> - <artifactId>sentry-core-common</artifactId> - </dependency> - <dependency> - <groupId>org.apache.sentry</groupId> <artifactId>sentry-core-model-search</artifactId> </dependency> <dependency> @@ -79,7 +71,7 @@ limitations under the License. </dependency> <dependency> <groupId>org.apache.sentry</groupId> - <artifactId>sentry-binding-solr</artifactId> + <artifactId>solr-sentry-core</artifactId> </dependency> <dependency> <groupId>org.apache.solr</groupId> http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java index 9ecf139..1c1f6f8 100644 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureDocumentAnalysisRequestHandler.java @@ -19,6 +19,7 @@ package org.apache.solr.handler; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SecureRequestHandlerUtil; /** * Secure (sentry-aware) version of DocumentAnalysisRequestHandler http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java index 819227b..62f9a19 100644 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureFieldAnalysisRequestHandler.java @@ -19,6 +19,7 @@ package org.apache.solr.handler; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SecureRequestHandlerUtil; /** * Secure (sentry-aware) version of FieldAnalysisRequestHandler http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java index 42213ae..bdcd830 100644 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureReplicationHandler.java @@ -18,6 +18,7 @@ package org.apache.solr.handler; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SecureRequestHandlerUtil; /** * Secure (sentry-aware) version of ReplicationHandler http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java deleted file mode 100644 index 94341b3..0000000 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/SecureRequestHandlerUtil.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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.handler; - -import java.util.EnumSet; -import java.util.Set; -import org.apache.sentry.core.model.search.SearchModelAction; -import org.apache.solr.request.SolrQueryRequest; -import org.apache.solr.response.SolrQueryResponse; -import org.apache.solr.sentry.SentryIndexAuthorizationSingleton; - -/** - * Utility functions for Secure (sentry-aware) versions of RequestHandlers - */ -public class SecureRequestHandlerUtil { - public static final Set<SearchModelAction> QUERY_ONLY = EnumSet.of(SearchModelAction.QUERY); - public static final Set<SearchModelAction> UPDATE_ONLY = EnumSet.of(SearchModelAction.UPDATE); - public static final Set<SearchModelAction> QUERY_AND_UPDATE = EnumSet.of(SearchModelAction.QUERY, SearchModelAction.UPDATE); - - // Hack to provide a test-only version of SentryIndexAuthorizationSingleton - public static SentryIndexAuthorizationSingleton testOverride = null; - - /** - * Attempt to authorize an administrative action. - * - * @param req request to check - * @param andActions 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 static void checkSentryAdmin(SolrQueryRequest req, Set<SearchModelAction> andActions, - String operation, boolean checkCollection, String collection) { - checkSentry(req, andActions, operation, true, checkCollection, collection); - } - - /** - * Attempt to authorize a collection action. The collection - * name will be pulled from the request. - */ - public static void checkSentryCollection(SolrQueryRequest req, Set<SearchModelAction> andActions, String operation) { - checkSentry(req, andActions, operation, false, false, null); - } - - /** - * Attempt to sync collection privileges with Sentry when the metadata has changed. - * ex: When the collection has been deleted, the privileges related to the collection - * were also needed to drop. When the collection has been renamed, the privileges must been - * renamed too. - */ - public static void syncDeleteCollection(String collection) { - final SentryIndexAuthorizationSingleton sentryInstance = - (testOverride == null)?SentryIndexAuthorizationSingleton.getInstance():testOverride; - sentryInstance.deleteCollection(collection); - } - - private static void checkSentry(SolrQueryRequest req, Set<SearchModelAction> andActions, - String operation, boolean admin, boolean checkCollection, String collection) { - // Sentry currently does have AND support for actions; need to check - // actions one at a time - final SentryIndexAuthorizationSingleton sentryInstance = - (testOverride == null)?SentryIndexAuthorizationSingleton.getInstance():testOverride; - for (SearchModelAction action : andActions) { - if (admin) { - sentryInstance.authorizeAdminAction(req, EnumSet.of(action), operation, checkCollection, collection); - } else { - sentryInstance.authorizeCollectionAction(req, EnumSet.of(action), operation); - } - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java index 88016ea..6192dff 100644 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureAdminHandlers.java @@ -25,10 +25,10 @@ import org.apache.solr.core.CoreContainer; import org.apache.solr.core.SolrCore; import org.apache.sentry.core.model.search.SearchModelAction; import org.apache.solr.handler.RequestHandlerBase; -import org.apache.solr.handler.SecureRequestHandlerUtil; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.request.SolrRequestHandler; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SecureRequestHandlerUtil; import org.apache.solr.util.plugin.SolrCoreAware; import org.apache.zookeeper.KeeperException; http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java index 15a6ba0..dc96698 100644 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCollectionsHandler.java @@ -22,9 +22,9 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.common.params.CollectionParams.CollectionAction; import org.apache.solr.common.params.CoreAdminParams; import org.apache.sentry.core.model.search.SearchModelAction; -import org.apache.solr.handler.SecureRequestHandlerUtil; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SecureRequestHandlerUtil; import org.apache.solr.core.CoreContainer; /** http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java index 57ccc94..ff6e281 100644 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java +++ b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/handler/admin/SecureCoreAdminHandler.java @@ -23,9 +23,9 @@ import org.apache.solr.common.params.SolrParams; import org.apache.solr.core.CoreContainer; import org.apache.solr.core.CoreDescriptor; import org.apache.solr.core.SolrCore; -import org.apache.solr.handler.SecureRequestHandlerUtil; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; +import org.apache.solr.sentry.SecureRequestHandlerUtil; /** * Secure (sentry-aware) version of CoreAdminHandler http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/AuditLogger.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/AuditLogger.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/AuditLogger.java deleted file mode 100644 index 7f3e391..0000000 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/AuditLogger.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 org.apache.lucene.util.Version; -import org.noggit.CharArr; -import org.noggit.JSONWriter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - - -/** - * Writes audit events to the audit log. This helps answer questions such as: - * Who did what action when from where, and what values were changed from what - * to what as a result? - */ -final class AuditLogger { - - public static final int ALLOWED = 1; - public static final int UNAUTHORIZED = 0; - - private final Logger logger; - - private static final boolean IS_ENABLED = - Boolean.valueOf( - System.getProperty(AuditLogger.class.getName() + ".isEnabled", "true")); - - private static final String SOLR_VERSION = Version.LATEST.toString(); - - - public AuditLogger() { - this.logger = LoggerFactory.getLogger(getClass()); - } - - public boolean isLogEnabled() { - return IS_ENABLED && logger.isInfoEnabled(); - } - - public void log( - String userName, - String impersonator, - String ipAddress, - String operation, - String operationParams, - long eventTime, - int allowed, - String collectionName) { - - if (!isLogEnabled()) { - return; - } - CharArr chars = new CharArr(512); - JSONWriter writer = new JSONWriter(chars, -1); - writer.startObject(); - writeField("solrVersion", SOLR_VERSION, writer); - writer.writeValueSeparator(); - writeField("eventTime", eventTime, writer); - writer.writeValueSeparator(); - writeField("allowed", allowed, writer); - writer.writeValueSeparator(); - writeField("collectionName", collectionName, writer); - writer.writeValueSeparator(); - writeField("operation", operation, writer); - writer.writeValueSeparator(); - writeField("operationParams", operationParams, writer); - writer.writeValueSeparator(); - writeField("ipAddress", ipAddress, writer); - writer.writeValueSeparator(); - writeField("username", userName, writer); - writer.writeValueSeparator(); - writeField("impersonator", impersonator, writer); - writer.endObject(); - logger.info("{}", chars); - } - - private void writeField(String key, Object value, JSONWriter writer) { - writer.writeString(key); - writer.writeNameSeparator(); - writer.write(value); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java deleted file mode 100644 index ec26ef3..0000000 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/RollingFileWithoutDeleteAppender.java +++ /dev/null @@ -1,176 +0,0 @@ -/** - * 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.io.File; -import java.io.IOException; -import java.io.InterruptedIOException; -import java.io.Writer; -import java.nio.file.Files; - -import org.apache.log4j.FileAppender; -import org.apache.log4j.Layout; -import org.apache.log4j.helpers.CountingQuietWriter; -import org.apache.log4j.helpers.LogLog; -import org.apache.log4j.helpers.OptionConverter; -import org.apache.log4j.spi.LoggingEvent; - -public class RollingFileWithoutDeleteAppender extends FileAppender { - /** - * The default maximum file size is 10MB. - */ - protected long maxFileSize = 10 * 1024 * 1024; - - private long nextRollover = 0; - - /** - * The default constructor simply calls its {@link FileAppender#FileAppender - * parents constructor}. - */ - public RollingFileWithoutDeleteAppender() { - super(); - } - - /** - * Instantiate a RollingFileAppender and open the file designated by - * <code>filename</code>. The opened filename will become the ouput - * destination for this appender. - * <p> - * If the <code>append</code> parameter is true, the file will be appended to. - * Otherwise, the file desginated by <code>filename</code> will be truncated - * before being opened. - */ - public RollingFileWithoutDeleteAppender(Layout layout, String filename, - boolean append) throws IOException { - super(layout, getLogFileName(filename), append); - } - - /** - * Instantiate a FileAppender and open the file designated by - * <code>filename</code>. The opened filename will become the output - * destination for this appender. - * <p> - * The file will be appended to. - */ - public RollingFileWithoutDeleteAppender(Layout layout, String filename) - throws IOException { - super(layout, getLogFileName(filename)); - } - - /** - * Get the maximum size that the output file is allowed to reach before being - * rolled over to backup files. - */ - public long getMaximumFileSize() { - return maxFileSize; - } - - /** - * Implements the usual roll over behaviour. - * <p> - * <code>File</code> is renamed <code>File.yyyyMMddHHmmss</code> and closed. A - * new <code>File</code> is created to receive further log output. - */ - // synchronization not necessary since doAppend is alreasy synched - public void rollOver() { - if (qw != null) { - long size = ((CountingQuietWriter) qw).getCount(); - LogLog.debug("rolling over count=" + size); - // if operation fails, do not roll again until - // maxFileSize more bytes are written - nextRollover = size + maxFileSize; - } - - this.closeFile(); // keep windows happy. - - String newFileName = getLogFileName(fileName); - try { - // This will also close the file. This is OK since multiple - // close operations are safe. - this.setFile(newFileName, false, bufferedIO, bufferSize); - nextRollover = 0; - } catch (IOException e) { - if (e instanceof InterruptedIOException) { - Thread.currentThread().interrupt(); - } - LogLog.error("setFile(" + newFileName + ", false) call failed.", e); - } - } - - public synchronized void setFile(String fileName, boolean append, - boolean bufferedIO, int bufferSize) throws IOException { - super.setFile(fileName, append, this.bufferedIO, this.bufferSize); - if (append) { - File f = new File(fileName); - ((CountingQuietWriter) qw).setCount(f.length()); - } - } - - /** - * Set the maximum size that the output file is allowed to reach before being - * rolled over to backup files. - * <p> - * This method is equivalent to {@link #setMaxFileSize} except that it is - * required for differentiating the setter taking a <code>long</code> argument - * from the setter taking a <code>String</code> argument by the JavaBeans - * {@link java.beans.Introspector Introspector}. - * - * @see #setMaxFileSize(String) - */ - public void setMaximumFileSize(long maxFileSize) { - this.maxFileSize = maxFileSize; - } - - /** - * Set the maximum size that the output file is allowed to reach before being - * rolled over to backup files. - * <p> - * In configuration files, the <b>MaxFileSize</b> option takes an long integer - * in the range 0 - 2^63. You can specify the value with the suffixes "KB", - * "MB" or "GB" so that the integer is interpreted being expressed - * respectively in kilobytes, megabytes or gigabytes. For example, the value - * "10KB" will be interpreted as 10240. - */ - public void setMaxFileSize(String value) { - maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1); - } - - protected void setQWForFiles(Writer writer) { - this.qw = new CountingQuietWriter(writer, errorHandler); - } - - /** - * This method differentiates RollingFileAppender from its super class. - */ - protected void subAppend(LoggingEvent event) { - super.subAppend(event); - - if (fileName != null && qw != null) { - long size = ((CountingQuietWriter) qw).getCount(); - if (size >= maxFileSize && size >= nextRollover) { - rollOver(); - } - } - } - - // Mangled file name. Append the current timestamp - private static String getLogFileName(String oldFileName) { - return oldFileName + "." + Long.toString(System.currentTimeMillis()); - } -} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java b/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java deleted file mode 100644 index 185884b..0000000 --- a/sentry-solr/solr-sentry-handlers/src/main/java/org/apache/solr/sentry/SentryIndexAuthorizationSingleton.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * 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 Logger log = - LoggerFactory.getLogger(SentryIndexAuthorizationSingleton.class); - - public static final String propertyName = "solr.authorization.sentry.site"; - private static final String USER_NAME = "solr.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 = null; // FIXME - - 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) - } - } - - if (collectionName == 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, collectionName); - throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, msg); - } else { // just warn - log.warn(msg); - auditLogger.log(userName.getName(), impersonator, ipAddress, - operation, paramString, eventTime, AuditLogger.ALLOWED, collectionName); - return; - } - } - collectionName = solrCore.getCoreDescriptor().getCloudDescriptor().getCollectionName(); - } - - Collection collection = new Collection(collectionName); - 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, collectionName); - throw new SolrException(SolrException.ErrorCode.UNAUTHORIZED, ex); - } - - auditLogger.log(userName.getName(), impersonator, ipAddress, - operation, paramString, eventTime, AuditLogger.ALLOWED, collectionName); - } - - /** - * 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) == false) { - 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); - } - - /** - * 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); - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-sentry/blob/aca4d0e9/sentry-solr/solr-sentry-handlers/src/test/java/org/apache/solr/sentry/SentrySingletonTestInstance.java ---------------------------------------------------------------------- diff --git a/sentry-solr/solr-sentry-handlers/src/test/java/org/apache/solr/sentry/SentrySingletonTestInstance.java b/sentry-solr/solr-sentry-handlers/src/test/java/org/apache/solr/sentry/SentrySingletonTestInstance.java index ae02466..664719f 100644 --- a/sentry-solr/solr-sentry-handlers/src/test/java/org/apache/solr/sentry/SentrySingletonTestInstance.java +++ b/sentry-solr/solr-sentry-handlers/src/test/java/org/apache/solr/sentry/SentrySingletonTestInstance.java @@ -21,7 +21,7 @@ import java.lang.reflect.Constructor; import org.apache.commons.io.FileUtils; import org.apache.solr.SolrTestCaseJ4; -import org.apache.solr.handler.SecureRequestHandlerUtil; +import org.apache.solr.sentry.SecureRequestHandlerUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
