Return-Path: X-Original-To: apmail-manifoldcf-commits-archive@www.apache.org Delivered-To: apmail-manifoldcf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B9A4619B6A for ; Tue, 5 Apr 2016 05:22:12 +0000 (UTC) Received: (qmail 29184 invoked by uid 500); 5 Apr 2016 05:22:12 -0000 Delivered-To: apmail-manifoldcf-commits-archive@manifoldcf.apache.org Received: (qmail 29133 invoked by uid 500); 5 Apr 2016 05:22:12 -0000 Mailing-List: contact commits-help@manifoldcf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@manifoldcf.apache.org Delivered-To: mailing list commits@manifoldcf.apache.org Received: (qmail 29123 invoked by uid 99); 5 Apr 2016 05:22:12 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 05 Apr 2016 05:22:12 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 0E2D4C0667 for ; Tue, 5 Apr 2016 05:22:12 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: 0.804 X-Spam-Level: X-Spam-Status: No, score=0.804 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RP_MATCHES_RCVD=-0.996] autolearn=disabled Received: from mx1-lw-eu.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id u58fJ2a4mB82 for ; Tue, 5 Apr 2016 05:22:07 +0000 (UTC) Received: from mailrelay1-us-west.apache.org (mailrelay1-us-west.apache.org [209.188.14.139]) by mx1-lw-eu.apache.org (ASF Mail Server at mx1-lw-eu.apache.org) with ESMTP id 7368B5F217 for ; Tue, 5 Apr 2016 05:22:06 +0000 (UTC) Received: from svn01-us-west.apache.org (svn.apache.org [10.41.0.6]) by mailrelay1-us-west.apache.org (ASF Mail Server at mailrelay1-us-west.apache.org) with ESMTP id 56512E0225 for ; Tue, 5 Apr 2016 05:22:05 +0000 (UTC) Received: from svn01-us-west.apache.org (localhost [127.0.0.1]) by svn01-us-west.apache.org (ASF Mail Server at svn01-us-west.apache.org) with ESMTP id 464ED3A0314 for ; Tue, 5 Apr 2016 05:22:05 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1737771 [1/2] - in /manifoldcf/integration/solr-6.x/trunk: ./ src/ src/main/ src/main/assembly/ src/main/java/ src/main/java/org/ src/main/java/org/apache/ src/main/java/org/apache/solr/ src/main/java/org/apache/solr/mcf/ src/test/ src/tes... Date: Tue, 05 Apr 2016 05:22:05 -0000 To: commits@manifoldcf.apache.org From: kwright@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20160405052205.464ED3A0314@svn01-us-west.apache.org> Author: kwright Date: Tue Apr 5 05:22:04 2016 New Revision: 1737771 URL: http://svn.apache.org/viewvc?rev=1737771&view=rev Log: Initial commit for solr 6.x plugin Added: manifoldcf/integration/solr-6.x/trunk/CHANGES.txt (with props) manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt (with props) manifoldcf/integration/solr-6.x/trunk/LICENSE.txt (with props) manifoldcf/integration/solr-6.x/trunk/NOTICE.txt (with props) manifoldcf/integration/solr-6.x/trunk/README.txt (with props) manifoldcf/integration/solr-6.x/trunk/pom.xml (with props) manifoldcf/integration/solr-6.x/trunk/src/ manifoldcf/integration/solr-6.x/trunk/src/main/ manifoldcf/integration/solr-6.x/trunk/src/main/assembly/ manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml (with props) manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml (with props) manifoldcf/integration/solr-6.x/trunk/src/main/java/ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFQParserPlugin.java (with props) manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFSearchComponent.java (with props) manifoldcf/integration/solr-6.x/trunk/src/test/ manifoldcf/integration/solr-6.x/trunk/src/test/java/ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java (with props) manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFSCLoadTest.java (with props) manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFSearchComponentTest.java (with props) Added: manifoldcf/integration/solr-6.x/trunk/CHANGES.txt URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/CHANGES.txt?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/CHANGES.txt (added) +++ manifoldcf/integration/solr-6.x/trunk/CHANGES.txt Tue Apr 5 05:22:04 2016 @@ -0,0 +1,7 @@ +Apache ManifoldCF Plugin for Apache Solr 5.x change Log +$Id$ + +======================= 2.3-dev ====================== + +Initial commit. +(Karl Wrigh) Propchange: manifoldcf/integration/solr-6.x/trunk/CHANGES.txt ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/CHANGES.txt ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt (added) +++ manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt Tue Apr 5 05:22:04 2016 @@ -0,0 +1,11 @@ +Apache ManifoldCF Plugin for Apache Solr 6.x requires +--------------------------------------------------- +* JRE 1.8 or above +* mvn 3.0 or higher + +For building and running Apache ManifoldCF Plugin for Apache Solr 6.x: +------------------------------------------------------------------ + +* Look at README.txt + + Propchange: manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/DEPENDENCIES.txt ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/LICENSE.txt URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/LICENSE.txt?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/LICENSE.txt (added) +++ manifoldcf/integration/solr-6.x/trunk/LICENSE.txt Tue Apr 5 05:22:04 2016 @@ -0,0 +1,177 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + Propchange: manifoldcf/integration/solr-6.x/trunk/LICENSE.txt ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/LICENSE.txt ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/NOTICE.txt URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/NOTICE.txt?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/NOTICE.txt (added) +++ manifoldcf/integration/solr-6.x/trunk/NOTICE.txt Tue Apr 5 05:22:04 2016 @@ -0,0 +1,5 @@ +Apache ManifoldCF Plugin for Apache Solr 6.x +Copyright 2010-2016 The Apache Software Foundation + +This product includes software developed by +The Apache Software Foundation (http://www.apache.org/). Propchange: manifoldcf/integration/solr-6.x/trunk/NOTICE.txt ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/NOTICE.txt ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/README.txt URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/README.txt?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/README.txt (added) +++ manifoldcf/integration/solr-6.x/trunk/README.txt Tue Apr 5 05:22:04 2016 @@ -0,0 +1,231 @@ +# 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. + + +Compatibility +------------ + +This version of this component is fully functional with Apache ManifoldCF 1.6 and +above. It is backwards compatible with earlier versions as well, except for the fact +that two additional Solr fields are required for this plugin to work. + + +Instructions for Building Apache ManifoldCF Plugin for Apache Solr 6.x from Source +------------------------------------------------------------------------------ + +1. Download the Java SE 8 JDK (Java Development Kit), or greater, from + http://www.oracle.com/technetwork/java/index.html. + You will need the JDK installed, and the %JAVA_HOME%\bin directory included + on your command path. To test this, issue a "java -version" command from your + shell and verify that the Java version is 1.8 or greater. + +2. Download and install Maven 3.0 or later. Maven installation and configuration + instructions can be found here: http://maven.apache.org + +3. Building distribution assemblies + + Execute the following command in order to build the distribution assemblies + + mvn package assembly:assembly + + The JAR package can be found in the target folder: + + target/apache-manifoldcf-solr-6.x-plugin-.jar where is the release version + + +Getting Started +--------------- + +There are two ways to hook up security to Solr in this package. The first is using +a Query Parser plugin. The second is using a Search Component. In both cases, +the first step is to have ManifoldCF installed and running. See: +http://manifoldcf.apache.org/release/trunk/en_US/how-to-build-and-deploy.html + +Then, in order to store the document authorization information you need to add +'allow' and 'deny' fields for documents, parents, and shares to your Solr +index. Depending on your schemaFactory setting in solrconfig.xml you have to +use either the schema.xml file for 'ClassicIndexSchemaFactory' or the schema +API for 'ManagedIndexSchemaFactory'. +See section 'Managed Schema Definition in SolrConfig' in the Solr Reference Gude +https://www.apache.org/dyn/closer.cgi/lucene/solr/ref-guide/apache-solr-ref-guide-5.0.pdf + +For schema.xml simply add the following field definitions to the +section: + + + + + + + + +To define the fields via schema API use the curl command instead: + +curl -X POST -H 'Content-type:application/json' --data-binary '{ + "add-field" : [ + { "name":"allow_token_document", "type":"string", "indexed":"true", + "stored":"true", "multiValued":"true", "required":"true", + "default":"__nosecurity__" }, + { "name":"allow_token_parent", "type":"string", "indexed":"true", + "stored":"true", "multiValued":"true", "required":"true", + "default":"__nosecurity__" }, + { "name":"allow_token_share", "type":"string", "indexed":"true", + "stored":"true", "multiValued":"true", "required":"true", + "default":"__nosecurity__" }, + { "name":"deny_token_document", "type":"string", "indexed":"true", + "stored":"true", "multiValued":"true", "required":"true", + "default":"__nosecurity__" }, + { "name":"deny_token_parent", "type":"string", "indexed":"true", + "stored":"true", "multiValued":"true", "required":"true", + "default":"__nosecurity__" }, + { "name":"deny_token_share", "type":"string", "indexed":"true", + "stored":"true", "multiValued":"true", "required":"true", + "default":"__nosecurity__"} +]}' http://localhost:8983/solr//schema + +Replace with your core or collection name respectively. + +The default value of "__nosecurity__" is manadatory because the queries will be +rewritten to use all of these 6 fields. If a field is missing in the index then +you will get no results for your search. + +Check the field definitions with + +curl http://localhost:8983/solr//schema/fields + + +Using the Query Parser Plugin +----------------------------- + +To set up the query parser plugin, modify your solrconfig.xml to add the query parser: + + + + http://localhost:8345/mcf-authority-service + 50 + + +Hook up the search component in the solrconfig.xml file wherever you want it, e.g.: + + + + {!manifoldCFSecurity} + + ... + + + +Using the Search Component +-------------------------- + +To set up the search component, modify your solrconfig.xml to add the search component: + + + + http://localhost:8345/mcf-authority-service + 50 + + +Hook up the search component in the solrconfig.xml file wherever you want it, e.g.: + + + + manifoldCFSecurity + + ... + + + +Supplying authenticated usernames and domains +---------------------------------------------- + +This component looks for the following parameters in the Solr request object: + +AuthenticatedUserName +AuthenticatedUserDomain +AuthenticatedUserName_XX +AuthenticatedUserDomain_XX + +At a minimum, AuthenticatedUserName must be present in order for the component to communicate with +the ManifoldCF Authority Service and obtain user access tokens. In that case, the user identity will consist +of one user and one authorization domain. If AuthenticatedUserDomain is not set, then the authorization domain +chosen will be the standard default domain, an empty string. + +If you need multiple user/domain tuples for the user identity, you may pass these as parameter pairs starting with +AuthenticatedUserName_0 and AuthenticatedUserDomain_0, and counting up as high as you like. + +Operation in conjunction with mod-authz-annotate +------------------------------------------------ + +An optional component of ManifoldCF can be built and deployed as part of Apache - mod-authz-annotate. The +mod-authz-annotate module obtains the Kerberos principal from the Kerberos tickets present if mod-auth-kerb is used, and uses +the MCF Authority Service to look up the appropriate access tokens. If you choose to use that architecture, +you will still need to use this Solr component to modify the user query. All you have to do is the following: + +- Make sure you do not set AuthenticatedUserName or AuthenticatedUserName_0 in the request +- Make sure the HTTP request from Apache to Solr translates all AAAGRP header values into "UserToken" parameters + for the Solr request + + + +Licensing +--------- + +Apache ManifoldCF Plugin for Apache Solr 6.x is licensed under the +Apache License 2.0. See the files called LICENSE.txt and NOTICE.txt +for more information. + +Cryptographic Software Notice +----------------------------- + +This distribution may include software that has been designed for use +with cryptographic software. The country in which you currently reside +may have restrictions on the import, possession, use, and/or re-export +to another country, of encryption software. BEFORE using any encryption +software, please check your country's laws, regulations and policies +concerning the import, possession, or use, and re-export of encryption +software, to see if this is permitted. See +for more information. + +The U.S. Government Department of Commerce, Bureau of Industry and +Security (BIS), has classified this software as Export Commodity +Control Number (ECCN) 5D002.C.1, which includes information security +software using or performing cryptographic functions with asymmetric +algorithms. The form and manner of this Apache Software Foundation +distribution makes it eligible for export under the License Exception +ENC Technology Software Unrestricted (TSU) exception (see the BIS +Export Administration Regulations, Section 740.13) for both object +code and source code. + +The following provides more details on the included software that +may be subject to export controls on cryptographic software: + + The Apache ManifoldCF Solr 6.x Plugin does not include any + implementation or usage of cryptographic software at this time. + +Contact +------- + + o For general information visit the main project site at + http://manifoldcf.apache.org + Propchange: manifoldcf/integration/solr-6.x/trunk/README.txt ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/README.txt ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/pom.xml URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/pom.xml?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/pom.xml (added) +++ manifoldcf/integration/solr-6.x/trunk/pom.xml Tue Apr 5 05:22:04 2016 @@ -0,0 +1,125 @@ + + + + + 4.0.0 + org.apache.manifoldcf.solr + + ManifoldCF Solr 6.x Plugin + apache-manifoldcf-solr-6.x-plugin + 2.3-SNAPSHOT + jar + ManifoldCF Plugin for Apache Solr 6.x + 2015 + + + The Apache Software Foundation + http://www.apache.org/ + + + + UTF-8 + 6.0.0 + + + + + org.apache.solr + solr-test-framework + ${solr.version} + jar + test + + + + org.apache.solr + solr-core + ${solr.version} + jar + compile + + + + commons-logging + commons-logging + 1.1.1 + jar + test + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + -Xmx1024m + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + utf-8 + 1.7 + 1.7 + + + + + maven-jar-plugin + 2.5 + + + + ${project.name} + ${project.version} + The Apache Software Foundation + ${project.name} + ${project.version} + The Apache Software Foundation + org.apache + ${project.url} + + + + + + + maven-assembly-plugin + 2.5.3 + + + src/main/assembly/bin.xml + src/main/assembly/src.xml + + gnu + + + + + + + \ No newline at end of file Propchange: manifoldcf/integration/solr-6.x/trunk/pom.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/pom.xml ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml (added) +++ manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml Tue Apr 5 05:22:04 2016 @@ -0,0 +1,51 @@ + + + + bin + + tar.gz + zip + + + + + + target + + 644 + 755 + + ${project.artifactId}-${project.version}.jar + + + + + . + + 644 + 755 + + README.txt + LICENSE.txt + NOTICE.txt + CHANGES.txt + DEPENDENCIES.txt + + + + Propchange: manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/src/main/assembly/bin.xml ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml (added) +++ manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml Tue Apr 5 05:22:04 2016 @@ -0,0 +1,44 @@ + + + + src + + tar.gz + zip + + + + + . + + 644 + 755 + + **/.* + **/.*/** + **/*.iml/** + **/*.patch/** + **/*.sh/** + **/*.bat/** + **/target/** + **/lib/** + **/data/** + + + + Propchange: manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/src/main/assembly/src.xml ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFQParserPlugin.java URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFQParserPlugin.java?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFQParserPlugin.java (added) +++ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFQParserPlugin.java Tue Apr 5 05:22:04 2016 @@ -0,0 +1,431 @@ +/** +* 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.mcf; + +import org.apache.http.ParseException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.SocketConfig; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.lucene.index.*; +import org.apache.lucene.search.*; +import org.apache.solr.search.QParserPlugin; +import org.apache.solr.search.QParser; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.request.SolrQueryRequest; +import org.apache.solr.common.SolrException; +import org.apache.solr.core.CloseHook; +import org.apache.solr.core.SolrCore; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.slf4j.*; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.net.*; + +/** +* Query parser plugin for ManifoldCF-specific document-level access control. +*/ +public class ManifoldCFQParserPlugin extends QParserPlugin +{ + /** The parameter that is supposed to contain the authenticated user name, possibly including the AD domain */ + static final public String AUTHENTICATED_USER_NAME = "AuthenticatedUserName"; + /** The parameter that is supposed to contain the MCF authorization domain, if any */ + static final public String AUTHENTICATED_USER_DOMAIN = "AuthenticatedUserDomain"; + /** If there are more than one user/domain, this prefix will allow us to get the users... */ + static final public String AUTHENTICATED_USER_NAME_PREFIX = "AuthenticatedUserName_"; + /** If there are more than one user/domain, this prefix will allow us to get the authorization domains... */ + static final public String AUTHENTICATED_USER_DOMAIN_PREFIX = "AuthenticatedUserDomain_"; + + /** This parameter is an array of strings, which contain the tokens to use if there is no authenticated user name. + * It's meant to work with mod_authz_annotate, + * running under Apache */ + static final public String USER_TOKENS = "UserTokens"; + + /** Special token for null security fields */ + static final public String NOSECURITY_TOKEN = "__nosecurity__"; + + /** A logger we can use */ + private static final Logger LOG = LoggerFactory.getLogger(ManifoldCFQParserPlugin.class); + + // Member variables + String authorityBaseURL = null; + String fieldAllowDocument = null; + String fieldDenyDocument = null; + String fieldAllowShare = null; + String fieldDenyShare = null; + String fieldAllowParent = null; + String fieldDenyParent = null; + int connectionTimeOut; + int socketTimeOut; + final Integer connectionManagerSynchronizer = 0; + PoolingHttpClientConnectionManager httpConnectionManager = null; + HttpClient client = null; + int poolSize; + + public ManifoldCFQParserPlugin() + { + super(); + } + + @Override + public void init(NamedList args) + { + authorityBaseURL = (String)args.get("AuthorityServiceBaseURL"); + if (authorityBaseURL == null) + authorityBaseURL = "http://localhost:8345/mcf-authority-service"; + Integer cTimeOut = (Integer)args.get("ConnectionTimeOut"); + connectionTimeOut = cTimeOut == null ? 60000 : cTimeOut; + Integer timeOut = (Integer)args.get("SocketTimeOut"); + socketTimeOut = timeOut == null ? 300000 : timeOut; + String allowAttributePrefix = (String)args.get("AllowAttributePrefix"); + String denyAttributePrefix = (String)args.get("DenyAttributePrefix"); + if (allowAttributePrefix == null) + allowAttributePrefix = "allow_token_"; + if (denyAttributePrefix == null) + denyAttributePrefix = "deny_token_"; + fieldAllowDocument = allowAttributePrefix+"document"; + fieldDenyDocument = denyAttributePrefix+"document"; + fieldAllowShare = allowAttributePrefix+"share"; + fieldDenyShare = denyAttributePrefix+"share"; + fieldAllowParent = allowAttributePrefix+"parent"; + fieldDenyParent = denyAttributePrefix+"parent"; + Integer connectionPoolSize = (Integer)args.get("ConnectionPoolSize"); + poolSize = (connectionPoolSize==null)?50:connectionPoolSize; + } + + protected void initializeClient(SolrCore core) + { + synchronized (connectionManagerSynchronizer) + { + if (client == null) + { + // Initialize the connection pool + httpConnectionManager = new PoolingHttpClientConnectionManager(); + httpConnectionManager.setMaxTotal(poolSize); + httpConnectionManager.setDefaultMaxPerRoute(poolSize); + httpConnectionManager.setDefaultSocketConfig(SocketConfig.custom() + .setTcpNoDelay(true) + .setSoTimeout(socketTimeOut) + .build()); + + RequestConfig.Builder requestBuilder = RequestConfig.custom() + .setCircularRedirectsAllowed(true) + .setSocketTimeout(socketTimeOut) + .setStaleConnectionCheckEnabled(true) + .setExpectContinueEnabled(true) + .setConnectTimeout(connectionTimeOut) + .setConnectionRequestTimeout(socketTimeOut); + + HttpClientBuilder clientBuilder = HttpClients.custom() + .setConnectionManager(httpConnectionManager) + .disableAutomaticRetries() + .setDefaultRequestConfig(requestBuilder.build()) + .setRedirectStrategy(new DefaultRedirectStrategy()); + + client = clientBuilder.build(); + + core.addCloseHook(new CloseHandler()); + } + } + } + + @Override + public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + initializeClient(req.getCore()); + return new ManifoldCFQueryParser(qstr,localParams,params,req); + } + + protected class ManifoldCFQueryParser extends QParser + { + public ManifoldCFQueryParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) + { + super(qstr,localParams,params,req); + } + + @Override + /** Create and return the Query object represented by qstr + * @see #getQuery() + **/ + public Query parse() + { + SolrParams params = req.getParams(); + + List userAccessTokens; + + // Map from domain to user + Map domainMap = new HashMap<>(); + + // Get the authenticated user name from the parameters + String authenticatedUserName = params.get(AUTHENTICATED_USER_NAME); + if (authenticatedUserName != null) + { + String authenticatedUserDomain = params.get(AUTHENTICATED_USER_DOMAIN); + if (authenticatedUserDomain == null) + authenticatedUserDomain = ""; + domainMap.put(authenticatedUserDomain, authenticatedUserName); + } + else + { + // Look for user names/domains using the prefix + int i = 0; + while (true) + { + String userName = params.get(AUTHENTICATED_USER_NAME_PREFIX+i); + String domain = params.get(AUTHENTICATED_USER_DOMAIN_PREFIX+i); + if (userName == null) + break; + if (domain == null) + domain = ""; + domainMap.put(domain,userName); + i++; + } + } + + // If this parameter is empty or does not exist, we have to presume this is a guest, and treat them accordingly + if (domainMap.size() == 0) + { + // No authenticated user name. + // mod_authz_annotate may be in use upstream, so look for tokens from it. + userAccessTokens = new ArrayList<>(); + String[] passedTokens = params.getParams(USER_TOKENS); + if (passedTokens == null) + { + // Only return 'public' documents (those with no security tokens at all) + LOG.info("Default no-user response (open documents only)"); + } + else + { + // Only return 'public' documents (those with no security tokens at all) + LOG.info("Group tokens received from caller"); + userAccessTokens.addAll(Arrays.asList(passedTokens)); + } + } + else + { + if(LOG.isInfoEnabled()){ + StringBuilder sb = new StringBuilder("["); + boolean first = true; + for (String domain : domainMap.keySet()) + { + if (!first) + sb.append(","); + else + first = false; + sb.append(domain).append(":").append(domainMap.get(domain)); + } + sb.append("]"); + LOG.info("Trying to match docs for user '"+sb.toString()+"'"); + } + // Valid authenticated user name. Look up access tokens for the user. + // Check the configuration arguments for validity + if (authorityBaseURL == null) + { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error initializing ManifoldCFSecurityFilter component: 'AuthorityServiceBaseURL' init parameter required"); + } + try + { + userAccessTokens = getAccessTokens(domainMap); + } + catch (IOException e) + { + LOG.error("IO exception communicating with MCF authority service: "+e.getMessage(),e); + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "IO exception communicating with MCF authority service: "+e.getMessage()); + } + } + + BooleanQuery bq = new BooleanQuery(); + //bf.setMaxClauseCount(100000); + + Query allowShareOpen = new TermQuery(new Term(fieldAllowShare,NOSECURITY_TOKEN)); + Query denyShareOpen = new TermQuery(new Term(fieldDenyShare,NOSECURITY_TOKEN)); + Query allowParentOpen = new TermQuery(new Term(fieldAllowParent,NOSECURITY_TOKEN)); + Query denyParentOpen = new TermQuery(new Term(fieldDenyParent,NOSECURITY_TOKEN)); + Query allowDocumentOpen = new TermQuery(new Term(fieldAllowDocument,NOSECURITY_TOKEN)); + Query denyDocumentOpen = new TermQuery(new Term(fieldDenyDocument,NOSECURITY_TOKEN)); + + if (userAccessTokens.size() == 0) + { + // Only open documents can be included. + // That query is: + // (fieldAllowShare is empty AND fieldDenyShare is empty AND fieldAllowDocument is empty AND fieldDenyDocument is empty) + // We're trying to map to: -(fieldAllowShare:*) , which should be pretty efficient in Solr because it is negated. If this turns out not to be so, then we should + // have the SolrConnector inject a special token into these fields when they otherwise would be empty, and we can trivially match on that token. + bq.add(allowShareOpen,BooleanClause.Occur.MUST); + bq.add(denyShareOpen,BooleanClause.Occur.MUST); + bq.add(allowParentOpen,BooleanClause.Occur.MUST); + bq.add(denyParentOpen,BooleanClause.Occur.MUST); + bq.add(allowDocumentOpen,BooleanClause.Occur.MUST); + bq.add(denyDocumentOpen,BooleanClause.Occur.MUST); + } + else + { + // Extend the query appropriately for each user access token. + bq.add(calculateCompleteSubquery(fieldAllowShare,fieldDenyShare,allowShareOpen,denyShareOpen,userAccessTokens), + BooleanClause.Occur.MUST); + bq.add(calculateCompleteSubquery(fieldAllowParent,fieldDenyParent,allowParentOpen,denyParentOpen,userAccessTokens), + BooleanClause.Occur.MUST); + bq.add(calculateCompleteSubquery(fieldAllowDocument,fieldDenyDocument,allowDocumentOpen,denyDocumentOpen,userAccessTokens), + BooleanClause.Occur.MUST); + } + + return new ConstantScoreQuery(bq); + } + + /** Calculate a complete subclause, representing something like: + * ((fieldAllowShare is empty AND fieldDenyShare is empty) OR fieldAllowShare HAS token1 OR fieldAllowShare HAS token2 ...) + * AND fieldDenyShare DOESN'T_HAVE token1 AND fieldDenyShare DOESN'T_HAVE token2 ... + */ + protected Query calculateCompleteSubquery(String allowField, String denyField, Query allowOpen, Query denyOpen, List userAccessTokens) + { + BooleanQuery bq = new BooleanQuery(); + BooleanQuery.setMaxClauseCount(1000000); + + // Add the empty-acl case + BooleanQuery subUnprotectedClause = new BooleanQuery(); + subUnprotectedClause.add(allowOpen,BooleanClause.Occur.MUST); + subUnprotectedClause.add(denyOpen,BooleanClause.Occur.MUST); + bq.add(subUnprotectedClause,BooleanClause.Occur.SHOULD); + for (String accessToken : userAccessTokens) + { + bq.add(new TermQuery(new Term(allowField,accessToken)),BooleanClause.Occur.SHOULD); + bq.add(new TermQuery(new Term(denyField,accessToken)),BooleanClause.Occur.MUST_NOT); + } + return bq; + } + + // Protected methods + + /** Get access tokens given a username */ + protected List getAccessTokens(Map domainMap) + throws IOException + { + // We can make this more complicated later, with support for https etc., but this is enough to demonstrate how it all should work. + StringBuilder urlBuffer = new StringBuilder(authorityBaseURL); + urlBuffer.append("/UserACLs"); + int i = 0; + for (String domain : domainMap.keySet()) + { + if (i == 0) + urlBuffer.append("?"); + else + urlBuffer.append("&"); + // For backwards compatibility, handle the singleton case specially + if (domainMap.size() == 1 && domain.length() == 0) + { + urlBuffer.append("username=").append(URLEncoder.encode(domainMap.get(domain),"utf-8")); + } + else + { + urlBuffer.append("username_").append(Integer.toString(i)).append("=").append(URLEncoder.encode(domainMap.get(domain),"utf-8")).append("&") + .append("domain_").append(Integer.toString(i)).append("=").append(URLEncoder.encode(domain,"utf-8")); + } + i++; + } + String theURL = urlBuffer.toString(); + + HttpGet method = new HttpGet(theURL); + try + { + HttpResponse httpResponse = client.execute(method); + int rval = httpResponse.getStatusLine().getStatusCode(); + if (rval != 200) + { + String response = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8); + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,"Couldn't fetch user's access tokens from ManifoldCF authority service: "+Integer.toString(rval)+"; "+response); + } + + try(InputStream is = httpResponse.getEntity().getContent()) + { + Charset charSet; + try + { + ContentType ct = ContentType.get(httpResponse.getEntity()); + if (ct == null) + charSet = StandardCharsets.UTF_8; + else + charSet = ct.getCharset(); + }catch (ParseException e){ + charSet = StandardCharsets.UTF_8; + } + + try( Reader r = new InputStreamReader(is,charSet); BufferedReader br = new BufferedReader(r)) { + // Read the tokens, one line at a time. If any authorities are down, we have no current way to note that, but someday we will. + List tokenList = new ArrayList<>(); + while (true) { + String line = br.readLine(); + if (line == null) + break; + if (line.startsWith("TOKEN:")) { + tokenList.add(line.substring("TOKEN:".length())); + } else { + // It probably says something about the state of the authority(s) involved, so log it + LOG.info("Saw authority response " + line); + } + } + return tokenList; + } + } + } + finally + { + method.abort(); + } + } + } + + /** CloseHook implementation. + */ + protected class CloseHandler extends CloseHook + { + public CloseHandler() + { + } + + @Override + public void preClose(SolrCore core) + { + } + + @Override + public void postClose(SolrCore core) + { + synchronized (connectionManagerSynchronizer) + { + // Close the connection pool + if (httpConnectionManager != null) + { + httpConnectionManager.shutdown(); + httpConnectionManager = null; + client = null; + } + } + } + + } + +} \ No newline at end of file Propchange: manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFQParserPlugin.java ------------------------------------------------------------------------------ svn:eol-style = native Added: manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFSearchComponent.java URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFSearchComponent.java?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFSearchComponent.java (added) +++ manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFSearchComponent.java Tue Apr 5 05:22:04 2016 @@ -0,0 +1,472 @@ +/** +* 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.mcf; + +import org.apache.http.ParseException; +import org.apache.http.client.HttpClient; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.SocketConfig; +import org.apache.http.entity.ContentType; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.lucene.index.*; +import org.apache.lucene.search.*; +import org.apache.solr.common.SolrException; +import org.apache.solr.common.params.CommonParams; +import org.apache.solr.common.params.ShardParams; +import org.apache.solr.common.params.SolrParams; +import org.apache.solr.common.util.NamedList; +import org.apache.solr.handler.component.ResponseBuilder; +import org.apache.solr.handler.component.SearchComponent; +import org.apache.solr.core.CloseHook; +import org.apache.solr.util.plugin.SolrCoreAware; +import org.apache.solr.core.SolrCore; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.HttpResponse; +import org.apache.http.util.EntityUtils; +import org.apache.http.impl.client.DefaultRedirectStrategy; +import org.slf4j.*; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.net.*; + +/** +* SearchComponent plugin for ManifoldCF-specific document-level access control. +* Configuration is under the SolrACLSecurity name. +*/ +public class ManifoldCFSearchComponent extends SearchComponent implements SolrCoreAware +{ + /** The component name */ + static final public String COMPONENT_NAME = "mcf"; + /** The parameter that is supposed to contain the authenticated user name, possibly including the AD domain */ + static final public String AUTHENTICATED_USER_NAME = "AuthenticatedUserName"; + /** The parameter that is supposed to contain the MCF authorization domain, if any */ + static final public String AUTHENTICATED_USER_DOMAIN = "AuthenticatedUserDomain"; + /** If there are more than one user/domain, this prefix will allow us to get the users... */ + static final public String AUTHENTICATED_USER_NAME_PREFIX = "AuthenticatedUserName_"; + /** If there are more than one user/domain, this prefix will allow us to get the authorization domains... */ + static final public String AUTHENTICATED_USER_DOMAIN_PREFIX = "AuthenticatedUserDomain_"; + + /** This parameter is an array of strings, which contain the tokens to use if there is no authenticated user name. + * It's meant to work with mod_authz_annotate, + * running under Apache */ + static final public String USER_TOKENS = "UserTokens"; + + /** Special token for null security fields */ + static final public String NOSECURITY_TOKEN = "__nosecurity__"; + + /** The queries that we will not attempt to interfere with */ + static final private String[] globalAllowed = { "solrpingquery" }; + + /** A logger we can use */ + private static final Logger LOG = LoggerFactory.getLogger(ManifoldCFSearchComponent.class); + + // Member variables + String authorityBaseURL = null; + String fieldAllowDocument = null; + String fieldDenyDocument = null; + String fieldAllowShare = null; + String fieldDenyShare = null; + String fieldAllowParent = null; + String fieldDenyParent = null; + int connectionTimeOut; + int socketTimeOut; + PoolingHttpClientConnectionManager httpConnectionManager = null; + HttpClient client = null; + int poolSize; + + public ManifoldCFSearchComponent() + { + super(); + } + + @Override + public void init(NamedList args) + { + super.init(args); + authorityBaseURL = (String)args.get("AuthorityServiceBaseURL"); + if (authorityBaseURL == null) + { + LOG.info("USING DEFAULT BASE URL!!"); + authorityBaseURL = "http://localhost:8345/mcf-authority-service"; + } + Integer cTimeOut = (Integer)args.get("ConnectionTimeOut"); + connectionTimeOut = cTimeOut == null ? 60000 : cTimeOut; + Integer timeOut = (Integer)args.get("SocketTimeOut"); + socketTimeOut = timeOut == null ? 300000 : timeOut; + String allowAttributePrefix = (String)args.get("AllowAttributePrefix"); + String denyAttributePrefix = (String)args.get("DenyAttributePrefix"); + if (allowAttributePrefix == null) + allowAttributePrefix = "allow_token_"; + if (denyAttributePrefix == null) + denyAttributePrefix = "deny_token_"; + fieldAllowDocument = allowAttributePrefix+"document"; + fieldDenyDocument = denyAttributePrefix+"document"; + fieldAllowShare = allowAttributePrefix+"share"; + fieldDenyShare = denyAttributePrefix+"share"; + fieldAllowParent = allowAttributePrefix+"parent"; + fieldDenyParent = denyAttributePrefix+"parent"; + Integer connectionPoolSize = (Integer)args.get("ConnectionPoolSize"); + poolSize = (connectionPoolSize==null)?50:connectionPoolSize; + + // Initialize the connection pool + httpConnectionManager = new PoolingHttpClientConnectionManager(); + httpConnectionManager.setMaxTotal(poolSize); + httpConnectionManager.setDefaultMaxPerRoute(poolSize); + httpConnectionManager.setDefaultSocketConfig(SocketConfig.custom() + .setTcpNoDelay(true) + .setSoTimeout(socketTimeOut) + .build()); + + RequestConfig.Builder requestBuilder = RequestConfig.custom() + .setCircularRedirectsAllowed(true) + .setSocketTimeout(socketTimeOut) + .setStaleConnectionCheckEnabled(true) + .setExpectContinueEnabled(true) + .setConnectTimeout(connectionTimeOut) + .setConnectionRequestTimeout(socketTimeOut); + + HttpClientBuilder clientBuilder = HttpClients.custom() + .setConnectionManager(httpConnectionManager) + .disableAutomaticRetries() + .setDefaultRequestConfig(requestBuilder.build()) + .setRedirectStrategy(new DefaultRedirectStrategy()); + + client = clientBuilder.build(); + + } + + @Override + public void prepare(ResponseBuilder rb) throws IOException + { + SolrParams params = rb.req.getParams(); + if (!params.getBool(COMPONENT_NAME, true) || params.get(ShardParams.SHARDS) != null) + return; + + // Log that we got here + //LOG.info("prepare() entry params:\n" + params + "\ncontext: " + rb.req.getContext()); + + String qry = params.get(CommonParams.Q); + if (qry != null) + { + //Check global allowed searches + for (String ga : globalAllowed) + { + if (qry.equalsIgnoreCase(ga.trim())) + // Allow this query through unchanged + return; + } + } + + List userAccessTokens; + + // Map from domain to user + Map domainMap = new HashMap<>(); + + // Get the authenticated user name from the parameters + String authenticatedUserName = params.get(AUTHENTICATED_USER_NAME); + if (authenticatedUserName != null) + { + String authenticatedUserDomain = params.get(AUTHENTICATED_USER_DOMAIN); + if (authenticatedUserDomain == null) + authenticatedUserDomain = ""; + domainMap.put(authenticatedUserDomain, authenticatedUserName); + } + else + { + // Look for user names/domains using the prefix + int i = 0; + while (true) + { + String userName = params.get(AUTHENTICATED_USER_NAME_PREFIX+i); + String domain = params.get(AUTHENTICATED_USER_DOMAIN_PREFIX+i); + if (userName == null) + break; + if (domain == null) + domain = ""; + domainMap.put(domain,userName); + i++; + } + } + + // If this parameter is empty or does not exist, we have to presume this is a guest, and treat them accordingly + if (domainMap.size() == 0) + { + // No authenticated user name. + // mod_authz_annotate may be in use upstream, so look for tokens from it. + userAccessTokens = new ArrayList<>(); + String[] passedTokens = params.getParams(USER_TOKENS); + if (passedTokens == null) + { + // Only return 'public' documents (those with no security tokens at all) + LOG.info("Default no-user response (open documents only)"); + } + else + { + // Only return 'public' documents (those with no security tokens at all) + LOG.info("Group tokens received from caller"); + userAccessTokens.addAll(Arrays.asList(passedTokens)); + } + } + else + { + if(LOG.isInfoEnabled()){ + StringBuilder sb = new StringBuilder("["); + boolean first = true; + for (String domain : domainMap.keySet()) + { + if (!first) + sb.append(","); + else + first = false; + sb.append(domain).append(":").append(domainMap.get(domain)); + } + sb.append("]"); + LOG.info("Trying to match docs for user '"+sb.toString()+"'"); + } + // Valid authenticated user name. Look up access tokens for the user. + // Check the configuration arguments for validity + if (authorityBaseURL == null) + { + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Error initializing ManifoldCFSecurityFilter component: 'AuthorityServiceBaseURL' init parameter required"); + } + userAccessTokens = getAccessTokens(domainMap); + } + + BooleanQuery bq = new BooleanQuery(); + //bf.setMaxClauseCount(100000); + + Query allowShareOpen = new TermQuery(new Term(fieldAllowShare,NOSECURITY_TOKEN)); + Query denyShareOpen = new TermQuery(new Term(fieldDenyShare,NOSECURITY_TOKEN)); + Query allowParentOpen = new TermQuery(new Term(fieldAllowParent,NOSECURITY_TOKEN)); + Query denyParentOpen = new TermQuery(new Term(fieldDenyParent,NOSECURITY_TOKEN)); + Query allowDocumentOpen = new TermQuery(new Term(fieldAllowDocument,NOSECURITY_TOKEN)); + Query denyDocumentOpen = new TermQuery(new Term(fieldDenyDocument,NOSECURITY_TOKEN)); + + if (userAccessTokens.size() == 0) + { + // Only open documents can be included. + // That query is: + // (fieldAllowShare is empty AND fieldDenyShare is empty AND fieldAllowDocument is empty AND fieldDenyDocument is empty) + // We're trying to map to: -(fieldAllowShare:*) , which should be pretty efficient in Solr because it is negated. If this turns out not to be so, then we should + // have the SolrConnector inject a special token into these fields when they otherwise would be empty, and we can trivially match on that token. + bq.add(allowShareOpen,BooleanClause.Occur.MUST); + bq.add(denyShareOpen,BooleanClause.Occur.MUST); + bq.add(allowParentOpen,BooleanClause.Occur.MUST); + bq.add(denyParentOpen,BooleanClause.Occur.MUST); + bq.add(allowDocumentOpen,BooleanClause.Occur.MUST); + bq.add(denyDocumentOpen,BooleanClause.Occur.MUST); + } + else + { + // Extend the query appropriately for each user access token. + bq.add(calculateCompleteSubquery(fieldAllowShare,fieldDenyShare,allowShareOpen,denyShareOpen,userAccessTokens), + BooleanClause.Occur.MUST); + bq.add(calculateCompleteSubquery(fieldAllowParent,fieldDenyParent,allowParentOpen,denyParentOpen,userAccessTokens), + BooleanClause.Occur.MUST); + bq.add(calculateCompleteSubquery(fieldAllowDocument,fieldDenyDocument,allowDocumentOpen,denyDocumentOpen,userAccessTokens), + BooleanClause.Occur.MUST); + } + + // Concatenate with the user's original query. + List list = rb.getFilters(); + if (list == null) + { + list = new ArrayList<>(); + rb.setFilters(list); + } + list.add(new ConstantScoreQuery(bq)); + } + + @Override + public void process(ResponseBuilder rb) throws IOException + { + //LOG.info("process() called"); + } + + /** Calculate a complete subclause, representing something like: + * ((fieldAllowShare is empty AND fieldDenyShare is empty) OR fieldAllowShare HAS token1 OR fieldAllowShare HAS token2 ...) + * AND fieldDenyShare DOESN'T_HAVE token1 AND fieldDenyShare DOESN'T_HAVE token2 ... + */ + protected Query calculateCompleteSubquery(String allowField, String denyField, Query allowOpen, Query denyOpen, List userAccessTokens) + { + BooleanQuery bq = new BooleanQuery(); + BooleanQuery.setMaxClauseCount(1000000); + + // Add the empty-acl case + BooleanQuery subUnprotectedClause = new BooleanQuery(); + subUnprotectedClause.add(allowOpen,BooleanClause.Occur.MUST); + subUnprotectedClause.add(denyOpen,BooleanClause.Occur.MUST); + bq.add(subUnprotectedClause,BooleanClause.Occur.SHOULD); + for (String accessToken : userAccessTokens) + { + bq.add(new TermQuery(new Term(allowField,accessToken)),BooleanClause.Occur.SHOULD); + bq.add(new TermQuery(new Term(denyField,accessToken)),BooleanClause.Occur.MUST_NOT); + } + return bq; + } + + //--------------------------------------------------------------------------------- + // SolrInfoMBean + //--------------------------------------------------------------------------------- + @Override + public String getDescription() + { + return "ManifoldCF Solr security enforcement plugin"; + } + + @Override + public String getVersion() + { + return "$Revision$"; + } + + @Override + public Category getCategory() + { + return Category.QUERYHANDLER; + } + + @Override + public String getSource() + { + return "$URL$"; + } + + @Override + public void inform(SolrCore core) + { + core.addCloseHook(new CloseHandler()); + } + + // Protected methods + + /** Get access tokens given a username */ + protected List getAccessTokens(Map domainMap) + throws IOException + { + // We can make this more complicated later, with support for https etc., but this is enough to demonstrate how it all should work. + StringBuilder urlBuffer = new StringBuilder(authorityBaseURL); + urlBuffer.append("/UserACLs"); + int i = 0; + for (String domain : domainMap.keySet()) + { + if (i == 0) + urlBuffer.append("?"); + else + urlBuffer.append("&"); + // For backwards compatibility, handle the singleton case specially + if (domainMap.size() == 1 && domain.length() == 0) + { + urlBuffer.append("username=").append(URLEncoder.encode(domainMap.get(domain),"utf-8")); + } + else + { + urlBuffer.append("username_").append(Integer.toString(i)).append("=").append(URLEncoder.encode(domainMap.get(domain),"utf-8")).append("&") + .append("domain_").append(Integer.toString(i)).append("=").append(URLEncoder.encode(domain,"utf-8")); + } + i++; + } + String theURL = urlBuffer.toString(); + + HttpGet method = new HttpGet(theURL); + try + { + HttpResponse httpResponse = client.execute(method); + int rval = httpResponse.getStatusLine().getStatusCode(); + if (rval != 200) + { + String response = EntityUtils.toString(httpResponse.getEntity(), StandardCharsets.UTF_8); + throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,"Couldn't fetch user's access tokens from ManifoldCF authority service: "+Integer.toString(rval)+"; "+response); + } + + try(InputStream is = httpResponse.getEntity().getContent()) + { + Charset charSet; + try + { + ContentType ct = ContentType.get(httpResponse.getEntity()); + if (ct == null) + charSet = StandardCharsets.UTF_8; + else + charSet = ct.getCharset(); + }catch (ParseException e){ + charSet = StandardCharsets.UTF_8; + } + + + try(Reader r = new InputStreamReader(is,charSet); BufferedReader br = new BufferedReader(r)) + { + // Read the tokens, one line at a time. If any authorities are down, we have no current way to note that, but someday we will. + List tokenList = new ArrayList<>(); + while (true) + { + String line = br.readLine(); + if (line == null) + break; + if (line.startsWith("TOKEN:")) + { + tokenList.add(line.substring("TOKEN:".length())); + } + else + { + // It probably says something about the state of the authority(s) involved, so log it + LOG.info("Saw authority response "+line); + } + } + return tokenList; + } + } + } + finally + { + method.abort(); + } + } + + /** CloseHook implementation. + */ + protected class CloseHandler extends CloseHook + { + public CloseHandler() + { + } + + @Override + public void preClose(SolrCore core) + { + } + + @Override + public void postClose(SolrCore core) + { + // Close the connection pool + if (httpConnectionManager != null) + { + httpConnectionManager.shutdown(); + httpConnectionManager = null; + client = null; + } + } + + } + +} \ No newline at end of file Propchange: manifoldcf/integration/solr-6.x/trunk/src/main/java/org/apache/solr/mcf/ManifoldCFSearchComponent.java ------------------------------------------------------------------------------ svn:eol-style = native Added: manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java (added) +++ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java Tue Apr 5 05:22:04 2016 @@ -0,0 +1,184 @@ +/** +* 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.mcf; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.solr.SolrTestCaseJ4; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +public class ManifoldCFQParserPluginTest extends SolrTestCaseJ4 { + + static MockMCFAuthorityService service; + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig-auth-qparser.xml","schema-auth.xml"); + service = new MockMCFAuthorityService(); + service.start(); + + // | share | document + // |--------------|-------------- + // | allow | deny | allow | deny + // ------------+-------+------+-------+------ + // da12 | | | 1, 2 | + // ------------+-------+------+-------+------ + // da13-dd3 | | | 1,3 | 3 + // ------------+-------+------+-------+------ + // sa123-sd13 | 1,2,3 | 1, 3 | | + // ------------+-------+------+-------+------ + // sa3-sd1-da23| 3 | 1 | 2,3 | + // ------------+-------+------+-------+------ + // notoken | | | | + // ------------+-------+------+-------+------ + // + assertU(adoc("id", "da12", "allow_token_document", "token1", "allow_token_document", "token2")); + assertU(adoc("id", "da13-dd3", "allow_token_document", "token1", "allow_token_document", "token3", "deny_token_document", "token3")); + assertU(adoc("id", "sa123-sd13", "allow_token_share", "token1", "allow_token_share", "token2", "allow_token_share", "token3", "deny_token_share", "token1", "deny_token_share", "token3")); + assertU(adoc("id", "sa3-sd1-da23", "allow_token_document", "token2", "allow_token_document", "token3", "allow_token_share", "token3", "deny_token_share", "token1")); + assertU(adoc("id", "notoken")); + assertU(commit()); + } + + @AfterClass + public static void afterClass() throws Exception { + service.stop(); + } + + @Test + public void testNullUsers() throws Exception { + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id"), + "//*[@numFound='1']", + "//result/doc[1]/str[@name='id'][.='notoken']"); + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "anonymous"), + "//*[@numFound='1']", + "//result/doc[1]/str[@name='id'][.='notoken']"); + } + + // da12 + // da13-dd3 + // sa123-sd13 + // sa3-sd1-da23 + // notoken + @Test + public void testAuthUsers() throws Exception { + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "user1"), + "//*[@numFound='3']", + "//result/doc[1]/str[@name='id'][.='da12']", + "//result/doc[2]/str[@name='id'][.='da13-dd3']", + "//result/doc[3]/str[@name='id'][.='notoken']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "user2"), + "//*[@numFound='3']", + "//result/doc[1]/str[@name='id'][.='da12']", + "//result/doc[2]/str[@name='id'][.='da13-dd3']", + "//result/doc[3]/str[@name='id'][.='notoken']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "user3"), + "//*[@numFound='2']", + "//result/doc[1]/str[@name='id'][.='da12']", + "//result/doc[2]/str[@name='id'][.='notoken']"); + } + + // da12 + // da13-dd3 + // sa123-sd13 + // sa3-sd1-da23 + // notoken + @Test + public void testUserTokens() throws Exception { + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "UserTokens", "token1"), + "//*[@numFound='3']", + "//result/doc[1]/str[@name='id'][.='da12']", + "//result/doc[2]/str[@name='id'][.='da13-dd3']", + "//result/doc[3]/str[@name='id'][.='notoken']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "UserTokens", "token2"), + "//*[@numFound='3']", + "//result/doc[1]/str[@name='id'][.='da12']", + "//result/doc[2]/str[@name='id'][.='sa123-sd13']", + "//result/doc[3]/str[@name='id'][.='notoken']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "UserTokens", "token3"), + "//*[@numFound='2']", + "//result/doc[1]/str[@name='id'][.='sa3-sd1-da23']", + "//result/doc[2]/str[@name='id'][.='notoken']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "UserTokens", "token2", "UserTokens", "token3"), + "//*[@numFound='3']", + "//result/doc[1]/str[@name='id'][.='da12']", + "//result/doc[2]/str[@name='id'][.='sa3-sd1-da23']", + "//result/doc[3]/str[@name='id'][.='notoken']"); + } + + static class MockMCFAuthorityService { + + Server server; + + public MockMCFAuthorityService() { + server = new Server(8347); + ContextHandlerCollection contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + + ServletContextHandler asContext = new ServletContextHandler(contexts,"/mcf-as",ServletContextHandler.SESSIONS); + asContext.addServlet(new ServletHolder(new UserACLServlet()), "/UserACLs"); + contexts.addHandler(asContext); + } + + public void start() throws Exception { + server.start(); + } + + public void stop() throws Exception { + server.stop(); + } + + // username | tokens rewarded + // ---------+------------------------------- + // null | (no tokens) + // user1 | token1 + // user2 | token1, token2 + // user3 | token1, token2, token3 + public static class UserACLServlet extends HttpServlet { + @Override + public void service(HttpServletRequest req, HttpServletResponse res) + throws IOException { + String user = req.getParameter("username"); + res.setStatus(HttpServletResponse.SC_OK); + if(user.equals("user1") || user.equals("user2") || user.equals("user3")) + res.getWriter().printf("TOKEN:token1\n"); + if(user.equals("user2") || user.equals("user3")) + res.getWriter().printf("TOKEN:token2\n"); + if(user.equals("user3")) + res.getWriter().printf("TOKEN:token3\n"); + } + } + } +} Propchange: manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFQParserPluginTest.java ------------------------------------------------------------------------------ svn:keywords = Id Added: manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFSCLoadTest.java URL: http://svn.apache.org/viewvc/manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFSCLoadTest.java?rev=1737771&view=auto ============================================================================== --- manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFSCLoadTest.java (added) +++ manifoldcf/integration/solr-6.x/trunk/src/test/java/org/apache/solr/mcf/ManifoldCFSCLoadTest.java Tue Apr 5 05:22:04 2016 @@ -0,0 +1,150 @@ +/** +* 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.mcf; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.solr.SolrTestCaseJ4; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import org.eclipse.jetty.server.handler.ContextHandlerCollection; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; + +public class ManifoldCFSCLoadTest extends SolrTestCaseJ4 { + + static MockMCFAuthorityService service; + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig-auth-load.xml","schema-auth.xml"); + service = new MockMCFAuthorityService(); + service.start(); + + // | share | document + // |--------------|-------------- + // | allow | deny | allow | deny + // ------------+-------+------+-------+------ + // da12 | | | 1, 2 | + // ------------+-------+------+-------+------ + // da13-dd3 | | | 1,3 | 3 + // ------------+-------+------+-------+------ + // sa123-sd13 | 1,2,3 | 1, 3 | | + // ------------+-------+------+-------+------ + // sa3-sd1-da23| 3 | 1 | 2,3 | + // ------------+-------+------+-------+------ + // notoken | | | | + // ------------+-------+------+-------+------ + // + int i = 0; + while (i < 1000) + { + assertU(adoc("id", "da12-"+i, "allow_token_document", "token1", "allow_token_document", "token2")); + assertU(adoc("id", "da13-dd3-"+i, "allow_token_document", "token1", "allow_token_document", "token3", "deny_token_document", "token3")); + assertU(adoc("id", "sa123-sd13-"+i, "allow_token_share", "token1", "allow_token_share", "token2", "allow_token_share", "token3", "deny_token_share", "token1", "deny_token_share", "token3")); + assertU(adoc("id", "sa3-sd1-da23-"+i, "allow_token_document", "token2", "allow_token_document", "token3", "allow_token_share", "token3", "deny_token_share", "token1")); + assertU(adoc("id", "notoken-"+i)); + i++; + } + assertU(optimize()); + assertU(commit()); + } + + @AfterClass + public static void afterClass() throws Exception { + service.stop(); + } + + @Test + public void testTimeQueries() throws Exception { + int i = 0; + long startTime = System.nanoTime(); + while (i < 1000) + { + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "user1"), + "//*[@numFound='3000']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "user2"), + "//*[@numFound='3000']"); + + assertQ(req("qt", "/mcf", "q", "*:*", "fl", "id", "AuthenticatedUserName", "user3"), + "//*[@numFound='2000']"); + + i++; + } + System.out.println("Query time (milliseconds) = " + TimeUnit.NANOSECONDS.toMillis(System.nanoTime()-startTime)); + } + + static class MockMCFAuthorityService { + + Server server; + + public MockMCFAuthorityService() { + server = new Server(8346); + ContextHandlerCollection contexts = new ContextHandlerCollection(); + server.setHandler(contexts); + + ServletContextHandler asContext = new ServletContextHandler(contexts,"/mcf-as",ServletContextHandler.SESSIONS); + asContext.addServlet(new ServletHolder(new UserACLServlet()), "/UserACLs"); + contexts.addHandler(asContext); + } + + public void start() throws Exception { + server.start(); + } + + public void stop() throws Exception { + server.stop(); + } + + // username | tokens rewarded + // ---------+------------------------------- + // null | (no tokens) + // user1 | token1 + // user2 | token1, token2 + // user3 | token1, token2, token3 + public static class UserACLServlet extends HttpServlet { + @Override + public void service(HttpServletRequest req, HttpServletResponse res) + throws IOException { + String user = req.getParameter("username"); + res.setStatus(HttpServletResponse.SC_OK); + if(user.equals("user1") || user.equals("user2") || user.equals("user3")) + res.getWriter().printf("TOKEN:token1\n"); + if(user.equals("user2") || user.equals("user3")) + res.getWriter().printf("TOKEN:token2\n"); + if(user.equals("user3")) + res.getWriter().printf("TOKEN:token3\n"); + int i = 0; + while (i < 100) + { + res.getWriter().printf("TOKEN:dummy"+i+"\n"); + i++; + } + } + } + } +}