diff --git a/nifi-assembly/pom.xml b/nifi-assembly/pom.xml index 718bce7d1578..2f2034345de7 100644 --- a/nifi-assembly/pom.xml +++ b/nifi-assembly/pom.xml @@ -1119,6 +1119,57 @@ language governing permissions and limitations under the License. --> + + include-ranger + + false + + allProfiles + + + + + + maven-assembly-plugin + + + make shared resource + + single + + package + + nifi-${project.version} + false + + 0775 + 0775 + 0664 + + + src/main/assembly/ranger.xml + + posix + + + + + + + + + org.apache.nifi + nifi-ranger-nar + 2.10.0.1 + nar + + + org.apache.nifi + nifi-ranger-resources + 2.10.0.1 + + + include-asn1 diff --git a/nifi-assembly/src/main/assembly/ranger.xml b/nifi-assembly/src/main/assembly/ranger.xml new file mode 100644 index 000000000000..e395cc532a66 --- /dev/null +++ b/nifi-assembly/src/main/assembly/ranger.xml @@ -0,0 +1,92 @@ + + + + bin + + dir + zip + tar.gz + + true + nifi-${project.version} + + + src/main/assembly/common.xml + + + + + + runtime + false + lib + 0770 + 0660 + true + + *:nifi-bootstrap-utils + *:nifi-bootstrap + *:nifi-property-protection-api + *:nifi-property-protection-factory + *:nifi-resources + *:nifi-docs + org.apache.nifi:nifi-ranger-resources:jar + + + + org.glassfish.jaxb:jaxb-runtime + jakarta.xml.bind:jakarta.xml.bind-api + javax.annotation:javax.annotation-api + + + + + runtime + false + ext/ranger/install/lib/ + 0770 + 0660 + true + + org.apache.nifi:nifi-ranger-resources:jar + org.slf4j:slf4j-api + org.slf4j:jcl-over-slf4j + org.apache.commons:commons-lang3 + com.google.guava:guava + + + + + runtime + false + ext/ranger/ + 0770 + 0770 + false + + org.apache.nifi:nifi-ranger-resources:jar + + true + + true + + scripts/ + + + + + + diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/pom.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/pom.xml new file mode 100644 index 000000000000..f4e066ebc553 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/pom.xml @@ -0,0 +1,35 @@ + + + + 4.0.0 + + + org.apache.nifi + nifi-ranger-bundle + 2.10.0.1 + + + nifi-ranger-nar + nar + + + + org.apache.nifi + nifi-ranger-plugin + 2.10.0.1 + + + diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/src/main/resources/META-INF/LICENSE b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/src/main/resources/META-INF/LICENSE new file mode 100644 index 000000000000..2bb247e447b9 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/src/main/resources/META-INF/LICENSE @@ -0,0 +1,404 @@ + + 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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +APACHE NIFI SUBCOMPONENTS: + +The Apache NiFi project contains subcomponents with separate copyright +notices and license terms. Your use of the source code for the these +subcomponents is subject to the terms and conditions of the following +licenses. + + The binary distribution of this product bundles 'JOpt Simple' under an MIT + style license. + + Copyright (c) 2009 Paul R. Holser, Jr. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + The binary distribution of this product bundles 'JCraft Jsch' which is available + under a BSD style license. + + Copyright (c) 2002-2015 Atsuhiko Yamanaka, JCraft,Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The binary distribution of this product bundles 'Protocol Buffers - Google's data interchange format' + which is available under a BSD style license. + + Copyright 2008 Google Inc. All rights reserved. + http://code.google.com/p/protobuf/ + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The binary distribution of this product bundles 'Woodstox StAX 2 API' which is + "licensed under standard BSD license" + + The binary distribution of this product bundles 'Azure SDK for Java' which is available under an MIT license. + + Copyright (c) 2015 Microsoft Azure + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + associated documentation files (the Software), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject + to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT + LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + The binary distribution of this product bundles 'Bouncy Castle' which is available under + the Bouncy Castle License (an MIT style license). + + Copyright (c) 2000-2023 The Legion of the Bouncy Castle Inc. (https://www.bouncycastle.org) + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial + portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + The binary distribution of this product bundles 'dnsjava' which is available under a BSD 3-Clause license. + + Copyright (c) 1998-2019, Brian Wellington + Copyright (c) 2005 VeriSign. All rights reserved. + Copyright (c) 2019-2023, dnsjava authors + + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + The binary distribution of this product bundles 'zstd-jni', which bundles + the 'Zstandard' (Zstd) library, available under a BSD license. + + BSD License + + For Zstandard software + + Copyright (c) 2016-present, Facebook, Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/src/main/resources/META-INF/NOTICE b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/src/main/resources/META-INF/NOTICE new file mode 100644 index 000000000000..97c6efd4481f --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-nar/src/main/resources/META-INF/NOTICE @@ -0,0 +1,386 @@ +nifi-ranger-nar +Copyright 2014-2024 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (http://www.apache.org/). + +****************** +Apache Software License v2 +****************** + + (ASLv2) Apache Avro + The following NOTICE information applies: + Apache Avro + Copyright 2009-2017 The Apache Software Foundation + + (ASLv2) Apache Commons Collections + The following NOTICE information applies: + Apache Commons Collections + Copyright 2001-2013 The Apache Software Foundation + + (ASLv2) Apache Commons Compress + The following NOTICE information applies: + Apache Commons Compress + Copyright 2002-2017 The Apache Software Foundation + + The files in the package org.apache.commons.compress.archivers.sevenz + were derived from the LZMA SDK, version 9.20 (C/ and CPP/7zip/), + which has been placed in the public domain: + + "LZMA SDK is placed in the public domain." (http://www.7-zip.org/sdk.html) + + (ASLv2) Apache Commons Codec + The following NOTICE information applies: + Apache Commons Codec + Copyright 2002-2014 The Apache Software Foundation + + src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java + contains test data from http://aspell.net/test/orig/batch0.tab. + Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) + + =============================================================================== + + The content of package org.apache.commons.codec.language.bm has been translated + from the original php source code available at http://stevemorse.org/phoneticinfo.htm + with permission from the original authors. + Original source copyright: + Copyright (c) 2008 Alexander Beider & Stephen P. Morse. + + (ASLv2) Apache Commons CLI + The following NOTICE information applies: + Apache Commons CLI + Copyright 2001-2009 The Apache Software Foundation + + (ASLv2) Apache Commons Configuration + The following NOTICE information applies: + Apache Commons Configuration + Copyright 2001-2008 The Apache Software Foundation + + (ASLv2) Apache Commons EL + The following NOTICE information applies: + Apache Commons EL + Copyright 1999-2007 The Apache Software Foundation + + EL-8 patch - Copyright 2004-2007 Jamie Taylor + http://issues.apache.org/jira/browse/EL-8 + + (ASLv2) Apache Directory Server + The following NOTICE information applies: + ApacheDS Protocol Kerberos Codec + Copyright 2003-2013 The Apache Software Foundation + + ApacheDS I18n + Copyright 2003-2013 The Apache Software Foundation + + Apache Directory API ASN.1 API + Copyright 2003-2013 The Apache Software Foundation + + Apache Directory LDAP API Utilities + Copyright 2003-2013 The Apache Software Foundation + + (ASLv2) Apache Jakarta HttpClient + The following NOTICE information applies: + Apache Jakarta HttpClient + Copyright 1999-2007 The Apache Software Foundation + + (ASLv2) Apache Commons IO + The following NOTICE information applies: + Apache Commons IO + Copyright 2002-2016 The Apache Software Foundation + + (ASLv2) Apache Commons Lang + The following NOTICE information applies: + Apache Commons Lang + Copyright 2001-2015 The Apache Software Foundation + + This product includes software from the Spring Framework, + under the Apache License 2.0 (see: StringUtils.containsWhitespace()) + + (ASLv2) Apache Commons Math + The following NOTICE information applies: + Apache Commons Math + Copyright 2001-2012 The Apache Software Foundation + + This product includes software developed by + The Apache Software Foundation (http://www.apache.org/). + + =============================================================================== + + The BracketFinder (package org.apache.commons.math3.optimization.univariate) + and PowellOptimizer (package org.apache.commons.math3.optimization.general) + classes are based on the Python code in module "optimize.py" (version 0.5) + developed by Travis E. Oliphant for the SciPy library (http://www.scipy.org/) + Copyright © 2003-2009 SciPy Developers. + =============================================================================== + + The LinearConstraint, LinearObjectiveFunction, LinearOptimizer, + RelationShip, SimplexSolver and SimplexTableau classes in package + org.apache.commons.math3.optimization.linear include software developed by + Benjamin McCann (http://www.benmccann.com) and distributed with + the following copyright: Copyright 2009 Google Inc. + =============================================================================== + + This product includes software developed by the + University of Chicago, as Operator of Argonne National + Laboratory. + The LevenbergMarquardtOptimizer class in package + org.apache.commons.math3.optimization.general includes software + translated from the lmder, lmpar and qrsolv Fortran routines + from the Minpack package + Minpack Copyright Notice (1999) University of Chicago. All rights reserved + =============================================================================== + + The GraggBulirschStoerIntegrator class in package + org.apache.commons.math3.ode.nonstiff includes software translated + from the odex Fortran routine developed by E. Hairer and G. Wanner. + Original source copyright: + Copyright (c) 2004, Ernst Hairer + =============================================================================== + + The EigenDecompositionImpl class in package + org.apache.commons.math3.linear includes software translated + from some LAPACK Fortran routines. Original source copyright: + Copyright (c) 1992-2008 The University of Tennessee. All rights reserved. + =============================================================================== + + The MersenneTwister class in package org.apache.commons.math3.random + includes software translated from the 2002-01-26 version of + the Mersenne-Twister generator written in C by Makoto Matsumoto and Takuji + Nishimura. Original source copyright: + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved + =============================================================================== + + The LocalizedFormatsTest class in the unit tests is an adapted version of + the OrekitMessagesTest class from the orekit library distributed under the + terms of the Apache 2 licence. Original source copyright: + Copyright 2010 CS Systèmes d'Information + =============================================================================== + + The HermiteInterpolator class and its corresponding test have been imported from + the orekit library distributed under the terms of the Apache 2 licence. Original + source copyright: + Copyright 2010-2012 CS Systèmes d'Information + =============================================================================== + + The creation of the package "o.a.c.m.analysis.integration.gauss" was inspired + by an original code donated by Sébastien Brisard. + =============================================================================== + + (ASLv2) Apache Commons Net + The following NOTICE information applies: + Apache Commons Net + Copyright 2001-2013 The Apache Software Foundation + + (ASLv2) Apache Curator + The following NOTICE information applies: + Curator Framework + Copyright 2011-2014 The Apache Software Foundation + + Curator Client + Copyright 2011-2014 The Apache Software Foundation + + Curator Recipes + Copyright 2011-2014 The Apache Software Foundation + + (ASLv2) Apache HttpComponents + The following NOTICE information applies: + Apache HttpClient + Copyright 1999-2015 The Apache Software Foundation + + Apache HttpCore + Copyright 2005-2015 The Apache Software Foundation + + Apache HttpMime + Copyright 1999-2013 The Apache Software Foundation + + This project contains annotations derived from JCIP-ANNOTATIONS + Copyright (c) 2005 Brian Goetz and Tim Peierls. See http://www.jcip.net + + (ASLv2) Apache Ranger + The following NOTICE information applies: + Apache Ranger Credential Builder + Copyright 2014-2016 The Apache Software Foundation + + Apache Ranger Plugins Audit + Copyright 2014-2016 The Apache Software Foundation + + Apache Ranger Plugins Common + Copyright 2014-2016 The Apache Software Foundation + + Apache Ranger Plugins Cred + Copyright 2014-2016 The Apache Software Foundation + + (ASLv2) Google GSON + The following NOTICE information applies: + Copyright 2008 Google Inc. + + (ASLv2) HTrace Core + The following NOTICE information applies: + In addition, this product includes software dependencies. See + the accompanying LICENSE.txt for a listing of dependencies + that are NOT Apache licensed (with pointers to their licensing) + + Apache HTrace includes an Apache Thrift connector to Zipkin. Zipkin + is a distributed tracing system that is Apache 2.0 Licensed. + Copyright 2012 Twitter, Inc. + + (ASLv2) Jackson JSON processor + The following NOTICE information applies: + # Jackson JSON processor + + Jackson is a high-performance, Free/Open Source JSON processing library. + It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has + been in development since 2007. + It is currently developed by a community of developers, as well as supported + commercially by FasterXML.com. + + ## Licensing + + Jackson core and extension components may licensed under different licenses. + To find the details that apply to this artifact see the accompanying LICENSE file. + For more information, including possible other licensing options, contact + FasterXML.com (http://fasterxml.com). + + ## Credits + + A list of contributors may be found from CREDITS file, which is included + in some artifacts (usually source distributions); but is always available + from the source code management (SCM) system project uses. + + (ASLv2) Jettison + The following NOTICE information applies: + Copyright 2006 Envoi Solutions LLC + + (ASLv2) Jets3t + The following NOTICE information applies: + + This product includes software developed by: + + The Apache Software Foundation (http://www.apache.org/). + + The ExoLab Project (http://www.exolab.org/) + + Sun Microsystems (http://www.sun.com/) + + Codehaus (http://castor.codehaus.org) + + Tatu Saloranta (http://wiki.fasterxml.com/TatuSaloranta) + + (ASLv2) Jetty + The following NOTICE information applies: + Jetty Web Container + Copyright 1995-2019 Mort Bay Consulting Pty Ltd. + + (ASLv2) Apache Kafka + The following NOTICE information applies: + Apache Kafka + Copyright 2012 The Apache Software Foundation. + + (ASLv2) Apache log4j + The following NOTICE information applies: + Apache log4j + Copyright 2007 The Apache Software Foundation + + (ASLv2) Apache Solr + The following NOTICE information applies: + Apache Solrj + Copyright 2006-2014 The Apache Software Foundation + + (ASLv2) Apache ZooKeeper + The following NOTICE information applies: + Apache ZooKeeper + Copyright 2009-2012 The Apache Software Foundation + + (ASLv2) The Netty Project + The following NOTICE information applies: + The Netty Project + Copyright 2011 The Netty Project + + (ASLv2) Snappy Java + The following NOTICE information applies: + This product includes software developed by Google + Snappy: http://code.google.com/p/snappy/ (New BSD License) + + This product includes software developed by Apache + PureJavaCrc32C from apache-hadoop-common http://hadoop.apache.org/ + (Apache 2.0 license) + + This library containd statically linked libstdc++. This inclusion is allowed by + "GCC RUntime Library Exception" + http://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html + + (ASLv2) Woodstox Core ASL + The following NOTICE information applies: + This product currently only contains code developed by authors + of specific components, as identified by the source code files. + + Since product implements StAX API, it has dependencies to StAX API + classes. + + (ASLv2) Yammer Metrics + The following NOTICE information applies: + Metrics + Copyright 2010-2012 Coda Hale and Yammer, Inc. + + This product includes software developed by Coda Hale and Yammer, Inc. + + This product includes code derived from the JSR-166 project (ThreadLocalRandom), which was released + with the following comments: + + Written by Doug Lea with assistance from members of JCP JSR-166 + Expert Group and released to the public domain, as explained at + http://creativecommons.org/publicdomain/zero/1.0/ + + (ASLv2) ZkClient + The following NOTICE information applies: + ZkClient + Copyright 2009 Stefan Groschupf + + (ASLv2) Amazon Web Services SDK + The following NOTICE information applies: + Copyright 2010-2014 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + This product includes software developed by + Amazon Technologies, Inc (http://www.amazon.com/). + + ********************** + THIRD PARTY COMPONENTS + ********************** + This software includes third party software subject to the following copyrights: + - XML parsing and utility functions from JetS3t - Copyright 2006-2009 James Murty. + - PKCS#1 PEM encoded private key parsing and utility functions from oauth.googlecode.com - Copyright 1998-2010 AOL Inc. + +************************ +Common Development and Distribution License 1.0 +************************ + +The following binary components are provided under the Common Development and Distribution License 1.0. See project link for details. + + (CDDL 1.0) JavaBeans Activation Framework (JAF) (javax.activation:activation:jar:1.1 - http://java.sun.com/products/javabeans/jaf/index.jsp) + (CDDL 1.0) (GPL3) Streaming API For XML (javax.xml.stream:stax-api:jar:1.0-2 - no url provided) + +************************ +Common Development and Distribution License 1.1 +************************ + +The following binary components are provided under the Common Development and Distribution License 1.1. See project link for details. + + (CDDL 1.1) (GPL2 w/ CPE) Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:jar:2.2.3-1 - http://jaxb.java.net/) + (CDDL 1.1) (GPL2 w/ CPE) Java Architecture For XML Binding (javax.xml.bind:jaxb-api:jar:2.2.2 - https://jaxb.dev.java.net/) + (CDDL 1.1) (GPL2 w/ CPE) jersey-bundle (com.sun.jersey:jersey-bundle:jar:1.17 - https://jersey.java.net/jersey-bundle/) + (CDDL 1.1) (GPL2 w/ CPE) jersey-core (com.sun.jersey:jersey-core:jar:1.19 - https://jersey.java.net/jersey-core/) + (CDDL 1.1) (GPL2 w/ CPE) jersey-server (com.sun.jersey:jersey-server:jar:1.19 - https://jersey.java.net/jersey-server/) + (CDDL 1.1) (GPL2 w/ CPE) jersey-json (com.sun.jersey:jersey-json:jar:1.19 - https://jersey.java.net/jersey-json/) + (CDDL 1.1) (GPL2 w/ CPE) JavaServer Pages(TM) API (javax.servlet.jsp:javax.servlet.jsp-api:jar:2.1 - http://jsp.java.net) + (CDDL 1.1) (GPL2 w/ CPE) Java Servlet API (javax.servlet:javax.servlet-api:jar:2.5 - http://servlet-spec.java.net) + +************************ +Eclipse Public License 1.0 +************************ + +The following binary components are provided under the Eclipse Public License 1.0. See project link for details. + + (EPL 1.0) Eclipse Link (org.eclipse.persistence:eclipselink:2.5.2 - http://www.eclipse.org/eclipselink/) + (EPL 1.0) Common Service Data Objects (org.eclipse.persistence:commonj.sdo:2.1.1 - http://www.eclipse.org/eclipselink/) + (EPL 1.0) Java Persistence API (org.eclipse.persistence:javax.persistence:2.1.0 - http://www.eclipse.org/eclipselink/) diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/pom.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/pom.xml new file mode 100644 index 000000000000..19ec9358fbb9 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/pom.xml @@ -0,0 +1,404 @@ + + + + 4.0.0 + + + org.apache.nifi + nifi-ranger-bundle + 2.10.0.1 + + + nifi-ranger-plugin + jar + + + org.apache.solr + solr-solrj + + + org.eclipse.jetty + jetty-client + + + org.eclipse.jetty + jetty-http + + + org.eclipse.jetty + jetty-io + + + org.eclipse.jetty + jetty-util + + + org.eclipse.jetty + jetty-alpn-client + + + org.eclipse.jetty + jetty-util-ajax + + + org.apache.nifi + nifi-api + + + org.apache.nifi + nifi-framework-api + + + org.apache.nifi + nifi-properties + + + org.slf4j + log4j-over-slf4j + + + org.slf4j + jcl-over-slf4j + + + org.apache.nifi + nifi-deprecation-log + + + org.apache.ranger + ranger-plugins-common + ${ranger.version} + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + com.google.code.findbugs + jsr305 + + + commons-logging + commons-logging + + + ch.qos.logback + logback-classic + + + + + org.apache.ranger + ranger-plugins-audit + ${ranger.version} + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + com.amazonaws + aws-java-sdk-logs + + + + + org.apache.ranger + credentialbuilder + ${ranger.version} + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + slf4j-api + + + commons-logging + commons-logging + + + + + com.github.stephenc.findbugs + findbugs-annotations + 1.3.9-1 + + + + org.apache.hadoop + hadoop-client + ${hadoop.version} + + + org.slf4j + slf4j-reload4j + + + log4j + log4j + + + commons-logging + commons-logging + + + + + + org.apache.hadoop + hadoop-common + ${hadoop.version} + + + com.fasterxml.jackson.core + jackson-databind + + + org.slf4j + slf4j-reload4j + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + ch.qos.logback + logback-classic + + + org.eclipse.jetty + jetty-webapp + + + org.eclipse.jetty + jetty-servlet + + + + + com.fasterxml.jackson.core + jackson-databind + + + org.apache.hadoop + hadoop-auth + + + org.apache.nifi + nifi-xml-processing + 2.10.0.1 + + + org.apache.nifi + nifi-mock + 2.10.0.1 + test + + + + + + + disable-ranger-tests + + + aarch64 + + + + true + + + + + include-hadoop-aws + + false + + + + org.apache.hadoop + hadoop-aws + ${hadoop.version} + + + + + + include-hadoop-azure + + false + + + + org.apache.hadoop + hadoop-azure + ${hadoop.version} + + + com.google.guava + guava + + + com.fasterxml.jackson.core + jackson-core + + + + + org.apache.hadoop + hadoop-azure-datalake + ${hadoop.version} + + + com.fasterxml.jackson.core + jackson-core + + + + + + + + include-hadoop-cloud-storage + + false + + + + org.apache.hadoop + hadoop-cloud-storage + ${hadoop.version} + + + commons-logging + commons-logging + + + + + + + + include-hadoop-ozone + + false + + + + org.apache.ozone + ozone-client + ${ozone.version} + + + commons-logging + commons-logging + + + org.apache.logging.log4j + log4j-core + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + + + org.bouncycastle + bcprov-jdk18on + + + org.bouncycastle + bcpkix-jdk18on + + + org.apache.ozone + ozone-filesystem + ${ozone.version} + + + + + + include-hadoop-gcp + + false + + + + com.google.cloud.bigdataoss + gcs-connector + hadoop3-${gcs.version} + + + com.google.cloud.bigdataoss + util + ${gcs.version} + + + com.google.cloud.bigdataoss + util-hadoop + hadoop3-${gcs.version} + + + com.google.cloud.bigdataoss + gcsio + ${gcs.version} + + + + + diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java new file mode 100644 index 000000000000..2feef8295596 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizer.java @@ -0,0 +1,208 @@ +/* + * 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.nifi.ranger.authorization; + +import org.apache.commons.lang.StringUtils; +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.AccessPolicyProvider; +import org.apache.nifi.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.AuthorizerInitializationContext; +import org.apache.nifi.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.authorization.ManagedAuthorizer; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderLookup; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.xml.processing.ProcessingException; +import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider; +import org.apache.nifi.xml.processing.transform.StandardTransformProvider; +import org.apache.nifi.xml.processing.transform.TransformProvider; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; +import java.util.Set; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +public class ManagedRangerAuthorizer extends RangerNiFiAuthorizer implements ManagedAuthorizer { + private static final String USER_GROUP_PROVIDER_ELEMENT = "userGroupProvider"; + + private UserGroupProviderLookup userGroupProviderLookup; + private UserGroupProvider userGroupProvider; + private RangerBasePluginWithPolicies nifiPlugin; + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + userGroupProviderLookup = initializationContext.getUserGroupProviderLookup(); + + super.initialize(initializationContext); + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + final String userGroupProviderKey = configurationContext.getProperty("User Group Provider").getValue(); + userGroupProvider = userGroupProviderLookup.getUserGroupProvider(userGroupProviderKey); + + // ensure the desired access policy provider has a user group provider + if (userGroupProvider == null) { + throw new AuthorizerCreationException(String.format("Unable to locate configured User Group Provider: %s", userGroupProviderKey)); + } + + super.onConfigured(configurationContext); + } + + @Override + protected RangerBasePluginWithPolicies createRangerBasePlugin(final String serviceType, final String appId) { + // override the method for creating the ranger base plugin so a user group provider can be specified + nifiPlugin = new RangerBasePluginWithPolicies(serviceType, appId, userGroupProvider); + return nifiPlugin; + } + + @Override + public AccessPolicyProvider getAccessPolicyProvider() { + return new AccessPolicyProvider() { + @Override + public Set getAccessPolicies() throws AuthorizationAccessException { + return nifiPlugin.getAccessPolicies(); + } + + @Override + public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + return nifiPlugin.getAccessPolicy(identifier); + } + + @Override + public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + return nifiPlugin.getAccessPolicy(resourceIdentifier, action); + } + + @Override + public UserGroupProvider getUserGroupProvider() { + return userGroupProvider; + } + + @Override + public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } + }; + } + + @Override + public String getFingerprint() throws AuthorizationAccessException { + final StringWriter out = new StringWriter(); + try { + // create the document + final StandardDocumentProvider documentProvider = new StandardDocumentProvider(); + final Document document = documentProvider.newDocument(); + + // create the root element + final Element managedRangerAuthorizationsElement = document.createElement("managedRangerAuthorizations"); + document.appendChild(managedRangerAuthorizationsElement); + + // create the user group provider element + final Element userGroupProviderElement = document.createElement(USER_GROUP_PROVIDER_ELEMENT); + managedRangerAuthorizationsElement.appendChild(userGroupProviderElement); + + // append fingerprint if the provider is configurable + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + userGroupProviderElement.appendChild(document.createTextNode(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint())); + } + + final TransformProvider transformProvider = new StandardTransformProvider(); + transformProvider.transform(new DOMSource(document), new StreamResult(out)); + } catch (final ProcessingException e) { + throw new AuthorizationAccessException("Unable to generate fingerprint", e); + } + + return out.toString(); + } + + @Override + public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { + if (StringUtils.isBlank(fingerprint)) { + return; + } + + final String userGroupFingerprint = parseFingerprint(fingerprint); + + if (StringUtils.isNotBlank(userGroupFingerprint) && userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).inheritFingerprint(userGroupFingerprint); + } + } + + @Override + public void forciblyInheritFingerprint(final String fingerprint) throws AuthorizationAccessException { + final String userGroupFingerprint = parseFingerprint(fingerprint); + + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).forciblyInheritFingerprint(userGroupFingerprint); + } + } + + @Override + public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { + final String userGroupFingerprint = parseFingerprint(proposedFingerprint); + + if (StringUtils.isNotBlank(userGroupFingerprint)) { + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).checkInheritability(userGroupFingerprint); + } else { + throw new UninheritableAuthorizationsException("User/Group fingerprint is not blank and the configured UserGroupProvider does not support fingerprinting."); + } + } + } + + private String parseFingerprint(final String fingerprint) throws AuthorizationAccessException { + final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8); + + try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) { + final StandardDocumentProvider documentProvider = new StandardDocumentProvider(); + final Document document = documentProvider.parse(in); + final Element rootElement = document.getDocumentElement(); + + final NodeList userGroupProviderList = rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT); + if (userGroupProviderList.getLength() != 1) { + throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint)); + } + + final Node userGroupProvider = userGroupProviderList.item(0); + return userGroupProvider.getTextContent(); + } catch (final ProcessingException | IOException e) { + throw new AuthorizationAccessException("Unable to parse fingerprint", e); + } + } +} diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java new file mode 100644 index 000000000000..9839c81866b3 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerBasePluginWithPolicies.java @@ -0,0 +1,293 @@ +/* + * 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.nifi.ranger.authorization; + +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.User; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.util.StringUtils; +import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Extends the base plugin to convert service policies into NiFi policy domain model. + */ +public class RangerBasePluginWithPolicies extends RangerBasePlugin { + + private static final Logger logger = LoggerFactory.getLogger(RangerBasePluginWithPolicies.class); + + private static final String WILDCARD_ASTERISK = "*"; + + private UserGroupProvider userGroupProvider; + private AtomicReference policies = new AtomicReference<>(new PolicyLookup()); + + public RangerBasePluginWithPolicies(final String serviceType, final String appId) { + this(serviceType, appId, null); + } + + public RangerBasePluginWithPolicies(final String serviceType, final String appId, final UserGroupProvider userGroupProvider) { + super(serviceType, appId); + this.userGroupProvider = userGroupProvider; // will be null if used outside of the ManagedRangerAuthorizer + } + + @Override + public void setPolicies(final ServicePolicies policies) { + super.setPolicies(policies); + + if (policies == null || policies.getPolicies() == null) { + this.policies.set(new PolicyLookup()); + } else { + this.policies.set(createPolicyLookup(policies)); + } + } + + /** + * Determines if a policy exists for the given resource. + * + * @param resourceIdentifier the id of the resource + * + * @return true if a policy exists for the given resource, false otherwise + */ + public boolean doesPolicyExist(final String resourceIdentifier, final RequestAction requestAction) { + if (resourceIdentifier == null) { + return false; + } + + final PolicyLookup policyLookup = policies.get(); + return policyLookup.getAccessPolicy(resourceIdentifier, requestAction) != null; + } + + public Set getAccessPolicies() throws AuthorizationAccessException { + return policies.get().getAccessPolicies(); + } + + public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + return policies.get().getAccessPolicy(identifier); + } + + public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + return policies.get().getAccessPolicy(resourceIdentifier, action); + } + + private PolicyLookup createPolicyLookup(final ServicePolicies servicePolicies) { + final Map policiesByIdentifier = new HashMap<>(); + final Map> policiesByResource = new HashMap<>(); + + logger.info("Converting Ranger ServicePolicies model into NiFi policy model for viewing purposes in NiFi UI."); + + servicePolicies.getPolicies().stream().forEach(policy -> { + // only consider policies that are enabled + if (Boolean.TRUE.equals(policy.getIsEnabled())) { + // get all the resources for this policy - excludes/recursive support disabled + final Set resources = policy.getResources().values().stream() + .filter(resource -> { + final boolean isMissingResource; + final boolean isWildcard; + if (resource.getValues() == null) { + isMissingResource = true; + isWildcard = false; + } else { + isMissingResource = false; + isWildcard = resource.getValues().stream().anyMatch(value -> value.contains(WILDCARD_ASTERISK)); + } + + final boolean isExclude = Boolean.TRUE.equals(resource.getIsExcludes()); + final boolean isRecursive = Boolean.TRUE.equals(resource.getIsRecursive()); + + if (isMissingResource) { + logger.warn("Encountered resources missing values. Skipping policy for viewing purposes. Will still be used for access decisions."); + } + if (isWildcard) { + logger.warn(String.format("Resources [%s] include a wildcard value. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + if (isExclude) { + logger.warn(String.format("Resources [%s] marked as an exclude policy. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + if (isRecursive) { + logger.warn(String.format("Resources [%s] marked as a recursive policy. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + + return !isMissingResource && !isWildcard && !isExclude && !isRecursive; + }) + .flatMap(resource -> resource.getValues().stream()) + .collect(Collectors.toSet()); + + policy.getPolicyItems().forEach(policyItem -> { + // get all the users for this policy item, excluding unknown users + final Set userIds = policyItem.getUsers().stream() + .map(userIdentity -> getUser(userIdentity)) + .filter(Objects::nonNull) + .map(user -> user.getIdentifier()) + .collect(Collectors.toSet()); + + // get all groups for this policy item, excluding unknown groups + final Set groupIds = policyItem.getGroups().stream() + .map(groupName -> getGroup(groupName)) + .filter(Objects::nonNull) + .map(group -> group.getIdentifier()) + .collect(Collectors.toSet()); + + // check if this policy item is a delegate admin + final boolean isDelegateAdmin = Boolean.TRUE.equals(policyItem.getDelegateAdmin()); + + policyItem.getAccesses().forEach(access -> { + try { + // interpret the request action + final RequestAction action = RequestAction.valueOf(access.getType()); + + // function for creating an access policy + final Function createPolicy = resource -> new AccessPolicy.Builder() + .identifierGenerateFromSeed(resource + access.getType()) + .resource(resource) + .action(action) + .addUsers(userIds) + .addGroups(groupIds) + .build(); + + resources.forEach(resource -> { + // create the access policy for the specified resource + final AccessPolicy accessPolicy = createPolicy.apply(resource); + policiesByIdentifier.put(accessPolicy.getIdentifier(), accessPolicy); + policiesByResource.computeIfAbsent(resource, r -> new HashMap<>()).put(action, accessPolicy); + + // if this is a delegate admin, also create the admin policy for the specified resource + if (isDelegateAdmin) { + // build the admin resource identifier + final String adminResource; + if (resource.startsWith("/")) { + adminResource = "/policies" + resource; + } else { + adminResource = "/policies/" + resource; + } + + final AccessPolicy adminAccessPolicy = createPolicy.apply(adminResource); + policiesByIdentifier.put(adminAccessPolicy.getIdentifier(), adminAccessPolicy); + policiesByResource.computeIfAbsent(adminResource, ar -> new HashMap<>()).put(action, adminAccessPolicy); + } + }); + } catch (final IllegalArgumentException e) { + logger.warn(String.format("Unrecognized request action '%s'. Skipping policy for viewing purposes. Will still be used for access decisions.", access.getType())); + } + }); + }); + } + }); + + return new PolicyLookup(policiesByIdentifier, policiesByResource); + } + + private User getUser(final String identity) { + if (userGroupProvider == null) { + // generate the user deterministically when running outside of the ManagedRangerAuthorizer + return new User.Builder().identifierGenerateFromSeed(identity).identity(identity).build(); + } else { + // find the user in question + final User user = userGroupProvider.getUserByIdentity(identity); + + if (user == null) { + logger.warn(String.format("Cannot find user '%s' in the configured User Group Provider. Skipping user for viewing purposes. Will still be used for access decisions.", identity)); + } + + return user; + } + } + + private Group getGroup(final String name) { + if (userGroupProvider == null) { + // generate the group deterministically when running outside of the ManagedRangerAuthorizer + return new Group.Builder().identifierGenerateFromSeed(name).name(name).build(); + } else { + // find the group in question + final Group group = userGroupProvider.getGroups().stream().filter(g -> g.getName().equals(name)).findFirst().orElse(null); + + if (group == null) { + logger.warn(String.format("Cannot find group '%s' in the configured User Group Provider. Skipping group for viewing purposes. Will still be used for access decisions.", name)); + } + + return group; + } + } + + private static class PolicyLookup { + + private final Map policiesByIdentifier; + private final Map> policiesByResource; + private final Set allPolicies; + + private PolicyLookup() { + this(null, null); + } + + private PolicyLookup(final Map policiesByIdentifier, final Map> policiesByResource) { + if (policiesByIdentifier == null) { + allPolicies = Collections.EMPTY_SET; + } else { + allPolicies = Collections.unmodifiableSet(new HashSet<>(policiesByIdentifier.values())); + } + + this.policiesByIdentifier = policiesByIdentifier; + this.policiesByResource = policiesByResource; + } + + private Set getAccessPolicies() throws AuthorizationAccessException { + return allPolicies; + } + + private AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + if (policiesByIdentifier == null) { + return null; + } + + return policiesByIdentifier.get(identifier); + } + + private AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + if (policiesByResource == null) { + return null; + } + + final Map policiesForResource = policiesByResource.get(resourceIdentifier); + + if (policiesForResource != null) { + return policiesForResource.get(action); + } + + return null; + } + } + +} diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java new file mode 100644 index 000000000000..1fed01efc631 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/java/org/apache/nifi/ranger/authorization/RangerNiFiAuthorizer.java @@ -0,0 +1,309 @@ +/* + * 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.nifi.ranger.authorization; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.authorization.AuthorizationAuditor; +import org.apache.nifi.authorization.AuthorizationRequest; +import org.apache.nifi.authorization.AuthorizationResult; +import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.AuthorizerInitializationContext; +import org.apache.nifi.authorization.UserContextKeys; +import org.apache.nifi.authorization.annotation.AuthorizerContext; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; +import org.apache.nifi.components.PropertyValue; +import org.apache.nifi.util.NiFiProperties; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.net.MalformedURLException; +import java.text.NumberFormat; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Authorizer implementation that uses Apache Ranger to make authorization decisions. + */ +public class RangerNiFiAuthorizer implements Authorizer, AuthorizationAuditor { + private static final Logger logger = LoggerFactory.getLogger(RangerNiFiAuthorizer.class); + + static final String RANGER_AUDIT_PATH_PROP = "Ranger Audit Config Path"; + static final String RANGER_SECURITY_PATH_PROP = "Ranger Security Config Path"; + static final String RANGER_KERBEROS_ENABLED_PROP = "Ranger Kerberos Enabled"; + static final String RANGER_SERVICE_TYPE_PROP = "Ranger Service Type"; + static final String RANGER_APP_ID_PROP = "Ranger Application Id"; + static final String RANGER_ADMIN_IDENTITY_PROP_PREFIX = "Ranger Admin Identity"; + static final Pattern RANGER_ADMIN_IDENTITY_PATTERN = Pattern.compile(RANGER_ADMIN_IDENTITY_PROP_PREFIX + "\\s?\\S*"); + + static final String RANGER_NIFI_RESOURCE_NAME = "nifi-resource"; + static final String DEFAULT_SERVICE_TYPE = "nifi"; + static final String DEFAULT_APP_ID = "nifi"; + static final String RESOURCES_RESOURCE = "/resources"; + static final String HADOOP_SECURITY_AUTHENTICATION = "hadoop.security.authentication"; + static final String KERBEROS_AUTHENTICATION = "kerberos"; + + private final Map resultLookup = new WeakHashMap<>(); + + private volatile RangerBasePluginWithPolicies nifiPlugin = null; + private volatile RangerDefaultAuditHandler defaultAuditHandler = null; + private volatile Set rangerAdminIdentity = null; + private volatile boolean rangerKerberosEnabled = false; + private volatile NiFiProperties nifiProperties; + private final NumberFormat numberFormat = NumberFormat.getInstance(); + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + try { + if (nifiPlugin == null) { + logger.info("RangerNiFiAuthorizer(): initializing base plugin"); + + final String serviceType = getConfigValue(configurationContext, RANGER_SERVICE_TYPE_PROP, DEFAULT_SERVICE_TYPE); + final String appId = getConfigValue(configurationContext, RANGER_APP_ID_PROP, DEFAULT_APP_ID); + + nifiPlugin = createRangerBasePlugin(serviceType, appId); + + final RangerPluginConfig pluginConfig = nifiPlugin.getConfig(); + + final PropertyValue securityConfigValue = configurationContext.getProperty(RANGER_SECURITY_PATH_PROP); + addRequiredResource(RANGER_SECURITY_PATH_PROP, securityConfigValue, pluginConfig); + + final PropertyValue auditConfigValue = configurationContext.getProperty(RANGER_AUDIT_PATH_PROP); + addRequiredResource(RANGER_AUDIT_PATH_PROP, auditConfigValue, pluginConfig); + + final String rangerKerberosEnabledValue = getConfigValue(configurationContext, RANGER_KERBEROS_ENABLED_PROP, Boolean.FALSE.toString()); + rangerKerberosEnabled = rangerKerberosEnabledValue.equals(Boolean.TRUE.toString()); + + if (rangerKerberosEnabled) { + // configure UGI for when RangerAdminRESTClient calls UserGroupInformation.isSecurityEnabled() + final Configuration securityConf = new Configuration(); + securityConf.set(HADOOP_SECURITY_AUTHENTICATION, KERBEROS_AUTHENTICATION); + UserGroupInformation.setConfiguration(securityConf); + + // login with the nifi principal and keytab, RangerAdminRESTClient will use Ranger's MiscUtil which + // will grab UserGroupInformation.getLoginUser() and call ugi.checkTGTAndReloginFromKeytab(); + final String nifiPrincipal = nifiProperties.getKerberosServicePrincipal(); + final String nifiKeytab = nifiProperties.getKerberosServiceKeytabLocation(); + + if (StringUtils.isBlank(nifiPrincipal) || StringUtils.isBlank(nifiKeytab)) { + throw new AuthorizerCreationException("Principal and Keytab must be provided when Kerberos is enabled"); + } + + UserGroupInformation.loginUserFromKeytab(nifiPrincipal.trim(), nifiKeytab.trim()); + } + + nifiPlugin.init(); + + defaultAuditHandler = new RangerDefaultAuditHandler(); + rangerAdminIdentity = getConfigValues(configurationContext, RANGER_ADMIN_IDENTITY_PATTERN, null); + + } else { + logger.info("RangerNiFiAuthorizer(): base plugin already initialized"); + } + } catch (Throwable t) { + throw new AuthorizerCreationException("Error creating RangerBasePlugin", t); + } + } + + protected RangerBasePluginWithPolicies createRangerBasePlugin(final String serviceType, final String appId) { + return new RangerBasePluginWithPolicies(serviceType, appId); + } + + @Override + public AuthorizationResult authorize(final AuthorizationRequest request) throws AuthorizationAccessException { + final String identity = request.getIdentity(); + final Set userGroups = request.getGroups(); + final String resourceIdentifier = request.getResource().getIdentifier(); + + // if a ranger admin identity was provided, and it contains the identity making the request, + // and the request is to retrieve the resources, then allow it through + if (rangerAdminIdentity != null && rangerAdminIdentity.contains(identity) + && resourceIdentifier.equals(RESOURCES_RESOURCE)) { + return AuthorizationResult.approved(); + } + + final String clientIp; + if (request.getUserContext() != null) { + clientIp = request.getUserContext().get(UserContextKeys.CLIENT_ADDRESS.name()); + } else { + clientIp = null; + } + + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RANGER_NIFI_RESOURCE_NAME, resourceIdentifier); + + final RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl(); + rangerRequest.setResource(resource); + rangerRequest.setAction(request.getAction().name()); + rangerRequest.setAccessType(request.getAction().name()); + rangerRequest.setUser(identity); + rangerRequest.setUserGroups(userGroups); + rangerRequest.setAccessTime(new Date()); + + if (!StringUtils.isBlank(clientIp)) { + rangerRequest.setClientIPAddress(clientIp); + } + + final long authStart = System.nanoTime(); + final RangerAccessResult result = nifiPlugin.isAccessAllowed(rangerRequest); + final long authNanos = System.nanoTime() - authStart; + logger.debug("Performed authorization against Ranger for Resource ID {}, Identity {} in {} nanos", resourceIdentifier, identity, numberFormat.format(authNanos)); + + // store the result for auditing purposes later if appropriate + if (request.isAccessAttempt()) { + synchronized (resultLookup) { + resultLookup.put(request, result); + } + } + + if (result != null && result.getIsAllowed()) { + // return approved + return AuthorizationResult.approved(); + } else { + // if result.getIsAllowed() is false, then we need to determine if it was because no policy exists for the + // given resource, or if it was because a policy exists but not for the given user or action + final boolean doesPolicyExist = nifiPlugin.doesPolicyExist(request.getResource().getIdentifier(), request.getAction()); + + if (doesPolicyExist) { + final String reason = result == null ? null : result.getReason(); + if (reason != null) { + logger.debug(String.format("Unable to authorize %s due to %s", identity, reason)); + } + + // a policy does exist for the resource so we were really denied access here + return AuthorizationResult.denied(request.getExplanationSupplier().get()); + } else { + // a policy doesn't exist so return resource not found so NiFi can work back up the resource hierarchy + return AuthorizationResult.resourceNotFound(); + } + } + } + + @Override + public void auditAccessAttempt(final AuthorizationRequest request, final AuthorizationResult result) { + final RangerAccessResult rangerResult; + synchronized (resultLookup) { + rangerResult = resultLookup.remove(request); + } + + if (rangerResult != null && rangerResult.getIsAudited()) { + AuthzAuditEvent event = defaultAuditHandler.getAuthzEvents(rangerResult); + + // update the event with the originally requested resource + event.setResourceType(RANGER_NIFI_RESOURCE_NAME); + event.setResourcePath(request.getRequestedResource().getIdentifier()); + + final long start = System.nanoTime(); + defaultAuditHandler.logAuthzAudit(event); + final long nanos = System.nanoTime() - start; + logger.debug("Logged authorization audits to Ranger in {} nanos", numberFormat.format(nanos)); + } + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + if (nifiPlugin != null) { + try { + nifiPlugin.cleanup(); + nifiPlugin = null; + } catch (Throwable t) { + throw new AuthorizerDestructionException("Error cleaning up RangerBasePlugin", t); + } + } + } + + @AuthorizerContext + public void setNiFiProperties(final NiFiProperties properties) { + this.nifiProperties = properties; + } + + /** + * Adds a resource to the RangerConfiguration singleton so it is already there by the time RangerBasePlugin.init() + * is called. + * + * @param name the name of the given PropertyValue from the AuthorizationConfigurationContext + * @param resourceValue the value for the given name, should be a full path to a file + * @param configuration the RangerConfiguration instance to add the resource to + */ + private void addRequiredResource(final String name, final PropertyValue resourceValue, final RangerConfiguration configuration) { + if (resourceValue == null || StringUtils.isBlank(resourceValue.getValue())) { + throw new AuthorizerCreationException(name + " must be specified."); + } + + final File resourceFile = new File(resourceValue.getValue()); + if (!resourceFile.exists() || !resourceFile.canRead()) { + throw new AuthorizerCreationException(resourceValue + " does not exist, or can not be read"); + } + + try { + configuration.addResource(resourceFile.toURI().toURL()); + } catch (MalformedURLException e) { + throw new AuthorizerCreationException("Error creating URI for " + resourceValue, e); + } + } + + private String getConfigValue(final AuthorizerConfigurationContext context, final String name, final String defaultValue) { + final PropertyValue configValue = context.getProperty(name); + + String retValue = defaultValue; + if (configValue != null && !StringUtils.isBlank(configValue.getValue())) { + retValue = configValue.getValue(); + } + + return retValue; + } + + private Set getConfigValues(final AuthorizerConfigurationContext context, final Pattern namePattern, final String defaultValue) { + final Set configValues = new HashSet<>(); + + for (Map.Entry entry : context.getProperties().entrySet()) { + Matcher matcher = namePattern.matcher(entry.getKey()); + if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) { + configValues.add(entry.getValue()); + } + } + + if (configValues.isEmpty() && (defaultValue != null)) { + configValues.add(defaultValue); + } + + return configValues; + } +} diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer new file mode 100755 index 000000000000..34d87976e8c1 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.authorization.Authorizer @@ -0,0 +1,16 @@ +# 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. +org.apache.nifi.ranger.authorization.RangerNiFiAuthorizer +org.apache.nifi.ranger.authorization.ManagedRangerAuthorizer diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java new file mode 100644 index 000000000000..7f8c17ce9058 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/ManagedRangerAuthorizerTest.java @@ -0,0 +1,227 @@ +/* + * 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.nifi.ranger.authorization; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.AuthorizerInitializationContext; +import org.apache.nifi.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderLookup; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.util.MockPropertyValue; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.io.File; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class ManagedRangerAuthorizerTest { + + private static final String TENANT_FINGERPRINT = + "" + + "" + + "" + + "" + + "" + + ""; + + private static final String EMPTY_FINGERPRINT = "" + + "" + + "" + + ""; + + private static final String NON_EMPTY_FINGERPRINT = "" + + "" + + "" + + "<tenants>" + + "<user identifier=\"user-id-1\" identity=\"user-1\"></user>" + + "<group identifier=\"group-id-1\" name=\"group-1\">" + + "<groupUser identifier=\"user-id-1\"></groupUser>" + + "</group>" + + "</tenants>" + + "" + + ""; + + private final String serviceType = "nifi"; + private final String appId = "nifiAppId"; + + @BeforeEach + public void setup() { + // have to initialize this system property before anything else + File krb5conf = new File("src/test/resources/krb5.conf"); + assertTrue(krb5conf.exists()); + System.setProperty("java.security.krb5.conf", krb5conf.getAbsolutePath()); + + // rest the authentication to simple in case any tests set it to kerberos + final Configuration securityConf = new Configuration(); + securityConf.set(RangerNiFiAuthorizer.HADOOP_SECURITY_AUTHENTICATION, "simple"); + UserGroupInformation.setConfiguration(securityConf); + + assertFalse(UserGroupInformation.isSecurityEnabled()); + } + + @Test + public void testNonConfigurableFingerPrint() { + final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + assertEquals(EMPTY_FINGERPRINT, managedRangerAuthorizer.getFingerprint()); + } + + @Test + public void testConfigurableEmptyFingerPrint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + when(userGroupProvider.getFingerprint()).thenReturn(""); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + assertEquals(EMPTY_FINGERPRINT, managedRangerAuthorizer.getFingerprint()); + } + + @Test + public void testConfigurableFingerPrint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + when(userGroupProvider.getFingerprint()).thenReturn(TENANT_FINGERPRINT); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + assertEquals(NON_EMPTY_FINGERPRINT, managedRangerAuthorizer.getFingerprint()); + } + + @Test + public void testInheritEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.inheritFingerprint(EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(0)).inheritFingerprint(anyString()); + } + + @Test + public void testInheritInvalidFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + assertThrows(AuthorizationAccessException.class, () -> managedRangerAuthorizer.inheritFingerprint("not a valid fingerprint")); + } + + @Test + public void testInheritNonEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.inheritFingerprint(NON_EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(1)).inheritFingerprint(TENANT_FINGERPRINT); + } + + @Test + public void testCheckInheritEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.checkInheritability(EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(0)).inheritFingerprint(anyString()); + } + + @Test + public void testCheckInheritInvalidFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + assertThrows(AuthorizationAccessException.class, () -> managedRangerAuthorizer.checkInheritability("not a valid fingerprint")); + } + + @Test + public void testCheckInheritNonEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + managedRangerAuthorizer.checkInheritability(NON_EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(1)).checkInheritability(TENANT_FINGERPRINT); + } + + @Test + public void testCheckInheritNonConfigurableUserGroupProvider() { + final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class); + + final ManagedRangerAuthorizer managedRangerAuthorizer = getStandardManagedAuthorizer(userGroupProvider); + assertThrows(UninheritableAuthorizationsException.class, () -> managedRangerAuthorizer.checkInheritability(NON_EMPTY_FINGERPRINT)); + } + + private ManagedRangerAuthorizer getStandardManagedAuthorizer(final UserGroupProvider userGroupProvider) { + final RangerBasePluginWithPolicies rangerBasePlugin = Mockito.mock(RangerBasePluginWithPolicies.class); + + final RangerPluginConfig pluginConfig = new RangerPluginConfig(serviceType, null, appId, null, null, null); + when(rangerBasePlugin.getConfig()).thenReturn(pluginConfig); + + final ManagedRangerAuthorizer managedAuthorizer = new MockManagedRangerAuthorizer(rangerBasePlugin); + + final AuthorizerConfigurationContext configurationContext = mock(AuthorizerConfigurationContext.class); + when(configurationContext.getProperty(eq("User Group Provider"))).thenReturn(new MockPropertyValue("user-group-provider", null)); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_SECURITY_PATH_PROP))).thenReturn(new MockPropertyValue("src/test/resources/ranger/ranger-nifi-security.xml")); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_AUDIT_PATH_PROP))).thenReturn(new MockPropertyValue("src/test/resources/ranger/ranger-nifi-audit.xml")); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_APP_ID_PROP))).thenReturn(new MockPropertyValue(appId)); + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_SERVICE_TYPE_PROP))).thenReturn(new MockPropertyValue(serviceType)); + + final UserGroupProviderLookup userGroupProviderLookup = mock(UserGroupProviderLookup.class); + when(userGroupProviderLookup.getUserGroupProvider("user-group-provider")).thenReturn(userGroupProvider); + + final AuthorizerInitializationContext initializationContext = mock(AuthorizerInitializationContext.class); + when(initializationContext.getUserGroupProviderLookup()).thenReturn(userGroupProviderLookup); + + managedAuthorizer.initialize(initializationContext); + managedAuthorizer.onConfigured(configurationContext); + + return managedAuthorizer; + } + + /** + * Extend ManagedRangerAuthorizer to inject a mock base plugin for testing. + */ + private static class MockManagedRangerAuthorizer extends ManagedRangerAuthorizer { + + RangerBasePluginWithPolicies mockRangerBasePlugin; + + public MockManagedRangerAuthorizer(RangerBasePluginWithPolicies mockRangerBasePlugin) { + this.mockRangerBasePlugin = mockRangerBasePlugin; + } + + @Override + protected RangerBasePluginWithPolicies createRangerBasePlugin(String serviceType, String appId) { + when(mockRangerBasePlugin.getAppId()).thenReturn(appId); + when(mockRangerBasePlugin.getServiceType()).thenReturn(serviceType); + return mockRangerBasePlugin; + } + } +} diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java new file mode 100644 index 000000000000..833fd0fc9c6f --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerBasePluginWithPolicies.java @@ -0,0 +1,550 @@ +/* + * 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.nifi.ranger.authorization; + +import org.apache.nifi.authorization.AccessPolicy; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.Group; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.User; +import org.apache.nifi.authorization.UserAndGroups; +import org.apache.nifi.authorization.UserGroupProvider; +import org.apache.nifi.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.authorization.exception.AuthorizerDestructionException; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestRangerBasePluginWithPolicies { + + @Test + public void testPoliciesWithoutUserGroupProvider() { + final String user1 = "user-1"; + final String group1 = "group-1"; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final String resourceIdentifier2 = "/resource-2"; + RangerPolicyResource resource2 = new RangerPolicyResource(resourceIdentifier2); + + final Map policy2Resources = new HashMap<>(); + policy2Resources.put(resourceIdentifier2, resource2); + + final RangerPolicyItem policy2Item = new RangerPolicyItem(); + policy2Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ"), new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy2Item.setGroups(Stream.of(group1).collect(Collectors.toList())); + + final RangerPolicy policy2 = new RangerPolicy(); + policy2.setResources(policy2Resources); + policy2.setPolicyItems(Stream.of(policy2Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + policies.add(policy2); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the two ranger policies converted into 3 nifi access policies + final Set accessPolicies = pluginWithPolicies.getAccessPolicies(); + assertEquals(3, accessPolicies.size()); + + // resource 1 -> read but no write + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + + // read + final AccessPolicy readResource1 = pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ); + assertNotNull(readResource1); + assertTrue(accessPolicies.contains(readResource1)); + assertEquals(readResource1, pluginWithPolicies.getAccessPolicy(readResource1.getIdentifier())); + assertEquals(1, readResource1.getUsers().size()); + assertTrue(readResource1.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user1).identity(user1).build().getIdentifier())); + assertTrue(readResource1.getGroups().isEmpty()); + + // but no write + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + + // resource 2 -> read and write + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.READ)); + + // read + final AccessPolicy readResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(readResource2); + assertTrue(accessPolicies.contains(readResource2)); + assertEquals(readResource2, pluginWithPolicies.getAccessPolicy(readResource2.getIdentifier())); + assertTrue(readResource2.getUsers().isEmpty()); + assertEquals(1, readResource2.getGroups().size()); + assertTrue(readResource2.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group1).name(group1).build().getIdentifier())); + + // and write + final AccessPolicy writeResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(writeResource2); + assertTrue(accessPolicies.contains(writeResource2)); + assertEquals(writeResource2, pluginWithPolicies.getAccessPolicy(writeResource2.getIdentifier())); + assertTrue(writeResource2.getUsers().isEmpty()); + assertEquals(1, writeResource2.getGroups().size()); + assertTrue(writeResource2.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group1).name(group1).build().getIdentifier())); + + // resource 3 -> no read or write + assertFalse(pluginWithPolicies.doesPolicyExist("resource-3", RequestAction.WRITE)); + assertFalse(pluginWithPolicies.doesPolicyExist("resource-3", RequestAction.READ)); + + // no read or write + assertNull(pluginWithPolicies.getAccessPolicy("resource-3", RequestAction.WRITE)); + assertNull(pluginWithPolicies.getAccessPolicy("resource-3", RequestAction.READ)); + } + + @Test + public void testNoPolicies() { + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + + assertFalse(pluginWithPolicies.doesPolicyExist("non-existent-resource", RequestAction.READ)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy("non-existent-identifier")); + assertNull(pluginWithPolicies.getAccessPolicy("non-existent-resource", RequestAction.READ)); + } + + @Test + public void testDisabledPolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setIsEnabled(false); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ)); + } + + @Test + public void testMissingResourceValue() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testWildcardResourceValue() { + final String resourceIdentifier1 = "*"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testExcludesPolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + resource1.setIsExcludes(true); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testRecursivePolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + resource1.setIsRecursive(true); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testDelegateAdmin() { + final String user1 = "user-1"; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ"), new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + policy1Item.setDelegateAdmin(true); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi"); + pluginWithPolicies.setPolicies(servicePolicies); + + assertEquals(4, pluginWithPolicies.getAccessPolicies().size()); + assertNotNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ)); + assertNotNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + assertNotNull(pluginWithPolicies.getAccessPolicy("/policies" + resourceIdentifier1, RequestAction.READ)); + assertNotNull(pluginWithPolicies.getAccessPolicy("/policies" + resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testPoliciesWithUserGroupProvider() { + final String user1 = "user-1"; // unknown according to user group provider + final String user2 = "user-2"; // known according to user group provider + final String group1 = "group-1"; // unknown according to user group provider + final String group2 = "group-2"; // known according to user group provider + + final UserGroupProvider userGroupProvider = new UserGroupProvider() { + @Override + public Set getUsers() throws AuthorizationAccessException { + return Stream.of(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build()).collect(Collectors.toSet()); + } + + @Override + public User getUser(String identifier) throws AuthorizationAccessException { + final User u2 = new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + if (u2.getIdentifier().equals(identifier)) { + return u2; + } else { + return null; + } + } + + @Override + public User getUserByIdentity(String identity) throws AuthorizationAccessException { + if (user2.equals(identity)) { + return new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + } else { + return null; + } + } + + @Override + public Set getGroups() throws AuthorizationAccessException { + return Stream.of(new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build()).collect(Collectors.toSet()); + } + + @Override + public Group getGroup(String identifier) throws AuthorizationAccessException { + final Group g2 = new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build(); + if (g2.getIdentifier().equals(identifier)) { + return g2; + } else { + return null; + } + } + + @Override + public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException { + if (user2.equals(identity)) { + return new UserAndGroups() { + @Override + public User getUser() { + return new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + } + + @Override + public Set getGroups() { + return Collections.EMPTY_SET; + } + }; + } else { + return null; + } + } + + @Override + public void initialize(UserGroupProviderInitializationContext initializationContext) throws AuthorizerCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws AuthorizerCreationException { + } + + @Override + public void preDestruction() throws AuthorizerDestructionException { + } + }; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicyResource resource1 = new RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicyItem policy1Item = new RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + policy1Item.setGroups(Stream.of(group2).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final String resourceIdentifier2 = "/resource-2"; + RangerPolicyResource resource2 = new RangerPolicyResource(resourceIdentifier2); + + final Map policy2Resources = new HashMap<>(); + policy2Resources.put(resourceIdentifier2, resource2); + + final RangerPolicyItem policy2Item = new RangerPolicyItem(); + policy2Item.setAccesses(Stream.of(new RangerPolicyItemAccess("READ"), new RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy2Item.setUsers(Stream.of(user2).collect(Collectors.toList())); + policy2Item.setGroups(Stream.of(group1).collect(Collectors.toList())); + + final RangerPolicy policy2 = new RangerPolicy(); + policy2.setResources(policy2Resources); + policy2.setPolicyItems(Stream.of(policy2Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + policies.add(policy2); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi", "nifi", userGroupProvider); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the two ranger policies converted into 3 nifi access policies + final Set accessPolicies = pluginWithPolicies.getAccessPolicies(); + assertEquals(3, accessPolicies.size()); + + // resource 1 -> read but no write + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + + // read + final AccessPolicy readResource1 = pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ); + assertNotNull(readResource1); + assertTrue(accessPolicies.contains(readResource1)); + assertEquals(readResource1, pluginWithPolicies.getAccessPolicy(readResource1.getIdentifier())); + assertTrue(readResource1.getUsers().isEmpty()); + assertEquals(1, readResource1.getGroups().size()); + assertTrue(readResource1.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build().getIdentifier())); + + // but no write + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + + // resource 2 -> read and write + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.READ)); + + // read + final AccessPolicy readResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(readResource2); + assertTrue(accessPolicies.contains(readResource2)); + assertEquals(readResource2, pluginWithPolicies.getAccessPolicy(readResource2.getIdentifier())); + assertEquals(1, readResource2.getUsers().size()); + assertTrue(readResource2.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build().getIdentifier())); + assertTrue(readResource2.getGroups().isEmpty()); + + // and write + final AccessPolicy writeResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(writeResource2); + assertTrue(accessPolicies.contains(writeResource2)); + assertEquals(writeResource2, pluginWithPolicies.getAccessPolicy(writeResource2.getIdentifier())); + assertEquals(1, writeResource2.getUsers().size()); + assertTrue(writeResource2.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build().getIdentifier())); + assertTrue(writeResource2.getGroups().isEmpty()); + } +} diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java new file mode 100644 index 000000000000..bcfd8ddd0fff --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/java/org/apache/nifi/ranger/authorization/TestRangerNiFiAuthorizer.java @@ -0,0 +1,572 @@ +/* + * 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.nifi.ranger.authorization; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.authorization.AuthorizationRequest; +import org.apache.nifi.authorization.AuthorizationResult; +import org.apache.nifi.authorization.Authorizer; +import org.apache.nifi.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.authorization.AuthorizerInitializationContext; +import org.apache.nifi.authorization.RequestAction; +import org.apache.nifi.authorization.Resource; +import org.apache.nifi.authorization.UserContextKeys; +import org.apache.nifi.authorization.exception.AuthorizerCreationException; +import org.apache.nifi.util.MockPropertyValue; +import org.apache.nifi.util.NiFiProperties; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mockito; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.isNotNull; +import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class TestRangerNiFiAuthorizer { + + private MockRangerNiFiAuthorizer authorizer; + private RangerBasePluginWithPolicies rangerBasePlugin; + private AuthorizerConfigurationContext configurationContext; + private NiFiProperties nifiProperties; + + private final String serviceType = "nifi"; + private final String appId = "nifiAppId"; + + private RangerAccessResult allowedResult; + private RangerAccessResult notAllowedResult; + + private Map authorizersXmlContent = null; + + @BeforeEach + public void setup() { + // have to initialize this system property before anything else + File krb5conf = new File("src/test/resources/krb5.conf"); + assertTrue(krb5conf.exists()); + System.setProperty("java.security.krb5.conf", krb5conf.getAbsolutePath()); + + // rest the authentication to simple in case any tests set it to kerberos + final Configuration securityConf = new Configuration(); + securityConf.set(RangerNiFiAuthorizer.HADOOP_SECURITY_AUTHENTICATION, "simple"); + UserGroupInformation.setConfiguration(securityConf); + + // initialize the content of authorizers.xml in case tests added further entries to it + authorizersXmlContent = new HashMap<>(); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_SECURITY_PATH_PROP, "src/test/resources/ranger/ranger-nifi-security.xml"); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_AUDIT_PATH_PROP, "src/test/resources/ranger/ranger-nifi-audit.xml"); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_APP_ID_PROP, appId); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_SERVICE_TYPE_PROP, serviceType); + configurationContext = createMockConfigContext(); + rangerBasePlugin = Mockito.mock(RangerBasePluginWithPolicies.class); + + final RangerPluginConfig pluginConfig = new RangerPluginConfig(serviceType, null, appId, null, null, null); + when(rangerBasePlugin.getConfig()).thenReturn(pluginConfig); + + authorizer = new MockRangerNiFiAuthorizer(rangerBasePlugin); + authorizer.onConfigured(configurationContext); + + assertFalse(UserGroupInformation.isSecurityEnabled()); + + allowedResult = Mockito.mock(RangerAccessResult.class); + when(allowedResult.getIsAllowed()).thenReturn(true); + + notAllowedResult = Mockito.mock(RangerAccessResult.class); + when(notAllowedResult.getIsAllowed()).thenReturn(false); + } + + private AuthorizerConfigurationContext createMockConfigContext() { + AuthorizerConfigurationContext configurationContext = Mockito.mock(AuthorizerConfigurationContext.class); + + for (Map.Entry entry : authorizersXmlContent.entrySet()) { + when(configurationContext.getProperty(eq(entry.getKey()))) + .thenReturn(new MockPropertyValue(entry.getValue())); + } + + when(configurationContext.getProperties()).thenReturn(authorizersXmlContent); + + return configurationContext; + } + + @Test + public void testOnConfigured() { + verify(rangerBasePlugin, times(1)).init(); + + assertEquals(appId, authorizer.mockRangerBasePlugin.getAppId()); + assertEquals(serviceType, authorizer.mockRangerBasePlugin.getServiceType()); + } + + @Test + public void testKerberosEnabledWithoutKeytab() { + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new MockPropertyValue("true")); + + nifiProperties = Mockito.mock(NiFiProperties.class); + when(nifiProperties.getKerberosServicePrincipal()).thenReturn(""); + + authorizer = new MockRangerNiFiAuthorizer(rangerBasePlugin); + authorizer.setNiFiProperties(nifiProperties); + + assertThrows(AuthorizerCreationException.class, () -> authorizer.onConfigured(configurationContext)); + } + + @Test + public void testKerberosEnabledWithoutPrincipal() { + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new MockPropertyValue("true")); + + nifiProperties = Mockito.mock(NiFiProperties.class); + when(nifiProperties.getKerberosServiceKeytabLocation()).thenReturn(""); + + authorizer = new MockRangerNiFiAuthorizer(rangerBasePlugin); + authorizer.setNiFiProperties(nifiProperties); + + assertThrows(AuthorizerCreationException.class, () -> authorizer.onConfigured(configurationContext)); + } + + @Test + public void testKerberosEnabledWithoutKeytabOrPrincipal() { + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new MockPropertyValue("true")); + + nifiProperties = Mockito.mock(NiFiProperties.class); + when(nifiProperties.getKerberosServiceKeytabLocation()).thenReturn(""); + when(nifiProperties.getKerberosServicePrincipal()).thenReturn(""); + + authorizer = new MockRangerNiFiAuthorizer(rangerBasePlugin); + authorizer.setNiFiProperties(nifiProperties); + + assertThrows(AuthorizerCreationException.class, () -> authorizer.onConfigured(configurationContext)); + } + + @Test + public void testKerberosEnabled() { + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new MockPropertyValue("true")); + + nifiProperties = Mockito.mock(NiFiProperties.class); + when(nifiProperties.getKerberosServiceKeytabLocation()).thenReturn("test"); + when(nifiProperties.getKerberosServicePrincipal()).thenReturn("test"); + + authorizer = new MockRangerNiFiAuthorizer(rangerBasePlugin); + authorizer.setNiFiProperties(nifiProperties); + + assertThrows(AuthorizerCreationException.class, () -> authorizer.onConfigured(configurationContext)); + } + + @Test + public void testApprovedWithDirectAccess() { + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + final String clientIp = "192.168.1.1"; + + final Map userContext = new HashMap<>(); + userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), clientIp); + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .userContext(userContext) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerNiFiAuthorizer.RANGER_NIFI_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + expectedRangerRequest.setClientIPAddress(clientIp); + + // a non-null result processor should be used for direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(allowedResult); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.approved().getResult(), result.getResult()); + } + + @Test + public void testApprovedWithNonDirectAccess() { + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .accessAttempt(false) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerNiFiAuthorizer.RANGER_NIFI_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // no result processor should be provided used non-direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(allowedResult); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.approved().getResult(), result.getResult()); + } + + @Test + public void testResourceNotFound() { + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerNiFiAuthorizer.RANGER_NIFI_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // no result processor should be provided used non-direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest)), + isNotNull()) + ).thenReturn(notAllowedResult); + + // return false when checking if a policy exists for the resource + when(rangerBasePlugin.doesPolicyExist(systemResource, action)).thenReturn(false); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.resourceNotFound().getResult(), result.getResult()); + } + + @Test + public void testDenied() { + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerNiFiAuthorizer.RANGER_NIFI_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // no result processor should be provided used non-direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(notAllowedResult); + + // return true when checking if a policy exists for the resource + when(rangerBasePlugin.doesPolicyExist(systemResource, action)).thenReturn(true); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.denied().getResult(), result.getResult()); + } + + @Test + public void testRangerAdminApproved() { + final String acceptableIdentity = "ranger-admin"; + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity); + + final String requestIdentity = "ranger-admin"; + runRangerAdminTest(RangerNiFiAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.approved().getResult()); + } + + @Test + public void testRangerAdminApprovedMultipleAcceptableIdentities() { + final String acceptableIdentity1 = "ranger-admin1"; + final String acceptableIdentity2 = "ranger-admin2"; + final String acceptableIdentity3 = "ranger-admin3"; + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity1); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 2", acceptableIdentity2); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 3", acceptableIdentity3); + + final String requestIdentity = "ranger-admin2"; + runRangerAdminTest(RangerNiFiAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.approved().getResult()); + } + + @Test + public void testRangerAdminApprovedMultipleAcceptableIdentities2() { + final String acceptableIdentity1 = "ranger-admin1"; + final String acceptableIdentity2 = "ranger-admin2"; + final String acceptableIdentity3 = "ranger-admin3"; + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity1); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 2", acceptableIdentity2); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 3", acceptableIdentity3); + + final String requestIdentity = "ranger-admin3"; + runRangerAdminTest(RangerNiFiAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.approved().getResult()); + } + + @Test + public void testRangerAdminDenied() { + final String acceptableIdentity = "ranger-admin"; + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity); + + final String requestIdentity = "ranger-admin"; + runRangerAdminTest("/flow", requestIdentity, AuthorizationResult.denied().getResult()); + } + + @Test + public void testRangerAdminDeniedMultipleAcceptableIdentities() { + final String acceptableIdentity1 = "ranger-admin1"; + final String acceptableIdentity2 = "ranger-admin2"; + final String acceptableIdentity3 = "ranger-admin3"; + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity1); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 2", acceptableIdentity2); + authorizersXmlContent.put(RangerNiFiAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 3", acceptableIdentity3); + + final String requestIdentity = "ranger-admin4"; + runRangerAdminTest(RangerNiFiAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.denied().getResult()); + } + + private void runRangerAdminTest(final String resourceIdentifier, final String requestIdentity, final AuthorizationResult.Result expectedResult) { + configurationContext = createMockConfigContext(); + + rangerBasePlugin = Mockito.mock(RangerBasePluginWithPolicies.class); + + final RangerPluginConfig pluginConfig = new RangerPluginConfig(serviceType, null, appId, null, null, null); + when(rangerBasePlugin.getConfig()).thenReturn(pluginConfig); + + authorizer = new MockRangerNiFiAuthorizer(rangerBasePlugin); + authorizer.onConfigured(configurationContext); + + final RequestAction action = RequestAction.WRITE; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(resourceIdentifier, resourceIdentifier)) + .action(action) + .identity(requestIdentity) + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerNiFiAuthorizer.RANGER_NIFI_RESOURCE_NAME, resourceIdentifier); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // return true when checking if a policy exists for the resource + when(rangerBasePlugin.doesPolicyExist(resourceIdentifier, action)).thenReturn(true); + + // a non-null result processor should be used for direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(notAllowedResult); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(expectedResult, result.getResult()); + } + + @Test + @Disabled + public void testIntegration() { + final AuthorizerInitializationContext initializationContext = Mockito.mock(AuthorizerInitializationContext.class); + final AuthorizerConfigurationContext configurationContext = Mockito.mock(AuthorizerConfigurationContext.class); + + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_SECURITY_PATH_PROP))) + .thenReturn(new MockPropertyValue("src/test/resources/ranger/ranger-nifi-security.xml")); + + when(configurationContext.getProperty(eq(RangerNiFiAuthorizer.RANGER_AUDIT_PATH_PROP))) + .thenReturn(new MockPropertyValue("src/test/resources/ranger/ranger-nifi-audit.xml")); + + Authorizer authorizer = new RangerNiFiAuthorizer(); + try { + authorizer.initialize(initializationContext); + authorizer.onConfigured(configurationContext); + + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new Resource() { + @Override + public String getIdentifier() { + return "/system"; + } + + @Override + public String getName() { + return "/system"; + } + + @Override + public String getSafeDescription() { + return "system"; + } + }) + .action(RequestAction.WRITE) + .identity("admin") + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + + final AuthorizationResult result = authorizer.authorize(request); + + assertEquals(AuthorizationResult.denied().getResult(), result.getResult()); + + } finally { + authorizer.preDestruction(); + } + } + + /** + * Extend RangerNiFiAuthorizer to inject a mock base plugin for testing. + */ + private static class MockRangerNiFiAuthorizer extends RangerNiFiAuthorizer { + + RangerBasePluginWithPolicies mockRangerBasePlugin; + + public MockRangerNiFiAuthorizer(RangerBasePluginWithPolicies mockRangerBasePlugin) { + this.mockRangerBasePlugin = mockRangerBasePlugin; + } + + @Override + protected RangerBasePluginWithPolicies createRangerBasePlugin(String serviceType, String appId) { + when(mockRangerBasePlugin.getAppId()).thenReturn(appId); + when(mockRangerBasePlugin.getServiceType()).thenReturn(serviceType); + return mockRangerBasePlugin; + } + } + + /** + * Resource implementation for testing. + */ + private static class MockResource implements Resource { + + private final String identifier; + private final String name; + + public MockResource(String identifier, String name) { + this.identifier = identifier; + this.name = name; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getSafeDescription() { + return name; + } + } + + /** + * Custom Mockito matcher for RangerAccessRequest objects. + */ + private static class RangerAccessRequestMatcher implements ArgumentMatcher { + + private final RangerAccessRequest request; + + public RangerAccessRequestMatcher(RangerAccessRequest request) { + this.request = request; + } + + @Override + public boolean matches(RangerAccessRequest argument) { + if (argument == null) { + return false; + } + + final boolean clientIpsMatch = (argument.getClientIPAddress() == null && request.getClientIPAddress() == null) + || (argument.getClientIPAddress() != null && request.getClientIPAddress() != null && argument.getClientIPAddress().equals(request.getClientIPAddress())); + + return argument.getResource().equals(request.getResource()) + && argument.getAccessType().equals(request.getAccessType()) + && argument.getAction().equals(request.getAction()) + && argument.getUser().equals(request.getUser()) + && clientIpsMatch; + } + } + +} diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/authorizers.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/authorizers.xml new file mode 100644 index 000000000000..ef87a8c51cdf --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/authorizers.xml @@ -0,0 +1,27 @@ + + + + + ranger-provider + org.apache.nifi.ranger.authorization.RangerNiFiAuthorizer + src/test/resources/ranger/ranger-nifi-audit.xml + src/test/resources/ranger/ranger-nifi-security.xml + nifi + nifi + CN=ranger-admin, OU=Apache Ranger, O=Apache, L=Santa Monica, ST=CA, C=US + false + + \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/krb5.conf b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/krb5.conf new file mode 100644 index 000000000000..0e3f142a9b48 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/krb5.conf @@ -0,0 +1,25 @@ +# 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. + +[libdefaults] + default_realm = EXAMPLE.COM + dns_lookup_kdc = false + dns_lookup_realm = false + +[realms] + EXAMPLE.COM = { + kdc = kerberos.example.com + admin_server = kerberos.example.com + } \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/core-site.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/core-site.xml new file mode 100644 index 000000000000..d590a5039c04 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/core-site.xml @@ -0,0 +1,22 @@ + + + + + + hadoop.security.authentication + simple + + \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-nifi-audit.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-nifi-audit.xml new file mode 100644 index 000000000000..3dbd576334b9 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-nifi-audit.xml @@ -0,0 +1,101 @@ + + + + + + xasecure.audit.is.enabled + true + + + + + xasecure.audit.destination.db + false + + + + xasecure.audit.destination.db.jdbc.driver + com.mysql.jdbc.Driver + + + + xasecure.audit.destination.db.jdbc.url + jdbc:mysql://localhost/ranger_audit + + + + xasecure.audit.destination.db.password + rangerlogger + + + + xasecure.audit.destination.db.user + rangerlogger + + + + xasecure.audit.destination.db.batch.filespool.dir + /tmp/audit/db/spool + + + + + + xasecure.audit.destination.hdfs + false + + + + xasecure.audit.destination.hdfs.dir + hdfs://localhost:8020/ranger/audit + + + + xasecure.audit.destination.hdfs.batch.filespool.dir + /tmp/audit/hdfs/spool + + + + + + xasecure.audit.destination.log4j + false + + + + xasecure.audit.destination.log4j.logger + ranger_audit_logger + + + + + xasecure.audit.destination.solr + true + + + + xasecure.audit.destination.solr.batch.filespool.dir + /tmp/audit/solr/spool + + + + xasecure.audit.destination.solr.urls + http://localhost:6083/solr/ranger_audits + + + diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-nifi-security.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-nifi-security.xml new file mode 100644 index 000000000000..b371dcc8436a --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-nifi-security.xml @@ -0,0 +1,83 @@ + + + + + + ranger.plugin.nifi.policy.rest.url + http://localhost:6080 + + URL to Ranger Admin + + + + + ranger.plugin.nifi.service.name + nifi + + Name of the Ranger service containing policies for this nifi instance + + + + + ranger.plugin.nifi.policy.source.impl + org.apache.ranger.admin.client.RangerAdminRESTClient + + Class to retrieve policies from the source + + + + + ranger.plugin.nifi.policy.rest.ssl.config.file + ranger-policymgr-ssl.xml + + Path to the file containing SSL details to contact Ranger Admin + + + + + ranger.plugin.nifi.policy.pollIntervalMs + 30000 + + How often to poll for changes in policies? + + + + + ranger.plugin.nifi.policy.cache.dir + /tmp + + Directory where Ranger policies are cached after successful retrieval from the source + + + + + ranger.plugin.nifi.policy.rest.client.connection.timeoutMs + 120000 + + RangerRestClient Connection Timeout in Milli Seconds + + + + + ranger.plugin.nifi.policy.rest.client.read.timeoutMs + 30000 + + RangerRestClient read Timeout in Milli Seconds + + + diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-policymgr-ssl.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-policymgr-ssl.xml new file mode 100644 index 000000000000..a6e05747a3d5 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-plugin/src/test/resources/ranger/ranger-policymgr-ssl.xml @@ -0,0 +1,63 @@ + + + + + + + xasecure.policymgr.clientssl.keystore + + + Java Keystore files + + + + xasecure.policymgr.clientssl.keystore.password + none + + password for keystore + + + + xasecure.policymgr.clientssl.truststore + + + java truststore file + + + + xasecure.policymgr.clientssl.truststore.password + none + + java truststore password + + + + xasecure.policymgr.clientssl.keystore.credential.file + + + java keystore credential file + + + + xasecure.policymgr.clientssl.truststore.credential.file + + + java truststore credential file + + + \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-resources/pom.xml b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-resources/pom.xml new file mode 100644 index 000000000000..22448804d547 --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-resources/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + + org.apache.nifi + nifi-ranger-bundle + 2.10.0.1 + + + nifi-ranger-resources + jar + + + + org.apache.commons + commons-lang3 + + + org.slf4j + log4j-over-slf4j + + + org.apache.ranger + credentialbuilder + ${ranger.version} + + + commons-configuration + commons-configuration + + + log4j + log4j + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + slf4j-reload4j + + + commons-logging + commons-logging + + + + + org.apache.commons + commons-configuration2 + + + commons-logging + commons-logging + + + + + diff --git a/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-resources/src/main/resources/scripts/ranger_credential_helper.py b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-resources/src/main/resources/scripts/ranger_credential_helper.py new file mode 100644 index 000000000000..bda3b89d9bfe --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/nifi-ranger-resources/src/main/resources/scripts/ranger_credential_helper.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# 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. + +import os +import argparse +import getpass +import sys + +from subprocess import Popen, PIPE + +if os.getenv('JAVA_HOME') is None: + print("[W] ---------- JAVA_HOME environment property not defined, using java in path. ----------") + JAVA_BIN = 'java' +else: + JAVA_BIN = os.path.join(os.getenv('JAVA_HOME'), 'bin', 'java') +print(f"Using Java:{JAVA_BIN}") + + +def main(): + parser = argparse.ArgumentParser(description="Manage Ranger JCEKS credential aliases.") + parser.add_argument("-l", "--libpath", dest="library_path", + help="Path to folder where credential libs are present") + parser.add_argument("-f", "--file", dest="jceks_file_path", help="Path to jceks file to use") + parser.add_argument("-k", "--key", dest="key", help="Key to use") + parser.add_argument("-c", "--create", dest="create", help="Add a new alias") + args = parser.parse_args() + + value = '' + if args.create: + value = getpass.getpass("Value: ") if sys.stdin.isatty() else sys.stdin.readline().rstrip("\n") + getorcreate = 'create' + else: + getorcreate = 'get' + call_keystore(args.library_path, args.jceks_file_path, args.key, value, getorcreate) + + +def call_keystore(libpath, filepath, aliasKey, aliasValue='', getorcreate='get'): + finalLibPath = libpath.replace('\\', '/').replace('//', '/') + finalFilePath = 'jceks://file/' + filepath.replace('\\', '/').replace('//', '/') + + if getorcreate == 'create': + commandtorun = [JAVA_BIN, '-cp', finalLibPath, 'org.apache.ranger.credentialapi.buildks', + 'create', aliasKey, '-value', aliasValue, '-provider', finalFilePath] + p = Popen(commandtorun, stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True) + output, error = p.communicate() + if p.returncode == 0: + print(f"Alias {aliasKey} created successfully!") + else: + print(f"Error creating Alias!! Error: {error.strip()}") + + elif getorcreate == 'get': + commandtorun = [JAVA_BIN, '-cp', finalLibPath, 'org.apache.ranger.credentialapi.buildks', + 'get', aliasKey, '-provider', finalFilePath] + p = Popen(commandtorun, stdin=PIPE, stdout=PIPE, stderr=PIPE, text=True) + output, error = p.communicate() + if p.returncode == 0: + print(f"Alias : {aliasKey} Value : {output.strip()}") + else: + print(f"Error getting value!! Error: {error.strip()}") + + else: + print('Invalid Arguments!!') + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-ranger-bundle/pom.xml b/nifi-extension-bundles/nifi-ranger-bundle/pom.xml new file mode 100644 index 000000000000..bfdae6a8a6bf --- /dev/null +++ b/nifi-extension-bundles/nifi-ranger-bundle/pom.xml @@ -0,0 +1,136 @@ + + + + 4.0.0 + + + org.apache.nifi + nifi-extension-bundles + 2.10.0.1 + + + nifi-ranger-bundle + pom + + + nifi-ranger-plugin + nifi-ranger-nar + nifi-ranger-resources + + + + + + + org.eclipse.jetty + jetty-client + 9.4.58.v20250814 + + + org.eclipse.jetty + jetty-http + 9.4.58.v20250814 + + + org.eclipse.jetty + jetty-io + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-util + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-alpn-client + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-util-ajax + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-alpn-java-client + ${ranger.jetty.version} + + + + commons-beanutils + commons-beanutils + 1.9.4 + + + + org.apache.hadoop + hadoop-common + ${hadoop.version} + + + org.apache.solr + solr-solrj + ${ranger.solr.version} + + + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt.version} + + + org.apache.hadoop + hadoop-auth + ${hadoop.version} + + + org.slf4j + slf4j-reload4j + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + + + + org.codehaus.jettison + jettison + 1.5.4 + + + org.apache.nifi + nifi-deprecation-log + 2.10.0.1 + + + org.apache.nifi + nifi-framework-api + 2.10.0.1 + + + + diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/GpfidstLoadConfig.java b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/GpfidstLoadConfig.java index 7223443efe82..e2d3e48364bb 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/GpfidstLoadConfig.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/GpfidstLoadConfig.java @@ -42,7 +42,9 @@ public long getGpfdistStreamBufferEnqueueTimeoutMs() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GpfidstLoadConfig that = (GpfidstLoadConfig) o; return asyncContextTimeoutMs == that.asyncContextTimeoutMs && gpfdistSegmentStreamBufferSize == that.gpfdistSegmentStreamBufferSize diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorId.java b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorId.java index 9fa6fff83cf3..3a57f612b347 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorId.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorId.java @@ -40,7 +40,9 @@ public int getSegmentId() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } RecordProcessorId that = (RecordProcessorId) o; return segmentId == that.segmentId && Objects.equals(sinkId, that.sinkId) && Objects.equals(txId, that.txId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorLoadingResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorLoadingResult.java index 2f7eca90d2e2..1629ae91b2d8 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorLoadingResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/metadata/RecordProcessorLoadingResult.java @@ -49,7 +49,9 @@ public void incrementRecordBytes(long processedBytes) { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } RecordProcessorLoadingResult that = (RecordProcessorLoadingResult) o; return recordCount == that.recordCount && recordBytes == that.recordBytes && Objects.equals(recordProcessorId, that.recordProcessorId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/service/LoadResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/service/LoadResult.java index 2aaecc573214..6b5898686c6f 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/service/LoadResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-gpfdist-service-api/src/main/java/org/apache/nifi/gpfdist/service/LoadResult.java @@ -53,7 +53,9 @@ public boolean isSuccess() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } LoadResult result = (LoadResult) o; return Objects.equals(loadId, result.loadId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/StandardGpfdistService.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/StandardGpfdistService.java index 056f91ed51ce..7f60b14c0169 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/StandardGpfdistService.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/StandardGpfdistService.java @@ -281,6 +281,7 @@ public TransferDataQueryExecutor getCreateWriteExternalTableQueryExecutor() { return createWriteExternalTableQueryExecutor; } + @Override public TransferDataQueryExecutor getInsertDataIntoTargetTableQueryExecutor() { return insertDataIntoTargetTableQueryExecutor; } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/cluster/ClusterStateNodeIndexService.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/cluster/ClusterStateNodeIndexService.java index def942ef63c1..73c572e018fc 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/cluster/ClusterStateNodeIndexService.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/cluster/ClusterStateNodeIndexService.java @@ -62,7 +62,7 @@ public int getNodeIndex() throws Exception { } private Set getHosts(String hostsStr) { - if (hostsStr == null || hostsStr.trim().isEmpty()) { + if (hostsStr == null || hostsStr.isEmpty()) { throw new IllegalStateException("Cluster hosts state is empty when getting node index for " + hostName); } final Set hosts = Arrays.stream(hostsStr.split(",")) diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/context/GpfdistContextId.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/context/GpfdistContextId.java index 4a1925578660..0881dc86fc40 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/context/GpfdistContextId.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/context/GpfdistContextId.java @@ -34,7 +34,9 @@ public String getId() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GpfdistContextId contextId = (GpfdistContextId) o; return Objects.equals(id, contextId.id); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/greengage/model/GreengageColumnDescription.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/greengage/model/GreengageColumnDescription.java index 8c6c8fe14400..00b84a4a9caf 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/greengage/model/GreengageColumnDescription.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/greengage/model/GreengageColumnDescription.java @@ -59,7 +59,9 @@ public boolean isNullable() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GreengageColumnDescription that = (GreengageColumnDescription) o; return required == that.required && Objects.equals(columnName, that.columnName) && Objects.equals(dataType, that.dataType); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/context/WriteContext.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/context/WriteContext.java index f4ac6dd13f36..e340c6058b52 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/context/WriteContext.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/context/WriteContext.java @@ -35,8 +35,8 @@ import java.util.stream.Collectors; public class WriteContext implements Context { - private final ConcurrentHashMap externalTableLocksBySink = new ConcurrentHashMap<>(); - private final ConcurrentHashMap externalTableCreatedBySink = new ConcurrentHashMap<>(); + private final Map externalTableLocksBySink = new ConcurrentHashMap<>(); + private final Map externalTableCreatedBySink = new ConcurrentHashMap<>(); private final ContextId contextId; private final Map metadataMap; private final Map recordSinkMap; @@ -134,6 +134,15 @@ public void close() { } } + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + WriteContext that = (WriteContext) o; + return Objects.equals(contextId, that.contextId); + } + @Override public int hashCode() { return Objects.hashCode(contextId); diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingResult.java index 2eafcf686b58..608440ca2488 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingResult.java @@ -26,9 +26,6 @@ public class LoadingResult { private final AtomicReference error = new AtomicReference<>(); private final Map segmentResults = new ConcurrentHashMap<>(); - public LoadingResult() { - } - public AtomicInteger getSegmentsCount() { return segmentsCount; } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingSegmentResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingSegmentResult.java index f5d2630824eb..0c18513cb54f 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingSegmentResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/metadata/LoadingSegmentResult.java @@ -35,7 +35,9 @@ public int getSegmentId() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } LoadingSegmentResult that = (LoadingSegmentResult) o; return segmentId == that.segmentId && loadedRecords == that.loadedRecords && loadedBytes == that.loadedBytes; } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistRecordSink.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistRecordSink.java index 8ac95ee97d93..906687832b5a 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistRecordSink.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistRecordSink.java @@ -296,7 +296,9 @@ public int hashCode() { @Override public boolean equals(Object o) { - if (!(o instanceof GpfdistRecordSink)) return false; + if (!(o instanceof GpfdistRecordSink)) { + return false; + } return Objects.equals(sinkId, ((GpfdistRecordSink) o).sinkId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistSegmentStream.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistSegmentStream.java index 0838eb209778..c54fae3c0653 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistSegmentStream.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/load/process/GpfdistSegmentStream.java @@ -19,6 +19,7 @@ import org.apache.nifi.logging.ComponentLog; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -31,7 +32,7 @@ public class GpfdistSegmentStream { private final RecordProcessorId recordProcessorId; private final int avgGpfdistPacketSize; private final int capacity; - private final ArrayBlockingQueue queue; + private final BlockingQueue queue; private final long enqueueTimeoutMs; private final ComponentLog logger; diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/CsvFormatConfig.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/CsvFormatConfig.java index 9c690f806cee..da865fe8c99e 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/CsvFormatConfig.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/CsvFormatConfig.java @@ -35,9 +35,6 @@ public class CsvFormatConfig implements DataFormatConfig { .setSkipHeaderRecord(true) .build(); - public CsvFormatConfig() { - } - public CSVFormat getCsvFormat() { return GREENGAGE_CSV_FORMAT; } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/DefaultExternalTableFormat.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/DefaultExternalTableFormat.java index 7fa20daa9d9e..19a95e091698 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/DefaultExternalTableFormat.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/metadata/DefaultExternalTableFormat.java @@ -59,7 +59,9 @@ public DataFormat getDataFormat() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } DefaultExternalTableFormat that = (DefaultExternalTableFormat) o; return Objects.equals(delimiter, that.delimiter) && Objects.equals(encoding, that.encoding) diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/deserialization/AbstractGreengageCSVRecordReader.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/deserialization/AbstractGreengageCSVRecordReader.java index 89016b1f94a4..2aaa9cba6dcf 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/deserialization/AbstractGreengageCSVRecordReader.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/deserialization/AbstractGreengageCSVRecordReader.java @@ -37,7 +37,7 @@ import static org.apache.nifi.gpfdist.service.util.GreengageColumnDataTypeConverter.parseArray; import static org.apache.nifi.gpfdist.service.util.GreengageColumnDataTypeConverter.parseMap; -abstract public class AbstractGreengageCSVRecordReader implements RecordReader { +public abstract class AbstractGreengageCSVRecordReader implements RecordReader { private final boolean trimDoubleQuote; protected final String dateFormat; protected final String timeFormat; diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/GreengageChunkId.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/GreengageChunkId.java index fa01c8ba9b9a..c8bb17da9d0d 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/GreengageChunkId.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/GreengageChunkId.java @@ -34,7 +34,9 @@ public int getSegmentId() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GreengageChunkId that = (GreengageChunkId) o; return segmentId == that.segmentId && Objects.equals(transactionId, that.transactionId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessingChunkId.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessingChunkId.java index 2e1e340025f2..aeb1783427cc 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessingChunkId.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessingChunkId.java @@ -40,7 +40,9 @@ public int getSegmentId() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } ProcessingChunkId that = (ProcessingChunkId) o; return segmentId == that.segmentId && Objects.equals(processorTaskId, that.processorTaskId) && Objects.equals(txId, that.txId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessorTaskResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessorTaskResult.java index 0a54f9aa9574..b987f9f5e77f 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessorTaskResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/ProcessorTaskResult.java @@ -79,7 +79,9 @@ public static ProcessorTaskResult aggregate(String processorTaskId, @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } ProcessorTaskResult that = (ProcessorTaskResult) o; return Objects.equals(processorTaskId, that.processorTaskId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/SegmentResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/SegmentResult.java index ac4567595f8d..b6f5f92f1823 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/SegmentResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/SegmentResult.java @@ -34,7 +34,9 @@ public SegmentResult(int segmentId, @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } SegmentResult that = (SegmentResult) o; return segmentId == that.segmentId && Objects.equals(transactions, that.transactions); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/TransactionResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/TransactionResult.java index 4b06a65a14ba..d6bd9d969ca2 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/TransactionResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/TransactionResult.java @@ -29,7 +29,9 @@ public TransactionResult(String txId, long recordsCount) { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } TransactionResult that = (TransactionResult) o; return recordsCount == that.recordsCount && Objects.equals(txId, that.txId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/UnloadingResult.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/UnloadingResult.java index 597dfc8c95d5..2e8d60240fce 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/UnloadingResult.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/dto/UnloadingResult.java @@ -49,7 +49,9 @@ public void increment() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } UnloadingResult that = (UnloadingResult) o; return Objects.equals(chunkId, that.chunkId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistChunkRequestProcessor.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistChunkRequestProcessor.java index 4ba17d34fc16..d306fb6c5e01 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistChunkRequestProcessor.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistChunkRequestProcessor.java @@ -74,7 +74,9 @@ public GreengageChunkId getChunkId() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GpfdistChunkRequestProcessor that = (GpfdistChunkRequestProcessor) o; return Objects.equals(chunkId, that.chunkId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistCsvDataProcessor.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistCsvDataProcessor.java index 0f7aa85a875e..9014f2eaf102 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistCsvDataProcessor.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistCsvDataProcessor.java @@ -86,7 +86,9 @@ public UnloadingResult getResult() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GpfdistCsvDataProcessor that = (GpfdistCsvDataProcessor) o; return Objects.equals(schema, that.schema) && Objects.equals(dataTypes, that.dataTypes) diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistRecordProcessingService.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistRecordProcessingService.java index d39f3aef6482..34482bf4836c 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistRecordProcessingService.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/GpfdistRecordProcessingService.java @@ -23,6 +23,7 @@ import java.util.Objects; import java.util.Optional; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -32,7 +33,7 @@ public class GpfdistRecordProcessingService implements RecordProcessingService { private static final int MINIMUM_RECORDS_BUFFER_SIZE = 1000; private final String processorTaskId; - private final ArrayBlockingQueue queue; + private final BlockingQueue queue; private final AtomicBoolean isStopped = new AtomicBoolean(false); private final Map segmentDataProcessors = new ConcurrentHashMap<>(); private final ComponentLog logger; @@ -129,7 +130,9 @@ public Collection getResult() { @Override public boolean equals(Object o) { - if (o == null || getClass() != o.getClass()) return false; + if (o == null || getClass() != o.getClass()) { + return false; + } GpfdistRecordProcessingService that = (GpfdistRecordProcessingService) o; return Objects.equals(processorTaskId, that.processorTaskId); } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/ProcessorTaskManager.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/ProcessorTaskManager.java index 707b92d4efc5..3bfdd27223e1 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/ProcessorTaskManager.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/unload/process/ProcessorTaskManager.java @@ -15,13 +15,14 @@ import org.apache.nifi.logging.ComponentLog; +import java.util.Queue; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; public class ProcessorTaskManager { private final int globalParallelFactor; private final ComponentLog logger; - private final ConcurrentLinkedQueue processorTasksQueue; + private final Queue processorTasksQueue; public ProcessorTaskManager(int nodeIndex, int nodeCount, @@ -66,7 +67,7 @@ public void release(ProcessorTask task) { } } - public ConcurrentLinkedQueue getProcessorTask() { + public Queue getProcessorTask() { return processorTasksQueue; } diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/util/GreengageUtil.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/util/GreengageUtil.java index ba89a402d6b0..38da1ab56e9b 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/util/GreengageUtil.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/main/java/org/apache/nifi/gpfdist/service/util/GreengageUtil.java @@ -54,7 +54,7 @@ public final class GreengageUtil { SUPPORTED_MAX_VALUE_TYPES.stream() .map(e -> e.name().toLowerCase()) .collect(Collectors.joining(", ")); - private final static Map TYPE_OID_TO_JDBC_TYPE_MAP = Map.ofEntries( + private static final Map TYPE_OID_TO_JDBC_TYPE_MAP = Map.ofEntries( Map.entry(Oid.INT2, Types.SMALLINT), Map.entry(Oid.INT4, Types.INTEGER), Map.entry(Oid.OID, Types.BIGINT), diff --git a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/test/java/org/apache/nifi/gpfdist/service/load/serialization/csv/CsvRecordSerializationServiceTest.java b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/test/java/org/apache/nifi/gpfdist/service/load/serialization/csv/CsvRecordSerializationServiceTest.java index 123aed91ed4e..500b85a7fbbf 100644 --- a/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/test/java/org/apache/nifi/gpfdist/service/load/serialization/csv/CsvRecordSerializationServiceTest.java +++ b/nifi-extension-bundles/nifi-standard-services/nifi-greengage-service-bundle/nifi-gpfdist-service/src/test/java/org/apache/nifi/gpfdist/service/load/serialization/csv/CsvRecordSerializationServiceTest.java @@ -154,55 +154,56 @@ void testSerializeAllDataTypes() { Timestamp timestampField = new Timestamp(1740027473375L); Timestamp zonedTimestampField = new Timestamp(1740002273375L); + Map recordValues1 = Map.ofEntries( + Map.entry(fieldNames.get(0), 1), + Map.entry(fieldNames.get(1), 2478701872L), + Map.entry(fieldNames.get(2), false), + Map.entry(fieldNames.get(3), true), + Map.entry(fieldNames.get(4), new Byte[]{-48, 120}), + Map.entry(fieldNames.get(5), "tt"), + Map.entry(fieldNames.get(6), "c4ca4238a0"), + Map.entry(fieldNames.get(7), "edc8acddc2e9a0a6aec79ddd681c75ac"), + Map.entry(fieldNames.get(8), dateField), + Map.entry(fieldNames.get(9), 0.557235836982727), + Map.entry(fieldNames.get(10), 6.559277), + Map.entry(fieldNames.get(11), "{\"a\": \"b\"}"), + Map.entry(fieldNames.get(12), 45.51123), + Map.entry(fieldNames.get(13), 10.3), + Map.entry(fieldNames.get(14), 15), + Map.entry(fieldNames.get(15), 5000), + Map.entry(fieldNames.get(16), timeField), + Map.entry(fieldNames.get(17), timestampField), + Map.entry(fieldNames.get(18), zonedTimestampField), + Map.entry(fieldNames.get(19), "c2142fe5-e305-42ab-8b95-598567e9ea86"), + Map.entry(fieldNames.get(20), "{val, val}"), + Map.entry(fieldNames.get(21), "{ISBN-13=978-1449370000, weight=11.2 ounces, paperback=243, publisher=postgresqltutorial.com, language=English}")); + + Map recordValues2 = new HashMap<>(); + recordValues2.put(fieldNames.get(0), 1); + recordValues2.put(fieldNames.get(0), null); + recordValues2.put(fieldNames.get(2), null); + recordValues2.put(fieldNames.get(3), null); + recordValues2.put(fieldNames.get(4), null); + recordValues2.put(fieldNames.get(5), null); + recordValues2.put(fieldNames.get(6), null); + recordValues2.put(fieldNames.get(7), null); + recordValues2.put(fieldNames.get(8), null); + recordValues2.put(fieldNames.get(9), null); + recordValues2.put(fieldNames.get(10), null); + recordValues2.put(fieldNames.get(11), null); + recordValues2.put(fieldNames.get(12), null); + recordValues2.put(fieldNames.get(13), null); + recordValues2.put(fieldNames.get(14), null); + recordValues2.put(fieldNames.get(15), null); + recordValues2.put(fieldNames.get(16), null); + recordValues2.put(fieldNames.get(17), null); + recordValues2.put(fieldNames.get(18), null); + recordValues2.put(fieldNames.get(19), null); + recordValues2.put(fieldNames.get(20), null); + recordValues2.put(fieldNames.get(21), null); List records = List.of( - new MapRecord(recordSchema, new HashMap<>() {{ - put(fieldNames.get(0), 1); - put(fieldNames.get(1), 2478701872L); - put(fieldNames.get(2), false); - put(fieldNames.get(3), true); - put(fieldNames.get(4), new Byte[]{-48, 120}); - put(fieldNames.get(5), "tt"); - put(fieldNames.get(6), "c4ca4238a0"); - put(fieldNames.get(7), "edc8acddc2e9a0a6aec79ddd681c75ac"); - put(fieldNames.get(8), dateField); - put(fieldNames.get(9), 0.557235836982727); - put(fieldNames.get(10), 6.559277); - put(fieldNames.get(11), "{\"a\": \"b\"}"); - put(fieldNames.get(12), 45.51123); - put(fieldNames.get(13), 10.3); - put(fieldNames.get(14), 15); - put(fieldNames.get(15), 5000); - put(fieldNames.get(16), timeField); - put(fieldNames.get(17), timestampField); - put(fieldNames.get(18), zonedTimestampField); - put(fieldNames.get(19), "c2142fe5-e305-42ab-8b95-598567e9ea86"); - put(fieldNames.get(20), "{val, val}"); - put(fieldNames.get(21), "{ISBN-13=978-1449370000, weight=11.2 ounces, paperback=243, publisher=postgresqltutorial.com, language=English}"); - }}, true, false), - new MapRecord(recordSchema, new HashMap<>() {{ - put(fieldNames.get(0), 1); - put(fieldNames.get(0), null); - put(fieldNames.get(2), null); - put(fieldNames.get(3), null); - put(fieldNames.get(4), null); - put(fieldNames.get(5), null); - put(fieldNames.get(6), null); - put(fieldNames.get(7), null); - put(fieldNames.get(8), null); - put(fieldNames.get(9), null); - put(fieldNames.get(10), null); - put(fieldNames.get(11), null); - put(fieldNames.get(12), null); - put(fieldNames.get(13), null); - put(fieldNames.get(14), null); - put(fieldNames.get(15), null); - put(fieldNames.get(16), null); - put(fieldNames.get(17), null); - put(fieldNames.get(18), null); - put(fieldNames.get(19), null); - put(fieldNames.get(20), null); - put(fieldNames.get(21), null); - }}, true, false)); + new MapRecord(recordSchema, recordValues1, true, false), + new MapRecord(recordSchema, recordValues2, true, false)); serializationService = new CsvRecordSerializationService(recordSchema, columns, csvFormatConfig, logger); records.forEach(r -> serializationService.append(r)); @@ -439,4 +440,4 @@ private String toString(Date date, String format) { return DateTimeFormatter.ofPattern(format) .format(zonedDateTime); } -} \ No newline at end of file +} diff --git a/nifi-extension-bundles/pom.xml b/nifi-extension-bundles/pom.xml index 6669ecd01951..a8444bc2ae76 100755 --- a/nifi-extension-bundles/pom.xml +++ b/nifi-extension-bundles/pom.xml @@ -64,6 +64,7 @@ nifi-cdc nifi-parquet-bundle nifi-py4j-extension-bundle + nifi-ranger-bundle nifi-extension-utils nifi-redis-bundle nifi-network-bundle diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/LICENSE b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/LICENSE new file mode 100644 index 000000000000..3d6c113c3e80 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/LICENSE @@ -0,0 +1,445 @@ + + Apache License + Version 2.0, January 2004 + https://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 + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +The binary distribution of this product bundles 'Slf4j' which is available under an MIT license. + + Copyright (c) 2004-2017 QOS.ch + All rights reserved. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +The binary distribution of this product bundles 'jopt-simple' which is available under an MIT license. + + Copyright (c) 2004-2016 Paul R. Holser, Jr. + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +The binary distribution of this product bundles 'ParaNamer' which is available under a BSD license. + + Portions copyright (c) 2006-2018 Paul Hammant & ThoughtWorks Inc + Portions copyright (c) 2000-2007 INRIA, France Telecom + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + THE POSSIBILITY OF SUCH DAMAGE. + +The binary distribution of this product bundles 'JSch' which is available under a BSD license. + + Copyright (c) 2002-2015 Atsuhiko Yamanaka, JCraft,Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + + 3. The names of the authors may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, + INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The binary distribution of this product bundles 'JLine Bundle' which is available under a BSD 3-Clause license. + + Copyright (c) 2002-2006, Marc Prud'hommeaux + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with + the distribution. + + Neither the name of JLine nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, + BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + +The binary distribution of this product bundles 'Protocol Buffers' which is available under a BSD 3-Clause license. + + Copyright 2008 Google Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Code generated by the Protocol Buffer compiler is owned by the owner + of the input file used when generating it. This code is not + standalone and requires a support library to be linked with it. This + support library is itself covered by the above license. + +The binary distribution of this product bundles 'Scala' which is available under a BSD 3-Clause license. + + Copyright (c) 2002- EPFL + Copyright (c) 2011- Lightbend, Inc. + + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the EPFL nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +This product bundles 'RE2/J' which is available under a Go license. + + This is a work derived from Russ Cox's RE2 in Go, whose license + https://golang.org/LICENSE is as follows: + + Copyright (c) 2009 The Go Authors. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google Inc. nor the names of its contributors + may be used to endorse or promote products derived from this + software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/NOTICE b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/NOTICE new file mode 100644 index 000000000000..074c9a86ad4b --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/NOTICE @@ -0,0 +1,449 @@ +nifi-registry-ranger-extension +Copyright 2020 The Apache Software Foundation + +This product includes software developed at +The Apache Software Foundation (https://www.apache.org/). + +****************** +Apache Software License v2 +****************** + + (ASLv2) Apache Avro + The following NOTICE information applies: + Apache Avro + Copyright 2009-2017 The Apache Software Foundation + + (ASLv2) Apache Commons Collections + The following NOTICE information applies: + Apache Commons Collections + Copyright 2001-2013 The Apache Software Foundation + + (ASLv2) Apache Commons Compress + The following NOTICE information applies: + Apache Commons Compress + Copyright 2002-2017 The Apache Software Foundation + + The files in the package org.apache.commons.compress.archivers.sevenz + were derived from the LZMA SDK, version 9.20 (C/ and CPP/7zip/), + which has been placed in the public domain: + + "LZMA SDK is placed in the public domain." (https://www.7-zip.org/sdk.html) + + (ASLv2) Apache Commons Codec + The following NOTICE information applies: + Apache Commons Codec + Copyright 2002-2014 The Apache Software Foundation + + src/test/org/apache/commons/codec/language/DoubleMetaphoneTest.java + contains test data from http://aspell.net/test/orig/batch0.tab. + Copyright (C) 2002 Kevin Atkinson (kevina@gnu.org) + + =============================================================================== + + The content of package org.apache.commons.codec.language.bm has been translated + from the original php source code available at https://stevemorse.org/phoneticinfo.htm + with permission from the original authors. + Original source copyright: + Copyright (c) 2008 Alexander Beider & Stephen P. Morse. + + (ASLv2) Apache Commons CLI + The following NOTICE information applies: + Apache Commons CLI + Copyright 2001-2009 The Apache Software Foundation + + (ASLv2) Apache Commons Configuration + The following NOTICE information applies: + Apache Commons Configuration + Copyright 2001-2008 The Apache Software Foundation + + (ASLv2) Apache Jakarta HttpClient + The following NOTICE information applies: + Apache Jakarta HttpClient + Copyright 1999-2007 The Apache Software Foundation + + (ASLv2) Apache Commons IO + The following NOTICE information applies: + Apache Commons IO + Copyright 2002-2016 The Apache Software Foundation + + (ASLv2) Apache Commons Lang + The following NOTICE information applies: + Apache Commons Lang + Copyright 2001-2015 The Apache Software Foundation + + This product includes software from the Spring Framework, + under the Apache License 2.0 (see: StringUtils.containsWhitespace()) + + (ASLv2) Apache Commons Math + The following NOTICE information applies: + Apache Commons Math + Copyright 2001-2012 The Apache Software Foundation + + This product includes software developed by + The Apache Software Foundation (https://www.apache.org/). + + =============================================================================== + + The BracketFinder (package org.apache.commons.math3.optimization.univariate) + and PowellOptimizer (package org.apache.commons.math3.optimization.general) + classes are based on the Python code in module "optimize.py" (version 0.5) + developed by Travis E. Oliphant for the SciPy library (https://www.scipy.org/) + Copyright © 2003-2009 SciPy Developers. + =============================================================================== + + The LinearConstraint, LinearObjectiveFunction, LinearOptimizer, + RelationShip, SimplexSolver and SimplexTableau classes in package + org.apache.commons.math3.optimization.linear include software developed by + Benjamin McCann (https://www.benmccann.com) and distributed with + the following copyright: Copyright 2009 Google Inc. + =============================================================================== + + This product includes software developed by the + University of Chicago, as Operator of Argonne National + Laboratory. + The LevenbergMarquardtOptimizer class in package + org.apache.commons.math3.optimization.general includes software + translated from the lmder, lmpar and qrsolv Fortran routines + from the Minpack package + Minpack Copyright Notice (1999) University of Chicago. All rights reserved + =============================================================================== + + The GraggBulirschStoerIntegrator class in package + org.apache.commons.math3.ode.nonstiff includes software translated + from the odex Fortran routine developed by E. Hairer and G. Wanner. + Original source copyright: + Copyright (c) 2004, Ernst Hairer + =============================================================================== + + The EigenDecompositionImpl class in package + org.apache.commons.math3.linear includes software translated + from some LAPACK Fortran routines. Original source copyright: + Copyright (c) 1992-2008 The University of Tennessee. All rights reserved. + =============================================================================== + + The MersenneTwister class in package org.apache.commons.math3.random + includes software translated from the 2002-01-26 version of + the Mersenne-Twister generator written in C by Makoto Matsumoto and Takuji + Nishimura. Original source copyright: + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved + =============================================================================== + + The LocalizedFormatsTest class in the unit tests is an adapted version of + the OrekitMessagesTest class from the orekit library distributed under the + terms of the Apache 2 licence. Original source copyright: + Copyright 2010 CS Systèmes d'Information + =============================================================================== + + The HermiteInterpolator class and its corresponding test have been imported from + the orekit library distributed under the terms of the Apache 2 licence. Original + source copyright: + Copyright 2010-2012 CS Systèmes d'Information + =============================================================================== + + The creation of the package "o.a.c.m.analysis.integration.gauss" was inspired + by an original code donated by Sébastien Brisard. + =============================================================================== + + (ASLv2) Apache Commons Net + The following NOTICE information applies: + Apache Commons Net + Copyright 2001-2013 The Apache Software Foundation + + (ASLv2) Apache Curator + The following NOTICE information applies: + Curator Framework + Copyright 2011-2014 The Apache Software Foundation + + Curator Client + Copyright 2011-2014 The Apache Software Foundation + + Curator Recipes + Copyright 2011-2014 The Apache Software Foundation + + (ASLv2) Apache HttpComponents + The following NOTICE information applies: + Apache HttpClient + Copyright 1999-2015 The Apache Software Foundation + + Apache HttpCore + Copyright 2005-2015 The Apache Software Foundation + + Apache HttpMime + Copyright 1999-2013 The Apache Software Foundation + + This project contains annotations derived from JCIP-ANNOTATIONS + Copyright (c) 2005 Brian Goetz and Tim Peierls. See https://www.jcip.net + + (ASLv2) Apache Ranger + The following NOTICE information applies: + Apache Ranger Credential Builder + Copyright 2014-2016 The Apache Software Foundation + + Apache Ranger Plugins Audit + Copyright 2014-2016 The Apache Software Foundation + + Apache Ranger Plugins Common + Copyright 2014-2016 The Apache Software Foundation + + Apache Ranger Plugins Cred + Copyright 2014-2016 The Apache Software Foundation + + (ASLv2) Google GSON + The following NOTICE information applies: + Copyright 2008 Google Inc. + + (ASLv2) Guava + The following NOTICE information applies: + Guava + Copyright 2015 The Guava Authors + + (ASLv2) Apache Hadoop + The following NOTICE information applies: + Apache Hadoop + Copyright 2014 The Apache Software Foundation. + + (ASLv2) HTrace Core + The following NOTICE information applies: + In addition, this product includes software dependencies. See + the accompanying LICENSE.txt for a listing of dependencies + that are NOT Apache licensed (with pointers to their licensing) + + Apache HTrace includes an Apache Thrift connector to Zipkin. Zipkin + is a distributed tracing system that is Apache 2.0 Licensed. + Copyright 2012 Twitter, Inc. + + (ASLv2) Jackson JSON processor + The following NOTICE information applies: + # Jackson JSON processor + + Jackson is a high-performance, Free/Open Source JSON processing library. + It was originally written by Tatu Saloranta (tatu.saloranta@iki.fi), and has + been in development since 2007. + It is currently developed by a community of developers, as well as supported + commercially by FasterXML.com. + + ## Licensing + + Jackson core and extension components may licensed under different licenses. + To find the details that apply to this artifact see the accompanying LICENSE file. + For more information, including possible other licensing options, contact + FasterXML.com (https://fasterxml.com). + + ## Credits + + A list of contributors may be found from CREDITS file, which is included + in some artifacts (usually source distributions); but is always available + from the source code management (SCM) system project uses. + + (ASLv2) Jettison + The following NOTICE information applies: + Copyright 2006 Envoi Solutions LLC + + (ASLv2) Jetty + The following NOTICE information applies: + Jetty Web Container + Copyright 1995-2017 Mort Bay Consulting Pty Ltd. + + (ASLv2) Apache Kafka + The following NOTICE information applies: + Apache Kafka + Copyright 2012 The Apache Software Foundation. + + scala-library is BSD-like licensed software (https://www.scala-lang.org/license.html) + + (ASLv2) Apache log4j + The following NOTICE information applies: + Apache log4j + Copyright 2007 The Apache Software Foundation + + (ASLv2) Apache Solr + The following NOTICE information applies: + Apache Solrj + Copyright 2006-2014 The Apache Software Foundation + + (ASLv2) Apache ZooKeeper + The following NOTICE information applies: + Apache ZooKeeper + Copyright 2009-2012 The Apache Software Foundation + + (ASLv2) The Netty Project + The following NOTICE information applies: + The Netty Project + Copyright 2011 The Netty Project + + (ASLv2) Snappy Java + The following NOTICE information applies: + This product includes software developed by Google + Snappy: https://code.google.com/p/snappy/ (New BSD License) + + This product includes software developed by Apache + PureJavaCrc32C from apache-hadoop-common https://hadoop.apache.org/ + (Apache 2.0 license) + + This library containd statically linked libstdc++. This inclusion is allowed by + "GCC RUntime Library Exception" + https://gcc.gnu.org/onlinedocs/libstdc++/manual/license.html + + (ASLv2) Woodstox Core ASL + The following NOTICE information applies: + This product currently only contains code developed by authors + of specific components, as identified by the source code files. + + Since product implements StAX API, it has dependencies to StAX API + classes. + + (ASLv2) Yammer Metrics + The following NOTICE information applies: + Metrics + Copyright 2010-2012 Coda Hale and Yammer, Inc. + + This product includes software developed by Coda Hale and Yammer, Inc. + + This product includes code derived from the JSR-166 project (ThreadLocalRandom), which was released + with the following comments: + + Written by Doug Lea with assistance from members of JCP JSR-166 + Expert Group and released to the public domain, as explained at + https://creativecommons.org/publicdomain/zero/1.0/ + + (ASLv2) ZkClient + The following NOTICE information applies: + ZkClient + Copyright 2009 Stefan Groschupf + + (ASLv2) Swagger Core library + The following NOTICE information applies: + Copyright 2016 SmartBear Software + + (ASLv2) json-smart + The following NOTICE information applies: + Copyright 2011 JSON-SMART authors + + (ASLv2) Apache Commons BeanUtils + The following NOTICE information applies: + Apache Commons BeanUtils + Copyright 2000-2008 The Apache Software Foundation + + (ASLv2) Apache Kerby + The following NOTICE information applies: + Apache Kerby + Copyright 2003-2018 The Apache Software Foundation + + (ASLv2) Nimbus JOSE + JWT + The following NOTICE information applies: + Nimbus JOSE + JWT + Copyright 2012 - 2018, Connect2id Ltd. + + (ASLv2) OkHttp + The following NOTICE information applies: + OkHttp + Copyright (C) 2014 Square, Inc. + + (ASLv2) Okio + The following NOTICE information applies: + Okio + Copyright (C) 2014 Square, Inc. + + (ASLv2) JCIP Annotations Under Apache License + The following NOTICE information applies: + JCIP Annotations Under Apache License + Copyright 2013 Stephen Connolly. + +************************ +Common Development and Distribution License 1.0 +************************ + +The following binary components are provided under the Common Development and Distribution License 1.0. See project link for details. + + (CDDL 1.0) JavaBeans Activation Framework (JAF) (javax.activation:activation:jar:1.1 - https://java.sun.com/products/javabeans/jaf/index.jsp) + (CDDL 1.0) (GPL3) Streaming API For XML (javax.xml.stream:stax-api:jar:1.0-2 - no url provided) + +************************ +Common Development and Distribution License 1.1 +************************ + +The following binary components are provided under the Common Development and Distribution License 1.1. See project link for details. + + (CDDL 1.1) (GPL2 w/ CPE) jersey-bundle (com.sun.jersey:jersey-bundle:jar:1.19.3 - https://jersey.java.net/jersey-bundle/) + (CDDL 1.1) (GPL2 w/ CPE) jersey-server (com.sun.jersey:jersey-server:jar:1.19 - https://jersey.java.net/jersey-server/) + (CDDL 1.1) (GPL2 w/ CPE) JavaServer Pages(TM) API (javax.servlet.jsp:javax.servlet.jsp-api:jar:2.1 - https://jsp.java.net) + (CDDL 1.1) (GPL2 w/ CPE) Java Servlet API (javax.servlet:javax.servlet-api:jar:2.5 - https://servlet-spec.java.net) + (CDDL 1.1) (GPL2 w/ CPE) javax.ws.rs-api (javax.ws.rs:javax.ws.rs-api:jar:2.1 - https://jax-rs-spec.java.net) + (CDDL 1.1) (GPL2 w/ CPE) JavaMail API (compat) (javax.mail:mail:jar:1.4.7 - https://java.net/projects/javamail/pages/Home) + (CDDL 1.1) (GPL2 w/ CPE) Java Architecture For XML Binding (javax.xml.bind:jaxb-api:jar:2.2.2 - https://jaxb.dev.java.net/) + (CDDL 1.1) (GPL2 w/ CPE) Old JAXB Runtime (com.sun.xml.bind:jaxb-impl:jar:2.2.3-1 - https://jaxb.java.net/) + +************************ +Eclipse Public License 1.0 +************************ + +The following binary components are provided under the Eclipse Public License 1.0. See project link for details. + + (EPL 1.0) Eclipse Link (org.eclipse.persistence:eclipselink:2.5.2 - https://www.eclipse.org/eclipselink/) + (EPL 1.0) Common Service Data Objects (org.eclipse.persistence:commonj.sdo:2.1.1 - https://www.eclipse.org/eclipselink/) + (EPL 1.0) Java Persistence API (org.eclipse.persistence:javax.persistence:2.1.0 - https://www.eclipse.org/eclipselink/) + +************************ +The MIT License +************************ + +The following binary components are provided under the MIT License. See project link for details. + + (MIT License) Simple Logging Facade for Java (SLF4J) + The following NOTICE information applies: + Copyright (c) 2004-2017 QOS.ch + All rights reserved. + https://www.slf4j.org/ + + (MIT License) JOpt Simple + Copyright (c) 2004-2016 Paul R. Holser, Jr. + https://jopt-simple.github.io/jopt-simple/ + +************************ +BSD License +************************ + +The following binary components are provided under the BSD License. See project link for details. + + (BSD) Paranamer + The following NOTICE information applies: + Portions copyright (c) 2006-2018 Paul Hammant & ThoughtWorks Inc + Portions copyright (c) 2000-2007 INRIA, France Telecom + All rights reserved. + https://github.com/paul-hammant/paranamer + + (BSD) JSch + The following NOTICE information applies: + Copyright (c) 2002-2015 Atsuhiko Yamanaka, JCraft,Inc. + All rights reserved. + https://www.jcraft.com/jsch/ + + (BSD 3-Clause) JLine Bundle + The following NOTICE information applies: + Copyright (c) 2002-2007, Marc Prud'hommeaux. All rights reserved. + https://github.com/jline/jline1 + + (BSD 3-Clause) Protocol Buffers + The following NOTICE information applies: + Copyright 2008 Google Inc. All rights reserved. + https://github.com/google/protobuf/tree/master/java + + (BSD 3-Clause) Scala + The following NOTICE information applies: + Copyright (c) 2002- EPFL + Copyright (c) 2011- Lightbend, Inc. + + All rights reserved. + https://www.scala-lang.org/ + +************************ +Go License +************************ + +The following binary components are provided under the Go License. See project link for details. + + (Go) RE2/J + The following NOTICE information applies: + Copyright (c) 2009 The Go Authors. All rights reserved. + https://github.com/google/re2j diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/README.md b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/README.md new file mode 100644 index 000000000000..52333f1f85fa --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/README.md @@ -0,0 +1,131 @@ + +# NiFi Registry Ranger extension + +This extension provides `org.apache.nifi.registry.ranger.RangerAuthorizer` class for NiFi Registry to authorize user requests by access policies defined at [Apache Ranger](https://ranger.apache.org/). + +## Prerequisites + +* Apache Ranger 1.2.0 or later is needed. + +## How to install + +### Enable Ranger extension at NiFi Registry build + +In order to enable Ranger extension when you build NiFi Registry, specify `include-ranger` profile with a maven install command: + +``` +cd nifi-assembly +mvn clean install -Pinclude-ranger +``` + +The extension will be packaged in the NiFi distribution and will be available in the ${NIFI_HOME}/ext/ranger directory after extraction. + +### Add Ranger extension to existing NiFi Registry + +Alternatively, you can add Ranger extension to an existing NiFi Registry. +To do so, build the extension with the following command: + +``` +cd nifi-assembly +mvn clean install -f nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly +``` + +The extension zip will be created as `nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/target/nifi-registry-ranger-assembly-xxx-bin.zip`. + +Unzip the file into arbitrary directory so that NiFi Registry can use, such as `${NIFI_REG_HOME}/ext/ranger`. +For example: + +``` +mkdir -p ${NIFI_REG_HOME}/ext/ranger +unzip -d ${NIFI_REG_HOME}/ext/ranger nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/target/nifi-registry-ranger-assembly-xxx-bin.zip +``` + +## NiFi Registry Configuration + +In order to use this extension, following NiFi Registry files need to be configured. + +### nifi-registry.properties + +``` +# Specify Ranger extension dir +nifi.registry.extension.dir.ranger=./ext/ranger/lib +# Specify Ranger authorizer identifier, which is defined at authorizers.xml +nifi.registry.security.authorizer=ranger-authorizer +``` + +### authorizers.xml + +Add following `authorizer` element: +``` + + ranger-authorizer + org.apache.nifi.registry.ranger.RangerAuthorizer + nifi-registry + + file-user-group-provider + + + nifi-registry-service-name + + + ./ext/ranger/conf/ranger-nifi-registry-security.xml + ./ext/ranger/conf/ranger-nifi-registry-audit.xml + + + ranger@NIFI + + + false + + +``` + +## Ranger Configuration + +At Ranger side, add a NiFi Registry service. NiFi Registry service has following configuration properties: + +- NiFi Registry URL: Specify corresponding NiFi Registry URL that will be managed by this Ranger service. E.g. `https://nifi-registry.example.com:18443/nifi-registry-api/policies/resources` +- Authentication Type: Should be `SSL`. Ranger authenticates itself to NiFi Registry by X.509 client certificate in the configured Keystore. +- Keystore: Specify a Keystore filepath to use for X.509 client certificate. +- Keystore Type: Specify the type of Keystore. E.g. `JKS` +- Keystore Password: Specify the password of Keystore. +- Truststore: Specify a Truststore filepath to verify NiFi Registry server certificate. +- Truststore Type: Specify the type of Truststore. E.g. `JKS` +- Truststore Password: Specify the password of Truststore. +- Add New Configurations: + - policy.download.auth.users: Required if Ranger is Kerberized. + Specify the NiFi Registry user to download policies, + which is configured by 'nifi.registry.kerberos.service.principal' at nifi-registry.properties, + when NiFi Registry Ranger authorizer is configured as 'Ranger Kerberos Enabled' to true. diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/conf/ranger-nifi-registry-audit.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/conf/ranger-nifi-registry-audit.xml new file mode 100644 index 000000000000..e34ef8873d1d --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/conf/ranger-nifi-registry-audit.xml @@ -0,0 +1,174 @@ + + + + + + xasecure.audit.is.enabled + true + + + + + xasecure.audit.destination.db + false + + + + xasecure.audit.destination.db.jdbc.driver + com.mysql.jdbc.Driver + + + + xasecure.audit.destination.db.jdbc.url + jdbc:mysql://localhost/ranger_audit + + + + xasecure.audit.destination.db.password + rangerlogger + + + + xasecure.audit.destination.db.user + rangerlogger + + + + xasecure.audit.destination.db.batch.filespool.dir + /tmp/audit/db/spool + + + + + + xasecure.audit.destination.hdfs + false + + + + xasecure.audit.destination.hdfs.dir + hdfs://localhost:8020/ranger/audit + + + + xasecure.audit.destination.hdfs.batch.filespool.dir + /tmp/audit/hdfs/spool + + + + + + + + + xasecure.audit.destination.log4j + false + + + + xasecure.audit.destination.log4j.logger + ranger_audit_logger + + + + + xasecure.audit.destination.solr + true + + + + xasecure.audit.destination.solr.batch.filespool.dir + /tmp/audit/solr/spool + + + + + xasecure.audit.destination.solr.urls + + + localhost:2181/solr + + + + + + xasecure.audit.destination.solr.force.use.inmemory.jaas.config + true + + + xasecure.audit.jaas.Client.option.useKeyTab + true + + + xasecure.audit.jaas.Client.option.storeKey + false + + + xasecure.audit.jaas.Client.option.serviceName + solr + + + xasecure.audit.jaas.Client.option.principal + nifi-registry@EXAMPLE.COM + + + xasecure.audit.jaas.Client.option.keyTab + /etc/security/keytabs/nifi-registry.keytab + + + xasecure.audit.jaas.Client.loginModuleName + com.sun.security.auth.module.Krb5LoginModule + + + xasecure.audit.jaas.Client.loginModuleControlFlag + required + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/conf/ranger-nifi-registry-security.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/conf/ranger-nifi-registry-security.xml new file mode 100644 index 000000000000..f271d6d8df3a --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/conf/ranger-nifi-registry-security.xml @@ -0,0 +1,92 @@ + + + + + + ranger.plugin.nifi-registry.policy.rest.url + http://localhost:6080 + + URL to Ranger Admin + + + + + ranger.plugin.nifi-registry.service.name + nifi-registry-service-name + + Name of the Ranger service containing policies for this NiFi Registry instance + + + + + ranger.plugin.nifi-registry.policy.source.impl + org.apache.ranger.admin.client.RangerAdminRESTClient + + Class to retrieve policies from the source + + + + + ranger.plugin.nifi-registry.policy.rest.ssl.config.file + ranger-policymgr-ssl.xml + + Path to the file containing SSL details to contact Ranger Admin + + + + + ranger.plugin.nifi-registry.policy.pollIntervalMs + 30000 + + How often to poll for changes in policies? + + + + + ranger.plugin.nifi-registry.policy.cache.dir + /tmp + + Directory where Ranger policies are cached after successful retrieval from the source + + + + + ranger.plugin.nifi-registry.policy.rest.client.connection.timeoutMs + 120000 + + RangerRestClient Connection Timeout in Milli Seconds + + + + + ranger.plugin.nifi-registry.policy.rest.client.read.timeoutMs + 30000 + + RangerRestClient read Timeout in Milli Seconds + + + + + xasecure.add-hadoop-authorization + true + + Enable SPNEGO authentication using principal and keytab to download policies from Ranger + + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/pom.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/pom.xml new file mode 100644 index 000000000000..7568c8d8c135 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/pom.xml @@ -0,0 +1,100 @@ + + + + + nifi-registry-ranger + org.apache.nifi.registry + 2.10.0.1 + + 4.0.0 + + nifi-registry-ranger-assembly + pom + Apache Ranger extension for Apache NiFi Registry + + + + org.apache.nifi.registry + nifi-registry-ranger-plugin + ${project.version} + + + org.slf4j + jcl-over-slf4j + runtime + + + + + + + maven-assembly-plugin + + true + + + + assembly-ranger-extension + + single + + package + + + src/main/assembly/extension.xml + + zip + + + + + + + + + + targz + + false + + + + + maven-assembly-plugin + + true + + + + assembly-ranger-extension + + single + + package + + + src/main/assembly/extension.xml + + tar.gz + + + + + + + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/src/main/assembly/extension.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/src/main/assembly/extension.xml new file mode 100644 index 000000000000..859ca1158de0 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-assembly/src/main/assembly/extension.xml @@ -0,0 +1,62 @@ + + + + bin + + zip + + false + + + runtime + false + lib + 0770 + 0664 + + + + + + ./README.md + ./ + README.md + 0644 + true + + + ./LICENSE + ./ + LICENSE + 0644 + true + + + ./NOTICE + ./ + NOTICE + 0644 + true + + + + + + conf + conf + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-jersey-bundle/pom.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-jersey-bundle/pom.xml new file mode 100644 index 000000000000..54a3cb2729a2 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-jersey-bundle/pom.xml @@ -0,0 +1,71 @@ + + + + + nifi-registry-ranger + org.apache.nifi.registry + 2.10.0.1 + + 4.0.0 + + This module removes javax.ws.rs package from jersey-bundle.jar which is used by ranger-plugins-common.jar in order to address javax.ws.rs version mismatch between jersey-bundle.jar and NiFi Registry. NiFi Registry uses javax.ws.rs version 2.1. Without doing this, NiFi Registry encounters java.lang.LinkageError: ClassCastException: attempting to castjar:file:nifi-registry-xxx/work/jetty/nifi-registry-web-api-xxx.war/webapp/WEB-INF/lib/javax.ws.rs-api.jar!/javax/ws/rs/ext/RuntimeDelegate.classtojar:file:/home/koji/nifi-registry-xxx/./ext/ranger/lib/jersey-bundle.jar!/javax/ws/rs/ext/RuntimeDelegate.class + + nifi-registry-ranger-jersey-bundle + + + + com.sun.jersey + jersey-bundle + 1.19.4 + + + + javax.ws.rs + jsr311-api + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + package + + shade + + + false + + + com.sun.jersey:jersey-bundle + + javax/ws/rs/** + + + + + + + + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/pom.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/pom.xml new file mode 100644 index 000000000000..70ac6bc17612 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/pom.xml @@ -0,0 +1,449 @@ + + + + + nifi-registry-ranger + org.apache.nifi.registry + 2.10.0.1 + + 4.0.0 + + nifi-registry-ranger-plugin + jar + + + + org.apache.nifi.registry + nifi-registry-data-model + 2.10.0.1 + + + + org.apache.nifi.registry + nifi-registry-security-api + + provided + + + org.apache.nifi.registry + nifi-registry-properties + + provided + + + org.apache.nifi + nifi-xml-processing + 2.10.0.1 + + + org.apache.nifi.registry + nifi-registry-ranger-jersey-bundle + 2.10.0.1 + + + com.sun.jersey + jersey-bundle + + + + + javax.ws.rs + javax.ws.rs-api + 2.1.1 + + + org.slf4j + log4j-over-slf4j + + + org.slf4j + jcl-over-slf4j + + + org.apache.nifi + nifi-deprecation-log + 2.10.0.1 + + + + + org.apache.ranger + ranger-plugins-common + ${ranger.version} + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + ch.qos.logback + logback-classic + + + com.google.code.findbugs + jsr305 + + + + com.sun.jersey + jersey-bundle + + + + com.sun.jersey + jersey-json + + + + + org.apache.ranger + ranger-plugins-audit + ${ranger.version} + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + + + org.apache.ranger + credentialbuilder + ${ranger.version} + + + org.slf4j + slf4j-reload4j + + + org.slf4j + slf4j-log4j12 + + + org.slf4j + slf4j-api + + + commons-logging + commons-logging + + + + + + + org.apache.hadoop + hadoop-client + ${hadoop.version} + + + org.apache.hadoop + hadoop-yarn-api + + + org.apache.hadoop + hadoop-yarn-client + + + org.apache.hadoop + hadoop-mapreduce-client + + + org.apache.hadoop + hadoop-mapreduce-client-core + + + org.apache.hadoop + hadoop-mapreduce-client-jobclient + + + commons-logging + commons-logging + + + + + + org.apache.hadoop + hadoop-common + ${hadoop.version} + + + com.google.code.findbugs + jsr305 + + + + javax.ws.rs + jsr311-api + + + + com.sun.jersey + jersey-core + + + org.slf4j + slf4j-reload4j + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + ch.qos.logback + logback-classic + + + + + org.apache.hadoop + hadoop-auth + ${hadoop.version} + + + org.slf4j + slf4j-reload4j + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + commons-logging + commons-logging + + + + + + + javax.mail + mail + 1.4.7 + + + org.codehaus.jettison + jettison + 1.5.4 + + + + + + + disable-ranger-tests + + + aarch64 + + + + true + + + + + include-hadoop-aws + + false + + + + org.apache.hadoop + hadoop-aws + ${hadoop.version} + + + + + + include-hadoop-azure + + false + + + + org.apache.hadoop + hadoop-azure + ${hadoop.version} + + + com.google.guava + guava + + + com.fasterxml.jackson.core + jackson-core + + + + + org.apache.hadoop + hadoop-azure-datalake + ${hadoop.version} + + + com.fasterxml.jackson.core + jackson-core + + + + + + + + include-hadoop-cloud-storage + + false + + + + org.apache.hadoop + hadoop-cloud-storage + ${hadoop.version} + + + commons-logging + commons-logging + + + + + + + + include-hadoop-ozone + + false + + + + org.apache.ozone + ozone-client + ${ozone.version} + + + commons-logging + commons-logging + + + org.apache.logging.log4j + log4j-core + + + org.bouncycastle + bcprov-jdk15on + + + org.bouncycastle + bcpkix-jdk15on + + + + + org.bouncycastle + bcprov-jdk18on + + + org.bouncycastle + bcpkix-jdk18on + + + org.apache.ozone + ozone-filesystem + ${ozone.version} + + + + + + include-hadoop-gcp + + false + + + + com.google.cloud.bigdataoss + gcs-connector + hadoop3-${gcs.version} + + + com.google.cloud.bigdataoss + util + ${gcs.version} + + + com.google.cloud.bigdataoss + util-hadoop + hadoop3-${gcs.version} + + + com.google.cloud.bigdataoss + gcsio + ${gcs.version} + + + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/java/org/apache/nifi/registry/ranger/RangerAuthorizer.java b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/java/org/apache/nifi/registry/ranger/RangerAuthorizer.java new file mode 100644 index 000000000000..8c021f1caa5f --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/java/org/apache/nifi/registry/ranger/RangerAuthorizer.java @@ -0,0 +1,452 @@ +/* + * 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.nifi.registry.ranger; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AccessPolicyProvider; +import org.apache.nifi.registry.security.authorization.AccessPolicyProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.AuthorizationAuditor; +import org.apache.nifi.registry.security.authorization.AuthorizationRequest; +import org.apache.nifi.registry.security.authorization.AuthorizationResult; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.AuthorizerInitializationContext; +import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.registry.security.authorization.ManagedAuthorizer; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.UserContextKeys; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; +import org.apache.nifi.registry.security.authorization.annotation.AuthorizerContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.exception.SecurityProviderCreationException; +import org.apache.nifi.registry.util.PropertyValue; +import org.apache.nifi.xml.processing.ProcessingException; +import org.apache.nifi.xml.processing.parsers.DocumentProvider; +import org.apache.nifi.xml.processing.parsers.StandardDocumentProvider; +import org.apache.nifi.xml.processing.transform.StandardTransformProvider; +import org.apache.nifi.xml.processing.transform.TransformProvider; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.authorization.hadoop.config.RangerConfiguration; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.net.MalformedURLException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +/** + * Authorizer implementation that uses Apache Ranger to make authorization decisions. + */ +public class RangerAuthorizer implements ManagedAuthorizer, AuthorizationAuditor { + + private static final Logger logger = LoggerFactory.getLogger(RangerAuthorizer.class); + + private static final String USER_GROUP_PROVIDER_ELEMENT = "userGroupProvider"; + + static final String USER_GROUP_PROVIDER = "User Group Provider"; + + static final String RANGER_AUDIT_PATH_PROP = "Ranger Audit Config Path"; + static final String RANGER_SECURITY_PATH_PROP = "Ranger Security Config Path"; + static final String RANGER_KERBEROS_ENABLED_PROP = "Ranger Kerberos Enabled"; + static final String RANGER_SERVICE_TYPE_PROP = "Ranger Service Type"; + static final String RANGER_APP_ID_PROP = "Ranger Application Id"; + static final String RANGER_ADMIN_IDENTITY_PROP_PREFIX = "Ranger Admin Identity"; + static final Pattern RANGER_ADMIN_IDENTITY_PATTERN = Pattern.compile(RANGER_ADMIN_IDENTITY_PROP_PREFIX + "\\s?\\S*"); + + static final String RANGER_NIFI_REG_RESOURCE_NAME = "nifi-registry-resource"; + private static final String DEFAULT_SERVICE_TYPE = "nifi-registry"; + private static final String DEFAULT_APP_ID = "nifi-registry"; + static final String RESOURCES_RESOURCE = "/policies"; + static final String HADOOP_SECURITY_AUTHENTICATION = "hadoop.security.authentication"; + private static final String KERBEROS_AUTHENTICATION = "kerberos"; + + private final Map resultLookup = new WeakHashMap<>(); + + private volatile RangerBasePluginWithPolicies rangerPlugin = null; + private volatile RangerDefaultAuditHandler defaultAuditHandler = null; + private volatile Set rangerAdminIdentity = null; + private volatile NiFiRegistryProperties registryProperties; + + private UserGroupProviderLookup userGroupProviderLookup; + private UserGroupProvider userGroupProvider; + + @Override + public void initialize(AuthorizerInitializationContext initializationContext) throws SecurityProviderCreationException { + userGroupProviderLookup = initializationContext.getUserGroupProviderLookup(); + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException { + final String userGroupProviderKey = configurationContext.getProperty(USER_GROUP_PROVIDER).getValue(); + if (StringUtils.isEmpty(userGroupProviderKey)) { + throw new SecurityProviderCreationException(USER_GROUP_PROVIDER + " must be specified."); + } + userGroupProvider = userGroupProviderLookup.getUserGroupProvider(userGroupProviderKey); + + // ensure the desired access policy provider has a user group provider + if (userGroupProvider == null) { + throw new SecurityProviderCreationException(String.format("Unable to locate configured User Group Provider: %s", userGroupProviderKey)); + } + + try { + if (rangerPlugin == null) { + logger.info("initializing base plugin"); + + final String serviceType = getConfigValue(configurationContext, RANGER_SERVICE_TYPE_PROP, DEFAULT_SERVICE_TYPE); + final String appId = getConfigValue(configurationContext, RANGER_APP_ID_PROP, DEFAULT_APP_ID); + + rangerPlugin = createRangerBasePlugin(serviceType, appId); + + final RangerPluginConfig pluginConfig = rangerPlugin.getConfig(); + + final PropertyValue securityConfigValue = configurationContext.getProperty(RANGER_SECURITY_PATH_PROP); + addRequiredResource(RANGER_SECURITY_PATH_PROP, securityConfigValue, pluginConfig); + + final PropertyValue auditConfigValue = configurationContext.getProperty(RANGER_AUDIT_PATH_PROP); + addRequiredResource(RANGER_AUDIT_PATH_PROP, auditConfigValue, pluginConfig); + + boolean rangerKerberosEnabled = Boolean.valueOf(getConfigValue(configurationContext, RANGER_KERBEROS_ENABLED_PROP, Boolean.FALSE.toString())); + + if (rangerKerberosEnabled) { + // configure UGI for when RangerAdminRESTClient calls UserGroupInformation.isSecurityEnabled() + final Configuration securityConf = new Configuration(); + securityConf.set(HADOOP_SECURITY_AUTHENTICATION, KERBEROS_AUTHENTICATION); + UserGroupInformation.setConfiguration(securityConf); + + // login with the nifi registry principal and keytab, RangerAdminRESTClient will use Ranger's MiscUtil which + // will grab UserGroupInformation.getLoginUser() and call ugi.checkTGTAndReloginFromKeytab(); + final String registryPrincipal = registryProperties.getKerberosServicePrincipal(); + final String registryKeytab = registryProperties.getKerberosServiceKeytabLocation(); + + if (StringUtils.isBlank(registryPrincipal) || StringUtils.isBlank(registryKeytab)) { + throw new SecurityProviderCreationException("Principal and Keytab must be provided when Kerberos is enabled"); + } + + UserGroupInformation.loginUserFromKeytab(registryPrincipal.trim(), registryKeytab.trim()); + } + + rangerPlugin.init(); + + defaultAuditHandler = new RangerDefaultAuditHandler(); + rangerAdminIdentity = getConfigValues(configurationContext, RANGER_ADMIN_IDENTITY_PATTERN, null); + + } else { + logger.info("base plugin already initialized"); + } + } catch (Throwable t) { + throw new SecurityProviderCreationException("Error creating RangerBasePlugin", t); + } + } + + protected RangerBasePluginWithPolicies createRangerBasePlugin(final String serviceType, final String appId) { + return new RangerBasePluginWithPolicies(serviceType, appId, userGroupProvider); + } + + @Override + public AuthorizationResult authorize(final AuthorizationRequest request) throws SecurityProviderCreationException { + final String identity = request.getIdentity(); + final Set userGroups = request.getGroups(); + final String resourceIdentifier = request.getResource().getIdentifier(); + + // if a ranger admin identity was provided, and it contains the identity making the request, + // and the request is to retrieve the resources, then allow it through + if (rangerAdminIdentity != null && rangerAdminIdentity.contains(identity) + && resourceIdentifier.equals(RESOURCES_RESOURCE)) { + return AuthorizationResult.approved(); + } + + final String clientIp; + if (request.getUserContext() != null) { + clientIp = request.getUserContext().get(UserContextKeys.CLIENT_ADDRESS.name()); + } else { + clientIp = null; + } + + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RANGER_NIFI_REG_RESOURCE_NAME, resourceIdentifier); + + final RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl(); + rangerRequest.setResource(resource); + rangerRequest.setAction(request.getAction().name()); + rangerRequest.setAccessType(request.getAction().name()); + rangerRequest.setUser(identity); + rangerRequest.setUserGroups(userGroups); + rangerRequest.setAccessTime(new Date()); + + if (!StringUtils.isBlank(clientIp)) { + rangerRequest.setClientIPAddress(clientIp); + } + + final RangerAccessResult result = rangerPlugin.isAccessAllowed(rangerRequest); + + // store the result for auditing purposes later if appropriate + if (request.isAccessAttempt()) { + synchronized (resultLookup) { + resultLookup.put(request, result); + } + } + + if (result != null && result.getIsAllowed()) { + // return approved + return AuthorizationResult.approved(); + } else { + // if result.getIsAllowed() is false, then we need to determine if it was because no policy exists for the + // given resource, or if it was because a policy exists but not for the given user or action + final boolean doesPolicyExist = rangerPlugin.doesPolicyExist(request.getResource().getIdentifier(), request.getAction()); + + if (doesPolicyExist) { + final String reason = result == null ? null : result.getReason(); + if (reason != null) { + logger.debug(String.format("Unable to authorize %s due to %s", identity, reason)); + } + + // a policy does exist for the resource so we were really denied access here + return AuthorizationResult.denied(request.getExplanationSupplier().get()); + } else { + // a policy doesn't exist so return resource not found so NiFi Registry can work back up the resource hierarchy + return AuthorizationResult.resourceNotFound(); + } + } + } + + @Override + public void auditAccessAttempt(final AuthorizationRequest request, final AuthorizationResult result) { + final RangerAccessResult rangerResult; + synchronized (resultLookup) { + rangerResult = resultLookup.remove(request); + } + + if (rangerResult != null && rangerResult.getIsAudited()) { + AuthzAuditEvent event = defaultAuditHandler.getAuthzEvents(rangerResult); + + // update the event with the originally requested resource + event.setResourceType(RANGER_NIFI_REG_RESOURCE_NAME); + event.setResourcePath(request.getRequestedResource().getIdentifier()); + + defaultAuditHandler.logAuthzAudit(event); + } + } + + @Override + public void preDestruction() throws SecurityProviderCreationException { + if (rangerPlugin != null) { + try { + rangerPlugin.cleanup(); + rangerPlugin = null; + } catch (Throwable t) { + throw new SecurityProviderCreationException("Error cleaning up RangerBasePlugin", t); + } + } + } + + @AuthorizerContext + public void setRegistryProperties(final NiFiRegistryProperties properties) { + this.registryProperties = properties; + } + + /** + * Adds a resource to the RangerConfiguration singleton so it is already there by the time RangerBasePlugin.init() + * is called. + * + * @param name the name of the given PropertyValue from the AuthorizationConfigurationContext + * @param resourceValue the value for the given name, should be a full path to a file + * @param configuration the RangerConfiguration to add the resource to + */ + private void addRequiredResource(final String name, final PropertyValue resourceValue, final RangerConfiguration configuration) { + if (resourceValue == null || StringUtils.isBlank(resourceValue.getValue())) { + throw new SecurityProviderCreationException(name + " must be specified."); + } + + final File resourceFile = new File(resourceValue.getValue()); + if (!resourceFile.exists() || !resourceFile.canRead()) { + throw new SecurityProviderCreationException(resourceValue + " does not exist, or can not be read"); + } + + try { + configuration.addResource(resourceFile.toURI().toURL()); + } catch (MalformedURLException e) { + throw new SecurityProviderCreationException("Error creating URI for " + resourceValue, e); + } + } + + private String getConfigValue(final AuthorizerConfigurationContext context, final String name, final String defaultValue) { + final PropertyValue configValue = context.getProperty(name); + + String retValue = defaultValue; + if (configValue != null && !StringUtils.isBlank(configValue.getValue())) { + retValue = configValue.getValue(); + } + + return retValue; + } + + private Set getConfigValues(final AuthorizerConfigurationContext context, final Pattern namePattern, final String defaultValue) { + final Set configValues = new HashSet<>(); + + for (Map.Entry entry : context.getProperties().entrySet()) { + Matcher matcher = namePattern.matcher(entry.getKey()); + if (matcher.matches() && !StringUtils.isBlank(entry.getValue())) { + configValues.add(entry.getValue()); + } + } + + if (configValues.isEmpty() && (defaultValue != null)) { + configValues.add(defaultValue); + } + + return configValues; + } + + @Override + public String getFingerprint() throws AuthorizationAccessException { + final StringWriter out = new StringWriter(); + try { + // create the document + final DocumentProvider documentProvider = new StandardDocumentProvider(); + final Document document = documentProvider.newDocument(); + + // create the root element + final Element managedRangerAuthorizationsElement = document.createElement("managedRangerAuthorizations"); + document.appendChild(managedRangerAuthorizationsElement); + + // create the user group provider element + final Element userGroupProviderElement = document.createElement(USER_GROUP_PROVIDER_ELEMENT); + managedRangerAuthorizationsElement.appendChild(userGroupProviderElement); + + // append fingerprint if the provider is configurable + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + userGroupProviderElement.appendChild(document.createTextNode(((ConfigurableUserGroupProvider) userGroupProvider).getFingerprint())); + } + + final TransformProvider transformProvider = new StandardTransformProvider(); + transformProvider.transform(new DOMSource(document), new StreamResult(out)); + } catch (final ProcessingException e) { + throw new AuthorizationAccessException("Unable to generate fingerprint", e); + } + + return out.toString(); + } + + private String parseFingerprint(final String fingerprint) throws AuthorizationAccessException { + final byte[] fingerprintBytes = fingerprint.getBytes(StandardCharsets.UTF_8); + + try (final ByteArrayInputStream in = new ByteArrayInputStream(fingerprintBytes)) { + final DocumentProvider documentProvider = new StandardDocumentProvider(); + final Document document = documentProvider.parse(in); + final Element rootElement = document.getDocumentElement(); + + final NodeList userGroupProviderList = rootElement.getElementsByTagName(USER_GROUP_PROVIDER_ELEMENT); + if (userGroupProviderList.getLength() != 1) { + throw new AuthorizationAccessException(String.format("Only one %s element is allowed: %s", USER_GROUP_PROVIDER_ELEMENT, fingerprint)); + } + + final Node userGroupProvider = userGroupProviderList.item(0); + return userGroupProvider.getTextContent(); + } catch (final ProcessingException | IOException e) { + throw new AuthorizationAccessException("Unable to parse fingerprint", e); + } + } + + @Override + public void inheritFingerprint(String fingerprint) throws AuthorizationAccessException { + if (StringUtils.isBlank(fingerprint)) { + return; + } + + final String userGroupFingerprint = parseFingerprint(fingerprint); + + if (StringUtils.isNotBlank(userGroupFingerprint) && userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).inheritFingerprint(userGroupFingerprint); + } + } + + @Override + public void checkInheritability(String proposedFingerprint) throws AuthorizationAccessException, UninheritableAuthorizationsException { + final String userGroupFingerprint = parseFingerprint(proposedFingerprint); + + if (StringUtils.isNotBlank(userGroupFingerprint)) { + if (userGroupProvider instanceof ConfigurableUserGroupProvider) { + ((ConfigurableUserGroupProvider) userGroupProvider).checkInheritability(userGroupFingerprint); + } else { + throw new UninheritableAuthorizationsException("User/Group fingerprint is not blank and the configured UserGroupProvider does not support fingerprinting."); + } + } + } + + @Override + public AccessPolicyProvider getAccessPolicyProvider() { + return new AccessPolicyProvider() { + @Override + public Set getAccessPolicies() throws AuthorizationAccessException { + return rangerPlugin.getAccessPolicies(); + } + + @Override + public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + return rangerPlugin.getAccessPolicy(identifier); + } + + @Override + public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + return rangerPlugin.getAccessPolicy(resourceIdentifier, action); + } + + @Override + public UserGroupProvider getUserGroupProvider() { + return userGroupProvider; + } + + @Override + public void initialize(AccessPolicyProviderInitializationContext initializationContext) throws SecurityProviderCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException { + } + + @Override + public void preDestruction() throws SecurityProviderCreationException { + } + }; + } +} diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/java/org/apache/nifi/registry/ranger/RangerBasePluginWithPolicies.java b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/java/org/apache/nifi/registry/ranger/RangerBasePluginWithPolicies.java new file mode 100644 index 000000000000..0d1c79e2d5f1 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/java/org/apache/nifi/registry/ranger/RangerBasePluginWithPolicies.java @@ -0,0 +1,291 @@ +/* + * 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.nifi.registry.ranger; + +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Extends the base plugin to convert service policies into NiFi Registry policy domain model. + */ +public class RangerBasePluginWithPolicies extends RangerBasePlugin { + + private static final Logger logger = LoggerFactory.getLogger(RangerBasePluginWithPolicies.class); + + private static final String WILDCARD_ASTERISK = "*"; + + private UserGroupProvider userGroupProvider; + private AtomicReference policies = new AtomicReference<>(new PolicyLookup()); + + public RangerBasePluginWithPolicies(final String serviceType, final String appId) { + this(serviceType, appId, null); + } + + public RangerBasePluginWithPolicies(final String serviceType, final String appId, final UserGroupProvider userGroupProvider) { + super(serviceType, appId); + this.userGroupProvider = userGroupProvider; // will be null if used outside of the managed RangerAuthorizer + } + + @Override + public void setPolicies(final ServicePolicies policies) { + super.setPolicies(policies); + + if (policies == null || policies.getPolicies() == null) { + this.policies.set(new PolicyLookup()); + } else { + this.policies.set(createPolicyLookup(policies)); + } + } + + /** + * Determines if a policy exists for the given resource. + * + * @param resourceIdentifier the id of the resource + * + * @return true if a policy exists for the given resource, false otherwise + */ + public boolean doesPolicyExist(final String resourceIdentifier, final RequestAction requestAction) { + if (resourceIdentifier == null) { + return false; + } + + final PolicyLookup policyLookup = policies.get(); + return policyLookup.getAccessPolicy(resourceIdentifier, requestAction) != null; + } + + public Set getAccessPolicies() throws AuthorizationAccessException { + return policies.get().getAccessPolicies(); + } + + public AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + return policies.get().getAccessPolicy(identifier); + } + + public AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + return policies.get().getAccessPolicy(resourceIdentifier, action); + } + + private PolicyLookup createPolicyLookup(final ServicePolicies servicePolicies) { + final Map policiesByIdentifier = new HashMap<>(); + final Map> policiesByResource = new HashMap<>(); + + logger.debug("Converting Ranger ServicePolicies model into NiFi Registry policy model for viewing purposes in NiFi Registry UI."); + + servicePolicies.getPolicies().stream().forEach(policy -> { + // only consider policies that are enabled + if (Boolean.TRUE.equals(policy.getIsEnabled())) { + // get all the resources for this policy - excludes/recursive support disabled + final Set resources = policy.getResources().values().stream() + .filter(resource -> { + final boolean isMissingResource; + final boolean isWildcard; + if (resource.getValues() == null) { + isMissingResource = true; + isWildcard = false; + } else { + isMissingResource = false; + isWildcard = resource.getValues().stream().anyMatch(value -> value.contains(WILDCARD_ASTERISK)); + } + + final boolean isExclude = Boolean.TRUE.equals(resource.getIsExcludes()); + final boolean isRecursive = Boolean.TRUE.equals(resource.getIsRecursive()); + + if (isMissingResource) { + logger.warn("Encountered resources missing values. Skipping policy for viewing purposes. Will still be used for access decisions."); + } + if (isWildcard) { + logger.warn(String.format("Resources [%s] include a wildcard value. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + if (isExclude) { + logger.warn(String.format("Resources [%s] marked as an exclude policy. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + if (isRecursive) { + logger.warn(String.format("Resources [%s] marked as a recursive policy. Skipping policy for viewing purposes. " + + "Will still be used for access decisions.", StringUtils.join(resource.getValues(), ", "))); + } + + return !isMissingResource && !isWildcard && !isExclude && !isRecursive; + }) + .flatMap(resource -> resource.getValues().stream()) + .collect(Collectors.toSet()); + + policy.getPolicyItems().forEach(policyItem -> { + // get all the users for this policy item, excluding unknown users + final Set userIds = policyItem.getUsers().stream() + .map(userIdentity -> getUser(userIdentity)) + .filter(Objects::nonNull) + .map(user -> user.getIdentifier()) + .collect(Collectors.toSet()); + + // get all groups for this policy item, excluding unknown groups + final Set groupIds = policyItem.getGroups().stream() + .map(groupName -> getGroup(groupName)) + .filter(Objects::nonNull) + .map(group -> group.getIdentifier()) + .collect(Collectors.toSet()); + + // check if this policy item is a delegate admin + final boolean isDelegateAdmin = Boolean.TRUE.equals(policyItem.getDelegateAdmin()); + + policyItem.getAccesses().forEach(access -> { + try { + // interpret the request action + final RequestAction action = RequestAction.valueOf(access.getType()); + + // function for creating an access policy + final Function createPolicy = resource -> new AccessPolicy.Builder() + .identifierGenerateFromSeed(resource + access.getType()) + .resource(resource) + .action(action) + .addUsers(userIds) + .addGroups(groupIds) + .build(); + + resources.forEach(resource -> { + // create the access policy for the specified resource + final AccessPolicy accessPolicy = createPolicy.apply(resource); + policiesByIdentifier.put(accessPolicy.getIdentifier(), accessPolicy); + policiesByResource.computeIfAbsent(resource, r -> new HashMap<>()).put(action, accessPolicy); + + // if this is a delegate admin, also create the admin policy for the specified resource + if (isDelegateAdmin) { + // build the admin resource identifier + final String adminResource; + if (resource.startsWith("/")) { + adminResource = "/policies" + resource; + } else { + adminResource = "/policies/" + resource; + } + + final AccessPolicy adminAccessPolicy = createPolicy.apply(adminResource); + policiesByIdentifier.put(adminAccessPolicy.getIdentifier(), adminAccessPolicy); + policiesByResource.computeIfAbsent(adminResource, ar -> new HashMap<>()).put(action, adminAccessPolicy); + } + }); + } catch (final IllegalArgumentException e) { + logger.warn(String.format("Unrecognized request action '%s'. Skipping policy for viewing purposes. Will still be used for access decisions.", access.getType())); + } + }); + }); + } + }); + + return new PolicyLookup(policiesByIdentifier, policiesByResource); + } + + private User getUser(final String identity) { + if (userGroupProvider == null) { + // generate the user deterministically when running outside of the ManagedRangerAuthorizer + return new User.Builder().identifierGenerateFromSeed(identity).identity(identity).build(); + } else { + // find the user in question + final User user = userGroupProvider.getUserByIdentity(identity); + + if (user == null) { + logger.warn(String.format("Cannot find user '%s' in the configured User Group Provider. Skipping user for viewing purposes. Will still be used for access decisions.", identity)); + } + + return user; + } + } + + private Group getGroup(final String name) { + if (userGroupProvider == null) { + // generate the group deterministically when running outside of the ManagedRangerAuthorizer + return new Group.Builder().identifierGenerateFromSeed(name).name(name).build(); + } else { + // find the group in question + final Group group = userGroupProvider.getGroups().stream().filter(g -> g.getName().equals(name)).findFirst().orElse(null); + + if (group == null) { + logger.warn(String.format("Cannot find group '%s' in the configured User Group Provider. Skipping group for viewing purposes. Will still be used for access decisions.", name)); + } + + return group; + } + } + + private static class PolicyLookup { + + private final Map policiesByIdentifier; + private final Map> policiesByResource; + private final Set allPolicies; + + private PolicyLookup() { + this(null, null); + } + + private PolicyLookup(final Map policiesByIdentifier, final Map> policiesByResource) { + if (policiesByIdentifier == null) { + allPolicies = Collections.EMPTY_SET; + } else { + allPolicies = Collections.unmodifiableSet(new HashSet<>(policiesByIdentifier.values())); + } + + this.policiesByIdentifier = policiesByIdentifier; + this.policiesByResource = policiesByResource; + } + + private Set getAccessPolicies() throws AuthorizationAccessException { + return allPolicies; + } + + private AccessPolicy getAccessPolicy(String identifier) throws AuthorizationAccessException { + if (policiesByIdentifier == null) { + return null; + } + + return policiesByIdentifier.get(identifier); + } + + private AccessPolicy getAccessPolicy(String resourceIdentifier, RequestAction action) throws AuthorizationAccessException { + if (policiesByResource == null) { + return null; + } + + final Map policiesForResource = policiesByResource.get(resourceIdentifier); + + if (policiesForResource != null) { + return policiesForResource.get(action); + } + + return null; + } + } + +} diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.Authorizer b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.Authorizer new file mode 100644 index 000000000000..f8c1bc3bf0fc --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/main/resources/META-INF/services/org.apache.nifi.registry.security.authorization.Authorizer @@ -0,0 +1,15 @@ +# 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. +org.apache.nifi.registry.ranger.RangerAuthorizer \ No newline at end of file diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/java/org/apache/nifi/registry/ranger/TestRangerAuthorizer.java b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/java/org/apache/nifi/registry/ranger/TestRangerAuthorizer.java new file mode 100644 index 000000000000..d03309f5c263 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/java/org/apache/nifi/registry/ranger/TestRangerAuthorizer.java @@ -0,0 +1,669 @@ +/* + * 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.nifi.registry.ranger; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.registry.security.authorization.AuthorizationRequest; +import org.apache.nifi.registry.security.authorization.AuthorizationResult; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.AuthorizerInitializationContext; +import org.apache.nifi.registry.security.authorization.ConfigurableUserGroupProvider; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.Resource; +import org.apache.nifi.registry.security.authorization.UserContextKeys; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderLookup; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.authorization.exception.UninheritableAuthorizationsException; +import org.apache.nifi.registry.security.exception.SecurityProviderCreationException; +import org.apache.nifi.registry.util.StandardPropertyValue; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.apache.ranger.plugin.policyengine.RangerAccessResultProcessor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class TestRangerAuthorizer { + + private static final String TENANT_FINGERPRINT = + "" + + "" + + "" + + "" + + "" + + ""; + + private static final String EMPTY_FINGERPRINT = "" + + "" + + "" + + ""; + + private static final String NON_EMPTY_FINGERPRINT = "" + + "" + + "" + + "<tenants>" + + "<user identifier=\"user-id-1\" identity=\"user-1\"></user>" + + "<group identifier=\"group-id-1\" name=\"group-1\">" + + "<groupUser identifier=\"user-id-1\"></groupUser>" + + "</group>" + + "</tenants>" + + "" + + ""; + + private MockRangerAuthorizer authorizer; + private RangerBasePluginWithPolicies rangerBasePlugin; + + private final String serviceType = "nifiRegistryService"; + private final String appId = "nifiRegistryAppId"; + + private RangerAccessResult allowedResult; + private RangerAccessResult notAllowedResult; + private Map authorizersXmlContent = null; + + @BeforeEach + public void initialization() { + authorizersXmlContent = new HashMap<>(); + authorizersXmlContent.put(RangerAuthorizer.USER_GROUP_PROVIDER, "user-group-provider"); + authorizersXmlContent.put(RangerAuthorizer.RANGER_SECURITY_PATH_PROP, "src/test/resources/ranger/ranger-nifi-registry-security.xml"); + authorizersXmlContent.put(RangerAuthorizer.RANGER_AUDIT_PATH_PROP, "src/test/resources/ranger/ranger-nifi-registry-audit.xml"); + authorizersXmlContent.put(RangerAuthorizer.RANGER_APP_ID_PROP, appId); + authorizersXmlContent.put(RangerAuthorizer.RANGER_SERVICE_TYPE_PROP, serviceType); + } + + private void setup(final NiFiRegistryProperties registryProperties, + final UserGroupProvider userGroupProvider, + final AuthorizerConfigurationContext configurationContext) { + // have to initialize this system property before anything else + File krb5conf = new File("src/test/resources/krb5.conf"); + assertTrue(krb5conf.exists()); + System.setProperty("java.security.krb5.conf", krb5conf.getAbsolutePath()); + + // rest the authentication to simple in case any tests set it to kerberos + final Configuration securityConf = new Configuration(); + securityConf.set(RangerAuthorizer.HADOOP_SECURITY_AUTHENTICATION, "simple"); + UserGroupInformation.setConfiguration(securityConf); + + rangerBasePlugin = mock(RangerBasePluginWithPolicies.class); + + final RangerPluginConfig pluginConfig = new RangerPluginConfig(serviceType, null, appId, null, null, null); + when(rangerBasePlugin.getConfig()).thenReturn(pluginConfig); + + authorizer = new MockRangerAuthorizer(rangerBasePlugin); + + final UserGroupProviderLookup userGroupProviderLookup = mock(UserGroupProviderLookup.class); + when(userGroupProviderLookup.getUserGroupProvider(eq("user-group-provider"))).thenReturn(userGroupProvider); + + final AuthorizerInitializationContext initializationContext = mock(AuthorizerInitializationContext.class); + when(initializationContext.getUserGroupProviderLookup()).thenReturn(userGroupProviderLookup); + + authorizer.setRegistryProperties(registryProperties); + authorizer.initialize(initializationContext); + authorizer.onConfigured(configurationContext); + + assertFalse(UserGroupInformation.isSecurityEnabled()); + + allowedResult = mock(RangerAccessResult.class); + when(allowedResult.getIsAllowed()).thenReturn(true); + + notAllowedResult = mock(RangerAccessResult.class); + when(notAllowedResult.getIsAllowed()).thenReturn(false); + } + + private AuthorizerConfigurationContext createMockConfigContext() { + AuthorizerConfigurationContext configurationContext = mock(AuthorizerConfigurationContext.class); + + for (Map.Entry entry : authorizersXmlContent.entrySet()) { + when(configurationContext.getProperty(eq(entry.getKey()))) + .thenReturn(new StandardPropertyValue(entry.getValue())); + } + + when(configurationContext.getProperties()).thenReturn(authorizersXmlContent); + + return configurationContext; + } + + @Test + public void testOnConfigured() { + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), createMockConfigContext()); + + verify(rangerBasePlugin, times(1)).init(); + + assertEquals(appId, authorizer.mockRangerBasePlugin.getAppId()); + assertEquals(serviceType, authorizer.mockRangerBasePlugin.getServiceType()); + } + + @Test + public void testKerberosEnabledWithoutKeytab() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + + when(configurationContext.getProperty(eq(RangerAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new StandardPropertyValue("true")); + + NiFiRegistryProperties registryProperties = mock(NiFiRegistryProperties.class); + when(registryProperties.getKerberosServicePrincipal()).thenReturn(""); + + assertThrows(SecurityProviderCreationException.class, () -> setup(registryProperties, mock(UserGroupProvider.class), configurationContext)); + } + + @Test + public void testKerberosEnabledWithoutPrincipal() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + + when(configurationContext.getProperty(eq(RangerAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new StandardPropertyValue("true")); + + NiFiRegistryProperties registryProperties = mock(NiFiRegistryProperties.class); + when(registryProperties.getKerberosServiceKeytabLocation()).thenReturn(""); + + assertThrows(SecurityProviderCreationException.class, () -> setup(registryProperties, mock(UserGroupProvider.class), configurationContext)); + } + + @Test + public void testKerberosEnabledWithoutKeytabOrPrincipal() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + + when(configurationContext.getProperty(eq(RangerAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new StandardPropertyValue("true")); + + NiFiRegistryProperties registryProperties = mock(NiFiRegistryProperties.class); + when(registryProperties.getKerberosServiceKeytabLocation()).thenReturn(""); + when(registryProperties.getKerberosServicePrincipal()).thenReturn(""); + + assertThrows(SecurityProviderCreationException.class, () -> setup(registryProperties, mock(UserGroupProvider.class), configurationContext)); + } + + @Test + public void testKerberosEnabled() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + + when(configurationContext.getProperty(eq(RangerAuthorizer.RANGER_KERBEROS_ENABLED_PROP))) + .thenReturn(new StandardPropertyValue("true")); + + NiFiRegistryProperties registryProperties = mock(NiFiRegistryProperties.class); + when(registryProperties.getKerberosServiceKeytabLocation()).thenReturn("test"); + when(registryProperties.getKerberosServicePrincipal()).thenReturn("test"); + + assertThrows(SecurityProviderCreationException.class, () -> setup(registryProperties, mock(UserGroupProvider.class), configurationContext)); + } + + @Test + public void testApprovedWithDirectAccess() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), configurationContext); + + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + final String clientIp = "192.168.1.1"; + + final Map userContext = new HashMap<>(); + userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), clientIp); + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .userContext(userContext) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerAuthorizer.RANGER_NIFI_REG_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + expectedRangerRequest.setClientIPAddress(clientIp); + + // a non-null result processor should be used for direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(allowedResult); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.approved().getResult(), result.getResult()); + } + + @Test + public void testApprovedWithNonDirectAccess() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), configurationContext); + + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .accessAttempt(false) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerAuthorizer.RANGER_NIFI_REG_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // no result processor should be provided used non-direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(allowedResult); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.approved().getResult(), result.getResult()); + } + + @Test + public void testResourceNotFound() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), configurationContext); + + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerAuthorizer.RANGER_NIFI_REG_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // no result processor should be provided used non-direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest)), + any(RangerAccessResultProcessor.class)) + ).thenReturn(notAllowedResult); + + // return false when checking if a policy exists for the resource + when(rangerBasePlugin.doesPolicyExist(systemResource, action)).thenReturn(false); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.resourceNotFound().getResult(), result.getResult()); + } + + @Test + public void testDenied() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), configurationContext); + + final String systemResource = "/system"; + final RequestAction action = RequestAction.WRITE; + final String user = "admin"; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(systemResource, systemResource)) + .action(action) + .identity(user) + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerAuthorizer.RANGER_NIFI_REG_RESOURCE_NAME, systemResource); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // no result processor should be provided used non-direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(notAllowedResult); + + // return true when checking if a policy exists for the resource + when(rangerBasePlugin.doesPolicyExist(systemResource, action)).thenReturn(true); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(AuthorizationResult.denied().getResult(), result.getResult()); + } + + @Test + public void testRangerAdminApproved() { + final String acceptableIdentity = "ranger-admin"; + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity); + + final String requestIdentity = "ranger-admin"; + runRangerAdminTest(RangerAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.approved().getResult()); + } + + @Test + public void testRangerAdminApprovedMultipleAcceptableIdentities() { + final String acceptableIdentity1 = "ranger-admin1"; + final String acceptableIdentity2 = "ranger-admin2"; + final String acceptableIdentity3 = "ranger-admin3"; + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity1); + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 2", acceptableIdentity2); + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 3", acceptableIdentity3); + + final String requestIdentity = "ranger-admin2"; + runRangerAdminTest(RangerAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.approved().getResult()); + } + + @Test + public void testRangerAdminApprovedMultipleAcceptableIdentities2() { + final String acceptableIdentity1 = "ranger-admin1"; + final String acceptableIdentity2 = "ranger-admin2"; + final String acceptableIdentity3 = "ranger-admin3"; + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity1); + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 2", acceptableIdentity2); + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 3", acceptableIdentity3); + + final String requestIdentity = "ranger-admin3"; + runRangerAdminTest(RangerAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.approved().getResult()); + } + + @Test + public void testRangerAdminDenied() { + final String acceptableIdentity = "ranger-admin"; + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity); + + final String requestIdentity = "ranger-admin"; + runRangerAdminTest("/flow", requestIdentity, AuthorizationResult.denied().getResult()); + } + + @Test + public void testRangerAdminDeniedMultipleAcceptableIdentities() { + final String acceptableIdentity1 = "ranger-admin1"; + final String acceptableIdentity2 = "ranger-admin2"; + final String acceptableIdentity3 = "ranger-admin3"; + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX, acceptableIdentity1); + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 2", acceptableIdentity2); + authorizersXmlContent.put(RangerAuthorizer.RANGER_ADMIN_IDENTITY_PROP_PREFIX + " 3", acceptableIdentity3); + + final String requestIdentity = "ranger-admin4"; + runRangerAdminTest(RangerAuthorizer.RESOURCES_RESOURCE, requestIdentity, AuthorizationResult.denied().getResult()); + } + + private void runRangerAdminTest(final String resourceIdentifier, final String requestIdentity, final AuthorizationResult.Result expectedResult) { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), configurationContext); + + final RequestAction action = RequestAction.WRITE; + + // the incoming NiFi request to test + final AuthorizationRequest request = new AuthorizationRequest.Builder() + .resource(new MockResource(resourceIdentifier, resourceIdentifier)) + .action(action) + .identity(requestIdentity) + .resourceContext(new HashMap<>()) + .accessAttempt(true) + .anonymous(false) + .build(); + + // the expected Ranger resource and request that are created + final RangerAccessResourceImpl resource = new RangerAccessResourceImpl(); + resource.setValue(RangerAuthorizer.RANGER_NIFI_REG_RESOURCE_NAME, resourceIdentifier); + + final RangerAccessRequestImpl expectedRangerRequest = new RangerAccessRequestImpl(); + expectedRangerRequest.setResource(resource); + expectedRangerRequest.setAction(request.getAction().name()); + expectedRangerRequest.setAccessType(request.getAction().name()); + expectedRangerRequest.setUser(request.getIdentity()); + + // return true when checking if a policy exists for the resource + when(rangerBasePlugin.doesPolicyExist(resourceIdentifier, action)).thenReturn(true); + + // a non-null result processor should be used for direct access + when(rangerBasePlugin.isAccessAllowed( + argThat(new RangerAccessRequestMatcher(expectedRangerRequest))) + ).thenReturn(notAllowedResult); + + final AuthorizationResult result = authorizer.authorize(request); + assertEquals(expectedResult, result.getResult()); + } + + /** + * Extend RangerAuthorizer to inject a mock base plugin for testing. + */ + private static class MockRangerAuthorizer extends RangerAuthorizer { + + RangerBasePluginWithPolicies mockRangerBasePlugin; + + MockRangerAuthorizer(RangerBasePluginWithPolicies mockRangerBasePlugin) { + this.mockRangerBasePlugin = mockRangerBasePlugin; + } + + @Override + protected RangerBasePluginWithPolicies createRangerBasePlugin(String serviceType, String appId) { + when(mockRangerBasePlugin.getAppId()).thenReturn(appId); + when(mockRangerBasePlugin.getServiceType()).thenReturn(serviceType); + return mockRangerBasePlugin; + } + } + + /** + * Resource implementation for testing. + */ + private static class MockResource implements Resource { + + private final String identifier; + private final String name; + + MockResource(String identifier, String name) { + this.identifier = identifier; + this.name = name; + } + + @Override + public String getIdentifier() { + return identifier; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getSafeDescription() { + return name; + } + } + + /** + * Custom Mockito matcher for RangerAccessRequest objects. + */ + private static class RangerAccessRequestMatcher implements ArgumentMatcher { + + private final RangerAccessRequest request; + + RangerAccessRequestMatcher(RangerAccessRequest request) { + this.request = request; + } + + @Override + public boolean matches(RangerAccessRequest other) { + final boolean clientIpsMatch = (other.getClientIPAddress() == null && request.getClientIPAddress() == null) + || (other.getClientIPAddress() != null && request.getClientIPAddress() != null && other.getClientIPAddress().equals(request.getClientIPAddress())); + + return other.getResource().equals(request.getResource()) + && other.getAccessType().equals(request.getAccessType()) + && other.getAction().equals(request.getAction()) + && other.getUser().equals(request.getUser()) + && clientIpsMatch; + } + } + + @Test + public void testNonConfigurableFingerPrint() { + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), mock(UserGroupProvider.class), configurationContext); + + assertEquals(EMPTY_FINGERPRINT, authorizer.getFingerprint()); + } + + @Test + public void testConfigurableEmptyFingerPrint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + when(userGroupProvider.getFingerprint()).thenReturn(""); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + assertEquals(EMPTY_FINGERPRINT, authorizer.getFingerprint()); + } + + @Test + public void testConfigurableFingerPrint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + when(userGroupProvider.getFingerprint()).thenReturn(TENANT_FINGERPRINT); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + assertEquals(NON_EMPTY_FINGERPRINT, authorizer.getFingerprint()); + } + + @Test + public void testInheritEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + authorizer.inheritFingerprint(EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(0)).inheritFingerprint(anyString()); + } + + @Test + public void testInheritInvalidFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + assertThrows(AuthorizationAccessException.class, () -> authorizer.inheritFingerprint("not a valid fingerprint")); + } + + @Test + public void testInheritNonEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + authorizer.inheritFingerprint(NON_EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(1)).inheritFingerprint(TENANT_FINGERPRINT); + } + + @Test + public void testCheckInheritEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + authorizer.checkInheritability(EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(0)).inheritFingerprint(anyString()); + } + + @Test + public void testCheckInheritInvalidFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + assertThrows(AuthorizationAccessException.class, () -> authorizer.checkInheritability("not a valid fingerprint")); + } + + @Test + public void testCheckInheritNonEmptyFingerprint() { + final ConfigurableUserGroupProvider userGroupProvider = mock(ConfigurableUserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + authorizer.checkInheritability(NON_EMPTY_FINGERPRINT); + + verify(userGroupProvider, times(1)).checkInheritability(TENANT_FINGERPRINT); + } + + @Test + public void testCheckInheritNonConfigurableUserGroupProvider() { + final UserGroupProvider userGroupProvider = mock(UserGroupProvider.class); + + final AuthorizerConfigurationContext configurationContext = createMockConfigContext(); + setup(mock(NiFiRegistryProperties.class), userGroupProvider, configurationContext); + + assertThrows(UninheritableAuthorizationsException.class, () -> authorizer.checkInheritability(NON_EMPTY_FINGERPRINT)); + } + +} diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/java/org/apache/nifi/registry/ranger/TestRangerBasePluginWithPolicies.java b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/java/org/apache/nifi/registry/ranger/TestRangerBasePluginWithPolicies.java new file mode 100644 index 000000000000..a58e3da2c5a5 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/java/org/apache/nifi/registry/ranger/TestRangerBasePluginWithPolicies.java @@ -0,0 +1,544 @@ +/* + * 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.nifi.registry.ranger; + +import org.apache.nifi.registry.security.authorization.AccessPolicy; +import org.apache.nifi.registry.security.authorization.AuthorizerConfigurationContext; +import org.apache.nifi.registry.security.authorization.Group; +import org.apache.nifi.registry.security.authorization.RequestAction; +import org.apache.nifi.registry.security.authorization.User; +import org.apache.nifi.registry.security.authorization.UserAndGroups; +import org.apache.nifi.registry.security.authorization.UserGroupProvider; +import org.apache.nifi.registry.security.authorization.UserGroupProviderInitializationContext; +import org.apache.nifi.registry.security.authorization.exception.AuthorizationAccessException; +import org.apache.nifi.registry.security.exception.SecurityProviderCreationException; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class TestRangerBasePluginWithPolicies { + + @Test + public void testPoliciesWithoutUserGroupProvider() { + final String user1 = "user-1"; + final String group1 = "group-1"; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final String resourceIdentifier2 = "/resource-2"; + RangerPolicy.RangerPolicyResource resource2 = new RangerPolicy.RangerPolicyResource(resourceIdentifier2); + + final Map policy2Resources = new HashMap<>(); + policy2Resources.put(resourceIdentifier2, resource2); + + final RangerPolicy.RangerPolicyItem policy2Item = new RangerPolicy.RangerPolicyItem(); + policy2Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("READ"), new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy2Item.setGroups(Stream.of(group1).collect(Collectors.toList())); + + final RangerPolicy policy2 = new RangerPolicy(); + policy2.setResources(policy2Resources); + policy2.setPolicyItems(Stream.of(policy2Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + policies.add(policy2); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the two ranger policies converted into 3 nifi-registry access policies + final Set accessPolicies = pluginWithPolicies.getAccessPolicies(); + assertEquals(3, accessPolicies.size()); + + // resource 1 -> read but no write + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + + // read + final AccessPolicy readResource1 = pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ); + assertNotNull(readResource1); + assertTrue(accessPolicies.contains(readResource1)); + assertEquals(readResource1, pluginWithPolicies.getAccessPolicy(readResource1.getIdentifier())); + assertEquals(1, readResource1.getUsers().size()); + assertTrue(readResource1.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user1).identity(user1).build().getIdentifier())); + assertTrue(readResource1.getGroups().isEmpty()); + + // but no write + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + + // resource 2 -> read and write + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.READ)); + + // read + final AccessPolicy readResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(readResource2); + assertTrue(accessPolicies.contains(readResource2)); + assertEquals(readResource2, pluginWithPolicies.getAccessPolicy(readResource2.getIdentifier())); + assertTrue(readResource2.getUsers().isEmpty()); + assertEquals(1, readResource2.getGroups().size()); + assertTrue(readResource2.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group1).name(group1).build().getIdentifier())); + + // and write + final AccessPolicy writeResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(writeResource2); + assertTrue(accessPolicies.contains(writeResource2)); + assertEquals(writeResource2, pluginWithPolicies.getAccessPolicy(writeResource2.getIdentifier())); + assertTrue(writeResource2.getUsers().isEmpty()); + assertEquals(1, writeResource2.getGroups().size()); + assertTrue(writeResource2.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group1).name(group1).build().getIdentifier())); + + // resource 3 -> no read or write + assertFalse(pluginWithPolicies.doesPolicyExist("resource-3", RequestAction.WRITE)); + assertFalse(pluginWithPolicies.doesPolicyExist("resource-3", RequestAction.READ)); + + // no read or write + assertNull(pluginWithPolicies.getAccessPolicy("resource-3", RequestAction.WRITE)); + assertNull(pluginWithPolicies.getAccessPolicy("resource-3", RequestAction.READ)); + } + + @Test + public void testNoPolicies() { + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + + assertFalse(pluginWithPolicies.doesPolicyExist("non-existent-resource", RequestAction.READ)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy("non-existent-identifier")); + assertNull(pluginWithPolicies.getAccessPolicy("non-existent-resource", RequestAction.READ)); + } + + @Test + public void testDisabledPolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setIsEnabled(false); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ)); + } + + @Test + public void testMissingResourceValue() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testWildcardResourceValue() { + final String resourceIdentifier1 = "*"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testExcludesPolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + resource1.setIsExcludes(true); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testRecursivePolicy() { + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + resource1.setIsRecursive(true); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the policy was skipped + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.getAccessPolicies().isEmpty()); + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testDelegateAdmin() { + final String user1 = "user-1"; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("READ"), new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + policy1Item.setDelegateAdmin(true); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry"); + pluginWithPolicies.setPolicies(servicePolicies); + + assertEquals(4, pluginWithPolicies.getAccessPolicies().size()); + assertNotNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ)); + assertNotNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + assertNotNull(pluginWithPolicies.getAccessPolicy("/policies" + resourceIdentifier1, RequestAction.READ)); + assertNotNull(pluginWithPolicies.getAccessPolicy("/policies" + resourceIdentifier1, RequestAction.WRITE)); + } + + @Test + public void testPoliciesWithUserGroupProvider() { + final String user1 = "user-1"; // unknown according to user group provider + final String user2 = "user-2"; // known according to user group provider + final String group1 = "group-1"; // unknown according to user group provider + final String group2 = "group-2"; // known according to user group provider + + final UserGroupProvider userGroupProvider = new UserGroupProvider() { + @Override + public Set getUsers() throws AuthorizationAccessException { + return Stream.of(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build()).collect(Collectors.toSet()); + } + + @Override + public User getUser(String identifier) throws AuthorizationAccessException { + final User u2 = new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + if (u2.getIdentifier().equals(identifier)) { + return u2; + } else { + return null; + } + } + + @Override + public User getUserByIdentity(String identity) throws AuthorizationAccessException { + if (user2.equals(identity)) { + return new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + } else { + return null; + } + } + + @Override + public Set getGroups() throws AuthorizationAccessException { + return Stream.of(new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build()).collect(Collectors.toSet()); + } + + @Override + public Group getGroup(String identifier) throws AuthorizationAccessException { + final Group g2 = new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build(); + if (g2.getIdentifier().equals(identifier)) { + return g2; + } else { + return null; + } + } + + @Override + public UserAndGroups getUserAndGroups(String identity) throws AuthorizationAccessException { + if (user2.equals(identity)) { + return new UserAndGroups() { + @Override + public User getUser() { + return new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build(); + } + + @Override + public Set getGroups() { + return Collections.EMPTY_SET; + } + }; + } else { + return null; + } + } + + @Override + public void initialize(UserGroupProviderInitializationContext initializationContext) throws SecurityProviderCreationException { + } + + @Override + public void onConfigured(AuthorizerConfigurationContext configurationContext) throws SecurityProviderCreationException { + } + + @Override + public void preDestruction() throws SecurityProviderCreationException { + } + }; + + final String resourceIdentifier1 = "/resource-1"; + RangerPolicy.RangerPolicyResource resource1 = new RangerPolicy.RangerPolicyResource(resourceIdentifier1); + + final Map policy1Resources = new HashMap<>(); + policy1Resources.put(resourceIdentifier1, resource1); + + final RangerPolicy.RangerPolicyItem policy1Item = new RangerPolicy.RangerPolicyItem(); + policy1Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("READ")).collect(Collectors.toList())); + policy1Item.setUsers(Stream.of(user1).collect(Collectors.toList())); + policy1Item.setGroups(Stream.of(group2).collect(Collectors.toList())); + + final RangerPolicy policy1 = new RangerPolicy(); + policy1.setResources(policy1Resources); + policy1.setPolicyItems(Stream.of(policy1Item).collect(Collectors.toList())); + + final String resourceIdentifier2 = "/resource-2"; + RangerPolicy.RangerPolicyResource resource2 = new RangerPolicy.RangerPolicyResource(resourceIdentifier2); + + final Map policy2Resources = new HashMap<>(); + policy2Resources.put(resourceIdentifier2, resource2); + + final RangerPolicy.RangerPolicyItem policy2Item = new RangerPolicy.RangerPolicyItem(); + policy2Item.setAccesses(Stream.of(new RangerPolicy.RangerPolicyItemAccess("READ"), new RangerPolicy.RangerPolicyItemAccess("WRITE")).collect(Collectors.toList())); + policy2Item.setUsers(Stream.of(user2).collect(Collectors.toList())); + policy2Item.setGroups(Stream.of(group1).collect(Collectors.toList())); + + final RangerPolicy policy2 = new RangerPolicy(); + policy2.setResources(policy2Resources); + policy2.setPolicyItems(Stream.of(policy2Item).collect(Collectors.toList())); + + final List policies = new ArrayList<>(); + policies.add(policy1); + policies.add(policy2); + + final RangerServiceDef serviceDef = new RangerServiceDef(); + serviceDef.setName("nifi-registry"); + + final ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicies(policies); + servicePolicies.setServiceDef(serviceDef); + + // set all the policies in the plugin + final RangerBasePluginWithPolicies pluginWithPolicies = new RangerBasePluginWithPolicies("nifi-registry", "nifi-registry", userGroupProvider); + pluginWithPolicies.setPolicies(servicePolicies); + + // ensure the two ranger policies converted into 3 nifi-registry access policies + final Set accessPolicies = pluginWithPolicies.getAccessPolicies(); + assertEquals(3, accessPolicies.size()); + + // resource 1 -> read but no write + assertFalse(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier1, RequestAction.READ)); + + // read + final AccessPolicy readResource1 = pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.READ); + assertNotNull(readResource1); + assertTrue(accessPolicies.contains(readResource1)); + assertEquals(readResource1, pluginWithPolicies.getAccessPolicy(readResource1.getIdentifier())); + assertTrue(readResource1.getUsers().isEmpty()); + assertEquals(1, readResource1.getGroups().size()); + assertTrue(readResource1.getGroups().contains(new Group.Builder().identifierGenerateFromSeed(group2).name(group2).build().getIdentifier())); + + // but no write + assertNull(pluginWithPolicies.getAccessPolicy(resourceIdentifier1, RequestAction.WRITE)); + + // resource 2 -> read and write + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.WRITE)); + assertTrue(pluginWithPolicies.doesPolicyExist(resourceIdentifier2, RequestAction.READ)); + + // read + final AccessPolicy readResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(readResource2); + assertTrue(accessPolicies.contains(readResource2)); + assertEquals(readResource2, pluginWithPolicies.getAccessPolicy(readResource2.getIdentifier())); + assertEquals(1, readResource2.getUsers().size()); + assertTrue(readResource2.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build().getIdentifier())); + assertTrue(readResource2.getGroups().isEmpty()); + + // and write + final AccessPolicy writeResource2 = pluginWithPolicies.getAccessPolicy(resourceIdentifier2, RequestAction.READ); + assertNotNull(writeResource2); + assertTrue(accessPolicies.contains(writeResource2)); + assertEquals(writeResource2, pluginWithPolicies.getAccessPolicy(writeResource2.getIdentifier())); + assertEquals(1, writeResource2.getUsers().size()); + assertTrue(writeResource2.getUsers().contains(new User.Builder().identifierGenerateFromSeed(user2).identity(user2).build().getIdentifier())); + assertTrue(writeResource2.getGroups().isEmpty()); + } +} diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/krb5.conf b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/krb5.conf new file mode 100644 index 000000000000..0e3f142a9b48 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/krb5.conf @@ -0,0 +1,25 @@ +# 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. + +[libdefaults] + default_realm = EXAMPLE.COM + dns_lookup_kdc = false + dns_lookup_realm = false + +[realms] + EXAMPLE.COM = { + kdc = kerberos.example.com + admin_server = kerberos.example.com + } \ No newline at end of file diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/core-site.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/core-site.xml new file mode 100644 index 000000000000..d590a5039c04 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/core-site.xml @@ -0,0 +1,22 @@ + + + + + + hadoop.security.authentication + simple + + \ No newline at end of file diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-nifi-registry-audit.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-nifi-registry-audit.xml new file mode 100644 index 000000000000..3dbd576334b9 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-nifi-registry-audit.xml @@ -0,0 +1,101 @@ + + + + + + xasecure.audit.is.enabled + true + + + + + xasecure.audit.destination.db + false + + + + xasecure.audit.destination.db.jdbc.driver + com.mysql.jdbc.Driver + + + + xasecure.audit.destination.db.jdbc.url + jdbc:mysql://localhost/ranger_audit + + + + xasecure.audit.destination.db.password + rangerlogger + + + + xasecure.audit.destination.db.user + rangerlogger + + + + xasecure.audit.destination.db.batch.filespool.dir + /tmp/audit/db/spool + + + + + + xasecure.audit.destination.hdfs + false + + + + xasecure.audit.destination.hdfs.dir + hdfs://localhost:8020/ranger/audit + + + + xasecure.audit.destination.hdfs.batch.filespool.dir + /tmp/audit/hdfs/spool + + + + + + xasecure.audit.destination.log4j + false + + + + xasecure.audit.destination.log4j.logger + ranger_audit_logger + + + + + xasecure.audit.destination.solr + true + + + + xasecure.audit.destination.solr.batch.filespool.dir + /tmp/audit/solr/spool + + + + xasecure.audit.destination.solr.urls + http://localhost:6083/solr/ranger_audits + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-nifi-registry-security.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-nifi-registry-security.xml new file mode 100644 index 000000000000..ab55fba7077e --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-nifi-registry-security.xml @@ -0,0 +1,83 @@ + + + + + + ranger.plugin.nifi-registry.policy.rest.url + http://localhost:6080 + + URL to Ranger Admin + + + + + ranger.plugin.nifi-registry.service.name + nifi-registry + + Name of the Ranger service containing policies for this nifi instance + + + + + ranger.plugin.nifi-registry.policy.source.impl + org.apache.ranger.admin.client.RangerAdminRESTClient + + Class to retrieve policies from the source + + + + + ranger.plugin.nifi-registry.policy.rest.ssl.config.file + ranger-policymgr-ssl.xml + + Path to the file containing SSL details to contact Ranger Admin + + + + + ranger.plugin.nifi-registry.policy.pollIntervalMs + 30000 + + How often to poll for changes in policies? + + + + + ranger.plugin.nifi-registry.policy.cache.dir + /tmp + + Directory where Ranger policies are cached after successful retrieval from the source + + + + + ranger.plugin.nifi-registry.policy.rest.client.connection.timeoutMs + 120000 + + RangerRestClient Connection Timeout in Milli Seconds + + + + + ranger.plugin.nifi-registry.policy.rest.client.read.timeoutMs + 30000 + + RangerRestClient read Timeout in Milli Seconds + + + diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-policymgr-ssl.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-policymgr-ssl.xml new file mode 100644 index 000000000000..a6e05747a3d5 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/nifi-registry-ranger-plugin/src/test/resources/ranger/ranger-policymgr-ssl.xml @@ -0,0 +1,63 @@ + + + + + + + xasecure.policymgr.clientssl.keystore + + + Java Keystore files + + + + xasecure.policymgr.clientssl.keystore.password + none + + password for keystore + + + + xasecure.policymgr.clientssl.truststore + + + java truststore file + + + + xasecure.policymgr.clientssl.truststore.password + none + + java truststore password + + + + xasecure.policymgr.clientssl.keystore.credential.file + + + java keystore credential file + + + + xasecure.policymgr.clientssl.truststore.credential.file + + + java truststore credential file + + + \ No newline at end of file diff --git a/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/pom.xml b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/pom.xml new file mode 100644 index 000000000000..9ca1e7744b25 --- /dev/null +++ b/nifi-registry/nifi-registry-extensions/nifi-registry-ranger/pom.xml @@ -0,0 +1,92 @@ + + + + + nifi-registry-extensions + org.apache.nifi.registry + 2.10.0.1 + + 4.0.0 + + nifi-registry-ranger + pom + + + nifi-registry-ranger-assembly + nifi-registry-ranger-jersey-bundle + nifi-registry-ranger-plugin + + + + + + org.eclipse.jetty + jetty-client + 9.4.58.v20250814 + + + org.eclipse.jetty + jetty-http + 9.4.58.v20250814 + + + org.eclipse.jetty + jetty-io + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-util + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-alpn-client + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-util-ajax + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-alpn-java-client + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-xml + ${ranger.jetty.version} + + + org.eclipse.jetty + jetty-security + ${ranger.jetty.version} + + + org.apache.solr + solr-solrj + ${ranger.solr.version} + + + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt.version} + + + + diff --git a/nifi-registry/nifi-registry-extensions/pom.xml b/nifi-registry/nifi-registry-extensions/pom.xml index af47faf54ccd..6ea2c0823215 100644 --- a/nifi-registry/nifi-registry-extensions/pom.xml +++ b/nifi-registry/nifi-registry-extensions/pom.xml @@ -26,5 +26,6 @@ nifi-registry-aws + nifi-registry-ranger diff --git a/nifi-system-tests/nifi-system-test-suite/src/main/java/org/apache/nifi/tests/system/arenadata/service/JdbcService.java b/nifi-system-tests/nifi-system-test-suite/src/main/java/org/apache/nifi/tests/system/arenadata/service/JdbcService.java index b56de3270468..422ee150dd4e 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/main/java/org/apache/nifi/tests/system/arenadata/service/JdbcService.java +++ b/nifi-system-tests/nifi-system-test-suite/src/main/java/org/apache/nifi/tests/system/arenadata/service/JdbcService.java @@ -23,7 +23,6 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.testcontainers.shaded.org.awaitility.Awaitility; -import javax.sql.DataSource; import java.time.Duration; import java.time.temporal.ChronoUnit; import java.util.List; @@ -33,6 +32,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import javax.sql.DataSource; public class JdbcService { diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/AllureExtension.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/AllureExtension.java index 72d0413a2b7b..3a50861b55d3 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/AllureExtension.java +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/AllureExtension.java @@ -43,4 +43,4 @@ public void beforeEach(ExtensionContext context) { }); Allure.suite("Greengage connector: ADB " + adbVersion); } -} \ No newline at end of file +} diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/GetGreengageRecordIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/GetGreengageRecordIT.java index 7af98800f321..d0049ee52bda 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/GetGreengageRecordIT.java +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/GetGreengageRecordIT.java @@ -60,32 +60,33 @@ public class GetGreengageRecordIT extends NifiSystemContainerizedIT { private static final String CREATE_EXTENSION_UUID_SQL = "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""; private static final String CREATE_TABLE_TEMPLATE_SQL = "CREATE TABLE %s (%s)"; private static final String ID_COLUMN = "id"; - private static final Map TABLE_COLUMNS = new LinkedHashMap<>() {{ - put(ID_COLUMN, "BIGSERIAL PRIMARY KEY"); - put("f_int", "INT"); - put("f_bigint", "BIGINT"); - put("f_bit", "BIT"); - put("f_bool", "BOOLEAN"); - put("f_bytea", "BYTEA"); - put("f_char", "CHAR(2)"); - put("code", "VARCHAR(10)"); - put("article", "VARCHAR"); - put("f_date", "DATE"); - put("f_float", "FLOAT8"); - put("f_real", "FLOAT4"); - put("f_jsonb", "JSONB"); - put("f_numeric", "DECIMAL(10, 5)"); - put("f_double", "DOUBLE PRECISION"); - put("f_tinyint", "SMALLINT"); - put("f_smallint", "SMALLINT"); - put("f_time", "TIME"); - put("f_timestampz", "TIMESTAMPTZ"); - put("f_timestamp", "TIMESTAMP"); - put("f_uuid", "UUID"); - put("f_text_array", "TEXT[]"); - put("f_hstore", "HSTORE"); - put("f_enum", "DAY"); - }}; + private static final Map TABLE_COLUMNS = new LinkedHashMap<>(); + static { + TABLE_COLUMNS.put(ID_COLUMN, "BIGSERIAL PRIMARY KEY"); + TABLE_COLUMNS.put("f_int", "INT"); + TABLE_COLUMNS.put("f_bigint", "BIGINT"); + TABLE_COLUMNS.put("f_bit", "BIT"); + TABLE_COLUMNS.put("f_bool", "BOOLEAN"); + TABLE_COLUMNS.put("f_bytea", "BYTEA"); + TABLE_COLUMNS.put("f_char", "CHAR(2)"); + TABLE_COLUMNS.put("code", "VARCHAR(10)"); + TABLE_COLUMNS.put("article", "VARCHAR"); + TABLE_COLUMNS.put("f_date", "DATE"); + TABLE_COLUMNS.put("f_float", "FLOAT8"); + TABLE_COLUMNS.put("f_real", "FLOAT4"); + TABLE_COLUMNS.put("f_jsonb", "JSONB"); + TABLE_COLUMNS.put("f_numeric", "DECIMAL(10, 5)"); + TABLE_COLUMNS.put("f_double", "DOUBLE PRECISION"); + TABLE_COLUMNS.put("f_tinyint", "SMALLINT"); + TABLE_COLUMNS.put("f_smallint", "SMALLINT"); + TABLE_COLUMNS.put("f_time", "TIME"); + TABLE_COLUMNS.put("f_timestampz", "TIMESTAMPTZ"); + TABLE_COLUMNS.put("f_timestamp", "TIMESTAMP"); + TABLE_COLUMNS.put("f_uuid", "UUID"); + TABLE_COLUMNS.put("f_text_array", "TEXT[]"); + TABLE_COLUMNS.put("f_hstore", "HSTORE"); + TABLE_COLUMNS.put("f_enum", "DAY"); + } /** * PG target columns derived from ADB. * Types that GetGreengageRecord converts are mapped to PG-compatible types: diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/NifiSystemContainerizedIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/NifiSystemContainerizedIT.java index 61518508da1c..24f91058a1ae 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/NifiSystemContainerizedIT.java +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/NifiSystemContainerizedIT.java @@ -82,11 +82,13 @@ public void setup(final TestInfo testInfo) throws IOException, NoSuchAlgorithmEx setupClient(9091); } + @Override protected void setupClient(final int apiPort) { nifiClient = createClient(apiPort); clientUtil = new NiFiClientUtil(nifiClient, getNiFiVersion(), getTestName()); } + @Override protected NiFiClient createClient(final int port) { final NiFiClientConfig.Builder clientConfigBuilder = new NiFiClientConfig.Builder() .baseUrl("http://localhost:" + port) diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/PutGreengageRecordIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/PutGreengageRecordIT.java index bd511400e43a..1fa600160bca 100644 --- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/PutGreengageRecordIT.java +++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/arenadata/PutGreengageRecordIT.java @@ -65,31 +65,34 @@ public class PutGreengageRecordIT extends NifiSystemContainerizedIT { private static final String CREATE_EXTENSION_UUID_SQL = "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\""; private static final String CREATE_TABLE_TEMPLATE_SQL = "CREATE TABLE %s (%s)"; private static final String ID_COLUMN = "id"; - private static final Map TABLE_COLUMNS = new LinkedHashMap<>() {{ - put(ID_COLUMN, "BIGSERIAL PRIMARY KEY"); - put("f_int", "INT"); - put("f_bigint", "BIGINT"); - put("f_bit", "BIT"); - put("f_bool", "BOOLEAN"); - put("f_bytea", "BYTEA"); - put("f_char", "CHAR(2)"); - put("code", "VARCHAR(10)"); - put("article", "VARCHAR"); - put("f_date", "DATE"); - put("f_float", "FLOAT8"); - put("f_real", "FLOAT4"); - put("f_jsonb", "JSONB"); - put("f_numeric", "DECIMAL(10, 5)"); - put("f_double", "DOUBLE PRECISION"); - put("f_tinyint", "SMALLINT"); - put("f_smallint", "SMALLINT"); - put("f_time", "TIME"); - put("f_timestampz", "TIMESTAMPTZ"); - put("f_timestamp", "TIMESTAMP"); - put("f_uuid", "UUID"); - put("f_text_array", "TEXT[]"); - put("f_hstore", "HSTORE"); - }}; + + private static final Map TABLE_COLUMNS = new LinkedHashMap<>(); + static { + TABLE_COLUMNS.put(ID_COLUMN, "BIGSERIAL PRIMARY KEY"); + TABLE_COLUMNS.put("f_int", "INT"); + TABLE_COLUMNS.put("f_bigint", "BIGINT"); + TABLE_COLUMNS.put("f_bit", "BIT"); + TABLE_COLUMNS.put("f_bool", "BOOLEAN"); + TABLE_COLUMNS.put("f_bytea", "BYTEA"); + TABLE_COLUMNS.put("f_char", "CHAR(2)"); + TABLE_COLUMNS.put("code", "VARCHAR(10)"); + TABLE_COLUMNS.put("article", "VARCHAR"); + TABLE_COLUMNS.put("f_date", "DATE"); + TABLE_COLUMNS.put("f_float", "FLOAT8"); + TABLE_COLUMNS.put("f_real", "FLOAT4"); + TABLE_COLUMNS.put("f_jsonb", "JSONB"); + TABLE_COLUMNS.put("f_numeric", "DECIMAL(10, 5)"); + TABLE_COLUMNS.put("f_double", "DOUBLE PRECISION"); + TABLE_COLUMNS.put("f_tinyint", "SMALLINT"); + TABLE_COLUMNS.put("f_smallint", "SMALLINT"); + TABLE_COLUMNS.put("f_time", "TIME"); + TABLE_COLUMNS.put("f_timestampz", "TIMESTAMPTZ"); + TABLE_COLUMNS.put("f_timestamp", "TIMESTAMP"); + TABLE_COLUMNS.put("f_uuid", "UUID"); + TABLE_COLUMNS.put("f_text_array", "TEXT[]"); + TABLE_COLUMNS.put("f_hstore", "HSTORE"); + } + private static final String GENERATE_DATASET_SQL = "select i,\n" + " 25000000000 * random(),\n" + " case when random() > 0.5 then 1 else 0 end::bit,\n" + diff --git a/pom.xml b/pom.xml index 6717dc2b1f4a..ef6acd093c77 100644 --- a/pom.xml +++ b/pom.xml @@ -142,6 +142,11 @@ 3.5.0 1.4.1 + + 2.6.0 + 8.11.3 + 9.4.58.v20250814 + 7.7.0