diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 0000000000..9a17f656ea --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,39 @@ +# 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. + +# https://cwiki.apache.org/confluence/display/INFRA/git+-+.asf.yaml+features + +github: + description: "Apache Ranger - To enable, monitor and manage comprehensive data security across the Hadoop platform and beyond" + homepage: https://ranger.apache.org + labels: + - apache + - ranger + - authz + - java + - python + - docker + enabled_merge_buttons: + squash: true + merge: false + rebase: false + notifications: + commits: commits@ranger.apache.org + issues: dev@ranger.apache.org + pullrequests: dev@ranger.apache.org + pullrequests_comment: dev@ranger.apache.org + jira_options: worklog link label + protected_branches: + master: {} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..d4286768da --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +## What changes were proposed in this pull request? + +(Please fill in changes proposed in this fix. Create an issue in ASF JIRA before opening a pull request and +set the title of the pull request which starts with +the corresponding JIRA issue number. (e.g. RANGER-XXXX: Fix a typo in YYY)) + + +## How was this patch tested? + +(Please explain how this patch was tested. Ex: unit tests, manual tests) +(If this patch involves UI changes, please attach a screen-shot; otherwise, remove this) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000000..3ddb1a277f --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,160 @@ +# 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. + + +# This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-maven + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +name: CI + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +jobs: + build-8: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 8 + uses: actions/setup-java@v4 + with: + java-version: '8' + distribution: 'temurin' + cache: maven + - name: build (8) + run: mvn -T 8 clean install --no-transfer-progress -B -V + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: target-8 + path: target/* + + build-11: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: '11' + distribution: 'temurin' + cache: maven + - name: build (11) + run: mvn -T 8 clean install -pl '!knox-agent' --no-transfer-progress -B -V + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: target-11 + path: target/* + + docker-build: + needs: + - build-8 + - build-11 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Download build-8 artifacts + uses: actions/download-artifact@v4 + with: + name: target-8 + + - name: Copy artifacts for docker build + run: | + cp ranger-*.tar.gz dev-support/ranger-docker/dist + cp version dev-support/ranger-docker/dist + + - name: Cache downloaded archives + uses: actions/cache@v3 + with: + path: dev-support/ranger-docker/downloads + key: ${{ runner.os }}-ranger-downloads-${{ hashFiles('dev-support/ranger-docker/.env') }} + restore-keys: | + ${{ runner.os }}-ranger-downloads- + + - name: Run download-archives.sh + run: | + cd dev-support/ranger-docker + ./download-archives.sh hadoop hive hbase kafka knox ozone + + - name: Clean up Docker space + run: docker system prune --all --force --volumes + + - name: Build all ranger-service images + run: | + cd dev-support/ranger-docker + docker compose -f docker-compose.ranger-base.yml build + export DOCKER_BUILDKIT=1 + export COMPOSE_DOCKER_CLI_BUILD=1 + export RANGER_DB_TYPE=postgres + docker compose \ + -f docker-compose.ranger-${RANGER_DB_TYPE}.yml \ + -f docker-compose.ranger.yml \ + -f docker-compose.ranger-usersync.yml \ + -f docker-compose.ranger-tagsync.yml \ + -f docker-compose.ranger-kms.yml \ + -f docker-compose.ranger-hadoop.yml \ + -f docker-compose.ranger-hbase.yml \ + -f docker-compose.ranger-kafka.yml \ + -f docker-compose.ranger-hive.yml \ + -f docker-compose.ranger-knox.yml \ + -f docker-compose.ranger-ozone.yml build + + - name: Bring up containers + run: | + cd dev-support/ranger-docker + ./scripts/ozone-plugin-docker-setup.sh + export RANGER_DB_TYPE=postgres + docker compose \ + -f docker-compose.ranger-${RANGER_DB_TYPE}.yml \ + -f docker-compose.ranger.yml \ + -f docker-compose.ranger-usersync.yml \ + -f docker-compose.ranger-tagsync.yml \ + -f docker-compose.ranger-kms.yml \ + -f docker-compose.ranger-hadoop.yml \ + -f docker-compose.ranger-hbase.yml \ + -f docker-compose.ranger-kafka.yml \ + -f docker-compose.ranger-hive.yml \ + -f docker-compose.ranger-knox.yml \ + -f docker-compose.ranger-ozone.yml up -d + - name: Check status of containers and remove them + run: | + sleep 60 + containers=(ranger ranger-zk ranger-solr ranger-postgres ranger-usersync ranger-tagsync ranger-kms ranger-hadoop ranger-hbase ranger-kafka ranger-hive ranger-knox ozone-om ozone-scm ozone-datanode); + flag=true; + for container in "${containers[@]}"; do + if [[ $(docker inspect -f '{{.State.Running}}' $container 2>/dev/null) == "true" ]]; then + echo "Container $container is running!"; + else + flag=false; + echo "Container $container is NOT running!"; + fi + done + + if [[ $flag == true ]]; then + echo "All required containers are up and running"; + docker stop $(docker ps -q) && docker rm $(docker ps -aq); + else + docker stop $(docker ps -q) && docker rm $(docker ps -aq); + exit 1; + fi diff --git a/.gitignore b/.gitignore index c2def9d8a1..5bd45411b9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,16 @@ .classpath .project /target/ +/venv/ winpkg/target .DS_Store .idea + +#Python +*.pyc +**/build +**/dist +**/apache_ranger.egg-info +.python-version +/security-admin/src/main/webapp/react-webapp/node_modules +**/target diff --git a/.project b/.project deleted file mode 100644 index 38afddad90..0000000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - ranger - - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - - diff --git a/.travis.yml b/.travis.yml index deac951610..711794b08c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,31 +15,47 @@ sudo: false -language: java +dist: bionic +language: generic + cache: directories: - - $HOME/.m2 -jdk: - - oraclejdk8 - - oraclejdk11 - - openjdk10 - - openjdk11 + - $HOME/.m2 env: - - KEY=default VALUE=default + global: + - KEY=default VALUE=default + # Environment to testing with different versions, disabled because ranger is not compatible # - KEY=hadoop.version VALUE=2.8.0 +jobs: + include: + - name: Linux AMD64 OpenJDK8 + env: JDK_VERSION=8 + - name: Linux AMD64 OpenJDK11 + env: JDK_VERSION=11 + - name: Linux ARM64 OpenJDK11 + arch: arm64-graviton2 + dist: focal + virt: lxd + group: edge + env: + - JDK_VERSION=11 + - MAVEN_ARGS="-DskipJSTests=true -P!all" + before_install: - export MAVEN_OPTS="-Xmx1200M -XX:MaxPermSize=768m -Xms512m" + - sudo apt update -y + - sudo apt install openjdk-${JDK_VERSION}-jdk maven install: - - mvn package -D$KEY=$VALUE -DskipTests -Dmaven.javadoc.skip=true -B -V + - mvn install $MAVEN_ARGS -D$KEY=$VALUE -DskipTests -Dmaven.javadoc.skip=true --no-transfer-progress -B -V # KafkaRangerAuthorizerGSSTest is a failing test, TestLdapUserGroup needs too much memory for travis script: - - mvn test -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl !plugin-kafka,!ugsync,!hive-agent - - mvn test -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl plugin-kafka -Dtest="*,!KafkaRangerAuthorizerGSSTest" - - mvn test -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl ugsync -Dtest="*,!TestLdapUserGroup" - - if [[ "$TRAVIS_JDK_VERSION" != "oraclejdk8" ]]; then mvn test -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl hive-agent -Dtest="*,!HIVERangerAuthorizerTest" -DfailIfNoTests=false ; fi - - if [[ "$TRAVIS_JDK_VERSION" == "oraclejdk8" ]]; then mvn test -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl hive-agent ; fi + - mvn test $MAVEN_ARGS -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl !plugin-kafka,!ugsync,!hive-agent + - mvn test $MAVEN_ARGS -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl plugin-kafka -Dtest="*,!KafkaRangerAuthorizerGSSTest" + - mvn test $MAVEN_ARGS -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl ugsync -Dtest="*,!TestLdapUserGroup" + - if [[ "$JDK_VERSION" != "8" ]]; then mvn test $MAVEN_ARGS -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl hive-agent -Dtest="*,!HIVERangerAuthorizerTest" -DfailIfNoTests=false ; fi + - if [[ "$JDK_VERSION" == "8" ]]; then mvn test $MAVEN_ARGS -D$KEY=$VALUE -Dmaven.javadoc.skip=true -B -V -pl hive-agent ; fi diff --git a/README.md b/README.md new file mode 100644 index 0000000000..dd4bf87275 --- /dev/null +++ b/README.md @@ -0,0 +1,129 @@ + +# Apache Ranger + +[![License](https://img.shields.io/:license-Apache%202-green.svg)](https://www.apache.org/licenses/LICENSE-2.0.txt) +[![PyPI Downloads](https://static.pepy.tech/personalized-badge/apache-ranger?period=month&units=international_system&left_color=black&right_color=orange&left_text=PyPI%20downloads)](https://pypi.org/project/apache-ranger/) +[![Documentation](https://img.shields.io/badge/docs-apache.org-blue.svg)](https://ranger.apache.org) +[![Wiki](https://img.shields.io/badge/ranger-wiki-orange)](https://cwiki.apache.org/confluence/display/RANGER/Index) + + +#### NOTE +Apache Ranger allows contributions via pull requests (PRs) on GitHub. +Alternatively, use [this](https://reviews.apache.org) to submit changes for review using the Review Board. +Also create a [ranger jira](https://issues.apache.org/jira/browse/RANGER) to go along with the review and mention it in the review board review. + + +## Building Ranger in Docker (Sandbox Install) + +Ranger is built using [Apache Maven](https://maven.apache.org/). To run Ranger: + +1. Check out the code from GIT [repository](https://github.com/apache/ranger.git) + +2. Ensure that docker & docker-compose is installed and running on your system. + +3. Ensure that JDK 1.8+ is installed on your system. + +4. Ensure that Apache Maven is installed on your system. + +5. Run the following command to build & run Ranger from Docker + + `./ranger_in_docker up` + +6. After successful completion of the above command, you should be able to view Ranger Admin Console by using URL: + ``` + http://:6080/ + + UserName: admin + Password: rangerR0cks! + ``` + +## Regular Build Process + +1. Check out the code from GIT repository + +2. On the root folder, please execute the following Maven command: + + `mvn clean compile package install` + + `mvn eclipse:eclipse` + + Ranger Admin UI tests depend on PhantomJS. If the build fails with npm or Karma errors you can either: + - install PhantomJS dependencies for your platform (bzip2 and fontconfig) + - skip JavaScript test execution: mvn -DskipJSTests ... + +3. After the above build command execution, you should see the following TAR files in the target folder: + ``` + ranger--admin.tar.gz + ranger--atlas-plugin.tar.gz + ranger--hbase-plugin.tar.gz + ranger--hdfs-plugin.tar.gz + ranger--hive-plugin.tar.gz + ranger--kafka-plugin.tar.gz + ranger--kms.tar.gz + ranger--knox-plugin.tar.gz + ranger--migration-util.tar.gz + ranger--ranger-tools.tar.gz + ranger--solr-plugin.tar.gz + ranger--sqoop-plugin.tar.gz + ranger--src.tar.gz + ranger--storm-plugin.tar.gz + ranger--tagsync.tar.gz + ranger--usersync.tar.gz + ranger--yarn-plugin.tar.gz + ranger--kylin-plugin.tar.gz + ranger--elasticsearch-plugin.tar.gz + ``` + +## Importing Apache Ranger Project into Eclipse + +1. Create an Eclipse workspace called 'ranger' + +2. Import maven project from the root directory where ranger source code is downloaded (and build) + + +## Deployment Process + + +### Installation Host Information +1. Ranger Admin Tool Component (ranger--admin.tar.gz) should be installed on a host where Policy Admin Tool web application runs on port 6080 (default). +2. Ranger User Synchronization Component (ranger--usersync.tar.gz) should be installed on a host to synchronize the external user/group information into Ranger database via Ranger Admin Tool. +3. Ranger Component plugin should be installed on the component boxes: + - HDFS Plugin needs to be installed on Name Node hosts. + - Hive Plugin needs to be installed on HiveServer2 hosts. + - HBase Plugin needs to be installed on both Master and Regional Server nodes. + - Knox Plugin needs to be installed on Knox gateway host. + - Storm Plugin needs to be installed on Storm hosts. + - Kafka/Solr Plugin needs to be installed on their respective component hosts. + - YARN plugin needs to be installed on YARN Resource Manager hosts. + - Sqoop plugin needs to be installed on Sqoop2 hosts. + - Kylin plugin needs to be installed on Kylin hosts. + - Elasticsearch plugin needs to be installed on Elasticsearch hosts. + +### Installation Process + +1. Download the tar.gz file into a temporary folder in the box where it needs to be installed. + +2. Expand the tar.gz file into /usr/lib/ranger/ folder + +3. Go to the component name under the expanded folder (e.g. /usr/lib/ranger/ranger--admin/) + +4. Modify the install.properties file with appropriate variables + +5. - If the module has setup.sh, execute ./setup.sh + - If the install.sh file does not exists, execute ./enable--plugin.sh + diff --git a/README.txt b/README.txt deleted file mode 100644 index fce972ab1b..0000000000 --- a/README.txt +++ /dev/null @@ -1,105 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -Check Apache Ranger Wiki for up to date instruction: -https://cwiki.apache.org/confluence/display/RANGER/Index - -NOTE about using sending pull request on github: -================================================ -Apache Ranger is currently NOT setup to use pull requests to take in the changes for commit. -Please use the apache review board to submit your code changes for review and commit. https://reviews.apache.org -Also create a jira to go along with the review and mention it in the review board review. https://issues.apache.org/jira/browse/RANGER - -Build Process -============= - -1. Check out the code from GIT repository - -2. On the root folder, please execute the following Maven command: - - $ mvn clean compile package install - $ mvn eclipse:eclipse - - (Ranger Admin UI tests depend on PhantomJS. If the build fails with npm or Karma errors you can either - i. install PhantomJS dependencies for your platform (bzip2 and fontconfig) - ii. skip JavaScript test execution: mvn -DskipJSTests ...) - -3. After the above build command execution, you should see the following TAR files in the target folder: - - - ranger--admin.tar.gz - ranger--atlas-plugin.tar.gz - ranger--hbase-plugin.tar.gz - ranger--hdfs-plugin.tar.gz - ranger--hive-plugin.tar.gz - ranger--kafka-plugin.tar.gz - ranger--kms.tar.gz - ranger--knox-plugin.tar.gz - ranger--migration-util.tar.gz - ranger--ranger-tools.tar.gz - ranger--solr-plugin.tar.gz - ranger--sqoop-plugin.tar.gz - ranger--src.tar.gz - ranger--storm-plugin.tar.gz - ranger--tagsync.tar.gz - ranger--usersync.tar.gz - ranger--yarn-plugin.tar.gz - ranger--kylin-plugin.tar.gz - ranger--elasticsearch-plugin.tar.gz - -Importing Apache Ranger Project into Eclipse -============================================ - -1. Create a Eclipse workspace called 'ranger' - -2. Import maven project from the root directory where ranger source code is downloaded (and build) - - -Deployment Process -================== - -Installation Host Information -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Ranger Admin Tool Component (ranger--admin.tar.gz) should be installed on a host where Policy Admin Tool web application runs on port 6080 (default). -2. Ranger User Synchronization Component (ranger--usersync.tar.gz) should be installed on a host to synchronize the external user/group information into Ranger database via Ranger Admin Tool. -3. Ranger Component plugin should be installed on the component boxes: - (a) HDFS Plugin needs to be installed on Name Node hosts - (b) Hive Plugin needs to be installed on HiveServer2 hosts - (c) HBase Plugin needs to be installed on both Master and Regional Server nodes. - (d) Knox Plugin needs to be installed on Knox gateway host. - (e) Storm Plugin needs to be installed on Storm hosts. - (f) Kafka/Solr Plugin needs to be installed on their respective component hosts. - (g) YARN plugin needs to be installed on YARN Resource Manager hosts - (h) Sqoop plugin needs to be installed on Sqoop2 hosts - (i) Kylin plugin needs to be installed on Kylin hosts - (j) Elasticsearch plugin needs to be installed on Elasticsearch hosts - -Installation Process -~~~~~~~~~~~~~~~~~~~~ - -1. Download the tar.gz file into a temporary folder in the box where it needs to be installed. - -2. Expand the tar.gz file into /usr/lib/ranger/ folder - -3. Go to the component name under the expanded folder (e.g. /usr/lib/ranger/ranger--admin/) - -4. Modify the install.properties file with appropriate variables - -5. If the module has setup.sh, - Execute ./setup.sh - - If the install.sh file does not exists, - Execute ./enable--plugin.sh - diff --git a/agents-audit/pom.xml b/agents-audit/pom.xml index e11cb0b541..dbb1502e6d 100644 --- a/agents-audit/pom.xml +++ b/agents-audit/pom.xml @@ -28,7 +28,7 @@ org.apache.ranger ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT .. @@ -38,56 +38,135 @@ ${project.version} - commons-logging - commons-logging - ${commons.logging.version} + commons-lang + commons-lang + ${commons.lang.version} org.apache.hadoop hadoop-common ${hadoop.version} - + com.google.guava guava + + net.minidev + json-smart + + + io.netty + netty-handler + + + io.netty + netty-transport-native-epoll + + + org.apache.commons + commons-text + + + org.apache.commons + commons-configuration2 + + + log4j + * + + + org.slf4j + * + + + org.apache.commons + commons-compress + + + org.apache.zookeeper + zookeeper + - org.eclipse.persistence - eclipselink - ${eclipse.jpa.version} - - - org.eclipse.persistence - javax.persistence - ${javax.persistence.version} + org.apache.commons + commons-text + ${commons.text.version} - log4j - log4j - ${log4j.version} + org.apache.commons + commons-configuration2 + ${commons.configuration.version} org.apache.kafka - kafka_${scala.binary.version} + kafka-clients ${kafka.version} - - net.jpountz.lz4 - lz4 - org.lz4 lz4-java + + org.slf4j + * + + + + + org.apache.solr + solr-solrj + ${solr.version} + + + io.netty + * + + + org.eclipse.jetty.http2 + * + + + org.apache.commons + commons-math3 + + + commons-io + commons-io + + + org.apache.httpcomponents + * + + + org.apache.zookeeper + * + + + org.codehaus.woodstox + * + + + org.eclipse.jetty + * + + + org.slf4j + * + - org.apache.solr - solr-solrj - ${solr.version} - + org.slf4j + slf4j-api + ${slf4j.version} + + + org.eclipse.jetty + jetty-client + ${jetty-client.version} + org.apache.httpcomponents httpclient @@ -98,15 +177,20 @@ httpmime ${httpcomponents.httpmime.version} - - com.google.guava - guava - ${google.guava.version} - org.elasticsearch elasticsearch ${elasticsearch.version} + + + org.elasticsearch + jna + + + org.apache.logging.log4j + * + + org.elasticsearch @@ -231,7 +315,7 @@ org.apache.lucene lucene-spatial - ${lucene.version} + 8.4.0 org.apache.lucene @@ -254,14 +338,88 @@ ${hppc.version} - org.apache.logging.log4j - log4j-core - ${log4j.core.version} + org.apache.httpcomponents + httpcore + ${httpcomponents.httpcore.version} + + + org.apache.hive + hive-storage-api + ${hive.storage-api.version} + + + * + * + + + + + org.apache.orc + orc-core + ${orc.version} + + + * + * + + + + + org.apache.orc + orc-shims + ${orc.version} + + + * + * + + + + + io.airlift + aircompressor + ${aircompressor.version} + + + * + * + + + + + com.amazonaws + aws-java-sdk-logs + ${aws-java-sdk.version} + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.vintage + junit-vintage-engine + ${junit.jupiter.version} + test + + + org.testng + testng + test + + + org.mockito + mockito-core + test - org.apache.logging.log4j - log4j-api - ${log4j.core.version} + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/dao/BaseDao.java b/agents-audit/src/main/java/org/apache/ranger/audit/dao/BaseDao.java deleted file mode 100644 index 124f89e883..0000000000 --- a/agents-audit/src/main/java/org/apache/ranger/audit/dao/BaseDao.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - package org.apache.ranger.audit.dao; - - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.List; - -import javax.persistence.EntityManager; -import javax.persistence.EntityTransaction; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; - -import org.apache.log4j.Logger; - -public abstract class BaseDao { - static final Logger logger = Logger.getLogger(BaseDao.class); - - protected DaoManager daoManager; - - protected Class tClass; - - public BaseDao(DaoManagerBase daoManager) { - this.init(daoManager); - } - - @SuppressWarnings("unchecked") - private void init(DaoManagerBase daoManager) { - this.daoManager = (DaoManager) daoManager; - - ParameterizedType genericSuperclass = (ParameterizedType) getClass() - .getGenericSuperclass(); - - Type type = genericSuperclass.getActualTypeArguments()[0]; - - if (type instanceof ParameterizedType) { - this.tClass = (Class) ((ParameterizedType) type).getRawType(); - } else { - this.tClass = (Class) type; - } - } - - public EntityManager getEntityManager() { - return daoManager.getEntityManager(); - } - - public boolean beginTransaction() { - boolean ret = false; - - EntityManager em = getEntityManager(); - - if(em != null) { - EntityTransaction et = em.getTransaction(); - - // check the transaction is not already active - if(et != null && !et.isActive()) { - et.begin(); - ret = true; - } - } - - return ret; - } - - public void commitTransaction() { - EntityManager em = getEntityManager(); - - if(em != null) { - em.flush(); - - EntityTransaction et = em.getTransaction(); - - if(et != null) { - et.commit(); - } - } - } - - public void rollbackTransaction() { - EntityManager em = getEntityManager(); - - if(em != null) { - EntityTransaction et = em.getTransaction(); - - if(et != null) { - et.rollback(); - } - } - } - - public T create(T obj) { - T ret = null; - - boolean trxBegan = beginTransaction(); - - getEntityManager().persist(obj); - - if(trxBegan) { - commitTransaction(); - } - - ret = obj; - - return ret; - } - - public T update(T obj) { - boolean trxBegan = beginTransaction(); - - getEntityManager().merge(obj); - - if(trxBegan) { - commitTransaction(); - } - - return obj; - } - - public boolean remove(Long id) { - return remove(getById(id)); - } - - public boolean remove(T obj) { - if (obj == null) { - return true; - } - - boolean trxBegan = beginTransaction(); - - getEntityManager().remove(obj); - - if(trxBegan) { - commitTransaction(); - } - - return trxBegan; - } - - public T getById(Long id) { - if (id == null) { - return null; - } - T ret = null; - try { - ret = getEntityManager().find(tClass, id); - } catch (NoResultException e) { - return null; - } - return ret; - } - - public List getAll() { - List ret = null; - - TypedQuery qry = getEntityManager().createQuery( - "SELECT t FROM " + tClass.getSimpleName() + " t", tClass); - - ret = qry.getResultList(); - - return ret; - } - - public Long getAllCount() { - Long ret = null; - - TypedQuery qry = getEntityManager().createQuery( - "SELECT count(t) FROM " + tClass.getSimpleName() + " t", - Long.class); - - ret = qry.getSingleResult(); - - return ret; - } - - public T getUniqueResult(TypedQuery qry) { - T ret = null; - - try { - ret = qry.getSingleResult(); - } catch (NoResultException e) { - // ignore - } - return ret; - } - - public List executeQuery(TypedQuery qry) { - List ret = null; - - ret = qry.getResultList(); - - return ret; - } - - public List findByNamedQuery(String namedQuery, String paramName, - Object refId) { - List ret = new ArrayList(); - - if (namedQuery == null) { - return ret; - } - try { - TypedQuery qry = getEntityManager().createNamedQuery(namedQuery, tClass); - qry.setParameter(paramName, refId); - ret = qry.getResultList(); - } catch (NoResultException e) { - // ignore - } - return ret; - } -} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/dao/DaoManager.java b/agents-audit/src/main/java/org/apache/ranger/audit/dao/DaoManager.java deleted file mode 100644 index b2f68efe51..0000000000 --- a/agents-audit/src/main/java/org/apache/ranger/audit/dao/DaoManager.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - package org.apache.ranger.audit.dao; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.PersistenceContext; - - -//@Component -public class DaoManager extends DaoManagerBase { - - @PersistenceContext - private EntityManagerFactory emf; - - static ThreadLocal sEntityManager; - - public void setEntityManagerFactory(EntityManagerFactory emf) { - this.emf = emf; - sEntityManager = new ThreadLocal(); - } - - @Override - public EntityManager getEntityManager() { - EntityManager em = null; - - if(sEntityManager != null) { - em = sEntityManager.get(); - - if(em == null && this.emf != null) { - em = this.emf.createEntityManager(); - - sEntityManager.set(em); - } - } else { - logger.error("EntityManagerFactory was not set in this thread.", new Throwable()); - } - - return em; - } -} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/AmazonCloudWatchAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/AmazonCloudWatchAuditDestination.java new file mode 100644 index 0000000000..f3ba77a3ec --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/AmazonCloudWatchAuditDestination.java @@ -0,0 +1,198 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.destination; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Properties; +import java.util.stream.Collectors; + +import com.amazonaws.services.logs.AWSLogs; +import com.amazonaws.services.logs.AWSLogsClientBuilder; +import com.amazonaws.services.logs.model.CreateLogStreamRequest; +import com.amazonaws.services.logs.model.InputLogEvent; +import com.amazonaws.services.logs.model.InvalidSequenceTokenException; +import com.amazonaws.services.logs.model.PutLogEventsRequest; +import com.amazonaws.services.logs.model.PutLogEventsResult; +import com.amazonaws.services.logs.model.ResourceNotFoundException; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.audit.model.AuditEventBase; +import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.concurrent.ThreadSafe; + +/** + * Writes audit events to Amazon CloudWatch Logs. + *

+ * Two properties are required: LogGroupName and LogStreamPrefix + *

+ * Thread-safety is ensured by making the log method synchronized. + * This is to avoid possible race condition on {@link #sequenceToken} which is required in PutLogEvents API. + * @see PutLogEvents API Reference + *

+ * Note: Amazon CloudWatch has limits on the payload size and request rate. + * Based on the traffic, adjust the batch size and flush interval accordingly. + *

+ * + * @see Amazon CloudWatch Logs Service Limits + */ +@ThreadSafe +public class AmazonCloudWatchAuditDestination extends AuditDestination { + + private static final Logger LOG = LoggerFactory.getLogger(AmazonCloudWatchAuditDestination.class); + + public static final String PROP_LOG_GROUP_NAME = "log_group"; + public static final String PROP_LOG_STREAM_PREFIX = "log_stream_prefix"; + public static final String CONFIG_PREFIX = "ranger.audit.amazon_cloudwatch"; + public static final String PROP_REGION = "region"; + + private String logGroupName; + private String logStreamName; + private AWSLogs logsClient; + private String sequenceToken; + private String regionName; + + @Override + public void init(Properties props, String propPrefix) { + LOG.info("init() called for CloudWatchAuditDestination"); + super.init(props, propPrefix); + + this.logGroupName = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_LOG_GROUP_NAME, "ranger_audits"); + this.logStreamName = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_LOG_STREAM_PREFIX) + MiscUtil.generateUniqueId(); + this.regionName = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_REGION); + + logsClient = getClient(); // Initialize client + createLogStream(); + } + + @Override + public void stop() { + super.stop(); + logStatus(); + } + + @Override + public synchronized boolean log(Collection collection) { + boolean ret = false; + AWSLogs client = getClient(); + + PutLogEventsRequest req = new PutLogEventsRequest() + .withLogEvents(toInputLogEvent(collection)) + .withLogGroupName(logGroupName) + .withLogStreamName(logStreamName); + + if (StringUtils.isNotBlank(sequenceToken)) { + req.setSequenceToken(sequenceToken); + } + + try { + sequenceToken = pushLogEvents(req, false, client); + addSuccessCount(collection.size()); + ret = true; + } catch (Throwable e) { + addFailedCount(collection.size()); + LOG.error("Failed to send audit events", e); + } + + return ret; + } + + private String pushLogEvents(PutLogEventsRequest req, + boolean retryingOnInvalidSeqToken, + AWSLogs client) { + String sequenceToken; + try { + PutLogEventsResult re = client.putLogEvents(req); + sequenceToken = re.getNextSequenceToken(); + } catch (ResourceNotFoundException ex) { + if (!retryingOnInvalidSeqToken) { + createLogStream(); + return pushLogEvents(req, true, client); + } + throw ex; + } catch (InvalidSequenceTokenException ex) { + if (retryingOnInvalidSeqToken) { + LOG.error("Unexpected invalid sequence token. Possible race condition occurred"); + throw ex; + } + + // LogStream may exist before first push attempt, re-obtain the sequence token + if (LOG.isDebugEnabled()) { + LOG.debug("Invalid sequence token. Plugin possibly restarted. Updating the sequence token and retrying"); + } + sequenceToken = ex.getExpectedSequenceToken(); + req.setSequenceToken(sequenceToken); + return pushLogEvents(req, true, client); + } + + return sequenceToken; + } + + /* + * (non-Javadoc) + * + * @see org.apache.ranger.audit.provider.AuditProvider#flush() + */ + @Override + public void flush() { + + } + + static Collection toInputLogEvent(Collection collection) { + return collection.stream() + .map(e -> new InputLogEvent() + .withMessage(MiscUtil.stringify(e)) + .withTimestamp(e.getEventTime().getTime())) + .sorted(Comparator.comparingLong(InputLogEvent::getTimestamp)) + .collect(Collectors.toList()); + } + + private void createLogStream() { + AWSLogs client = getClient(); + CreateLogStreamRequest req = new CreateLogStreamRequest() + .withLogGroupName(logGroupName) + .withLogStreamName(logStreamName); + + LOG.info(String.format("Creating Log Stream `%s` in Log Group `%s`", logStreamName, logGroupName)); + client.createLogStream(req); + } + + private AWSLogs getClient() { + if (logsClient == null) { + synchronized (AmazonCloudWatchAuditDestination.class) { + if (logsClient == null) { + logsClient = newClient(); + } + } + } + + return logsClient; + } + + private AWSLogs newClient() { + if (StringUtils.isBlank(regionName)) { + return AWSLogsClientBuilder.standard().build(); + } + return AWSLogsClientBuilder.standard().withRegion(regionName).build(); + } +} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/AuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/AuditDestination.java index 41d0e82783..c221487c24 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/AuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/AuditDestination.java @@ -21,16 +21,16 @@ import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.provider.BaseAuditHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class needs to be extended by anyone who wants to build custom * destination */ public abstract class AuditDestination extends BaseAuditHandler { - private static final Log logger = LogFactory.getLog(AuditDestination.class); + private static final Logger logger = LoggerFactory.getLogger(AuditDestination.class); public AuditDestination() { logger.info("AuditDestination() enter"); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/DBAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/DBAuditDestination.java deleted file mode 100644 index 79f07d7a56..0000000000 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/DBAuditDestination.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.ranger.audit.destination; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityTransaction; -import javax.persistence.Persistence; - -import org.apache.ranger.audit.dao.DaoManager; -import org.apache.ranger.audit.entity.AuthzAuditEventDbObj; -import org.apache.ranger.audit.model.AuditEventBase; -import org.apache.ranger.audit.provider.MiscUtil; - -public class DBAuditDestination extends AuditDestination { - - private static final Log logger = LogFactory - .getLog(DBAuditDestination.class); - - public static final String PROP_DB_JDBC_DRIVER = "jdbc.driver"; - public static final String PROP_DB_JDBC_URL = "jdbc.url"; - public static final String PROP_DB_USER = "user"; - public static final String PROP_DB_PASSWORD = "password"; - public static final String PROP_DB_PASSWORD_ALIAS = "password.alias"; - - private EntityManagerFactory entityManagerFactory; - private DaoManager daoManager; - - private String jdbcDriver = null; - private String jdbcURL = null; - private String dbUser = null; - private String dbPasswordAlias = "auditDBCred"; - - public DBAuditDestination() { - logger.info("DBAuditDestination() called"); - } - - @Override - public void init(Properties props, String propPrefix) { - logger.info("init() called"); - super.init(props, propPrefix); - // Initial connect - connect(); - - // initialize the database related classes - AuthzAuditEventDbObj.init(props); - } - - /* - * (non-Javadoc) - * - * @see - * org.apache.ranger.audit.provider.AuditHandler#logger(java.util.Collection - * ) - */ - @Override - public boolean log(Collection events) { - boolean retValue = false; - logStatusIfRequired(); - addTotalCount(events.size()); - - if (beginTransaction()) { - boolean isFailed = false; - for (AuditEventBase event : events) { - try { - event.persist(daoManager); - } catch (Throwable t) { - logger.error("Error persisting data. event=" + event, t); - isFailed = true; - break; - } - } - if (isFailed) { - retValue = false; - rollbackTransaction(); - } else { - retValue = commitTransaction(); - } - } - - if (retValue) { - addSuccessCount(events.size()); - } else { - addDeferredCount(events.size()); - } - return retValue; - } - - @Override - public void stop() { - cleanUp(); - super.stop(); - } - - // Local methods - protected void connect() { - if (isDbConnected()) { - return; - } - try { - jdbcDriver = MiscUtil.getStringProperty(props, propPrefix + "." - + PROP_DB_JDBC_DRIVER); - jdbcURL = MiscUtil.getStringProperty(props, propPrefix + "." - + PROP_DB_JDBC_URL); - dbUser = MiscUtil.getStringProperty(props, propPrefix + "." - + PROP_DB_USER); - String dbPasswordFromProp = MiscUtil.getStringProperty(props, - propPrefix + "." + PROP_DB_PASSWORD); - String tmpAlias = MiscUtil.getStringProperty(props, propPrefix - + "." + PROP_DB_PASSWORD_ALIAS); - dbPasswordAlias = tmpAlias != null ? tmpAlias : dbPasswordAlias; - String credFile = MiscUtil.getStringProperty(props, - AUDIT_DB_CREDENTIAL_PROVIDER_FILE); - - if (jdbcDriver == null || jdbcDriver.isEmpty()) { - logger.fatal("JDBC driver not provided. Set property name " - + propPrefix + "." + PROP_DB_JDBC_DRIVER); - return; - } - if (jdbcURL == null || jdbcURL.isEmpty()) { - logger.fatal("JDBC URL not provided. Set property name " - + propPrefix + "." + PROP_DB_JDBC_URL); - return; - } - if (dbUser == null || dbUser.isEmpty()) { - logger.fatal("DB user not provided. Set property name " - + propPrefix + "." + PROP_DB_USER); - return; - } - String dbPassword = MiscUtil.getCredentialString(credFile, - dbPasswordAlias); - - if (dbPassword == null || dbPassword.isEmpty()) { - // If password is not in credential store, let's try password - // from property - dbPassword = dbPasswordFromProp; - } - - if (dbPassword == null || dbPassword.isEmpty()) { - logger.warn("DB password not provided. Will assume it is empty and continue"); - } - logger.info("JDBC Driver=" + jdbcDriver + ", JDBC URL=" + jdbcURL - + ", dbUser=" + dbUser + ", passwordAlias=" - + dbPasswordAlias + ", credFile=" + credFile - + ", usingPassword=" + (dbPassword == null ? "no" : "yes")); - - Map dbProperties = new HashMap(); - dbProperties.put("javax.persistence.jdbc.driver", jdbcDriver); - dbProperties.put("javax.persistence.jdbc.url", jdbcURL); - dbProperties.put("javax.persistence.jdbc.user", dbUser); - if (dbPassword != null) { - dbProperties.put("javax.persistence.jdbc.password", dbPassword); - } - - entityManagerFactory = Persistence.createEntityManagerFactory( - "xa_server", dbProperties); - - logger.info("entityManagerFactory=" + entityManagerFactory); - - daoManager = new DaoManager(); - daoManager.setEntityManagerFactory(entityManagerFactory); - - // this forces the connection to be made to DB - if (daoManager.getEntityManager() == null) { - logger.error("Error connecting audit database. EntityManager is null. dbURL=" - + jdbcURL + ", dbUser=" + dbUser); - } else { - logger.info("Connected to audit database. dbURL=" + jdbcURL - + ", dbUser=" + dbUser); - } - - } catch (Throwable t) { - logger.error("Error connecting audit database. dbURL=" + jdbcURL - + ", dbUser=" + dbUser, t); - } - } - - private synchronized void cleanUp() { - logger.info("DBAuditDestination: cleanUp()"); - - try { - if (entityManagerFactory != null && entityManagerFactory.isOpen()) { - entityManagerFactory.close(); - } - } catch (Exception excp) { - logger.error("DBAuditDestination.cleanUp(): failed", excp); - } finally { - entityManagerFactory = null; - daoManager = null; - } - logStatus(); - } - - private EntityManager getEntityManager() { - DaoManager daoMgr = daoManager; - - if (daoMgr != null) { - try { - return daoMgr.getEntityManager(); - } catch (Exception excp) { - logger.error("DBAuditDestination.getEntityManager(): failed", - excp); - - cleanUp(); - } - } - - return null; - } - - private boolean isDbConnected() { - EntityManager em = getEntityManager(); - return em != null && em.isOpen(); - } - - private void clearEntityManager() { - try { - EntityManager em = getEntityManager(); - - if (em != null) { - em.clear(); - } - } catch (Exception excp) { - logger.warn("DBAuditDestination.clearEntityManager(): failed", excp); - } - } - - private EntityTransaction getTransaction() { - if (!isDbConnected()) { - connect(); - } - - EntityManager em = getEntityManager(); - - return em != null ? em.getTransaction() : null; - } - - private boolean beginTransaction() { - EntityTransaction trx = getTransaction(); - - if (trx != null && !trx.isActive()) { - trx.begin(); - } - - if (trx == null) { - logger.warn("DBAuditDestination.beginTransaction(): trx is null"); - } - - return trx != null; - } - - private boolean commitTransaction() { - boolean ret = false; - EntityTransaction trx = null; - - try { - trx = getTransaction(); - - if (trx != null && trx.isActive()) { - trx.commit(); - ret = true; - } else { - throw new Exception("trx is null or not active"); - } - } catch (Throwable excp) { - logger.error("DBAuditDestination.commitTransaction(): failed", excp); - - cleanUp(); // so that next insert will try to init() - } finally { - clearEntityManager(); - } - - return ret; - } - - private boolean rollbackTransaction() { - boolean ret = false; - EntityTransaction trx = null; - - try { - trx = getTransaction(); - - if (trx != null && trx.isActive()) { - trx.rollback(); - ret = true; - } else { - throw new Exception("trx is null or not active"); - } - } catch (Throwable excp) { - logger.error("DBAuditDestination.rollbackTransaction(): failed", - excp); - - cleanUp(); // so that next insert will try to init() - } finally { - clearEntityManager(); - } - - return ret; - } - -} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/ElasticSearchAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/ElasticSearchAuditDestination.java index d4897a406e..8324d998b2 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/ElasticSearchAuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/ElasticSearchAuditDestination.java @@ -29,11 +29,13 @@ import java.util.Locale; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; + import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.thirdparty.com.google.common.util.concurrent.ThreadFactoryBuilder; import org.apache.http.HttpHost; import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.client.CredentialsProvider; @@ -55,12 +57,14 @@ import org.elasticsearch.client.RestClient; import org.elasticsearch.client.RestClientBuilder; import org.elasticsearch.client.RestHighLevelClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosTicket; public class ElasticSearchAuditDestination extends AuditDestination { - private static final Log LOG = LogFactory.getLog(ElasticSearchAuditDestination.class); + private static final Logger LOG = LoggerFactory.getLogger(ElasticSearchAuditDestination.class); public static final String CONFIG_URLS = "urls"; public static final String CONFIG_PORT = "port"; @@ -71,8 +75,8 @@ public class ElasticSearchAuditDestination extends AuditDestination { public static final String CONFIG_PREFIX = "ranger.audit.elasticsearch"; public static final String DEFAULT_INDEX = "ranger_audits"; - private String index = "index"; - private volatile RestHighLevelClient client = null; + private String index = CONFIG_INDEX; + private final AtomicReference clientRef = new AtomicReference<>(null); private String protocol; private String user; private int port; @@ -125,12 +129,12 @@ public boolean log(Collection events) { ArrayList eventList = new ArrayList<>(events); BulkRequest bulkRequest = new BulkRequest(); try { - for (AuditEventBase event : eventList) { + eventList.forEach(event -> { AuthzAuditEvent authzEvent = (AuthzAuditEvent) event; String id = authzEvent.getEventId(); Map doc = toDoc(authzEvent); bulkRequest.add(new IndexRequest(index).id(id).source(doc)); - } + }); } catch (Exception ex) { addFailedCount(eventList.size()); logFailedEvent(eventList, ex); @@ -148,7 +152,7 @@ public boolean log(Collection events) { addFailedCount(1); logFailedEvent(Arrays.asList(itemRequest), itemResponse.getFailureMessage()); } else { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug(String.format("Indexed %s", itemRequest.getEventKey())); } addSuccessCount(1); @@ -170,7 +174,7 @@ public boolean log(Collection events) { */ @Override public void flush() { - + // Empty flush method } public boolean isAsync() { @@ -178,20 +182,24 @@ public boolean isAsync() { } synchronized RestHighLevelClient getClient() { + RestHighLevelClient client = clientRef.get(); if (client == null) { synchronized (ElasticSearchAuditDestination.class) { + client = clientRef.get(); if (client == null) { client = newClient(); + clientRef.set(client); } } } if (subject != null) { KerberosTicket ticket = CredentialsProviderUtil.getTGT(subject); try { - if (new Date().getTime() > ticket.getEndTime().getTime()){ - client = null; + if (new Date().getTime() > ticket.getEndTime().getTime()) { + clientRef.set(null); CredentialsProviderUtil.ticketExpireTime80 = 0; - newClient(); + client = newClient(); + clientRef.set(client); } else if (CredentialsProviderUtil.ticketWillExpire(ticket)) { subject = CredentialsProviderUtil.login(user, password); } @@ -209,8 +217,12 @@ public static RestClientBuilder getRestClientBuilder(String urls, String protoco RestClientBuilder restClientBuilder = RestClient.builder( MiscUtil.toArray(urls, ",").stream() .map(x -> new HttpHost(x, port, protocol)) - .toArray(i -> new HttpHost[i]) + .toArray(HttpHost[]::new) ); + ThreadFactory clientThreadFactory = new ThreadFactoryBuilder() + .setNameFormat("ElasticSearch rest client %s") + .setDaemon(true) + .build(); if (StringUtils.isNotBlank(user) && StringUtils.isNotBlank(password) && !user.equalsIgnoreCase("NONE") && !password.equalsIgnoreCase("NONE")) { if (password.contains("keytab") && new File(password).exists()) { final KerberosCredentialsProvider credentialsProvider = @@ -218,6 +230,7 @@ public static RestClientBuilder getRestClientBuilder(String urls, String protoco Lookup authSchemeRegistry = RegistryBuilder.create() .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory()).build(); restClientBuilder.setHttpClientConfigCallback(clientBuilder -> { + clientBuilder.setThreadFactory(clientThreadFactory); clientBuilder.setDefaultCredentialsProvider(credentialsProvider); clientBuilder.setDefaultAuthSchemeRegistry(authSchemeRegistry); return clientBuilder; @@ -225,14 +238,20 @@ public static RestClientBuilder getRestClientBuilder(String urls, String protoco } else { final CredentialsProvider credentialsProvider = CredentialsProviderUtil.getBasicCredentials(user, password); - restClientBuilder.setHttpClientConfigCallback(clientBuilder -> - clientBuilder.setDefaultCredentialsProvider(credentialsProvider)); + restClientBuilder.setHttpClientConfigCallback(clientBuilder -> { + clientBuilder.setThreadFactory(clientThreadFactory); + clientBuilder.setDefaultCredentialsProvider(credentialsProvider); + return clientBuilder; + }); } } else { LOG.error("ElasticSearch Credentials not provided!!"); final CredentialsProvider credentialsProvider = null; - restClientBuilder.setHttpClientConfigCallback(clientBuilder -> - clientBuilder.setDefaultCredentialsProvider(credentialsProvider)); + restClientBuilder.setHttpClientConfigCallback(clientBuilder -> { + clientBuilder.setThreadFactory(clientThreadFactory); + clientBuilder.setDefaultCredentialsProvider(credentialsProvider); + return clientBuilder; + }); } return restClientBuilder; } @@ -244,30 +263,31 @@ private RestHighLevelClient newClient() { } RestClientBuilder restClientBuilder = getRestClientBuilder(hosts, protocol, user, password, port); - RestHighLevelClient restHighLevelClient = new RestHighLevelClient(restClientBuilder); - if (LOG.isDebugEnabled()) { - LOG.debug("Initialized client"); - } - boolean exits = false; - try { - exits = restHighLevelClient.indices().open(new OpenIndexRequest(this.index), RequestOptions.DEFAULT).isShardsAcknowledged(); - } catch (Exception e) { - LOG.warn("Error validating index " + this.index); - } - if(exits) { + try (RestHighLevelClient restHighLevelClient = new RestHighLevelClient(restClientBuilder)) { if (LOG.isDebugEnabled()) { - LOG.debug("Index exists"); + LOG.debug("Initialized client"); } - } else { - LOG.info("Index does not exist"); + boolean exists = false; + try { + exists = restHighLevelClient.indices().open(new OpenIndexRequest(this.index), RequestOptions.DEFAULT).isShardsAcknowledged(); + } catch (Exception e) { + LOG.warn("Error validating index " + this.index); + } + if (exists) { + if (LOG.isDebugEnabled()) { + LOG.debug("Index exists"); + } + } else { + LOG.info("Index does not exist"); + } + return restHighLevelClient; } - return restHighLevelClient; } catch (Throwable t) { lastLoggedAt.updateAndGet(lastLoggedAt -> { long now = System.currentTimeMillis(); long elapsed = now - lastLoggedAt; if (elapsed > TimeUnit.MINUTES.toMillis(1)) { - LOG.fatal("Can't connect to ElasticSearch server: " + connectionString(), t); + LOG.error("Can't connect to ElasticSearch server: " + connectionString(), t); return now; } else { return lastLoggedAt; @@ -297,7 +317,7 @@ private String getStringProperty(Properties props, String propName, String defau } Map toDoc(AuthzAuditEvent auditEvent) { - Map doc = new HashMap(); + Map doc = new HashMap<>(); doc.put("id", auditEvent.getEventId()); doc.put("access", auditEvent.getAccessType()); doc.put("enforcer", auditEvent.getAclEnforcer()); @@ -320,6 +340,8 @@ Map toDoc(AuthzAuditEvent auditEvent) { doc.put("event_count", auditEvent.getEventCount()); doc.put("event_dur_ms", auditEvent.getEventDurationMS()); doc.put("tags", auditEvent.getTags()); + doc.put("datasets", auditEvent.getDatasets()); + doc.put("projects", auditEvent.getProjects()); doc.put("cluster", auditEvent.getClusterName()); doc.put("zoneName", auditEvent.getZoneName()); doc.put("agentHost", auditEvent.getAgentHostname()); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/FileAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/FileAuditDestination.java index 609b98520d..2bab08ac2f 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/FileAuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/FileAuditDestination.java @@ -29,17 +29,17 @@ import java.util.List; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class write the logs to local file */ public class FileAuditDestination extends AuditDestination { - private static final Log logger = LogFactory - .getLog(FileAuditDestination.class); + private static final Logger logger = LoggerFactory + .getLogger(FileAuditDestination.class); public static final String PROP_FILE_LOCAL_DIR = "dir"; public static final String PROP_FILE_LOCAL_FILE_NAME_FORMAT = "filename.format"; @@ -75,31 +75,24 @@ public void init(Properties prop, String propPrefix) { + PROP_FILE_FILE_ROLLOVER, fileRolloverSec); if (logFolderProp == null || logFolderProp.isEmpty()) { - logger.error("File destination folder is not configured. Please set " - + propPrefix - + "." - + PROP_FILE_LOCAL_DIR - + ". name=" - + getName()); + logger.error("File destination folder is not configured. Please set {}. {}. name= {}", propPrefix, PROP_FILE_LOCAL_DIR, getName()); return; } logFolder = new File(logFolderProp); if (!logFolder.isDirectory()) { logFolder.mkdirs(); if (!logFolder.isDirectory()) { - logger.error("FileDestination folder not found and can't be created. folder=" - + logFolder.getAbsolutePath() + ", name=" + getName()); + logger.error("FileDestination folder not found and can't be created. folder={}, name={}", logFolder.getAbsolutePath(), getName()); return; } } - logger.info("logFolder=" + logFolder + ", name=" + getName()); + logger.info("logFolder={}, name={}", logFolder, getName()); if (logFileNameFormat == null || logFileNameFormat.isEmpty()) { logFileNameFormat = "%app-type%_ranger_audit.log"; } - logger.info("logFileNameFormat=" + logFileNameFormat + ", destName=" - + getName()); + logger.info("logFileNameFormat={}, destName={}", logFileNameFormat, getName()); initDone = true; } @@ -110,7 +103,7 @@ synchronized public boolean logJSON(Collection events) { addTotalCount(events.size()); if (isStopped) { - logError("log() called after stop was requested. name=" + getName()); + logError("logJSON() called after stop was requested. name={}", getName()); addDeferredCount(events.size()); return false; } @@ -141,7 +134,7 @@ public boolean log(Collection events) { if (isStopped) { addTotalCount(events.size()); addDeferredCount(events.size()); - logError("log() called after stop was requested. name=" + getName()); + logError("log() called after stop was requested. name={}", getName()); return false; } List jsonList = new ArrayList(); @@ -152,7 +145,7 @@ public boolean log(Collection events) { addTotalCount(1); addFailedCount(1); logFailedEvent(event); - logger.error("Error converting to JSON. event=" + event); + logger.error("Error converting to JSON. event={}", event); } } return logJSON(jsonList); @@ -178,8 +171,7 @@ synchronized public void stop() { logWriter.flush(); logWriter.close(); } catch (Throwable t) { - logger.error("Error on closing log writter. Exception will be ignored. name=" - + getName() + ", fileName=" + currentFileName); + logger.error("Error on closing log writer. Exception will be ignored. name= {}, fileName= {}", getName(), currentFileName); } logWriter = null; } @@ -211,16 +203,14 @@ synchronized private PrintWriter getLogFileStream() throws Exception { if (!newLogFile.exists()) { // Move the file if (!outLogFile.renameTo(newLogFile)) { - logger.error("Error renameing file. " + outLogFile - + " to " + newLogFile); + logger.error("Error renameing file. {} to {} " , outLogFile, newLogFile); } break; } } } if (!outLogFile.exists()) { - logger.info("Creating new file. destName=" + getName() - + ", fileName=" + fileName); + logger.info("Creating new file. destName={} , fileName={} ", getName(), fileName); // Open the file logWriter = new PrintWriter(new BufferedWriter(new FileWriter( outLogFile))); @@ -239,14 +229,12 @@ private void closeFileIfNeeded() { return; } if (System.currentTimeMillis() - fileCreateTime.getTime() > fileRolloverSec * 1000) { - logger.info("Closing file. Rolling over. name=" + getName() - + ", fileName=" + currentFileName); + logger.info("Closing file. Rolling over. name={} , fileName={}", getName(), currentFileName); try { logWriter.flush(); logWriter.close(); } catch (Throwable t) { - logger.error("Error on closing log writter. Exception will be ignored. name=" - + getName() + ", fileName=" + currentFileName); + logger.error("Error on closing log writter. Exception will be ignored. name={} , fileName={}", getName(), currentFileName); } logWriter = null; currentFileName = null; diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java index 906ff341f5..4ad8dfd985 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/HDFSAuditDestination.java @@ -19,114 +19,46 @@ package org.apache.ranger.audit.destination; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.URI; -import java.security.PrivilegedAction; +import java.io.File; import java.security.PrivilegedExceptionAction; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.FSDataOutputStream; -import org.apache.hadoop.fs.FileSystem; -import org.apache.hadoop.fs.Path; import org.apache.ranger.audit.model.AuditEventBase; +import org.apache.ranger.audit.provider.AuditWriterFactory; import org.apache.ranger.audit.provider.MiscUtil; -import org.apache.ranger.audit.utils.RollingTimeUtil; +import org.apache.ranger.audit.utils.RangerAuditWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * This class write the logs to local file */ public class HDFSAuditDestination extends AuditDestination { - private static final Log logger = LogFactory - .getLog(HDFSAuditDestination.class); + private static final Logger logger = LoggerFactory + .getLogger(HDFSAuditDestination.class); - public static final String PROP_HDFS_DIR = "dir"; - public static final String PROP_HDFS_SUBDIR = "subdir"; - public static final String PROP_HDFS_FILE_NAME_FORMAT = "filename.format"; - public static final String PROP_HDFS_ROLLOVER = "file.rollover.sec"; - public static final String PROP_HDFS_ROLLOVER_PERIOD = "file.rollover.period"; - - int fileRolloverSec = 24 * 60 * 60; // In seconds - - private String logFileNameFormat; - - private String rolloverPeriod; - - boolean initDone = false; - - private String logFolder; - - private PrintWriter logWriter = null; - volatile FSDataOutputStream ostream = null; // output stream wrapped in logWriter - - private String currentFileName; - - private boolean isStopped = false; - - private RollingTimeUtil rollingTimeUtil = null; - - private Date nextRollOverTime = null; - - private boolean rollOverByDuration = false; + private Map auditConfigs = null; + private String auditProviderName = null; + private RangerAuditWriter auditWriter = null; + private boolean initDone = false; + private boolean isStopped = false; @Override public void init(Properties prop, String propPrefix) { super.init(prop, propPrefix); - - // Initialize properties for this class - // Initial folder and file properties - String logFolderProp = MiscUtil.getStringProperty(props, propPrefix - + "." + PROP_HDFS_DIR); - if (logFolderProp == null || logFolderProp.isEmpty()) { - logger.fatal("File destination folder is not configured. Please set " - + propPrefix + "." + PROP_HDFS_DIR + ". name=" + getName()); - return; - } - - String logSubFolder = MiscUtil.getStringProperty(props, propPrefix - + "." + PROP_HDFS_SUBDIR); - if (logSubFolder == null || logSubFolder.isEmpty()) { - logSubFolder = "%app-type%/%time:yyyyMMdd%"; - } - - logFileNameFormat = MiscUtil.getStringProperty(props, propPrefix + "." - + PROP_HDFS_FILE_NAME_FORMAT); - fileRolloverSec = MiscUtil.getIntProperty(props, propPrefix + "." - + PROP_HDFS_ROLLOVER, fileRolloverSec); - - if (logFileNameFormat == null || logFileNameFormat.isEmpty()) { - logFileNameFormat = "%app-type%_ranger_audit_%hostname%" + ".log"; - } - - logFolder = logFolderProp + "/" + logSubFolder; - logger.info("logFolder=" + logFolder + ", destName=" + getName()); - logger.info("logFileNameFormat=" + logFileNameFormat + ", destName=" - + getName()); - logger.info("config=" + configProps.toString()); - - rolloverPeriod = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_HDFS_ROLLOVER_PERIOD); - rollingTimeUtil = RollingTimeUtil.getInstance(); - - //file.rollover.period is used for rolling over. If it could compute the next roll over time using file.rollover.period - //it fall back to use file.rollover.sec for find next rollover time. If still couldn't find default will be 1day window - //for rollover. - if(StringUtils.isEmpty(rolloverPeriod) ) { - rolloverPeriod = rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec); - } + this.auditProviderName = getName(); + this.auditConfigs = configProps; try { - nextRollOverTime = rollingTimeUtil.computeNextRollingTime(rolloverPeriod); - } catch ( Exception e) { - logger.warn("Rollover by file.rollover.period failed...will be using the file.rollover.sec for hdfs audit file rollover...",e); - rollOverByDuration = true; - nextRollOverTime = rollOverByDuration(); + this.auditWriter = getWriter(); + this.initDone = true; + } catch (Exception e) { + logger.error("Error while getting Audit writer", e); } - initDone = true; } @Override @@ -140,62 +72,76 @@ synchronized public boolean logJSON(final Collection events) { } if (isStopped) { addDeferredCount(events.size()); - logError("log() called after stop was requested. name=" + getName()); + logError("log() called after stop was requested. name={}", getName()); return false; } - - PrintWriter out = null; try { + boolean ret = auditWriter.log(events); + if (!ret) { + addDeferredCount(events.size()); + return false; + } + } catch (Throwable t) { + addDeferredCount(events.size()); + logError("Error writing to log file.", t); + return false; + } finally { if (logger.isDebugEnabled()) { - logger.debug("UGI=" + MiscUtil.getUGILoginUser() - + ". Will write to HDFS file=" + currentFileName); + logger.debug("Flushing HDFS audit. Event Size:{}", events.size()); + } + if (auditWriter != null) { + flush(); } + } + addSuccessCount(events.size()); + return true; + } - out = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { - @Override - public PrintWriter run() throws Exception { - PrintWriter out = getLogFileStream(); - for (String event : events) { - out.println(event); - } - return out; - }; - }); + @Override + synchronized public boolean logFile(final File file) { + logStatusIfRequired(); + if (!initDone) { + return false; + } + if (isStopped) { + logError("log() called after stop was requested. name={}", getName()); + return false; + } - // flush and check the stream for errors - if (out.checkError()) { - // In theory, this count may NOT be accurate as part of the messages may have been successfully written. - // However, in practice, since client does buffering, either all of none would succeed. - addDeferredCount(events.size()); - out.close(); - logWriter = null; - ostream = null; + try { + boolean ret = auditWriter.logFile(file); + if (!ret) { return false; } } catch (Throwable t) { - addDeferredCount(events.size()); logError("Error writing to log file.", t); return false; } finally { - logger.info("Flushing HDFS audit. Event Size:" + events.size()); - if (out != null) { + logger.info("Flushing HDFS audit. File:{}{}", file.getAbsolutePath(), file.getName()); + if (auditWriter != null) { flush(); } } - addSuccessCount(events.size()); return true; } @Override public void flush() { - logger.info("Flush called. name=" + getName()); - MiscUtil.executePrivilegedAction(new PrivilegedAction() { - @Override - public Void run() { - hflush(); + if (logger.isDebugEnabled()) { + logger.debug("==> HDFSAuditDestination.flush() called. name={}", getName()); + } + try { + MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + auditWriter.flush(); return null; - } - }); + }); + } catch (Exception excp) { + logger.error("HDFSAuditDestination.flush() failed", excp); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== HDFSAuditDestination.flush() called. name={}", getName()); + } } /* @@ -210,7 +156,7 @@ public boolean log(Collection events) { logStatusIfRequired(); addTotalCount(events.size()); addDeferredCount(events.size()); - logError("log() called after stop was requested. name=" + getName()); + logError("log() called after stop was requested. name={}", getName()); return false; } List jsonList = new ArrayList(); @@ -218,7 +164,7 @@ public boolean log(Collection events) { try { jsonList.add(MiscUtil.stringify(event)); } catch (Throwable t) { - logger.error("Error converting to JSON. event=" + event); + logger.error("Error converting to JSON. event={}", event); addTotalCount(1); addFailedCount(1); logFailedEvent(event); @@ -241,151 +187,14 @@ public void start() { @Override synchronized public void stop() { - isStopped = true; - if (logWriter != null) { - try { - logWriter.flush(); - logWriter.close(); - } catch (Throwable t) { - logger.error("Error on closing log writter. Exception will be ignored. name=" - + getName() + ", fileName=" + currentFileName); - } - logWriter = null; - ostream = null; - } + auditWriter.stop(); logStatus(); + isStopped = true; } - // Helper methods in this class - synchronized private PrintWriter getLogFileStream() throws Exception { - closeFileIfNeeded(); - - // Either there are no open log file or the previous one has been rolled - // over - if (logWriter == null) { - Date currentTime = new Date(); - // Create a new file - String fileName = MiscUtil.replaceTokens(logFileNameFormat, - currentTime.getTime()); - String parentFolder = MiscUtil.replaceTokens(logFolder, - currentTime.getTime()); - Configuration conf = createConfiguration(); - - String fullPath = parentFolder + Path.SEPARATOR + fileName; - String defaultPath = fullPath; - URI uri = URI.create(fullPath); - FileSystem fileSystem = FileSystem.get(uri, conf); - - Path hdfPath = new Path(fullPath); - logger.info("Checking whether log file exists. hdfPath=" + fullPath + ", UGI=" + MiscUtil.getUGILoginUser()); - int i = 0; - while (fileSystem.exists(hdfPath)) { - i++; - int lastDot = defaultPath.lastIndexOf('.'); - String baseName = defaultPath.substring(0, lastDot); - String extension = defaultPath.substring(lastDot); - fullPath = baseName + "." + i + extension; - hdfPath = new Path(fullPath); - logger.info("Checking whether log file exists. hdfPath=" - + fullPath); - } - logger.info("Log file doesn't exists. Will create and use it. hdfPath=" - + fullPath); - // Create parent folders - createParents(hdfPath, fileSystem); - - // Create the file to write - logger.info("Creating new log file. hdfPath=" + fullPath); - ostream = fileSystem.create(hdfPath); - logWriter = new PrintWriter(ostream); - currentFileName = fullPath; - } - return logWriter; - } - - Configuration createConfiguration() { - Configuration conf = new Configuration(); - for (Map.Entry entry : configProps.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - // for ease of install config file may contain properties with empty value, skip those - if (StringUtils.isNotEmpty(value)) { - conf.set(key, value); - } - logger.info("Adding property to HDFS config: " + key + " => " + value); - } - - logger.info("Returning HDFS Filesystem Config: " + conf.toString()); - return conf; - } - - private void createParents(Path pathLogfile, FileSystem fileSystem) - throws Exception { - logger.info("Creating parent folder for " + pathLogfile); - Path parentPath = pathLogfile != null ? pathLogfile.getParent() : null; - - if (parentPath != null && fileSystem != null - && !fileSystem.exists(parentPath)) { - fileSystem.mkdirs(parentPath); - } - } - - private void closeFileIfNeeded() throws FileNotFoundException, IOException { - if (logWriter == null) { - return; - } - - if ( System.currentTimeMillis() > nextRollOverTime.getTime() ) { - logger.info("Closing file. Rolling over. name=" + getName() - + ", fileName=" + currentFileName); - try { - logWriter.flush(); - logWriter.close(); - } catch (Throwable t) { - logger.error("Error on closing log writter. Exception will be ignored. name=" - + getName() + ", fileName=" + currentFileName); - } - - logWriter = null; - ostream = null; - currentFileName = null; - - if (!rollOverByDuration) { - try { - if(StringUtils.isEmpty(rolloverPeriod) ) { - rolloverPeriod = rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec); - } - nextRollOverTime = rollingTimeUtil.computeNextRollingTime(rolloverPeriod); - } catch ( Exception e) { - logger.warn("Rollover by file.rollover.period failed...will be using the file.rollover.sec for hdfs audit file rollover...",e); - nextRollOverTime = rollOverByDuration(); - } - } else { - nextRollOverTime = rollOverByDuration(); - } - } - } - - private void hflush() { - if (ostream != null) { - try { - synchronized (this) { - if (ostream != null) - // 1) PrinterWriter does not have bufferring of its own so - // we need to flush its underlying stream - // 2) HDFS flush() does not really flush all the way to disk. - ostream.hflush(); - logger.info("Flush HDFS audit logs completed....."); - } - } catch (IOException e) { - logger.error("Error on flushing log writer: " + e.getMessage() + - "\nException will be ignored. name=" + getName() + ", fileName=" + currentFileName); - } - } - } - - private Date rollOverByDuration() { - long rollOverTime = rollingTimeUtil.computeNextRollingTime(fileRolloverSec,nextRollOverTime); - return new Date(rollOverTime); + public RangerAuditWriter getWriter() throws Exception { + AuditWriterFactory auditWriterFactory = AuditWriterFactory.getInstance(); + auditWriterFactory.init(props, propPrefix, auditProviderName, auditConfigs); + return auditWriterFactory.getAuditWriter(); } } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/Log4JAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/Log4JAuditDestination.java index 1dd35c92a7..6da3e75875 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/Log4JAuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/Log4JAuditDestination.java @@ -22,16 +22,16 @@ import java.util.Collection; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Log4JAuditDestination extends AuditDestination { - private static final Log logger = LogFactory - .getLog(Log4JAuditDestination.class); + private static final Logger logger = LoggerFactory + .getLogger(Log4JAuditDestination.class); - private static Log auditLogger = null; + private static Logger auditLogger = null; public static final String PROP_LOG4J_LOGGER = "logger"; public static final String DEFAULT_LOGGER_PREFIX = "ranger.audit"; @@ -54,7 +54,7 @@ public void init(Properties prop, String propPrefix) { + loggerName); } logger.info("Logger name for " + getName() + " is " + loggerName); - auditLogger = LogFactory.getLog(loggerName); + auditLogger = LoggerFactory.getLogger(loggerName); logger.info("Done initializing logger for audit. name=" + getName() + ", loggerName=" + loggerName); } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java b/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java index 7631e5882e..017e15b2e8 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/destination/SolrAuditDestination.java @@ -20,13 +20,13 @@ package org.apache.ranger.audit.destination; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.audit.provider.MiscUtil; import org.apache.ranger.audit.utils.InMemoryJAASConfiguration; -import org.apache.ranger.audit.utils.SolrAppUtil; +import org.apache.ranger.audit.utils.KerberosAction; +import org.apache.ranger.audit.utils.KerberosUser; +import org.apache.ranger.audit.utils.KerberosJAASConfigUser; import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.impl.HttpClientUtil; @@ -36,6 +36,8 @@ import org.apache.solr.client.solrj.response.UpdateResponse; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrInputDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; @@ -62,14 +64,15 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; +import javax.security.auth.login.LoginException; import java.util.Arrays; import java.util.Optional; public class SolrAuditDestination extends AuditDestination { - private static final Log LOG = LogFactory - .getLog(SolrAuditDestination.class); + private static final Logger LOG = LoggerFactory + .getLogger(SolrAuditDestination.class); public static final String PROP_SOLR_URLS = "urls"; public static final String PROP_SOLR_ZK = "zookeepers"; @@ -80,6 +83,7 @@ public class SolrAuditDestination extends AuditDestination { public static final String PROP_JAVA_SECURITY_AUTH_LOGIN_CONFIG = "java.security.auth.login.config"; private volatile SolrClient solrClient = null; + private volatile KerberosUser kerberosUser = null; public SolrAuditDestination() { } @@ -94,8 +98,28 @@ public void init(Properties props, String propPrefix) { @Override public void stop() { - super.stop(); + LOG.info("SolrAuditDestination.stop() called.."); logStatus(); + + if (solrClient != null) { + try { + solrClient.close(); + } catch (IOException ioe) { + LOG.error("Error while stopping slor!", ioe); + } finally { + solrClient = null; + } + } + + if (kerberosUser != null) { + try { + kerberosUser.logout(); + } catch (LoginException excp) { + LOG.error("Error logging out keytab user", excp); + } finally { + kerberosUser = null; + } + } } synchronized void connect() { @@ -139,98 +163,60 @@ synchronized void connect() { if (zkHosts != null && !zkHosts.isEmpty()) { LOG.info("Connecting to solr cloud using zkHosts=" + zkHosts); - try { - // Instantiate - Krb5HttpClientBuilder krbBuild = new Krb5HttpClientBuilder(); - SolrHttpClientBuilder kb = krbBuild.getBuilder(); - HttpClientUtil.setHttpClientBuilder(kb); - - final List zkhosts = new ArrayList(Arrays.asList(zkHosts.split(","))); - final CloudSolrClient solrCloudClient = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { - @Override - public CloudSolrClient run() throws Exception { - CloudSolrClient solrCloudClient = new CloudSolrClient.Builder(zkhosts, Optional.empty()).build(); - return solrCloudClient; - }; - }); - - solrCloudClient.setDefaultCollection(collectionName); - me = solrClient = solrCloudClient; + try (Krb5HttpClientBuilder krbBuild = new Krb5HttpClientBuilder()) { + SolrHttpClientBuilder kb = krbBuild.getBuilder(); + HttpClientUtil.setHttpClientBuilder(kb); + + final List zkhosts = new ArrayList(Arrays.asList(zkHosts.split(","))); + final CloudSolrClient solrCloudClient = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { + @Override + public CloudSolrClient run() throws Exception { + CloudSolrClient solrCloudClient = new CloudSolrClient.Builder(zkhosts, Optional.empty()).build(); + return solrCloudClient; + } + + ; + }); + + solrCloudClient.setDefaultCollection(collectionName); + me = solrClient = solrCloudClient; } catch (Throwable t) { - LOG.fatal("Can't connect to Solr server. ZooKeepers=" + LOG.error("Can't connect to Solr server. ZooKeepers=" + zkHosts, t); } - finally { - resetInitializerInSOLR(); - } } else if (solrURLs != null && !solrURLs.isEmpty()) { - try { - LOG.info("Connecting to Solr using URLs=" + solrURLs); - Krb5HttpClientBuilder krbBuild = new Krb5HttpClientBuilder(); - SolrHttpClientBuilder kb = krbBuild.getBuilder(); - HttpClientUtil.setHttpClientBuilder(kb); - final List solrUrls = solrURLs; - final LBHttpSolrClient lbSolrClient = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { - @Override - public LBHttpSolrClient run() throws Exception { - LBHttpSolrClient.Builder builder = new LBHttpSolrClient.Builder(); - builder.withBaseSolrUrl(solrUrls.get(0)); - builder.withConnectionTimeout(1000); - LBHttpSolrClient lbSolrClient = builder.build(); - return lbSolrClient; - }; - }); - - for (int i = 1; i < solrURLs.size(); i++) { - lbSolrClient.addSolrServer(solrURLs.get(i)); - } - me = solrClient = lbSolrClient; + try (Krb5HttpClientBuilder krbBuild = new Krb5HttpClientBuilder()) { + LOG.info("Connecting to Solr using URLs=" + solrURLs); + SolrHttpClientBuilder kb = krbBuild.getBuilder(); + HttpClientUtil.setHttpClientBuilder(kb); + final List solrUrls = solrURLs; + final LBHttpSolrClient lbSolrClient = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { + @Override + public LBHttpSolrClient run() throws Exception { + LBHttpSolrClient.Builder builder = new LBHttpSolrClient.Builder(); + builder.withBaseSolrUrl(solrUrls.get(0)); + builder.withConnectionTimeout(1000); + LBHttpSolrClient lbSolrClient = builder.build(); + return lbSolrClient; + } + + ; + }); + + for (int i = 1; i < solrURLs.size(); i++) { + lbSolrClient.addSolrServer(solrURLs.get(i)); + } + me = solrClient = lbSolrClient; } catch (Throwable t) { - LOG.fatal("Can't connect to Solr server. URL=" + LOG.error("Can't connect to Solr server. URL=" + solrURLs, t); } - finally { - resetInitializerInSOLR(); - } } } } } } - - private void resetInitializerInSOLR() { - javax.security.auth.login.Configuration solrConfig = javax.security.auth.login.Configuration.getConfiguration(); - String solrConfigClassName = solrConfig.getClass().getName(); - String solrJassConfigEnd = "SolrJaasConfiguration"; - if (solrConfigClassName.endsWith(solrJassConfigEnd)) { - try { - Field f = solrConfig.getClass().getDeclaredField("initiateAppNames"); - if (f != null) { - f.setAccessible(true); - HashSet val = new HashSet(); - f.set(solrConfig, val); - if ( LOG.isDebugEnabled() ) { - LOG.debug("resetInitializerInSOLR: successfully reset the initiateAppNames"); - } - - } else { - if ( LOG.isDebugEnabled() ) { - LOG.debug("resetInitializerInSOLR: not applying on class [" + solrConfigClassName + "] as it does not have initiateAppNames variable name."); - } - } - } catch (Throwable t) { - logError("resetInitializerInSOLR: Unable to reset SOLRCONFIG.initiateAppNames to be empty", t); - } - } - else { - if ( LOG.isDebugEnabled() ) { - LOG.debug("resetInitializerInSOLR: not applying on class [" + solrConfigClassName + "] as it does not endwith [" + solrJassConfigEnd + "]"); - } - } - - } - @Override public boolean log(Collection events) { boolean ret = false; @@ -255,7 +241,7 @@ public boolean log(Collection events) { docs.add(document); } try { - final UpdateResponse response = SolrAppUtil.addDocsToSolr(solrClient, docs); + final UpdateResponse response = addDocsToSolr(solrClient, docs); if (response.getStatus() != 0) { addFailedCount(events.size()); @@ -309,6 +295,8 @@ SolrInputDocument toSolrDoc(AuthzAuditEvent auditEvent) { doc.setField("event_count", auditEvent.getEventCount()); doc.setField("event_dur_ms", auditEvent.getEventDurationMS()); doc.setField("tags", auditEvent.getTags()); + doc.addField("datasets", auditEvent.getDatasets()); + doc.addField("projects", auditEvent.getProjects()); doc.setField("cluster", auditEvent.getClusterName()); doc.setField("zoneName", auditEvent.getZoneName()); doc.setField("agentHost", auditEvent.getAgentHostname()); @@ -336,12 +324,19 @@ private void init() { LOG.warn("No Client JAAS config present in solr audit config. Ranger Audit to Kerberized Solr will fail..."); } } + LOG.info("Loading SolrClient JAAS config from Ranger audit config if present..."); - InMemoryJAASConfiguration.init(props); - } catch (Exception e) { - LOG.error("ERROR: Unable to load SolrClient JAAS config from Audit config file. Audit to Kerberized Solr will fail...", e); - } - finally { + + InMemoryJAASConfiguration conf = InMemoryJAASConfiguration.init(props); + + KerberosUser kerberosUser = new KerberosJAASConfigUser("Client", conf); + + if (kerberosUser.getPrincipal() != null) { + this.kerberosUser = kerberosUser; + } + } catch (Exception e) { + LOG.error("ERROR: Unable to load SolrClient JAAS config from Audit config file. Audit to Kerberized Solr will fail...", e); + } finally { String confFileName = System.getProperty(PROP_JAVA_SECURITY_AUTH_LOGIN_CONFIG); LOG.info("In solrAuditDestination.init() (finally) : JAAS Configuration set as [" + confFileName + "]"); } @@ -455,6 +450,27 @@ private SSLContext getSSLContext(KeyManager[] kmList, TrustManager[] tmList) { return sslContext; } + private UpdateResponse addDocsToSolr(final SolrClient solrClient, final Collection docs) throws Exception { + final UpdateResponse ret; + + try { + final PrivilegedExceptionAction action = () -> solrClient.add(docs); + + if (kerberosUser != null) { + // execute the privileged action as the given keytab user + final KerberosAction kerberosAction = new KerberosAction<>(kerberosUser, action, LOG); + + ret = (UpdateResponse) kerberosAction.execute(); + } else { + ret = action.run(); + } + } catch (Exception e) { + throw e; + } + + return ret; + } + private InputStream getFileInputStream(String fileName) throws IOException { InputStream in = null; if (StringUtils.isNotEmpty(fileName)) { diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/entity/AuthzAuditEventDbObj.java b/agents-audit/src/main/java/org/apache/ranger/audit/entity/AuthzAuditEventDbObj.java deleted file mode 100644 index 6830e9504d..0000000000 --- a/agents-audit/src/main/java/org/apache/ranger/audit/entity/AuthzAuditEventDbObj.java +++ /dev/null @@ -1,412 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - - package org.apache.ranger.audit.entity; - -import java.io.Serializable; -import java.util.Date; -import java.util.Properties; - -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Table; -import javax.persistence.Temporal; -import javax.persistence.TemporalType; -import javax.persistence.SequenceGenerator; - -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ranger.audit.model.AuthzAuditEvent; -import org.apache.ranger.audit.provider.MiscUtil; - -/** - * Entity implementation class for Entity: AuthzAuditEventDbObj - * - */ -@Entity -@Table(name="xa_access_audit") -public class AuthzAuditEventDbObj implements Serializable { - - private static final Log LOG = LogFactory.getLog(AuthzAuditEventDbObj.class); - - private static final long serialVersionUID = 1L; - - static int MaxValueLengthAccessType = 255; - static int MaxValueLengthAclEnforcer = 255; - static int MaxValueLengthAgentId = 255; - static int MaxValueLengthClientIp = 255; - static int MaxValueLengthClientType = 255; - static int MaxValueLengthRepoName = 255; - static int MaxValueLengthResultReason = 255; - static int MaxValueLengthSessionId = 255; - static int MaxValueLengthRequestUser = 255; - static int MaxValueLengthAction = 2000; - static int MaxValueLengthRequestData = 4000; - static int MaxValueLengthResourcePath = 4000; - static int MaxValueLengthResourceType = 255; - - private long auditId; - private int repositoryType; - private String repositoryName; - private String user; - private Date timeStamp; - private String accessType; - private String resourcePath; - private String resourceType; - private String action; - private int accessResult; - private String agentId; - private long policyId; - private String resultReason; - private String aclEnforcer; - private String sessionId; - private String clientType; - private String clientIP; - private String requestData; - private long seqNum; - private long eventCount; - private long eventDurationMS; - private String tags; - - public static void init(Properties props) - { - LOG.info("AuthzAuditEventDbObj.init()"); - - final String AUDIT_DB_MAX_COLUMN_VALUE = "xasecure.audit.destination.db.max.column.length"; - MaxValueLengthAccessType = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "access_type", MaxValueLengthAccessType); - logMaxColumnValue("access_type", MaxValueLengthAccessType); - - MaxValueLengthAclEnforcer = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "acl_enforcer", MaxValueLengthAclEnforcer); - logMaxColumnValue("acl_enforcer", MaxValueLengthAclEnforcer); - - MaxValueLengthAction = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "action", MaxValueLengthAction); - logMaxColumnValue("action", MaxValueLengthAction); - - MaxValueLengthAgentId = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "agent_id", MaxValueLengthAgentId); - logMaxColumnValue("agent_id", MaxValueLengthAgentId); - - MaxValueLengthClientIp = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "client_id", MaxValueLengthClientIp); - logMaxColumnValue("client_id", MaxValueLengthClientIp); - - MaxValueLengthClientType = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "client_type", MaxValueLengthClientType); - logMaxColumnValue("client_type", MaxValueLengthClientType); - - MaxValueLengthRepoName = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "repo_name", MaxValueLengthRepoName); - logMaxColumnValue("repo_name", MaxValueLengthRepoName); - - MaxValueLengthResultReason = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "result_reason", MaxValueLengthResultReason); - logMaxColumnValue("result_reason", MaxValueLengthResultReason); - - MaxValueLengthSessionId = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "session_id", MaxValueLengthSessionId); - logMaxColumnValue("session_id", MaxValueLengthSessionId); - - MaxValueLengthRequestUser = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "request_user", MaxValueLengthRequestUser); - logMaxColumnValue("request_user", MaxValueLengthRequestUser); - - MaxValueLengthRequestData = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "request_data", MaxValueLengthRequestData); - logMaxColumnValue("request_data", MaxValueLengthRequestData); - - MaxValueLengthResourcePath = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "resource_path", MaxValueLengthResourcePath); - logMaxColumnValue("resource_path", MaxValueLengthResourcePath); - - MaxValueLengthResourceType = MiscUtil.getIntProperty(props, AUDIT_DB_MAX_COLUMN_VALUE + "." + "resource_type", MaxValueLengthResourceType); - logMaxColumnValue("resource_type", MaxValueLengthResourceType); - } - - public static void logMaxColumnValue(String columnName, int configuredMaxValueLength) { - LOG.info("Setting max column value for column[" + columnName + "] to [" + configuredMaxValueLength + "]."); - if (configuredMaxValueLength == 0) { - LOG.info("Max length of column[" + columnName + "] was 0! Column will NOT be emitted in the audit."); - } else if (configuredMaxValueLength < 0) { - LOG.info("Max length of column[" + columnName + "] was less than 0! Column value will never be truncated."); - } - } - - - public AuthzAuditEventDbObj() { - super(); - } - - public AuthzAuditEventDbObj(AuthzAuditEvent event) { - super(); - Date utcDate=null; - if(event.getEventTime()!=null){ - utcDate=MiscUtil.getUTCDateForLocalDate(event.getEventTime()); - }else{ - utcDate=MiscUtil.getUTCDate(); - } - this.repositoryType = event.getRepositoryType(); - this.repositoryName = event.getRepositoryName(); - this.user = event.getUser(); - this.timeStamp = utcDate; - this.accessType = event.getAccessType(); - this.resourcePath = event.getResourcePath(); - this.resourceType = event.getResourceType(); - this.action = event.getAction(); - this.accessResult = event.getAccessResult(); - this.agentId = event.getAgentId(); - this.policyId = event.getPolicyId(); - this.resultReason = event.getResultReason(); - this.aclEnforcer = event.getAclEnforcer(); - this.sessionId = event.getSessionId(); - this.clientType = event.getClientType(); - this.clientIP = event.getClientIP(); - this.requestData = event.getRequestData(); - this.seqNum = event.getSeqNum(); - this.eventCount = event.getEventCount(); - this.eventDurationMS= event.getEventDurationMS(); - this.tags = StringUtils.join(event.getTags(), ", "); - } - - @Id - @SequenceGenerator(name="XA_ACCESS_AUDIT_SEQ",sequenceName="XA_ACCESS_AUDIT_SEQ",allocationSize=1) - @GeneratedValue(strategy=GenerationType.AUTO,generator="XA_ACCESS_AUDIT_SEQ") - @Column(name = "id", unique = true, nullable = false) - public long getAuditId() { - return this.auditId; - } - - public void setAuditId(long auditId) { - this.auditId = auditId; - } - - @Column(name = "repo_type") - public int getRepositoryType() { - return this.repositoryType; - } - - public void setRepositoryType(int repositoryType) { - this.repositoryType = repositoryType; - } - - @Column(name = "repo_name") - public String getRepositoryName() { - return truncate(this.repositoryName, MaxValueLengthRepoName, "repo_name"); - } - - public void setRepositoryName(String repositoryName) { - this.repositoryName = repositoryName; - } - - @Column(name = "request_user") - public String getUser() { - return truncate(this.user, MaxValueLengthRequestUser, "request_user"); - } - - public void setUser(String user) { - this.user = user; - } - - @Temporal(TemporalType.TIMESTAMP) - @Column(name = "event_time") - public Date getTimeStamp() { - return this.timeStamp; - } - - public void setTimeStamp(Date timeStamp) { - this.timeStamp = timeStamp; - } - - @Column(name = "access_type") - public String getAccessType() { - return truncate(this.accessType, MaxValueLengthAccessType, "access_type"); - } - - public void setAccessType(String accessType) { - this.accessType = accessType; - } - - @Column(name = "resource_path") - public String getResourcePath() { - return truncate(this.resourcePath, MaxValueLengthResourcePath, "resource_path"); - } - - public void setResourcePath(String resourcePath) { - this.resourcePath = resourcePath; - } - - @Column(name = "resource_type") - public String getResourceType() { - return truncate(this.resourceType, MaxValueLengthResourceType, "resource_type"); - } - - public void setResourceType(String resourceType) { - this.resourceType = resourceType; - } - - @Column(name = "action") - public String getAction() { - return truncate(this.action, MaxValueLengthAction, "action"); - } - - public void setAction(String action) { - this.action = action; - } - - @Column(name = "access_result") - public int getAccessResult() { - return this.accessResult; - } - - public void setAccessResult(int accessResult) { - this.accessResult = accessResult; - } - - @Column(name = "agent_id") - public String getAgentId() { - return truncate(this.agentId, MaxValueLengthAgentId, "agent_id"); - } - - public void setAgentId(String agentId) { - this.agentId = agentId; - } - - @Column(name = "policy_id") - public long getPolicyId() { - return this.policyId; - } - - public void setPolicyId(long policyId) { - this.policyId = policyId; - } - - @Column(name = "result_reason") - public String getResultReason() { - return truncate(this.resultReason, MaxValueLengthResultReason, "result_reason"); - } - - public void setResultReason(String resultReason) { - this.resultReason = resultReason; - } - - @Column(name = "acl_enforcer") - public String getAclEnforcer() { - return truncate(this.aclEnforcer, MaxValueLengthAclEnforcer, "acl_enforcer"); - } - - public void setAclEnforcer(String aclEnforcer) { - this.aclEnforcer = aclEnforcer; - } - - @Column(name = "session_id") - public String getSessionId() { - return truncate(this.sessionId, MaxValueLengthSessionId, "session_id"); - } - - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - @Column(name = "client_type") - public String getClientType() { - return truncate(this.clientType, MaxValueLengthClientType, "client_type"); - } - - public void setClientType(String clientType) { - this.clientType = clientType; - } - - @Column(name = "client_ip") - public String getClientIP() { - return truncate(this.clientIP, MaxValueLengthClientIp, "client_ip"); - } - - public void setClientIP(String clientIP) { - this.clientIP = clientIP; - } - - @Column(name = "request_data") - public String getRequestData() { - return truncate(this.requestData, MaxValueLengthRequestData, "request_data"); - } - - public void setRequestData(String requestData) { - this.requestData = requestData; - } - - @Column(name = "seq_num") - public long getSeqNum() { return this.seqNum; } - - public void setSeqNum(long seqNum) { this.seqNum = seqNum; } - - @Column(name = "event_count") - public long getEventCount() { return this.eventCount; } - - public void setEventCount(long eventCount) { this.eventCount = eventCount; } - - @Column(name = "event_dur_ms") - public long getEventDurationMS() { return this.eventDurationMS; } - - public void setEventDurationMS(long eventDurationMS) { this.eventDurationMS = eventDurationMS; } - - @Column(name = "tags") - public String getTags() { - return this.tags; - } - - public void setTags(String tags) { - this.tags = tags; - } - - static final String TruncationMarker = "..."; - static final int TruncationMarkerLength = TruncationMarker.length(); - - protected String truncate(String value, int limit, String columnName) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> getTrunctedValue(%s, %d, %s)", value, limit, columnName)); - } - - String result = value; - if (value != null) { - if (limit < 0) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Truncation is suppressed for column[%s]: old value [%s], new value[%s]", columnName, value, result)); - } - } else if (limit == 0) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Column[%s] is to be excluded from audit: old value [%s], new value[%s]", columnName, value, result)); - } - result = null; - } else { - if (value.length() > limit) { - if (limit <= TruncationMarkerLength) { - // NOTE: If value is to be truncated to a size that is less than of equal to the Truncation Marker then we won't put the marker in!! - result = value.substring(0, limit); - } else { - StringBuilder sb = new StringBuilder(value.substring(0, limit - TruncationMarkerLength)); - sb.append(TruncationMarker); - result = sb.toString(); - } - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Truncating value for column[%s] to [%d] characters: old value [%s], new value[%s]", columnName, limit, value, result)); - } - } - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== getTrunctedValue(%s, %d, %s): %s", value, limit, columnName, result)); - } - return result; - } -} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditEventBase.java b/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditEventBase.java index b5791467af..84eaebec9c 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditEventBase.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditEventBase.java @@ -21,15 +21,11 @@ import java.util.Date; -import org.apache.ranger.audit.dao.DaoManager; - public abstract class AuditEventBase { protected AuditEventBase() { } - public abstract void persist(DaoManager daoManager); - public abstract String getEventKey(); public abstract Date getEventTime (); public abstract void setEventCount(long eventCount); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditIndexRecord.java b/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditIndexRecord.java new file mode 100644 index 0000000000..d0ea2b9087 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/model/AuditIndexRecord.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.model; + +import java.util.Date; + +public class AuditIndexRecord { + String id; + String filePath; + int linePosition = 0; + SPOOL_FILE_STATUS status = SPOOL_FILE_STATUS.write_inprogress; + Date fileCreateTime; + Date writeCompleteTime; + Date doneCompleteTime; + Date lastSuccessTime; + Date lastFailedTime; + int failedAttemptCount = 0; + boolean lastAttempt = false; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public int getLinePosition() { + return linePosition; + } + + public void setLinePosition(int linePosition) { + this.linePosition = linePosition; + } + + public SPOOL_FILE_STATUS getStatus() { + return status; + } + + public void setStatus(SPOOL_FILE_STATUS status) { + this.status = status; + } + + public Date getFileCreateTime() { + return fileCreateTime; + } + + public void setFileCreateTime(Date fileCreateTime) { + this.fileCreateTime = fileCreateTime; + } + + public Date getWriteCompleteTime() { + return writeCompleteTime; + } + + public void setWriteCompleteTime(Date writeCompleteTime) { + this.writeCompleteTime = writeCompleteTime; + } + + public Date getDoneCompleteTime() { + return doneCompleteTime; + } + + public void setDoneCompleteTime(Date doneCompleteTime) { + this.doneCompleteTime = doneCompleteTime; + } + + public Date getLastSuccessTime() { + return lastSuccessTime; + } + + public void setLastSuccessTime(Date lastSuccessTime) { + this.lastSuccessTime = lastSuccessTime; + } + + public Date getLastFailedTime() { + return lastFailedTime; + } + + public void setLastFailedTime(Date lastFailedTime) { + this.lastFailedTime = lastFailedTime; + } + + public int getFailedAttemptCount() { + return failedAttemptCount; + } + + public void setFailedAttemptCount(int failedAttemptCount) { + this.failedAttemptCount = failedAttemptCount; + } + + public boolean getLastAttempt() { + return lastAttempt; + } + + public void setLastAttempt(boolean lastAttempt) { + this.lastAttempt = lastAttempt; + } + + @Override + public String toString() { + return "AuditIndexRecord [id=" + id + ", filePath=" + filePath + + ", linePosition=" + linePosition + ", status=" + status + + ", fileCreateTime=" + fileCreateTime + + ", writeCompleteTime=" + writeCompleteTime + + ", doneCompleteTime=" + doneCompleteTime + + ", lastSuccessTime=" + lastSuccessTime + + ", lastFailedTime=" + lastFailedTime + + ", failedAttemptCount=" + failedAttemptCount + + ", lastAttempt=" + lastAttempt + "]"; + } +} + + diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/model/AuthzAuditEvent.java b/agents-audit/src/main/java/org/apache/ranger/audit/model/AuthzAuditEvent.java index 28db58cd96..5ed88eb6ab 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/model/AuthzAuditEvent.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/model/AuthzAuditEvent.java @@ -23,105 +23,112 @@ import java.util.HashSet; import java.util.Set; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.apache.commons.lang.StringUtils; -import org.apache.ranger.audit.dao.DaoManager; -import org.apache.ranger.audit.entity.AuthzAuditEventDbObj; -import com.google.gson.annotations.SerializedName; +import com.fasterxml.jackson.annotation.JsonProperty; +@JsonSerialize public class AuthzAuditEvent extends AuditEventBase { protected static String FIELD_SEPARATOR = ";"; protected static final int MAX_ACTION_FIELD_SIZE = 1800; protected static final int MAX_REQUEST_DATA_FIELD_SIZE = 1800; - @SerializedName("repoType") + @JsonProperty("repoType") protected int repositoryType = 0; - @SerializedName("repo") + @JsonProperty("repo") protected String repositoryName = null; - @SerializedName("reqUser") + @JsonProperty("reqUser") protected String user = null; - @SerializedName("evtTime") + @JsonProperty("evtTime") protected Date eventTime = new Date(); - @SerializedName("access") + @JsonProperty("access") protected String accessType = null; - @SerializedName("resource") + @JsonProperty("resource") protected String resourcePath = null; - @SerializedName("resType") + @JsonProperty("resType") protected String resourceType = null; - @SerializedName("action") + @JsonProperty("action") protected String action = null; - @SerializedName("result") + @JsonProperty("result") protected short accessResult = 0; // 0 - DENIED; 1 - ALLOWED; HTTP return // code - @SerializedName("agent") + @JsonProperty("agent") protected String agentId = null; - @SerializedName("policy") + @JsonProperty("policy") protected long policyId = 0; - @SerializedName("reason") + @JsonProperty("reason") protected String resultReason = null; - @SerializedName("enforcer") + @JsonProperty("enforcer") protected String aclEnforcer = null; - @SerializedName("sess") + @JsonProperty("sess") protected String sessionId = null; - @SerializedName("cliType") + @JsonProperty("cliType") protected String clientType = null; - @SerializedName("cliIP") + @JsonProperty("cliIP") protected String clientIP = null; - @SerializedName("reqData") + @JsonProperty("reqData") protected String requestData = null; - @SerializedName("agentHost") + @JsonProperty("agentHost") protected String agentHostname = null; - @SerializedName("logType") + @JsonProperty("logType") protected String logType = null; - @SerializedName("id") + @JsonProperty("id") protected String eventId = null; /** * This to ensure order within a session. Order not guaranteed across * processes and hosts */ - @SerializedName("seq_num") + @JsonProperty("seq_num") protected long seqNum = 0; - @SerializedName("event_count") + @JsonProperty("event_count") protected long eventCount = 1; - @SerializedName("event_dur_ms") + @JsonProperty("event_dur_ms") protected long eventDurationMS = 0; - @SerializedName("tags") + @JsonProperty("tags") protected Set tags = new HashSet<>(); - @SerializedName("additional_info") + @JsonProperty("datasets") + protected Set datasets = null; + + @JsonProperty("projects") + protected Set projects = null; + + @JsonProperty("additional_info") protected String additionalInfo; - @SerializedName("cluster_name") + @JsonProperty("cluster_name") protected String clusterName; - @SerializedName("zone_name") + @JsonProperty("zone_name") protected String zoneName; - @SerializedName("policy_version") + @JsonProperty("policy_version") protected Long policyVersion; public AuthzAuditEvent() { @@ -474,18 +481,34 @@ public long getEventDurationMS() { return eventDurationMS; } - public Set getTags() { - return tags; - } - public void setEventDurationMS(long frequencyDurationMS) { this.eventDurationMS = frequencyDurationMS; } + public Set getTags() { + return tags; + } + public void setTags(Set tags) { this.tags = tags; } + public Set getDatasets() { + return datasets; + } + + public void setDatasets(Set datasets) { + this.datasets = datasets; + } + + public Set getProjects() { + return projects; + } + + public void setProjects(Set projects) { + this.projects = projects; + } + public String getClusterName() { return clusterName; } @@ -514,6 +537,7 @@ public void setClusterName(String clusterName) { public void setAdditionalInfo(String additionalInfo) { this.additionalInfo = additionalInfo; } + @JsonIgnore @Override public String getEventKey() { String key = user + "^" + accessType + "^" + resourcePath + "^" @@ -563,10 +587,9 @@ protected StringBuilder toString(StringBuilder sb) { .append(FIELD_SEPARATOR).append("event_count=") .append(eventCount).append(FIELD_SEPARATOR) .append("event_dur_ms=").append(eventDurationMS) - .append(FIELD_SEPARATOR) - .append("tags=").append("[") - .append(StringUtils.join(tags, ", ")) - .append("]") + .append(FIELD_SEPARATOR).append("tags=").append("[").append(StringUtils.join(tags, ", ")).append("]") + .append(FIELD_SEPARATOR).append("datasets=").append("[").append(datasets != null ? StringUtils.join(datasets, ", ") : "").append("]") + .append(FIELD_SEPARATOR).append("projects=").append("[").append(projects != null ? StringUtils.join(projects, ", ") : "").append("]") .append(FIELD_SEPARATOR).append("clusterName=").append(clusterName) .append(FIELD_SEPARATOR).append("zoneName=").append(zoneName) .append(FIELD_SEPARATOR).append("policyVersion=").append(policyVersion) @@ -574,10 +597,4 @@ protected StringBuilder toString(StringBuilder sb) { return sb; } - - @Override - public void persist(DaoManager daoManager) { - daoManager.getAuthzAuditEventDao().create( - new AuthzAuditEventDbObj(this)); - } } diff --git a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/PolicyMgrUserGroupBuilder.java b/agents-audit/src/main/java/org/apache/ranger/audit/model/SPOOL_FILE_STATUS.java similarity index 71% rename from ugsync/src/main/java/org/apache/ranger/ldapusersync/process/PolicyMgrUserGroupBuilder.java rename to agents-audit/src/main/java/org/apache/ranger/audit/model/SPOOL_FILE_STATUS.java index d27518119a..3765c9c923 100644 --- a/ugsync/src/main/java/org/apache/ranger/ldapusersync/process/PolicyMgrUserGroupBuilder.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/model/SPOOL_FILE_STATUS.java @@ -17,13 +17,8 @@ * under the License. */ -package org.apache.ranger.ldapusersync.process; +package org.apache.ranger.audit.model; -public class PolicyMgrUserGroupBuilder extends org.apache.ranger.unixusersync.process.PolicyMgrUserGroupBuilder { - - public static void main(String[] args) throws Throwable { - PolicyMgrUserGroupBuilder ugbuilder = new PolicyMgrUserGroupBuilder(); - ugbuilder.init(); - - } +public enum SPOOL_FILE_STATUS { + pending, write_inprogress, read_inprogress, done } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AsyncAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AsyncAuditProvider.java index c74a3eac05..2a3ec77b6a 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AsyncAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AsyncAuditProvider.java @@ -25,14 +25,14 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class AsyncAuditProvider extends MultiDestAuditProvider implements Runnable { - private static final Log LOG = LogFactory.getLog(AsyncAuditProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(AsyncAuditProvider.class); private static int sThreadCount = 0; @@ -120,7 +120,7 @@ public void stop() { while (mThread.isAlive()) { try { LOG.info(String.format("Waiting for child thread of %s to exit. Sleeping for %d secs", mName, mStopLoopIntervalSecs)); - mThread.join(mStopLoopIntervalSecs * 1000); + mThread.join(mStopLoopIntervalSecs * 1000L); } catch (InterruptedException e) { LOG.warn("Interrupted while waiting for child thread to join! Proceeding with stop", e); break; @@ -255,7 +255,7 @@ public void waitToComplete(long maxWaitSeconds) { && (maxWaitSeconds <= 0 || maxWaitSeconds > waitTime); waitTime += mWaitToCompleteLoopIntervalSecs) { try { LOG.info(String.format("%d messages yet to be flushed by %s. Sleeoping for %d sec", mQueue.size(), mName, mWaitToCompleteLoopIntervalSecs)); - Thread.sleep(mWaitToCompleteLoopIntervalSecs * 1000); + Thread.sleep(mWaitToCompleteLoopIntervalSecs * 1000L); } catch (InterruptedException excp) { // someone really wants service to exit, abandon unwritten audits and exit. LOG.warn("Caught interrupted exception! " + mQueue.size() + " messages still unflushed! Won't wait for queue to flush, exiting...", excp); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditFileCacheProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditFileCacheProvider.java index 8e3dd720d1..7957f2e10e 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditFileCacheProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditFileCacheProvider.java @@ -18,10 +18,10 @@ */ package org.apache.ranger.audit.provider; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.queue.AuditFileCacheProviderSpool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Properties; @@ -31,7 +31,7 @@ */ public class AuditFileCacheProvider extends BaseAuditHandler { - private static final Log logger = LogFactory.getLog(AuditFileCacheProvider.class); + private static final Logger logger = LoggerFactory.getLogger(AuditFileCacheProvider.class); AuditFileCacheProviderSpool fileSpooler = null; AuditHandler consumer = null; diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java index 4ce31dd099..dd02255fc9 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditHandler.java @@ -18,6 +18,7 @@ package org.apache.ranger.audit.provider; +import java.io.File; import java.util.Collection; import java.util.Properties; @@ -28,7 +29,8 @@ public interface AuditHandler { boolean log(Collection events); boolean logJSON(String event); - boolean logJSON(Collection events); + boolean logJSON(Collection events); + boolean logFile(File file); void init(Properties prop); void init(Properties prop, String basePropertyName); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java index 1be9c2f86a..c10dd9ffaa 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditProviderFactory.java @@ -19,14 +19,13 @@ package org.apache.ranger.audit.provider; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.util.ShutdownHookManager; import org.apache.ranger.audit.destination.*; import org.apache.ranger.audit.provider.hdfs.HdfsAuditProvider; @@ -34,8 +33,11 @@ import org.apache.ranger.audit.provider.solr.SolrAuditProvider; import org.apache.ranger.audit.queue.AuditAsyncQueue; import org.apache.ranger.audit.queue.AuditBatchQueue; +import org.apache.ranger.audit.queue.AuditFileQueue; import org.apache.ranger.audit.queue.AuditQueue; import org.apache.ranger.audit.queue.AuditSummaryQueue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /* * TODO: @@ -45,11 +47,10 @@ */ public class AuditProviderFactory { - private static final Log LOG = LogFactory - .getLog(AuditProviderFactory.class); + private static final Logger LOG = LoggerFactory + .getLogger(AuditProviderFactory.class); public static final String AUDIT_IS_ENABLED_PROP = "xasecure.audit.is.enabled"; - public static final String AUDIT_DB_IS_ENABLED_PROP = "xasecure.audit.db.is.enabled"; public static final String AUDIT_HDFS_IS_ENABLED_PROP = "xasecure.audit.hdfs.is.enabled"; public static final String AUDIT_LOG4J_IS_ENABLED_PROP = "xasecure.audit.log4j.is.enabled"; public static final String AUDIT_KAFKA_IS_ENABLED_PROP = "xasecure.audit.kafka.is.enabled"; @@ -58,6 +59,8 @@ public class AuditProviderFactory { public static final String AUDIT_DEST_BASE = "xasecure.audit.destination"; public static final String AUDIT_SHUTDOWN_HOOK_MAX_WAIT_SEC = "xasecure.audit.shutdown.hook.max.wait.seconds"; public static final String AUDIT_IS_FILE_CACHE_PROVIDER_ENABLE_PROP = "xasecure.audit.provider.filecache.is.enabled"; + public static final String FILE_QUEUE_TYPE = "filequeue"; + public static final String DEFAULT_QUEUE_TYPE = "memoryqueue"; public static final int AUDIT_SHUTDOWN_HOOK_MAX_WAIT_SEC_DEFAULT = 30; public static final int AUDIT_ASYNC_MAX_QUEUE_SIZE_DEFAULT = 10 * 1024; @@ -71,6 +74,7 @@ public class AuditProviderFactory { private String componentAppType = ""; private boolean mInitDone = false; private JVMShutdownHook jvmShutdownHook = null; + private ArrayList hbaseAppTypes = new ArrayList<>(Arrays.asList("hbaseMaster","hbaseRegional")); public AuditProviderFactory() { LOG.info("AuditProviderFactory: creating.."); @@ -128,8 +132,6 @@ public synchronized void init(Properties props, String appType) { return; } - boolean isAuditToDbEnabled = MiscUtil.getBooleanProperty(props, - AUDIT_DB_IS_ENABLED_PROP, false); boolean isAuditToHdfsEnabled = MiscUtil.getBooleanProperty(props, AUDIT_HDFS_IS_ENABLED_PROP, false); boolean isAuditToLog4jEnabled = MiscUtil.getBooleanProperty(props, @@ -200,13 +202,13 @@ public synchronized void init(Properties props, String appType) { qProvider.init(props, queuePropPrefix); providers.add(queueProvider); } else { - LOG.fatal("Provider queue doesn't extend AuditQueue. Destination=" + LOG.error("Provider queue doesn't extend AuditQueue. Destination=" + destName + " can't be created. queueName=" + queueName); } } else { - LOG.fatal("Queue provider for destination " + destName + LOG.error("Queue provider for destination " + destName + " can't be created. queueName=" + queueName); } } else { @@ -278,7 +280,7 @@ public synchronized void init(Properties props, String appType) { } else { LOG.info("No v3 audit configuration found. Trying v2 audit configurations"); if (!isEnabled - || !(isAuditToDbEnabled || isAuditToHdfsEnabled + || !(isAuditToHdfsEnabled || isAuditToKafkaEnabled || isAuditToLog4jEnabled || isAuditToSolrEnabled || providers.size() == 0)) { LOG.info("AuditProviderFactory: Audit not enabled.."); @@ -288,31 +290,6 @@ public synchronized void init(Properties props, String appType) { return; } - if (isAuditToDbEnabled) { - LOG.info("DbAuditProvider is enabled"); - DbAuditProvider dbProvider = new DbAuditProvider(); - - boolean isAuditToDbAsync = MiscUtil.getBooleanProperty(props, - DbAuditProvider.AUDIT_DB_IS_ASYNC_PROP, false); - - if (isAuditToDbAsync) { - int maxQueueSize = MiscUtil.getIntProperty(props, - DbAuditProvider.AUDIT_DB_MAX_QUEUE_SIZE_PROP, - AUDIT_ASYNC_MAX_QUEUE_SIZE_DEFAULT); - int maxFlushInterval = MiscUtil.getIntProperty(props, - DbAuditProvider.AUDIT_DB_MAX_FLUSH_INTERVAL_PROP, - AUDIT_ASYNC_MAX_FLUSH_INTERVAL_DEFAULT); - - AsyncAuditProvider asyncProvider = new AsyncAuditProvider( - "DbAuditProvider", maxQueueSize, maxFlushInterval, - dbProvider); - - providers.add(asyncProvider); - } else { - providers.add(dbProvider); - } - } - if (isAuditToHdfsEnabled) { LOG.info("HdfsAuditProvider is enabled"); @@ -411,7 +388,7 @@ public synchronized void init(Properties props, String appType) { mProvider.start(); } - installJvmSutdownHook(props); + installJvmShutdownHook(props); } private AuditHandler getProviderFromConfig(Properties props, @@ -431,7 +408,7 @@ private AuditHandler getProviderFromConfig(Properties props, .newInstance(); } } catch (Exception e) { - LOG.fatal("Can't instantiate audit class for providerName=" + LOG.error("Can't instantiate audit class for providerName=" + providerName + ", className=" + className + ", propertyPrefix=" + propPrefix, e); } @@ -444,14 +421,14 @@ private AuditHandler getProviderFromConfig(Properties props, provider = new SolrAuditDestination(); } else if (providerName.equalsIgnoreCase("elasticsearch")) { provider = new ElasticSearchAuditDestination(); + } else if (providerName.equalsIgnoreCase("amazon_cloudwatch")) { + provider = new AmazonCloudWatchAuditDestination(); } else if (providerName.equalsIgnoreCase("kafka")) { provider = new KafkaAuditProvider(); - } else if (providerName.equalsIgnoreCase("db")) { - provider = new DBAuditDestination(); } else if (providerName.equalsIgnoreCase("log4j")) { provider = new Log4JAuditDestination(); } else if (providerName.equalsIgnoreCase("batch")) { - provider = new AuditBatchQueue(consumer); + provider = getAuditProvider(props, propPrefix, consumer); } else if (providerName.equalsIgnoreCase("async")) { provider = new AuditAsyncQueue(consumer); } else { @@ -461,7 +438,7 @@ private AuditHandler getProviderFromConfig(Properties props, } if (provider != null && provider instanceof AuditQueue) { if (consumer == null) { - LOG.fatal("consumer can't be null for AuditQueue. queue=" + LOG.error("consumer can't be null for AuditQueue. queue=" + provider.getName() + ", propertyPrefix=" + propPrefix); provider = null; } @@ -469,14 +446,41 @@ private AuditHandler getProviderFromConfig(Properties props, return provider; } + private AuditHandler getAuditProvider(Properties props, String propPrefix, AuditHandler consumer) { + AuditHandler ret = null; + String queueType = MiscUtil.getStringProperty(props, propPrefix + "." + "queuetype", DEFAULT_QUEUE_TYPE); + + if (LOG.isDebugEnabled()) { + LOG.debug("==> AuditProviderFactory.getAuditProvider() propPerfix= " + propPrefix + ", " + " queueType= " + queueType); + } + + if (FILE_QUEUE_TYPE.equalsIgnoreCase(queueType)) { + AuditFileQueue auditFileQueue = new AuditFileQueue(consumer); + String propPrefixFileQueue = propPrefix + "." + FILE_QUEUE_TYPE; + auditFileQueue.init(props, propPrefixFileQueue); + ret = new AuditBatchQueue(auditFileQueue); + } else { + ret = new AuditBatchQueue(consumer); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== AuditProviderFactory.getAuditProvider()"); + } + + return ret; + } + private AuditHandler getDefaultProvider() { return new DummyAuditProvider(); } - private void installJvmSutdownHook(Properties props) { + private void installJvmShutdownHook(Properties props) { int shutdownHookMaxWaitSeconds = MiscUtil.getIntProperty(props, AUDIT_SHUTDOWN_HOOK_MAX_WAIT_SEC, AUDIT_SHUTDOWN_HOOK_MAX_WAIT_SEC_DEFAULT); jvmShutdownHook = new JVMShutdownHook(mProvider, shutdownHookMaxWaitSeconds); - ShutdownHookManager.get().addShutdownHook(jvmShutdownHook, RANGER_AUDIT_SHUTDOWN_HOOK_PRIORITY); + String appType = this.componentAppType; + if (appType != null && !hbaseAppTypes.contains(appType)) { + ShutdownHookManager.get().addShutdownHook(jvmShutdownHook, RANGER_AUDIT_SHUTDOWN_HOOK_PRIORITY); + } } private static class RangerAsyncAuditCleanup implements Runnable { @@ -498,7 +502,7 @@ public void run() { try { startCleanup.acquire(); } catch (InterruptedException e) { - LOG.info("RangerAsyncAuditCleanup: Interrupted while waiting for audit startCleanup signal! Exiting the thread...", e); + LOG.error("RangerAsyncAuditCleanup: Interrupted while waiting for audit startCleanup signal! Exiting the thread...", e); break; } LOG.info("RangerAsyncAuditCleanup: Starting cleanup"); @@ -543,7 +547,7 @@ public void run() { LOG.warn("JVMShutdownHook: could not detect finishing of audit cleanup even after waiting for " + maxWait + " seconds!"); } } catch (InterruptedException e) { - LOG.info("JVMShutdownHook: Interrupted while waiting for completion of Async executor!", e); + LOG.error("JVMShutdownHook: Interrupted while waiting for completion of Async executor!", e); } LOG.info("JVMShutdownHook: Interrupting ranger async audit cleanup thread"); cleanupThread.interrupt(); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditWriterFactory.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditWriterFactory.java new file mode 100644 index 0000000000..38844c95d3 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/AuditWriterFactory.java @@ -0,0 +1,116 @@ +package org.apache.ranger.audit.provider; + +/* + * 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 org.apache.commons.lang.StringUtils; +import org.apache.ranger.audit.utils.RangerAuditWriter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.Properties; + +public class AuditWriterFactory { + private static final Logger logger = LoggerFactory.getLogger(AuditWriterFactory.class); + public static final String AUDIT_FILETYPE_DEFAULT = "json"; + public static final String AUDIT_JSON_FILEWRITER_IMPL = "org.apache.ranger.audit.utils.RangerJSONAuditWriter"; + public static final String AUDIT_ORC_FILEWRITER_IMPL = "org.apache.ranger.audit.utils.RangerORCAuditWriter"; + + public Map auditConfigs = null; + public Properties props = null; + public String propPrefix = null; + public String auditProviderName = null; + public RangerAuditWriter auditWriter = null; + private static volatile AuditWriterFactory me = null; + + public static AuditWriterFactory getInstance() { + AuditWriterFactory auditWriter = me; + if (auditWriter == null) { + synchronized (AuditWriterFactory.class) { + auditWriter = me; + if (auditWriter == null) { + me = auditWriter = new AuditWriterFactory(); + } + } + } + return auditWriter; + } + + public void init(Properties props, String propPrefix, String auditProviderName, Map auditConfigs) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> AuditWriterFactory.init()"); + } + this.props = props; + this.propPrefix = propPrefix; + this.auditProviderName = auditProviderName; + this.auditConfigs = auditConfigs; + String auditFileType = MiscUtil.getStringProperty(props, propPrefix + ".batch.filequeue.filetype", AUDIT_FILETYPE_DEFAULT); + String writerClass = MiscUtil.getStringProperty(props, propPrefix + ".filewriter.impl"); + + auditWriter = StringUtils.isEmpty(writerClass) ? createWriter(getDefaultWriter(auditFileType)) : createWriter(writerClass); + + if (auditWriter != null) { + auditWriter.init(props, propPrefix, auditProviderName, auditConfigs); + if (logger.isDebugEnabled()) { + logger.debug("<== AuditWriterFactory.init() :" + auditWriter.getClass().getName()); + } + } + } + + public RangerAuditWriter createWriter(String writerClass) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> AuditWriterFactory.createWriter()"); + } + RangerAuditWriter ret = null; + try { + Class cls = (Class) Class.forName(writerClass); + ret = cls.newInstance(); + } catch (Exception e) { + throw e; + } + if (logger.isDebugEnabled()) { + logger.debug("<== AuditWriterFactory.createWriter()"); + } + return ret; + } + + public String getDefaultWriter(String auditFileType) { + if (logger.isDebugEnabled()) { + logger.debug("==> AuditWriterFactory.getDefaultWriter()"); + } + String ret = null; + switch (auditFileType) { + case "orc": + ret = AUDIT_ORC_FILEWRITER_IMPL; + break; + case "json": + ret = AUDIT_JSON_FILEWRITER_IMPL; + break; + } + if (logger.isDebugEnabled()) { + logger.debug("<== AuditWriterFactory.getDefaultWriter() :" + ret); + } + return ret; + } + + public RangerAuditWriter getAuditWriter(){ + return this.auditWriter; + } +} \ No newline at end of file diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java index 6138ca0eb7..94c6d754b0 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/BaseAuditHandler.java @@ -18,13 +18,12 @@ */ package org.apache.ranger.audit.provider; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.gson.GsonBuilder; - +import java.io.File; import java.util.*; import java.util.concurrent.atomic.AtomicLong; @@ -32,10 +31,14 @@ import javax.net.ssl.TrustManagerFactory; public abstract class BaseAuditHandler implements AuditHandler { - private static final Log LOG = LogFactory.getLog(BaseAuditHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(BaseAuditHandler.class); static final String AUDIT_LOG_FAILURE_REPORT_MIN_INTERVAL_PROP = "xasecure.audit.log.failure.report.min.interval.ms"; - protected static final String AUDIT_DB_CREDENTIAL_PROVIDER_FILE = "xasecure.audit.credential.provider.file"; + + static final String AUDIT_LOG_STATUS_LOG_ENABLED = "xasecure.audit.log.status.log.enabled"; + static final String AUDIT_LOG_STATUS_LOG_INTERVAL_SEC = "xasecure.audit.log.status.log.interval.sec"; + static final boolean DEFAULT_AUDIT_LOG_STATUS_LOG_ENABLED = false; + static final long DEFAULT_AUDIT_LOG_STATUS_LOG_INTERVAL_SEC = 5L * 60; // 5 minutes public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE = "xasecure.policymgr.clientssl.keystore"; public static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE = "xasecure.policymgr.clientssl.keystore.type"; @@ -51,11 +54,11 @@ public abstract class BaseAuditHandler implements AuditHandler { public static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); public static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); - public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLS"; + public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2"; public static final String PROP_CONFIG = "config"; - - private int mLogFailureReportMinIntervalInMs = 60 * 1000; + public static final String FAILED_TO_LOG_AUDIT_EVENT = "failed to log audit event: {}"; + private int mLogFailureReportMinIntervalInMs = 60 * 1000; private AtomicLong mFailedLogLastReportTime = new AtomicLong(0); private AtomicLong mFailedLogCountSinceLastReport = new AtomicLong(0); @@ -89,11 +92,13 @@ public abstract class BaseAuditHandler implements AuditHandler { long lastStashedCount = 0; long lastDeferredCount = 0; - long lastStatusLogTime = System.currentTimeMillis(); - long statusLogIntervalMS = 1 * 60 * 1000; + boolean statusLogEnabled = DEFAULT_AUDIT_LOG_STATUS_LOG_ENABLED; + long statusLogIntervalMS = DEFAULT_AUDIT_LOG_STATUS_LOG_INTERVAL_SEC * 1000; + long lastStatusLogTime = System.currentTimeMillis(); + long nextStatusLogTime = lastStatusLogTime + statusLogIntervalMS; protected Properties props = null; - protected Map configProps = new HashMap(); + protected Map configProps = new HashMap<>(); @Override public void init(Properties props) { @@ -126,17 +131,22 @@ public void init(Properties props, String basePropertyName) { } LOG.info("providerName=" + getName()); - try { - new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create(); - } catch (Throwable excp) { - LOG.warn( - "Log4jAuditProvider.init(): failed to create GsonBuilder object. events will be formated using toString(), instead of Json", - excp); - } - mLogFailureReportMinIntervalInMs = MiscUtil.getIntProperty(props, AUDIT_LOG_FAILURE_REPORT_MIN_INTERVAL_PROP, 60 * 1000); + boolean globalStatusLogEnabled = MiscUtil.getBooleanProperty(props, AUDIT_LOG_STATUS_LOG_ENABLED, DEFAULT_AUDIT_LOG_STATUS_LOG_ENABLED); + long globalStatusLogIntervalSec = MiscUtil.getLongProperty(props, AUDIT_LOG_STATUS_LOG_INTERVAL_SEC, DEFAULT_AUDIT_LOG_STATUS_LOG_INTERVAL_SEC); + + statusLogEnabled = MiscUtil.getBooleanProperty(props, basePropertyName + ".status.log.enabled", globalStatusLogEnabled); + statusLogIntervalMS = MiscUtil.getLongProperty(props, basePropertyName + ".status.log.interval.sec", globalStatusLogIntervalSec) * 1000; + + nextStatusLogTime = lastStatusLogTime + statusLogIntervalMS; + + LOG.info(AUDIT_LOG_STATUS_LOG_ENABLED + "=" + globalStatusLogEnabled); + LOG.info(AUDIT_LOG_STATUS_LOG_INTERVAL_SEC + "=" + globalStatusLogIntervalSec); + LOG.info(basePropertyName + ".status.log.enabled=" + statusLogEnabled); + LOG.info(basePropertyName + ".status.log.interval.sec=" + (statusLogIntervalMS / 1000)); + String configPropsNamePrefix = propPrefix + "." + PROP_CONFIG + "."; for (Object propNameObj : props.keySet()) { String propName = propNameObj.toString(); @@ -185,13 +195,18 @@ public boolean logJSON(String event) { */ @Override public boolean logJSON(Collection events) { - List eventList = new ArrayList(events.size()); + List eventList = new ArrayList<>(events.size()); for (String event : events) { eventList.add(MiscUtil.fromJson(event, AuthzAuditEvent.class)); } return log(eventList); } + @Override + public boolean logFile(File file) { + return logFile(file); + } + public String getParentPath() { return parentPath; } @@ -269,9 +284,10 @@ public long getLastDeferredCount() { return lastDeferredCount; } + public boolean isStatusLogEnabled() { return statusLogEnabled; } + public void logStatusIfRequired() { - long currTime = System.currentTimeMillis(); - if ((currTime - lastStatusLogTime) > statusLogIntervalMS) { + if (System.currentTimeMillis() > nextStatusLogTime) { logStatus(); } } @@ -279,9 +295,10 @@ public void logStatusIfRequired() { public void logStatus() { try { long currTime = System.currentTimeMillis(); - long diffTime = currTime - lastStatusLogTime; + lastStatusLogTime = currTime; + nextStatusLogTime = currTime + statusLogIntervalMS; long diffCount = totalCount - lastIntervalCount; long diffSuccess = totalSuccessCount - lastIntervalSuccessCount; @@ -300,45 +317,50 @@ public void logStatus() { lastStashedCount = totalStashedCount; lastDeferredCount = totalDeferredCount; - String finalPath = ""; - String tFinalPath = getFinalPath(); - if (!getName().equals(tFinalPath)) { - finalPath = ", finalDestination=" + tFinalPath; - } + if (statusLogEnabled) { + String finalPath = ""; + String tFinalPath = getFinalPath(); + if (!getName().equals(tFinalPath)) { + finalPath = ", finalDestination=" + tFinalPath; + } - String msg = "Audit Status Log: name=" - + getName() - + finalPath - + ", interval=" - + formatIntervalForLog(diffTime) - + ", events=" - + diffCount - + (diffSuccess > 0 ? (", succcessCount=" + diffSuccess) - : "") - + (diffFailed > 0 ? (", failedCount=" + diffFailed) : "") - + (diffStashed > 0 ? (", stashedCount=" + diffStashed) : "") - + (diffDeferred > 0 ? (", deferredCount=" + diffDeferred) - : "") - + ", totalEvents=" - + totalCount - + (totalSuccessCount > 0 ? (", totalSuccessCount=" + totalSuccessCount) - : "") - + (totalFailedCount > 0 ? (", totalFailedCount=" + totalFailedCount) - : "") - + (totalStashedCount > 0 ? (", totalStashedCount=" + totalStashedCount) - : "") - + (totalDeferredCount > 0 ? (", totalDeferredCount=" + totalDeferredCount) - : ""); - LOG.info(msg); - } catch (Throwable t) { + logAuditStatus(diffTime, diffCount, diffSuccess, diffFailed, diffStashed, diffDeferred, finalPath); + } + } catch (Exception t) { LOG.error("Error while printing stats. auditProvider=" + getName()); } } - - public void logError(String msg) { + private void logAuditStatus(long diffTime, long diffCount, long diffSuccess, long diffFailed, long diffStashed, long diffDeferred, String finalPath) { + String msg = "Audit Status Log: name=" + + getName() + + finalPath + + ", interval=" + + formatIntervalForLog(diffTime) + + ", events=" + + diffCount + + (diffSuccess > 0 ? (", succcessCount=" + diffSuccess) + : "") + + (diffFailed > 0 ? (", failedCount=" + diffFailed) : "") + + (diffStashed > 0 ? (", stashedCount=" + diffStashed) : "") + + (diffDeferred > 0 ? (", deferredCount=" + diffDeferred) + : "") + + ", totalEvents=" + + totalCount + + (totalSuccessCount > 0 ? (", totalSuccessCount=" + totalSuccessCount) + : "") + + (totalFailedCount > 0 ? (", totalFailedCount=" + totalFailedCount) + : "") + + (totalStashedCount > 0 ? (", totalStashedCount=" + totalStashedCount) + : "") + + (totalDeferredCount > 0 ? (", totalDeferredCount=" + totalDeferredCount) + : ""); + LOG.info(msg); + } + + public void logError(String msg, Object arg) { long currTimeMS = System.currentTimeMillis(); if (currTimeMS - lastErrorLogMS > errorLogIntervalMS) { - LOG.error(msg); + LOG.error(msg, arg); lastErrorLogMS = currTimeMS; } } @@ -391,19 +413,13 @@ public void logFailedEvent(AuditEventBase event, Throwable excp) { mFailedLogCountSinceLastReport.set(0); if (excp != null) { - LOG.warn( - "failed to log audit event: " - + MiscUtil.stringify(event), excp); + LOG.warn(FAILED_TO_LOG_AUDIT_EVENT, MiscUtil.stringify(event), excp); } else { - LOG.warn("failed to log audit event: " - + MiscUtil.stringify(event)); + LOG.warn(FAILED_TO_LOG_AUDIT_EVENT, MiscUtil.stringify(event)); } if (countLifeTime > 1) { // no stats to print for the 1st failure - LOG.warn("Log failure count: " + countSinceLastReport - + " in past " - + formatIntervalForLog(timeSinceLastReport) + "; " - + countLifeTime + " during process lifetime"); + LOG.warn("Log failure count: {} in past {}; {} during process lifetime", countSinceLastReport, formatIntervalForLog(timeSinceLastReport), countLifeTime); } } } @@ -430,14 +446,10 @@ public void logFailedEvent(AuditEventBase event, String message) { mFailedLogLastReportTime.set(now); mFailedLogCountSinceLastReport.set(0); - LOG.warn("failed to log audit event: " + MiscUtil.stringify(event) - + ", errorMessage=" + message); + LOG.warn("failed to log audit event: {} , errorMessage={}", MiscUtil.stringify(event), message); if (countLifeTime > 1) { // no stats to print for the 1st failure - LOG.warn("Log failure count: " + countSinceLastReport - + " in past " - + formatIntervalForLog(timeSinceLastReport) + "; " - + countLifeTime + " during process lifetime"); + LOG.warn("Log failure count: {} in past {}; {} during process lifetime", countSinceLastReport, formatIntervalForLog(timeSinceLastReport), countLifeTime); } } } @@ -462,16 +474,13 @@ public void logFailedEventJSON(String event, Throwable excp) { mFailedLogCountSinceLastReport.set(0); if (excp != null) { - LOG.warn("failed to log audit event: " + event, excp); + LOG.warn(FAILED_TO_LOG_AUDIT_EVENT, event, excp); } else { - LOG.warn("failed to log audit event: " + event); + LOG.warn(FAILED_TO_LOG_AUDIT_EVENT, event); } if (countLifeTime > 1) { // no stats to print for the 1st failure - LOG.warn("Log failure count: " + countSinceLastReport - + " in past " - + formatIntervalForLog(timeSinceLastReport) + "; " - + countLifeTime + " during process lifetime"); + LOG.warn("Log failure count: {} in past {}; {} during process lifetime", countSinceLastReport, formatIntervalForLog(timeSinceLastReport), countLifeTime); } } } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/DbAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/DbAuditProvider.java deleted file mode 100644 index 95909a9c69..0000000000 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/DbAuditProvider.java +++ /dev/null @@ -1,370 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.ranger.audit.provider; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; -import java.util.Properties; - -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityTransaction; -import javax.persistence.Persistence; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ranger.audit.dao.DaoManager; -import org.apache.ranger.audit.destination.AuditDestination; -import org.apache.ranger.audit.entity.AuthzAuditEventDbObj; -import org.apache.ranger.audit.model.AuditEventBase; -import org.apache.ranger.audit.model.AuthzAuditEvent; -import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider; - - -/* - * NOTE: - * - Instances of this class are not thread-safe. - */ -public class DbAuditProvider extends AuditDestination { - - private static final Log LOG = LogFactory.getLog(DbAuditProvider.class); - - public static final String AUDIT_DB_IS_ASYNC_PROP = "xasecure.audit.db.is.async"; - public static final String AUDIT_DB_MAX_QUEUE_SIZE_PROP = "xasecure.audit.db.async.max.queue.size"; - public static final String AUDIT_DB_MAX_FLUSH_INTERVAL_PROP = "xasecure.audit.db.async.max.flush.interval.ms"; - - private static final String AUDIT_DB_BATCH_SIZE_PROP = "xasecure.audit.db.batch.size"; - private static final String AUDIT_DB_RETRY_MIN_INTERVAL_PROP = "xasecure.audit.db.config.retry.min.interval.ms"; - private static final String AUDIT_JPA_CONFIG_PROP_PREFIX = "xasecure.audit.jpa."; - private static final String AUDIT_DB_CREDENTIAL_PROVIDER_FILE = "xasecure.audit.credential.provider.file"; - private static final String AUDIT_DB_CREDENTIAL_PROVIDER_ALIAS = "auditDBCred"; - private static final String AUDIT_JPA_JDBC_PASSWORD = "javax.persistence.jdbc.password"; - - private EntityManagerFactory entityManagerFactory; - private DaoManager daoManager; - - private int mCommitBatchSize = 1; - private int mDbRetryMinIntervalMs = 60 * 1000; - private ArrayList mUncommitted = new ArrayList(); - private Map mDbProperties = null; - private long mLastDbFailedTime = 0; - - public DbAuditProvider() { - LOG.info("DbAuditProvider: creating.."); - } - - @Override - public void init(Properties props) { - LOG.info("DbAuditProvider.init()"); - - super.init(props); - - mDbProperties = MiscUtil.getPropertiesWithPrefix(props, AUDIT_JPA_CONFIG_PROP_PREFIX); - mCommitBatchSize = MiscUtil.getIntProperty(props, AUDIT_DB_BATCH_SIZE_PROP, 1000); - mDbRetryMinIntervalMs = MiscUtil.getIntProperty(props, AUDIT_DB_RETRY_MIN_INTERVAL_PROP, 15 * 1000); - - boolean isAsync = MiscUtil.getBooleanProperty(props, AUDIT_DB_IS_ASYNC_PROP, false); - - if(! isAsync) { - mCommitBatchSize = 1; // Batching not supported in sync mode - } - - String jdbcPassword = getCredentialString(MiscUtil.getStringProperty(props, AUDIT_DB_CREDENTIAL_PROVIDER_FILE), AUDIT_DB_CREDENTIAL_PROVIDER_ALIAS); - - if(jdbcPassword != null && !jdbcPassword.isEmpty()) { - mDbProperties.put(AUDIT_JPA_JDBC_PASSWORD, jdbcPassword); - } - - // initialize the database related classes - AuthzAuditEventDbObj.init(props); - } - - @Override - public boolean log(AuditEventBase event) { - LOG.debug("DbAuditProvider.log()"); - - boolean isSuccess = false; - - try { - if(preCreate()) { - DaoManager daoMgr = daoManager; - - if(daoMgr != null) { - event.persist(daoMgr); - - isSuccess = postCreate(event); - } - } - } catch(Exception excp) { - logDbError("DbAuditProvider.log(): failed", excp); - } finally { - if(! isSuccess) { - logFailedEvent(event); - } - } - LOG.debug("<== DbAuditProvider.log()"); - return isSuccess; - } - - @Override - public boolean log(Collection events) { - boolean ret = true; - for (AuditEventBase event : events) { - ret = log(event); - if(!ret) { - break; - } - } - return ret; - } - - @Override - public boolean logJSON(String event) { - AuditEventBase eventObj = MiscUtil.fromJson(event, - AuthzAuditEvent.class); - return log(eventObj); - } - - @Override - public boolean logJSON(Collection events) { - boolean ret = true; - for (String event : events) { - ret = logJSON(event); - if( !ret ) { - break; - } - } - return ret; - } - - @Override - public void start() { - LOG.info("DbAuditProvider.start()"); - - init(); - } - - @Override - public void stop() { - LOG.info("DbAuditProvider.stop()"); - - cleanUp(); - } - - @Override - public void flush() { - if(mUncommitted.size() > 0) { - boolean isSuccess = commitTransaction(); - - if(! isSuccess) { - for(AuditEventBase evt : mUncommitted) { - logFailedEvent(evt); - } - } - - mUncommitted.clear(); - } - } - - private synchronized boolean init() { - long now = System.currentTimeMillis(); - - if((now - mLastDbFailedTime) < mDbRetryMinIntervalMs) { - return false; - } - - LOG.info("DbAuditProvider: init()"); - LOG.info("java.library.path:"+System.getProperty("java.library.path")); - try { - entityManagerFactory = Persistence.createEntityManagerFactory("xa_server", mDbProperties); - - daoManager = new DaoManager(); - daoManager.setEntityManagerFactory(entityManagerFactory); - - daoManager.getEntityManager(); // this forces the connection to be made to DB - } catch(Exception excp) { - logDbError("DbAuditProvider: DB initalization failed", excp); - - cleanUp(); - - return false; - } - - return true; - } - - private synchronized void cleanUp() { - LOG.info("DbAuditProvider: cleanUp()"); - - try { - if(entityManagerFactory != null && entityManagerFactory.isOpen()) { - entityManagerFactory.close(); - } - } catch(Exception excp) { - LOG.error("DbAuditProvider.cleanUp(): failed", excp); - } finally { - entityManagerFactory = null; - daoManager = null; - } - } - - private boolean isDbConnected() { - EntityManager em = getEntityManager(); - - return em != null && em.isOpen(); - } - - private EntityManager getEntityManager() { - DaoManager daoMgr = daoManager; - - if(daoMgr != null) { - try { - return daoMgr.getEntityManager(); - } catch(Exception excp) { - logDbError("DbAuditProvider.getEntityManager(): failed", excp); - - cleanUp(); - } - } - - return null; - } - - private void clearEntityManager() { - try { - EntityManager em = getEntityManager(); - - if(em != null) { - em.clear(); - } - } catch(Exception excp) { - LOG.warn("DbAuditProvider.clearEntityManager(): failed", excp); - } - } - - private EntityTransaction getTransaction() { - EntityManager em = getEntityManager(); - - return em != null ? em.getTransaction() : null; - } - - private boolean isInTransaction() { - EntityTransaction trx = getTransaction(); - - return trx != null && trx.isActive(); - } - - private boolean beginTransaction() { - EntityTransaction trx = getTransaction(); - - if(trx != null && !trx.isActive()) { - trx.begin(); - } - - if(trx == null) { - LOG.warn("DbAuditProvider.beginTransaction(): trx is null"); - } - - return trx != null; - } - - private boolean commitTransaction() { - boolean ret = false; - EntityTransaction trx = null; - - try { - trx = getTransaction(); - - if(trx != null && trx.isActive()) { - trx.commit(); - - ret =true; - } else { - throw new Exception("trx is null or not active"); - } - } catch(Exception excp) { - logDbError("DbAuditProvider.commitTransaction(): failed", excp); - - cleanUp(); // so that next insert will try to init() - } finally { - clearEntityManager(); - } - - return ret; - } - - private boolean preCreate() { - boolean ret = true; - - if(!isDbConnected()) { - ret = init(); - } - - if(ret) { - if(! isInTransaction()) { - ret = beginTransaction(); - } - } - - return ret; - } - - private boolean postCreate(AuditEventBase event) { - boolean ret = true; - - if(mCommitBatchSize <= 1) { - ret = commitTransaction(); - } else { - mUncommitted.add(event); - - if((mUncommitted.size() % mCommitBatchSize) == 0) { - ret = commitTransaction(); - - if(! ret) { - for(AuditEventBase evt : mUncommitted) { - if(evt != event) { - logFailedEvent(evt); - } - } - } - - mUncommitted.clear(); - } - } - return ret; - } - - private void logDbError(String msg, Exception excp) { - long now = System.currentTimeMillis(); - - if((now - mLastDbFailedTime) > mDbRetryMinIntervalMs) { - mLastDbFailedTime = now; - } - - LOG.warn(msg, excp); - } - - private String getCredentialString(String url,String alias) { - if(url != null && alias != null) { - return RangerCredentialProvider.getInstance().getCredentialString(url,alias); - } - return null; - } -} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java index 05f882ff32..cbd25ab7c7 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/DummyAuditProvider.java @@ -17,6 +17,7 @@ */ package org.apache.ranger.audit.provider; +import java.io.File; import java.util.Collection; import java.util.Properties; @@ -103,4 +104,12 @@ public String getName() { return this.getClass().getName(); } + /* (non-Javadoc) + * @see org.apache.ranger.audit.provider.AuditProvider#getAuditFileType() + */ + @Override + public boolean logFile(File file) { + return logFile(file); + } + } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/LocalFileLogBuffer.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/LocalFileLogBuffer.java index 769b86c632..d720ebcccb 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/LocalFileLogBuffer.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/LocalFileLogBuffer.java @@ -32,7 +32,7 @@ import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; -import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.Comparator; import java.util.TreeSet; @@ -244,16 +244,11 @@ private synchronized void openFile() { if(ostream != null) { mWriter = createWriter(ostream); - - if(mWriter != null) { - mLogger.debug("LocalFileLogBuffer.openFile(): opened file " + mBufferFilename); - - mNextFlushTime = System.currentTimeMillis() + (mFlushIntervalSeconds * 1000L); - } else { - mLogger.warn("LocalFileLogBuffer.openFile(): failed to open file for write " + mBufferFilename); - - mBufferFilename = null; - } + mLogger.debug("LocalFileLogBuffer.openFile(): opened file " + mBufferFilename); + mNextFlushTime = System.currentTimeMillis() + (mFlushIntervalSeconds * 1000L); + } else { + mLogger.warn("LocalFileLogBuffer.openFile(): failed to open file for write " + mBufferFilename); + mBufferFilename = null; } } finally { if(mWriter == null) { @@ -387,7 +382,7 @@ public void addLogfile(String filename) { if(filename != null) { synchronized(mCompletedLogfiles) { mCompletedLogfiles.add(filename); - mCompletedLogfiles.notify(); + mCompletedLogfiles.notifyAll(); } } @@ -420,14 +415,15 @@ public void run() { return; } - loginUser.doAs(new PrivilegedAction() { - @Override - public Integer run() { + try { + loginUser.doAs((PrivilegedExceptionAction) () -> { doRun(); return 0; - } - }); + }); + } catch (Exception excp) { + mLogger.error("DestinationDispatcherThread.run(): failed", excp); + } } private void doRun() { diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jAuditProvider.java index 353c809633..0a874ef7df 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jAuditProvider.java @@ -21,17 +21,17 @@ import java.util.Collection; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.destination.AuditDestination; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Log4jAuditProvider extends AuditDestination { - private static final Log LOG = LogFactory.getLog(Log4jAuditProvider.class); - private static final Log AUDITLOG = LogFactory.getLog("xaaudit." + Log4jAuditProvider.class.getName()); + private static final Logger LOG = LoggerFactory.getLogger(Log4jAuditProvider.class); + private static final Logger AUDITLOG = LoggerFactory.getLogger("xaaudit." + Log4jAuditProvider.class.getName()); public static final String AUDIT_LOG4J_IS_ASYNC_PROP = "xasecure.audit.log4j.is.async"; public static final String AUDIT_LOG4J_MAX_QUEUE_SIZE_PROP = "xasecure.audit.log4j.async.max.queue.size"; diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jTracer.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jTracer.java index bdb1a4774d..cd5befcd2d 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jTracer.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/Log4jTracer.java @@ -16,12 +16,12 @@ */ package org.apache.ranger.audit.provider; -import org.apache.commons.logging.Log; +import org.slf4j.Logger; public class Log4jTracer implements DebugTracer { - private Log mLogger = null; + private Logger mLogger = null; - public Log4jTracer(Log logger) { + public Log4jTracer(Logger logger) { mLogger = logger; } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java index e2b74489b8..8004b75dde 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MiscUtil.java @@ -24,6 +24,9 @@ import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; @@ -46,23 +49,23 @@ import javax.security.auth.login.Configuration; import javax.security.auth.login.LoginContext; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authentication.util.KerberosName; import org.apache.hadoop.security.authentication.util.KerberosUtil; -import org.apache.log4j.helpers.LogLog; import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.fasterxml.jackson.core.JsonParser; +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; import static org.apache.hadoop.util.PlatformName.IBM_JAVA; public class MiscUtil { - private static final Log logger = LogFactory.getLog(MiscUtil.class); + private static final Logger logger = LoggerFactory.getLogger(MiscUtil.class); public static final String TOKEN_START = "%"; public static final String TOKEN_END = "%"; @@ -78,7 +81,21 @@ public class MiscUtil { public static String LINE_SEPARATOR = System.getProperty("line.separator"); - private static Gson sGsonBuilder = null; + static private final ThreadLocal MAPPER = new ThreadLocal() { + @Override + protected ObjectMapper initialValue() { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); + objectMapper.setDateFormat(dateFormat); + objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); + objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + return objectMapper; + } + } ; + + static public ObjectMapper getMapper() { + return MAPPER.get(); + } private static String sApplicationType = null; private static UserGroupInformation ugiLoginUser = null; private static Subject subjectLoginUser = null; @@ -88,15 +105,6 @@ public class MiscUtil { private static int logInterval = 30000; // 30 seconds static { - try { - sGsonBuilder = new GsonBuilder().setDateFormat( - "yyyy-MM-dd HH:mm:ss.SSS").create(); - } catch (Throwable excp) { - LogLog.warn( - "failed to create GsonBuilder object. stringify() will return obj.toString(), instead of Json", - excp); - } - initLocalHost(); } @@ -203,7 +211,7 @@ public static String getSystemProperty(String propertyName) { ret = propertyName != null ? System.getProperty(propertyName) : null; } catch (Exception excp) { - LogLog.warn("getSystemProperty(" + propertyName + ") failed", excp); + logger.warn("getSystemProperty(" + propertyName + ") failed", excp); } return ret; @@ -215,7 +223,7 @@ public static String getEnv(String envName) { try { ret = envName != null ? System.getenv(envName) : null; } catch (Exception excp) { - LogLog.warn("getenv(" + envName + ") failed", excp); + logger.warn("getenv(" + envName + ") failed", excp); } return ret; @@ -229,7 +237,7 @@ public static String getFormattedTime(long time, String format) { ret = sdf.format(time); } catch (Exception excp) { - LogLog.warn("SimpleDateFormat.format() failed: " + format, excp); + logger.warn("SimpleDateFormat.format() failed: " + format, excp); } return ret; @@ -244,7 +252,7 @@ public static void createParents(File file) { if (!parentDir.exists()) { if (!parentDir.mkdirs()) { - LogLog.warn("createParents(): failed to create " + logger.warn("createParents(): failed to create " + parentDir.getAbsolutePath()); } } @@ -312,8 +320,13 @@ public static String stringify(T log) { if (log != null) { if (log instanceof String) { ret = (String) log; - } else if (MiscUtil.sGsonBuilder != null) { - ret = MiscUtil.sGsonBuilder.toJson(log); + } else if (getMapper() != null) { + try { + ret = getMapper().writeValueAsString(log); + } catch (Exception e) { + logger.error("Error occurred while processing JSOn object " + log, e); + ret = log.toString(); // Fallback to default toString() method + } } else { ret = log.toString(); } @@ -323,7 +336,12 @@ public static String stringify(T log) { } static public T fromJson(String jsonStr, Class clazz) { - return sGsonBuilder.fromJson(jsonStr, clazz); + try { + return getMapper().readValue(jsonStr, clazz); + } catch (Exception exception) { + logger.error("Error occurred while processing JSOn object " + jsonStr, exception); + } + return null; } public static String getStringProperty(Properties props, String propName) { @@ -339,6 +357,19 @@ public static String getStringProperty(Properties props, String propName) { return ret; } + public static String getStringProperty(Properties props, String propName, String defValue) { + String ret = defValue; + + if (props != null && propName != null) { + String val = props.getProperty(propName); + if (val != null) { + ret = val; + } + } + + return ret; + } + public static boolean getBooleanProperty(Properties props, String propName, boolean defValue) { boolean ret = defValue; @@ -461,7 +492,7 @@ public static UserGroupInformation createUGIFromSubject(Subject subject) logger.info("Default UGI before using new Subject:" + UserGroupInformation.getLoginUser()); } catch (Throwable t) { - logger.error(t); + logger.error("failed to get login user", t); } ugi = UserGroupInformation.getUGIFromSubject(subject); logger.info("SUBJECT.UGI.NAME=" + ugi.getUserName() + ", ugi=" @@ -600,7 +631,7 @@ static public Set getGroupsForRequestUser(String userName) { return Collections.emptySet(); } - static public boolean logErrorMessageByInterval(Log useLogger, + static public boolean logErrorMessageByInterval(Logger useLogger, String message) { return logErrorMessageByInterval(useLogger, message, null); } @@ -610,7 +641,7 @@ static public boolean logErrorMessageByInterval(Log useLogger, * @param message * @param e */ - static public boolean logErrorMessageByInterval(Log useLogger, + static public boolean logErrorMessageByInterval(Logger useLogger, String message, Throwable e) { if (message == null) { return false; @@ -771,6 +802,45 @@ public static void authWithKerberos(String keytab, String principal, } + public static void loginWithKeyTab(String keytab, String principal, String nameRules) { + if (logger.isDebugEnabled()) { + logger.debug("==> MiscUtil.loginWithKeyTab() keytab= " + keytab + "principal= " + principal + "nameRules= " + nameRules); + } + + if (keytab == null || principal == null) { + logger.error("Failed to login as keytab or principal is null!"); + return; + } + + String[] spnegoPrincipals; + UserGroupInformation ugi; + + try { + if (principal.equals("*")) { + spnegoPrincipals = KerberosUtil.getPrincipalNames(keytab, Pattern.compile("HTTP/.*")); + if (spnegoPrincipals.length == 0) { + logger.error("No principals found in keytab= " + keytab); + } + } else { + spnegoPrincipals = new String[] { principal }; + } + + if (nameRules != null) { + KerberosName.setRules(nameRules); + } + + logger.info("Creating UGI from keytab directly. keytab= " + keytab + ", principal= " + spnegoPrincipals[0]); + ugi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(spnegoPrincipals[0], keytab); + MiscUtil.setUGILoginUser(ugi, null); + } catch (Exception e) { + logger.error("Failed to login with given keytab= " + keytab + "principal= " + principal + "nameRules= " + nameRules, e); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== MiscUtil.loginWithKeyTab()"); + } + } + static class LogHistory { long lastLogTime = 0; int counter = 0; @@ -842,7 +912,7 @@ private static void initLocalHost() { try { local_hostname = InetAddress.getLocalHost().getHostName(); } catch (Throwable excp) { - LogLog.warn("getHostname()", excp); + logger.warn("getHostname()", excp); } if ( logger.isDebugEnabled() ) { logger.debug("<== MiscUtil.initLocalHost()"); @@ -872,4 +942,73 @@ private static class RandomHolder { static final Random random = new Random(); } + // Utility methods + public static int toInt(Object value) { + if (value == null) { + return 0; + } + if (value instanceof Integer) { + return (Integer) value; + } + if (value.toString().isEmpty()) { + return 0; + } + try { + return Integer.valueOf(value.toString()); + } catch (Throwable t) { + logger.error("Error converting value to integer. Value = " + value, t); + } + return 0; + } + + public static long toLong(Object value) { + if (value == null) { + return 0; + } + if (value instanceof Long) { + return (Long) value; + } + if (value.toString().isEmpty()) { + return 0; + } + try { + return Long.valueOf(value.toString()); + } catch (Throwable t) { + logger.error("Error converting value to long. Value = " + value, t); + } + return 0; + } + + public static Date toDate(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date) { + return (Date) value; + } + try { + // TODO: Do proper parsing based on Solr response value + return new Date(value.toString()); + } catch (Throwable t) { + logger.error("Error converting value to date. Value = " + value, t); + } + return null; + } + + public static Date toLocalDate(Object value) { + if (value == null) { + return null; + } + if (value instanceof Date) { + return (Date) value; + } + try { + LocalDateTime localDateTime = LocalDateTime.parse(value.toString(), DateTimeFormatter.ISO_DATE_TIME); + return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()); + } catch (Throwable t) { + logger.error("Error converting value to date. Value = " + value, t); + } + return null; + } + } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java index 282f5abfa0..5ac8c0ee03 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/MultiDestAuditProvider.java @@ -17,19 +17,20 @@ */ package org.apache.ranger.audit.provider; +import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MultiDestAuditProvider extends BaseAuditHandler { - private static final Log LOG = LogFactory - .getLog(MultiDestAuditProvider.class); + private static final Logger LOG = LoggerFactory + .getLogger(MultiDestAuditProvider.class); protected List mProviders = new ArrayList(); static final String DEFAULT_NAME = "multi_dest"; @@ -86,8 +87,10 @@ public void setName(String name) { public void addAuditProvider(AuditHandler provider) { if (provider != null) { - LOG.info("MultiDestAuditProvider.addAuditProvider(providerType=" - + provider.getClass().getCanonicalName() + ")"); + if(LOG.isDebugEnabled()) { + LOG.debug("MultiDestAuditProvider.addAuditProvider(providerType=" + + provider.getClass().getCanonicalName() + ")"); + } mProviders.add(provider); if (provider instanceof BaseAuditHandler) { @@ -155,6 +158,19 @@ public boolean logJSON(Collection events) { return true; } + + @Override + public boolean logFile(File file) { + for (AuditHandler provider : mProviders) { + try { + provider.logFile(file); + } catch (Throwable excp) { + logFailedEventJSON(file.getAbsolutePath(), excp); + } + } + return true; + } + @Override public void start() { for (AuditHandler provider : mProviders) { diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/StandAloneAuditProviderFactory.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/StandAloneAuditProviderFactory.java index 4306b24c0e..5ed77da538 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/StandAloneAuditProviderFactory.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/StandAloneAuditProviderFactory.java @@ -18,11 +18,11 @@ package org.apache.ranger.audit.provider; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class StandAloneAuditProviderFactory extends AuditProviderFactory { - private static final Log LOG = LogFactory.getLog(StandAloneAuditProviderFactory.class); + private static final Logger LOG = LoggerFactory.getLogger(StandAloneAuditProviderFactory.class); private volatile static StandAloneAuditProviderFactory sFactory = null; diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/hdfs/HdfsAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/hdfs/HdfsAuditProvider.java index 65429ad67c..83ff017081 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/hdfs/HdfsAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/hdfs/HdfsAuditProvider.java @@ -19,17 +19,17 @@ import java.util.Map; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.BufferedAuditProvider; import org.apache.ranger.audit.provider.DebugTracer; import org.apache.ranger.audit.provider.LocalFileLogBuffer; import org.apache.ranger.audit.provider.Log4jTracer; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HdfsAuditProvider extends BufferedAuditProvider { - private static final Log LOG = LogFactory.getLog(HdfsAuditProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(HdfsAuditProvider.class); public static final String AUDIT_HDFS_IS_ASYNC_PROP = "xasecure.audit.hdfs.is.async"; public static final String AUDIT_HDFS_MAX_QUEUE_SIZE_PROP = "xasecure.audit.hdfs.async.max.queue.size"; diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/kafka/KafkaAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/kafka/KafkaAuditProvider.java index a0c25427a8..3a452c22a6 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/kafka/KafkaAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/kafka/KafkaAuditProvider.java @@ -16,7 +16,7 @@ */ package org.apache.ranger.audit.provider.kafka; -import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -24,16 +24,16 @@ import org.apache.kafka.clients.producer.KafkaProducer; import org.apache.kafka.clients.producer.Producer; import org.apache.kafka.clients.producer.ProducerRecord; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.destination.AuditDestination; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class KafkaAuditProvider extends AuditDestination { - private static final Log LOG = LogFactory.getLog(KafkaAuditProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(KafkaAuditProvider.class); public static final String AUDIT_MAX_QUEUE_SIZE_PROP = "xasecure.audit.kafka.async.max.queue.size"; public static final String AUDIT_MAX_FLUSH_INTERVAL_PROP = "xasecure.audit.kafka.async.max.flush.interval.ms"; @@ -74,18 +74,12 @@ public void init(Properties props) { LOG.info("Connecting to Kafka producer using properties:" + kakfaProps.toString()); - producer = MiscUtil.executePrivilegedAction(new PrivilegedAction>() { - @Override - public Producer run(){ - Producer producer = new KafkaProducer(kakfaProps); - return producer; - }; - }); + producer = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction>) () -> new KafkaProducer<>(kakfaProps)); initDone = true; } } catch (Throwable t) { - LOG.fatal("Error initializing kafka:", t); + LOG.error("Error initializing kafka:", t); } } @@ -115,12 +109,9 @@ public boolean log(AuditEventBase event) { final ProducerRecord keyedMessage = new ProducerRecord( topic, message); - MiscUtil.executePrivilegedAction(new PrivilegedAction() { - @Override - public Void run(){ - producer.send(keyedMessage); - return null; - }; + MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + producer.send(keyedMessage); + return null; }); } else { @@ -169,12 +160,9 @@ public void stop() { LOG.info("stop() called"); if (producer != null) { try { - MiscUtil.executePrivilegedAction(new PrivilegedAction() { - @Override - public Void run() { - producer.close(); - return null; - }; + MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + producer.close(); + return null; }); } catch (Throwable t) { LOG.error("Error closing Kafka producer"); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/provider/solr/SolrAuditProvider.java b/agents-audit/src/main/java/org/apache/ranger/audit/provider/solr/SolrAuditProvider.java index dac006c6e3..691cef0021 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/provider/solr/SolrAuditProvider.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/provider/solr/SolrAuditProvider.java @@ -19,14 +19,13 @@ package org.apache.ranger.audit.provider.solr; +import java.io.IOException; import java.security.PrivilegedExceptionAction; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.destination.AuditDestination; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.model.AuthzAuditEvent; @@ -36,9 +35,11 @@ import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.response.UpdateResponse; import org.apache.solr.common.SolrInputDocument; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SolrAuditProvider extends AuditDestination { - private static final Log LOG = LogFactory.getLog(SolrAuditProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(SolrAuditProvider.class); public static final String AUDIT_MAX_QUEUE_SIZE_PROP = "xasecure.audit.solr.async.max.queue.size"; public static final String AUDIT_MAX_FLUSH_INTERVAL_PROP = "xasecure.audit.solr.async.max.flush.interval.ms"; @@ -88,7 +89,7 @@ void connect() { lastConnectTime = new Date(); if (solrURL == null || solrURL.isEmpty()) { - LOG.fatal("Solr URL for Audit is empty"); + LOG.error("Solr URL for Audit is empty"); return; } @@ -108,7 +109,7 @@ public SolrClient run() throws Exception { me = solrClient; } catch (Throwable t) { - LOG.fatal("Can't connect to Solr server. URL=" + LOG.error("Can't connect to Solr server. URL=" + solrURL, t); } } @@ -229,8 +230,16 @@ public void start() { */ @Override public void stop() { - // TODO Auto-generated method stub - + LOG.info("SolrAuditProvider.stop() called.."); + try { + if (solrClient != null) { + solrClient.close(); + } + } catch (IOException ioe) { + LOG.error("Error while stopping slor!", ioe); + } finally { + solrClient = null; + } } /* @@ -281,6 +290,8 @@ SolrInputDocument toSolrDoc(AuthzAuditEvent auditEvent) { doc.addField("action", auditEvent.getAction()); doc.addField("evtTime", auditEvent.getEventTime()); doc.addField("tags", auditEvent.getTags()); + doc.addField("datasets", auditEvent.getDatasets()); + doc.addField("projects", auditEvent.getProjects()); doc.addField("cluster", auditEvent.getClusterName()); doc.addField("zone", auditEvent.getZoneName()); doc.addField("agentHost", auditEvent.getAgentHostname()); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditAsyncQueue.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditAsyncQueue.java index f31772ae54..b226b4e201 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditAsyncQueue.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditAsyncQueue.java @@ -23,17 +23,17 @@ import java.util.Collection; import java.util.concurrent.LinkedBlockingQueue; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.MDC; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.AuditHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; /** * This is a non-blocking queue with no limit on capacity. */ public class AuditAsyncQueue extends AuditQueue implements Runnable { - private static final Log logger = LogFactory.getLog(AuditAsyncQueue.class); + private static final Logger logger = LoggerFactory.getLogger(AuditAsyncQueue.class); LinkedBlockingQueue queue = new LinkedBlockingQueue(); Thread consumerThread = null; @@ -56,8 +56,13 @@ public AuditAsyncQueue(AuditHandler consumer) { */ @Override public boolean log(AuditEventBase event) { + logStatusIfRequired(); + + addTotalCount(1); + // Add to the queue and return ASAP if (queue.size() >= getMaxQueueSize()) { + addFailedCount(1); return false; } queue.add(event); @@ -130,10 +135,21 @@ public void run() { MDC.clear(); runLogAudit(); } catch (Throwable t) { - logger.fatal("Exited thread abnormaly. queue=" + getName(), t); + logger.error("Exited thread abnormaly. queue=" + getName(), t); } } + @Override + public void logStatus() { + super.logStatus(); + + if (isStatusLogEnabled()) { + logger.info("AuditAsyncQueue.log(name={}): totalCount={}, currentQueueLength={}", getName(), getTotalCount(), queue.size()); + } + } + + public int size() { return queue.size(); } + public void runLogAudit() { while (true) { try { @@ -150,6 +166,8 @@ public void runLogAudit() { eventList.add(event); queue.drainTo(eventList, MAX_DRAIN - 1); consumer.log(eventList); + + logStatusIfRequired(); } } catch (InterruptedException e) { logger.info("Caught exception in consumer thread. Shutdown might be in progress"); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditBatchQueue.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditBatchQueue.java index 113a230167..103f926566 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditBatchQueue.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditBatchQueue.java @@ -26,14 +26,14 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.MDC; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.AuditHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; public class AuditBatchQueue extends AuditQueue implements Runnable { - private static final Log logger = LogFactory.getLog(AuditBatchQueue.class); + private static final Logger logger = LoggerFactory.getLogger(AuditBatchQueue.class); private BlockingQueue queue = null; private Collection localBatchBuffer = new ArrayList(); @@ -56,8 +56,13 @@ public AuditBatchQueue(AuditHandler consumer) { */ @Override public boolean log(AuditEventBase event) { - // Add to batchQueue. Block if full - queue.add(event); + try { + // Add to batchQueue. Block if full + queue.put(event); + } catch (InterruptedException ex) { + throw new RuntimeException(ex); + } + return true; } @@ -214,7 +219,7 @@ public void run() { MDC.clear(); runLogAudit(); } catch (Throwable t) { - logger.fatal("Exited thread abnormaly. queue=" + getName(), t); + logger.error("Exited thread abnormaly. queue=" + getName(), t); } } @@ -232,15 +237,12 @@ public void runLogAudit() { boolean fileSpoolDrain = false; try { if (fileSpoolerEnabled && fileSpooler.isPending()) { - int percentUsed = queue.size() * 100 - / getMaxQueueSize(); - long lastAttemptDelta = fileSpooler - .getLastAttemptTimeDelta(); + int percentUsed = queue.size() * 100 / getMaxQueueSize(); + long lastAttemptDelta = fileSpooler.getLastAttemptTimeDelta(); fileSpoolDrain = lastAttemptDelta > fileSpoolMaxWaitTime; // If we should even read from queue? - if (!isDrain() && !fileSpoolDrain - && percentUsed < fileSpoolDrainThresholdPercent) { + if (!isDrain() && !fileSpoolDrain && percentUsed < fileSpoolDrainThresholdPercent) { // Since some files are still under progress and it is // not in drain mode, lets wait and retry if (nextDispatchDuration > 0) { @@ -254,10 +256,8 @@ public void runLogAudit() { AuditEventBase event = null; - if (!isToSpool && !isDrain() && !fileSpoolDrain - && nextDispatchDuration > 0) { - event = queue.poll(nextDispatchDuration, - TimeUnit.MILLISECONDS); + if (!isToSpool && !isDrain() && !fileSpoolDrain && nextDispatchDuration > 0) { + event = queue.poll(nextDispatchDuration, TimeUnit.MILLISECONDS); } else { // For poll() is non blocking event = queue.poll(); @@ -266,15 +266,11 @@ public void runLogAudit() { if (event != null) { localBatchBuffer.add(event); if (getMaxBatchSize() >= localBatchBuffer.size()) { - queue.drainTo(localBatchBuffer, getMaxBatchSize() - - localBatchBuffer.size()); + queue.drainTo(localBatchBuffer, getMaxBatchSize() - localBatchBuffer.size()); } } else { // poll returned due to timeout, so reseting clock - nextDispatchDuration = lastDispatchTime - - System.currentTimeMillis() - + getMaxBatchInterval(); - + nextDispatchDuration = lastDispatchTime - System.currentTimeMillis() + getMaxBatchInterval(); lastDispatchTime = System.currentTimeMillis(); } } catch (InterruptedException e) { @@ -288,8 +284,7 @@ public void runLogAudit() { if (localBatchBuffer.size() > 0 && isToSpool) { // Let spool to the file directly if (isDestActive) { - logger.info("Switching to file spool. Queue=" + getName() - + ", dest=" + consumer.getName()); + logger.info("Switching to file spool. Queue = {}, dest = {}", getName(), consumer.getName()); } isDestActive = false; // Just before stashing @@ -297,20 +292,18 @@ public void runLogAudit() { fileSpooler.stashLogs(localBatchBuffer); addStashedCount(localBatchBuffer.size()); localBatchBuffer.clear(); - } else if (localBatchBuffer.size() > 0 - && (isDrain() - || localBatchBuffer.size() >= getMaxBatchSize() || nextDispatchDuration <= 0)) { + } else if (localBatchBuffer.size() > 0 && + (isDrain() || localBatchBuffer.size() >= getMaxBatchSize() || nextDispatchDuration <= 0)) { if (fileSpoolerEnabled && !isDestActive) { - logger.info("Switching to writing to destination. Queue=" - + getName() + ", dest=" + consumer.getName()); + logger.info("Switching to writing to the destination. Queue = {}, dest = {}", + getName(), consumer.getName()); } // Reset time just before sending the logs lastDispatchTime = System.currentTimeMillis(); boolean ret = consumer.log(localBatchBuffer); if (!ret) { if (fileSpoolerEnabled) { - logger.info("Switching to file spool. Queue=" - + getName() + ", dest=" + consumer.getName()); + logger.info("Switching to file spool. Queue = {}, dest = {}", getName(), consumer.getName()); // Transient error. Stash and move on fileSpooler.stashLogs(localBatchBuffer); isDestActive = false; @@ -329,9 +322,8 @@ public void runLogAudit() { if (isDrain()) { if (!queue.isEmpty() || localBatchBuffer.size() > 0) { - logger.info("Queue is not empty. Will retry. queue.size)=" - + queue.size() + ", localBatchBuffer.size()=" - + localBatchBuffer.size()); + logger.info("Queue is not empty. Will retry. queue.size = {}, localBatchBuffer.size = {}", + queue.size(), localBatchBuffer.size()); } else { break; } @@ -344,12 +336,10 @@ public void runLogAudit() { } } - logger.info("Exiting consumerThread. Queue=" + getName() + ", dest=" - + consumer.getName()); + logger.info("Exiting consumerThread. Queue = {}, dest = {}", getName(), consumer.getName()); try { // Call stop on the consumer - logger.info("Calling to stop consumer. name=" + getName() - + ", consumer.name=" + consumer.getName()); + logger.info("Calling to stop consumer. name = {}, consumer.name = {}", getName(), consumer.getName()); consumer.stop(); if (fileSpoolerEnabled) { diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java index 41513ba409..c61d99af58 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileCacheProviderSpool.java @@ -19,18 +19,32 @@ package org.apache.ranger.audit.queue; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.MDC; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.audit.provider.MiscUtil; - -import java.io.*; -import java.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -42,7 +56,7 @@ */ public class AuditFileCacheProviderSpool implements Runnable { - private static final Log logger = LogFactory.getLog(AuditFileCacheProviderSpool.class); + private static final Logger logger = LoggerFactory.getLogger(AuditFileCacheProviderSpool.class); public enum SPOOL_FILE_STATUS { pending, write_inprogress, read_inprogress, done @@ -56,10 +70,10 @@ public enum SPOOL_FILE_STATUS { public static final String PROP_FILE_SPOOL_FILE_ROLLOVER = "filespool.file.rollover.sec"; public static final String PROP_FILE_SPOOL_INDEX_FILE = "filespool.index.filename"; public static final String PROP_FILE_SPOOL_DEST_RETRY_MS = "filespool.destination.retry.ms"; + public static final String PROP_FILE_SPOOL_BATCH_SIZE = "filespool.buffer.size"; public static final String AUDIT_IS_FILE_CACHE_PROVIDER_ENABLE_PROP = "xasecure.audit.provider.filecache.is.enabled"; public static final String FILE_CACHE_PROVIDER_NAME = "AuditFileCacheProviderSpool"; - public static final int AUDIT_BATCH_SIZE_DEFAULT = 1000; AuditHandler consumerProvider = null; @@ -79,6 +93,7 @@ public enum SPOOL_FILE_STATUS { int fileRolloverSec = 24 * 60 * 60; // In seconds int maxArchiveFiles = 100; int errorLogIntervalMS = 30 * 1000; // Every 30 seconds + int auditBatchSize = 1000; long lastErrorLogMS = 0; boolean isAuditFileCacheProviderEnabled = false; boolean closeFile = false; @@ -98,8 +113,6 @@ public enum SPOOL_FILE_STATUS { boolean isDestDown = false; boolean isSpoolingSuccessful = true; - private Gson gson = null; - public AuditFileCacheProviderSpool(AuditHandler consumerProvider) { this.consumerProvider = consumerProvider; } @@ -123,8 +136,6 @@ public boolean init(Properties props, String basePropertyName) { } try { - gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss.SSS") - .create(); // Initial folder and file properties String logFolderProp = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_FILE_SPOOL_LOCAL_DIR); @@ -151,7 +162,7 @@ public boolean init(Properties props, String basePropertyName) { + FILE_CACHE_PROVIDER_NAME); if (logFolderProp == null || logFolderProp.isEmpty()) { - logger.fatal("Audit spool folder is not configured. Please set " + logger.error("Audit spool folder is not configured. Please set " + propPrefix + "." + PROP_FILE_SPOOL_LOCAL_DIR @@ -162,7 +173,7 @@ public boolean init(Properties props, String basePropertyName) { if (!logFolder.isDirectory()) { boolean result = logFolder.mkdirs(); if (!logFolder.isDirectory() || !result) { - logger.fatal("File Spool folder not found and can't be created. folder=" + logger.error("File Spool folder not found and can't be created. folder=" + logFolder.getAbsolutePath() + ", queueName=" + FILE_CACHE_PROVIDER_NAME); @@ -212,7 +223,7 @@ public boolean init(Properties props, String basePropertyName) { if (!indexFile.exists()) { boolean ret = indexFile.createNewFile(); if (!ret) { - logger.fatal("Error creating index file. fileName=" + logger.error("Error creating index file. fileName=" + indexFile.getPath()); return false; } @@ -230,7 +241,7 @@ public boolean init(Properties props, String basePropertyName) { if (!indexDoneFile.exists()) { boolean ret = indexDoneFile.createNewFile(); if (!ret) { - logger.fatal("Error creating index done file. fileName=" + logger.error("Error creating index done file. fileName=" + indexDoneFile.getPath()); return false; } @@ -271,10 +282,14 @@ public boolean init(Properties props, String basePropertyName) { } } catch (Throwable t) { - logger.fatal("Error initializing File Spooler. queue=" + logger.error("Error initializing File Spooler. queue=" + FILE_CACHE_PROVIDER_NAME, t); return false; } + + auditBatchSize = MiscUtil.getIntProperty(props, propPrefix + + "." + PROP_FILE_SPOOL_BATCH_SIZE, auditBatchSize); + initDone = true; logger.debug("<== AuditFileCacheProviderSpool.init()"); @@ -576,9 +591,13 @@ void loadIndexFile() throws IOException { String line; while ((line = br.readLine()) != null) { if (!line.isEmpty() && !line.startsWith("#")) { - AuditIndexRecord record = gson.fromJson(line, - AuditIndexRecord.class); - indexRecords.add(record); + try { + AuditIndexRecord record = MiscUtil.fromJson(line, + AuditIndexRecord.class); + indexRecords.add(record); + } catch (Exception e) { + logger.error("Error parsing following JSON: "+line, e); + } } } } finally { @@ -624,7 +643,7 @@ synchronized void removeIndexRecord(AuditIndexRecord indexRecord) synchronized void saveIndexFile() throws FileNotFoundException, IOException { PrintWriter out = new PrintWriter(indexFile,"UTF-8"); for (AuditIndexRecord auditIndexRecord : indexRecords) { - out.println(gson.toJson(auditIndexRecord)); + out.println(MiscUtil.stringify(auditIndexRecord)); } out.close(); // printIndex(); @@ -636,7 +655,7 @@ void appendToDoneFile(AuditIndexRecord indexRecord) logger.info("Moving to done file. " + indexRecord.filePath + ", queueName=" + FILE_CACHE_PROVIDER_NAME + ", consumer=" + consumerProvider.getName()); - String line = gson.toJson(indexRecord); + String line = MiscUtil.stringify(indexRecord); PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream( indexDoneFile, true),"UTF-8"))); out.println(line); @@ -684,7 +703,8 @@ public boolean accept(File pathname) { int filesDeletedCount = 0; while ((line = br.readLine()) != null) { if (!line.isEmpty() && !line.startsWith("#")) { - AuditIndexRecord record = gson.fromJson(line, + try { + AuditIndexRecord record = MiscUtil.fromJson(line, AuditIndexRecord.class); logFile = new File(record.filePath); String fileName = logFile.getName(); @@ -704,6 +724,9 @@ public boolean accept(File pathname) { break; } } + } catch (Exception e) { + logger.error("Error parsing following JSON: "+line, e); + } } } } finally { @@ -765,7 +788,7 @@ public void run() { MDC.clear(); runLogAudit(); } catch (Throwable t) { - logger.fatal("Exited thread without abnormaly. queue=" + logger.error("Exited thread without abnormaly. queue=" + consumerProvider.getName(), t); } } @@ -824,7 +847,7 @@ public void runLogAudit() { AuditEventBase event = MiscUtil.fromJson(line, AuthzAuditEvent.class); events.add(event); - if (events.size() == AUDIT_BATCH_SIZE_DEFAULT) { + if (events.size() == auditBatchSize) { boolean ret = sendEvent(events, currentConsumerIndexRecord, currLine); if (!ret) { diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueue.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueue.java new file mode 100644 index 0000000000..a4e0683665 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueue.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.audit.queue; + +import org.apache.ranger.audit.model.AuditEventBase; +import org.apache.ranger.audit.provider.AuditHandler; +import org.apache.ranger.audit.provider.BaseAuditHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Properties; + +/* + AuditFileQueue class does the work of stashing the audit logs into Local Filesystem before sending it to the AuditBatchQueue Consumer +*/ + +public class AuditFileQueue extends BaseAuditHandler { + private static final Logger logger = LoggerFactory.getLogger(AuditFileQueue.class); + + AuditFileQueueSpool fileSpooler = null; + AuditHandler consumer = null; + + static final String DEFAULT_NAME = "batch"; + + public AuditFileQueue(AuditHandler consumer) { + this.consumer = consumer; + } + + public void init(Properties prop, String basePropertyName) { + String propPrefix = "xasecure.audit.batch"; + if (basePropertyName != null) { + propPrefix = basePropertyName; + } + super.init(prop, propPrefix); + + //init AuditFileQueueSpooler thread to send Local logs to destination + fileSpooler = new AuditFileQueueSpool(consumer); + fileSpooler.init(prop,propPrefix); + } + + @Override + public boolean log(AuditEventBase event) { + boolean ret = false; + if ( event != null) { + fileSpooler.stashLogs(event); + if (fileSpooler.isSpoolingSuccessful()) { + ret = true; + } + } + return ret; + } + + @Override + public boolean log(Collection events) { + boolean ret = true; + if ( events != null) { + for (AuditEventBase event : events) { + ret = log(event); + } + } + return ret; + } + + + @Override + public void start() { + // Start the consumer thread + if (consumer != null) { + consumer.start(); + } + if (fileSpooler != null) { + // start AuditFileSpool thread + fileSpooler.start(); + } + } + + @Override + public void stop() { + logger.info("Stop called. name=" + getName()); + if (consumer != null) { + consumer.stop(); + } + } + + @Override + public void waitToComplete() { + logger.info("waitToComplete called. name=" + getName()); + if ( consumer != null) { + consumer.waitToComplete(); + } + } + + @Override + public void waitToComplete(long timeout) { + logger.info("waitToComplete called. name=" + getName()); + if ( consumer != null) { + consumer.waitToComplete(timeout); + } + } + + @Override + public void flush() { + logger.info("waitToComplete. name=" + getName()); + if ( consumer != null) { + consumer.flush(); + } + } + +} \ No newline at end of file diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueueSpool.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueueSpool.java new file mode 100644 index 0000000000..f87ec55ab1 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileQueueSpool.java @@ -0,0 +1,987 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +package org.apache.ranger.audit.queue; + +import org.apache.ranger.audit.model.AuditEventBase; +import org.apache.ranger.audit.model.AuditIndexRecord; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.audit.model.SPOOL_FILE_STATUS; +import org.apache.ranger.audit.provider.AuditHandler; +import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * This class temporarily stores logs in Local file system before it despatches each logs in file to the AuditBatchQueue Consumer. + * This gets instantiated only when AuditFileCacheProvider is enabled (xasecure.audit.provider.filecache.is.enabled). + * When AuditFileCacheProvider is enabled all the logs are stored in local file system before sent to destination. + */ + +public class AuditFileQueueSpool implements Runnable { + private static final Logger logger = LoggerFactory.getLogger(AuditFileQueueSpool.class); + + public static final String PROP_FILE_SPOOL_LOCAL_DIR = "filespool.dir"; + public static final String PROP_FILE_SPOOL_LOCAL_FILE_NAME = "filespool.filename.format"; + public static final String PROP_FILE_SPOOL_ARCHIVE_DIR = "filespool.archive.dir"; + public static final String PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT = "filespool.archive.max.files"; + public static final String PROP_FILE_SPOOL_FILENAME_PREFIX = "filespool.file.prefix"; + public static final String PROP_FILE_SPOOL_FILE_ROLLOVER = "filespool.file.rollover.sec"; + public static final String PROP_FILE_SPOOL_INDEX_FILE = "filespool.index.filename"; + public static final String PROP_FILE_SPOOL_DEST_RETRY_MS = "filespool.destination.retry.ms"; + public static final String PROP_FILE_SPOOL_BATCH_SIZE = "filespool.buffer.size"; + public static final String FILE_QUEUE_PROVIDER_NAME = "AuditFileQueueSpool"; + public static final String DEFAULT_AUDIT_FILE_TYPE = "json"; + + AuditHandler consumerProvider = null; + BlockingQueue indexQueue = new LinkedBlockingQueue(); + List indexRecords = new ArrayList(); + + // Folder and File attributes + File logFolder = null; + String logFileNameFormat = null; + File archiveFolder = null; + String fileNamePrefix = null; + String indexFileName = null; + File indexFile = null; + String indexDoneFileName = null; + String auditFileType = null; + File indexDoneFile = null; + int retryDestinationMS = 30 * 1000; // Default 30 seconds + int fileRolloverSec = 24 * 60 * 60; // In seconds + int maxArchiveFiles = 100; + int errorLogIntervalMS = 30 * 1000; // Every 30 seconds + long lastErrorLogMS = 0; + boolean isAuditFileCacheProviderEnabled = false; + boolean closeFile = false; + boolean isPending = false; + long lastAttemptTime = 0; + long bufferSize = 1000; + boolean initDone = false; + + PrintWriter logWriter = null; + AuditIndexRecord currentWriterIndexRecord = null; + AuditIndexRecord currentConsumerIndexRecord = null; + + BufferedReader logReader = null; + Thread destinationThread = null; + + boolean isWriting = true; + boolean isDrain = false; + boolean isDestDown = false; + boolean isSpoolingSuccessful = true; + + public AuditFileQueueSpool(AuditHandler consumerProvider) { + this.consumerProvider = consumerProvider; + } + + public void init(Properties prop) { + init(prop, null); + } + + public boolean init(Properties props, String basePropertyName) { + logger.debug("==> AuditFileQueueSpool.init()"); + + if (initDone) { + logger.error("init() called more than once. queueProvider=" + + "" + ", consumerProvider=" + + consumerProvider.getName()); + return true; + } + String propPrefix = "xasecure.audit.filespool"; + if (basePropertyName != null) { + propPrefix = basePropertyName; + } + + try { + // Initial folder and file properties + String logFolderProp = MiscUtil.getStringProperty(props, propPrefix + + "." + PROP_FILE_SPOOL_LOCAL_DIR); + logFileNameFormat = MiscUtil.getStringProperty(props, + basePropertyName + "." + PROP_FILE_SPOOL_LOCAL_FILE_NAME); + String archiveFolderProp = MiscUtil.getStringProperty(props, + propPrefix + "." + PROP_FILE_SPOOL_ARCHIVE_DIR); + fileNamePrefix = MiscUtil.getStringProperty(props, propPrefix + "." + + PROP_FILE_SPOOL_FILENAME_PREFIX); + indexFileName = MiscUtil.getStringProperty(props, propPrefix + "." + + PROP_FILE_SPOOL_INDEX_FILE); + retryDestinationMS = MiscUtil.getIntProperty(props, propPrefix + + "." + PROP_FILE_SPOOL_DEST_RETRY_MS, retryDestinationMS); + fileRolloverSec = MiscUtil.getIntProperty(props, propPrefix + "." + + PROP_FILE_SPOOL_FILE_ROLLOVER, fileRolloverSec); + maxArchiveFiles = MiscUtil.getIntProperty(props, propPrefix + "." + + PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT, maxArchiveFiles); + logger.info("retryDestinationMS=" + retryDestinationMS + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME); + logger.info("fileRolloverSec=" + fileRolloverSec + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + logger.info("maxArchiveFiles=" + maxArchiveFiles + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + + if (logFolderProp == null || logFolderProp.isEmpty()) { + logger.error("Audit spool folder is not configured. Please set " + + propPrefix + + "." + + PROP_FILE_SPOOL_LOCAL_DIR + + ". queueName=" + FILE_QUEUE_PROVIDER_NAME); + return false; + } + logFolder = new File(logFolderProp); + if (!logFolder.isDirectory()) { + boolean result = logFolder.mkdirs(); + if (!logFolder.isDirectory() || !result) { + logger.error("File Spool folder not found and can't be created. folder=" + + logFolder.getAbsolutePath() + + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + return false; + } + } + logger.info("logFolder=" + logFolder + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + + if (logFileNameFormat == null || logFileNameFormat.isEmpty()) { + logFileNameFormat = "spool_" + "%app-type%" + "_" + + "%time:yyyyMMdd-HHmm.ss%.log"; + } + logger.info("logFileNameFormat=" + logFileNameFormat + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME); + + if (archiveFolderProp == null || archiveFolderProp.isEmpty()) { + archiveFolder = new File(logFolder, "archive"); + } else { + archiveFolder = new File(archiveFolderProp); + } + if (!archiveFolder.isDirectory()) { + boolean result = archiveFolder.mkdirs(); + if (!archiveFolder.isDirectory() || !result) { + logger.error("File Spool archive folder not found and can't be created. folder=" + + archiveFolder.getAbsolutePath() + + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + return false; + } + } + logger.info("archiveFolder=" + archiveFolder + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + + if (indexFileName == null || indexFileName.isEmpty()) { + if (fileNamePrefix == null || fileNamePrefix.isEmpty()) { + fileNamePrefix = FILE_QUEUE_PROVIDER_NAME + "_" + + consumerProvider.getName(); + } + indexFileName = "index_" + fileNamePrefix + "_" + "%app-type%" + + ".json"; + indexFileName = MiscUtil.replaceTokens(indexFileName, + System.currentTimeMillis()); + } + + indexFile = new File(logFolder, indexFileName); + if (!indexFile.exists()) { + boolean ret = indexFile.createNewFile(); + if (!ret) { + logger.error("Error creating index file. fileName=" + + indexFile.getPath()); + return false; + } + } + logger.info("indexFile=" + indexFile + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + + int lastDot = indexFileName.lastIndexOf('.'); + if (lastDot < 0) { + lastDot = indexFileName.length() - 1; + } + indexDoneFileName = indexFileName.substring(0, lastDot) + + "_closed.json"; + indexDoneFile = new File(logFolder, indexDoneFileName); + if (!indexDoneFile.exists()) { + boolean ret = indexDoneFile.createNewFile(); + if (!ret) { + logger.error("Error creating index done file. fileName=" + + indexDoneFile.getPath()); + return false; + } + } + logger.info("indexDoneFile=" + indexDoneFile + ", queueName=" + + FILE_QUEUE_PROVIDER_NAME); + + // Load index file + loadIndexFile(); + for (AuditIndexRecord auditIndexRecord : indexRecords) { + if (!auditIndexRecord.getStatus().equals(SPOOL_FILE_STATUS.done)) { + isPending = true; + } + if (auditIndexRecord.getStatus() + .equals(SPOOL_FILE_STATUS.write_inprogress)) { + currentWriterIndexRecord = auditIndexRecord; + logger.info("currentWriterIndexRecord=" + + currentWriterIndexRecord.getFilePath() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME); + } + if (auditIndexRecord.getStatus() + .equals(SPOOL_FILE_STATUS.read_inprogress)) { + indexQueue.add(auditIndexRecord); + } + } + printIndex(); + for (int i = 0; i < indexRecords.size(); i++) { + AuditIndexRecord auditIndexRecord = indexRecords.get(i); + if (auditIndexRecord.getStatus().equals(SPOOL_FILE_STATUS.pending)) { + File consumerFile = new File(auditIndexRecord.getFilePath()); + if (!consumerFile.exists()) { + logger.error("INIT: Consumer file=" + + consumerFile.getPath() + " not found."); + } else { + indexQueue.add(auditIndexRecord); + } + } + } + + auditFileType = MiscUtil.getStringProperty(props, propPrefix + ".filetype", DEFAULT_AUDIT_FILE_TYPE); + if (auditFileType == null) { + auditFileType = DEFAULT_AUDIT_FILE_TYPE; + } + + } catch (Throwable t) { + logger.error("Error initializing File Spooler. queue=" + + FILE_QUEUE_PROVIDER_NAME, t); + return false; + } + + bufferSize = MiscUtil.getLongProperty(props, propPrefix + + "." + PROP_FILE_SPOOL_BATCH_SIZE, bufferSize); + + initDone = true; + + logger.debug("<== AuditFileQueueSpool.init()"); + return true; + } + + /** + * Start looking for outstanding logs and update status according. + */ + public void start() { + if (!initDone) { + logger.error("Cannot start Audit File Spooler. Initilization not done yet. queueName=" + + FILE_QUEUE_PROVIDER_NAME); + return; + } + + logger.info("Starting writerThread, queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName()); + + // Let's start the thread to read + destinationThread = new Thread(this, FILE_QUEUE_PROVIDER_NAME + "_" + + consumerProvider.getName() + "_destWriter"); + destinationThread.setDaemon(true); + destinationThread.start(); + } + + public void stop() { + if (!initDone) { + logger.error("Cannot stop Audit File Spooler. Initilization not done. queueName=" + + FILE_QUEUE_PROVIDER_NAME); + return; + } + logger.info("Stop called, queueName=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + + isDrain = true; + flush(); + + PrintWriter out = getOpenLogFileStream(); + if (out != null) { + // If write is still going on, then let's give it enough time to + // complete + for (int i = 0; i < 3; i++) { + if (isWriting) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // ignore + } + continue; + } + try { + logger.info("Closing open file, queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName()); + + out.flush(); + out.close(); + break; + } catch (Throwable t) { + logger.debug("Error closing spool out file.", t); + } + } + } + try { + if (destinationThread != null) { + destinationThread.interrupt(); + } + destinationThread = null; + } catch (Throwable e) { + // ignore + } + } + + public void flush() { + if (!initDone) { + logger.error("Cannot flush Audit File Spooler. Initilization not done. queueName=" + + FILE_QUEUE_PROVIDER_NAME); + return; + } + PrintWriter out = getOpenLogFileStream(); + if (out != null) { + out.flush(); + } + } + + /** + * If any files are still not processed. Also, if the destination is not + * reachable + * + * @return + */ + public boolean isPending() { + if (!initDone) { + logError("isPending(): File Spooler not initialized. queueName=" + + FILE_QUEUE_PROVIDER_NAME); + return false; + } + + return isPending; + } + + /** + * Milliseconds from last attempt time + * + * @return + */ + public long getLastAttemptTimeDelta() { + if (lastAttemptTime == 0) { + return 0; + } + return System.currentTimeMillis() - lastAttemptTime; + } + + synchronized public void stashLogs(AuditEventBase event) { + + if (isDrain) { + // Stop has been called, so this method shouldn't be called + logger.error("stashLogs() is called after stop is called. event=" + + event); + return; + } + try { + isWriting = true; + PrintWriter logOut = getLogFileStream(); + // Convert event to json + String jsonStr = MiscUtil.stringify(event); + logOut.println(jsonStr); + logOut.flush(); + isPending = true; + isSpoolingSuccessful = true; + } catch (Throwable t) { + isSpoolingSuccessful = false; + logger.error("Error writing to file. event=" + event, t); + } finally { + isWriting = false; + } + + } + + synchronized public void stashLogs(Collection events) { + for (AuditEventBase event : events) { + stashLogs(event); + } + flush(); + } + + synchronized public void stashLogsString(String event) { + if (isDrain) { + // Stop has been called, so this method shouldn't be called + logger.error("stashLogs() is called after stop is called. event=" + + event); + return; + } + try { + isWriting = true; + PrintWriter logOut = getLogFileStream(); + logOut.println(event); + } catch (Exception ex) { + logger.error("Error writing to file. event=" + event, ex); + } finally { + isWriting = false; + } + + } + + synchronized public boolean isSpoolingSuccessful() { + return isSpoolingSuccessful; + } + + synchronized public void stashLogsString(Collection events) { + for (String event : events) { + stashLogsString(event); + } + flush(); + } + + /** + * This return the current file. If there are not current open output file, + * then it will return null + * + * @return + * @throws Exception + */ + synchronized private PrintWriter getOpenLogFileStream() { + return logWriter; + } + + /** + * @return + * @throws Exception + */ + synchronized private PrintWriter getLogFileStream() throws Exception { + closeFileIfNeeded(); + // Either there are no open log file or the previous one has been rolled + // over + if (currentWriterIndexRecord == null) { + Date currentTime = new Date(); + // Create a new file + String fileName = MiscUtil.replaceTokens(logFileNameFormat, + currentTime.getTime()); + String newFileName = fileName; + File outLogFile = null; + int i = 0; + while (true) { + outLogFile = new File(logFolder, newFileName); + File archiveLogFile = new File(archiveFolder, newFileName); + if (!outLogFile.exists() && !archiveLogFile.exists()) { + break; + } + i++; + int lastDot = fileName.lastIndexOf('.'); + String baseName = fileName.substring(0, lastDot); + String extension = fileName.substring(lastDot); + newFileName = baseName + "." + i + extension; + } + fileName = newFileName; + logger.info("Creating new file. queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", fileName=" + fileName); + // Open the file + logWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream( + outLogFile),"UTF-8"))); + + AuditIndexRecord tmpIndexRecord = new AuditIndexRecord(); + + tmpIndexRecord.setId(MiscUtil.generateUniqueId()); + tmpIndexRecord.setFilePath(outLogFile.getPath()); + tmpIndexRecord.setStatus(SPOOL_FILE_STATUS.write_inprogress); + tmpIndexRecord.setFileCreateTime(currentTime); + tmpIndexRecord.setLastAttempt(true); + currentWriterIndexRecord = tmpIndexRecord; + indexRecords.add(currentWriterIndexRecord); + saveIndexFile(); + + } else { + if (logWriter == null) { + // This means the process just started. We need to open the file + // in append mode. + logger.info("Opening existing file for append. queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", fileName=" + + currentWriterIndexRecord.getFilePath()); + logWriter = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream( + currentWriterIndexRecord.getFilePath(), true),"UTF-8"))); + } + } + return logWriter; + } + + synchronized private void closeFileIfNeeded() throws FileNotFoundException, + IOException { + // Is there file open to write or there are no pending file, then close + // the active file + if (currentWriterIndexRecord != null) { + // Check whether the file needs to rolled + rollOverSpoolFileByTime(); + + if (closeFile) { + // Roll the file + if (logWriter != null) { + logWriter.flush(); + logWriter.close(); + logWriter = null; + closeFile = false; + } + currentWriterIndexRecord.setStatus(SPOOL_FILE_STATUS.pending); + currentWriterIndexRecord.setWriteCompleteTime(new Date()); + saveIndexFile(); + logger.info("Adding file to queue. queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", fileName=" + + currentWriterIndexRecord.getFilePath()); + indexQueue.add(currentWriterIndexRecord); + currentWriterIndexRecord = null; + } + } + } + + private void rollOverSpoolFileByTime() { + if (System.currentTimeMillis() + - currentWriterIndexRecord.getFileCreateTime().getTime() > fileRolloverSec * 1000) { + closeFile = true; + logger.info("Closing file. Rolling over. queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", fileName=" + + currentWriterIndexRecord.getFilePath()); + } + } + + /** + * Load the index file + * + * @throws IOException + */ + void loadIndexFile() throws IOException { + logger.info("Loading index file. fileName=" + indexFile.getPath()); + BufferedReader br = null; + try { + br = new BufferedReader(new InputStreamReader(new FileInputStream(indexFile), "UTF-8")); + indexRecords.clear(); + String line; + while ((line = br.readLine()) != null) { + if (!line.isEmpty() && !line.startsWith("#")) { + try { + AuditIndexRecord record = MiscUtil.fromJson(line, + AuditIndexRecord.class); + indexRecords.add(record); + } catch (Exception e) { + logger.error("Error parsing following JSON: "+line, e); + } + } + } + } finally { + if (br!= null) { + br.close(); + } + } + } + + synchronized void printIndex() { + logger.info("INDEX printIndex() ==== START"); + Iterator iter = indexRecords.iterator(); + while (iter.hasNext()) { + AuditIndexRecord record = iter.next(); + logger.info("INDEX=" + record + ", isFileExist=" + + (new File(record.getFilePath()).exists())); + } + logger.info("INDEX printIndex() ==== END"); + } + + synchronized void removeIndexRecord(AuditIndexRecord indexRecord) + throws FileNotFoundException, IOException { + Iterator iter = indexRecords.iterator(); + while (iter.hasNext()) { + AuditIndexRecord record = iter.next(); + if (record.getId().equals(indexRecord.getId())) { + logger.info("Removing file from index. file=" + record.getFilePath() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + + iter.remove(); + appendToDoneFile(record); + } + } + saveIndexFile(); + // If there are no more files in the index, then let's assume the + // destination is now available + if (indexRecords.size() == 0) { + isPending = false; + } + } + + synchronized void saveIndexFile() throws FileNotFoundException, IOException { + try (PrintWriter out = new PrintWriter(indexFile, "UTF-8")) { + for (AuditIndexRecord auditIndexRecord : indexRecords) { + out.println(MiscUtil.stringify(auditIndexRecord)); + } + } + } + + void appendToDoneFile(AuditIndexRecord indexRecord) + throws FileNotFoundException, IOException { + logger.info("Moving to done file. " + indexRecord.getFilePath() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName()); + String line = MiscUtil.stringify(indexRecord); + PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream( + indexDoneFile, true),"UTF-8"))); + out.println(line); + out.flush(); + out.close(); + + // After Each file is read and audit events are pushed into pipe, we flush to reach the destination immediate. + consumerProvider.flush(); + + // Move to archive folder + File logFile = null; + File archiveFile = null; + try { + logFile = new File(indexRecord.getFilePath()); + String fileName = logFile.getName(); + archiveFile = new File(archiveFolder, fileName); + logger.info("Moving logFile " + logFile + " to " + archiveFile); + boolean result = logFile.renameTo(archiveFile); + if (!result) { + logger.error("Error moving log file to archive folder. Unable to rename" + + logFile + " to archiveFile=" + archiveFile); + } + } catch (Throwable t) { + logger.error("Error moving log file to archive folder. logFile=" + + logFile + ", archiveFile=" + archiveFile, t); + } + + // After archiving the file flush the pipe + consumerProvider.flush(); + + archiveFile = null; + try { + // Remove old files + File[] logFiles = archiveFolder.listFiles(new FileFilter() { + public boolean accept(File pathname) { + return pathname.getName().toLowerCase().endsWith(".log"); + } + }); + + if (logFiles != null && logFiles.length > maxArchiveFiles) { + int filesToDelete = logFiles.length - maxArchiveFiles; + BufferedReader br = new BufferedReader(new FileReader( + indexDoneFile)); + try { + int filesDeletedCount = 0; + while ((line = br.readLine()) != null) { + if (!line.isEmpty() && !line.startsWith("#")) { + try { + AuditIndexRecord record = MiscUtil.fromJson(line, + AuditIndexRecord.class); + logFile = new File(record.getFilePath()); + String fileName = logFile.getName(); + archiveFile = new File(archiveFolder, fileName); + if (archiveFile.exists()) { + logger.info("Deleting archive file " + + archiveFile); + boolean ret = archiveFile.delete(); + if (!ret) { + logger.error("Error deleting archive file. archiveFile=" + + archiveFile); + } + filesDeletedCount++; + if (filesDeletedCount >= filesToDelete) { + logger.info("Deleted " + filesDeletedCount + + " files"); + break; + } + } + } catch (Exception e) { + logger.error("Error parsing following JSON: "+line, e); + } + } + } + } finally { + br.close(); + } + } + } catch (Throwable t) { + logger.error("Error deleting older archive file. archiveFile=" + + archiveFile, t); + } + + } + + void logError(String msg) { + long currTimeMS = System.currentTimeMillis(); + if (currTimeMS - lastErrorLogMS > errorLogIntervalMS) { + logger.error(msg); + lastErrorLogMS = currTimeMS; + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try { + //This is done to clear the MDC context to avoid issue with Ranger Auditing for Knox + MDC.clear(); + runLogAudit(); + } catch (Throwable t) { + logger.error("Exited thread without abnormaly. queue=" + + consumerProvider.getName(), t); + } + } + + public void runLogAudit() { + // boolean isResumed = false; + while (true) { + try { + if (isDestDown) { + logger.info("Destination is down. sleeping for " + + retryDestinationMS + + " milli seconds. indexQueue=" + indexQueue.size() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + Thread.sleep(retryDestinationMS); + } + // Let's pause between each iteration + if (currentConsumerIndexRecord == null) { + currentConsumerIndexRecord = indexQueue.poll( + retryDestinationMS, TimeUnit.MILLISECONDS); + } else { + Thread.sleep(retryDestinationMS); + } + + if (isDrain) { + // Need to exit + break; + } + if (currentConsumerIndexRecord == null) { + closeFileIfNeeded(); + continue; + } + + boolean isRemoveIndex = false; + File consumerFile = new File( + currentConsumerIndexRecord.getFilePath()); + if (!consumerFile.exists()) { + logger.error("Consumer file=" + consumerFile.getPath() + + " not found."); + printIndex(); + isRemoveIndex = true; + } else { + // Let's open the file to write + BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream( + currentConsumerIndexRecord.getFilePath()),"UTF-8")); + try { + if (auditFileType.equalsIgnoreCase(DEFAULT_AUDIT_FILE_TYPE)) { + // if Audit File format is JSON each audit file in the Local Spool Location will be copied + // to HDFS location as JSON + File srcFile = new File(currentConsumerIndexRecord.getFilePath()); + logFile(srcFile); + } else { + // If Audit File format is ORC, each records in audit files in the Local Spool Location will be + // read and converted into ORC format and pushed into an ORC file. + logEvent(br); + } + logger.info("Done reading file. file=" + + currentConsumerIndexRecord.getFilePath() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + // The entire file is read + currentConsumerIndexRecord.setStatus(SPOOL_FILE_STATUS.done); + currentConsumerIndexRecord.setDoneCompleteTime(new Date()); + currentConsumerIndexRecord.setLastAttempt(true); + + isRemoveIndex = true; + } catch (Exception ex) { + isDestDown = true; + logError("Destination down. queueName=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName()); + lastAttemptTime = System.currentTimeMillis(); + // Update the index file + currentConsumerIndexRecord.setLastFailedTime(new Date()); + currentConsumerIndexRecord.setFailedAttemptCount(currentConsumerIndexRecord.getFailedAttemptCount() + 1); + currentConsumerIndexRecord.setLastAttempt(false); + saveIndexFile(); + } finally { + br.close(); + } + } + if (isRemoveIndex) { + // Remove this entry from index + removeIndexRecord(currentConsumerIndexRecord); + currentConsumerIndexRecord = null; + closeFileIfNeeded(); + } + } catch (InterruptedException e) { + logger.info("Caught exception in consumer thread. Shutdown might be in progress"); + } catch (Throwable t) { + logger.error("Exception in destination writing thread.", t); + } + } + logger.info("Exiting file spooler. provider=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + } + + private void logEvent(BufferedReader br) throws Exception { + String line; + int currLine = 0; + int startLine = currentConsumerIndexRecord.getLinePosition(); + List events = new ArrayList<>(); + while ((line = br.readLine()) != null) { + currLine++; + if (currLine < startLine) { + continue; + } + AuditEventBase event = MiscUtil.fromJson(line, AuthzAuditEvent.class); + events.add(event); + + if (events.size() == bufferSize) { + boolean ret = sendEvent(events, + currentConsumerIndexRecord, currLine); + if (!ret) { + throw new Exception("Destination down"); + } + events.clear(); + } + } + if (events.size() > 0) { + boolean ret = sendEvent(events, + currentConsumerIndexRecord, currLine); + if (!ret) { + throw new Exception("Destination down"); + } + events.clear(); + } + } + + private boolean sendEvent(List events, AuditIndexRecord indexRecord, + int currLine) { + boolean ret = true; + try { + ret = consumerProvider.log(events); + if (!ret) { + // Need to log error after fixed interval + logError("Error sending logs to consumer. provider=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName()); + } else { + // Update index and save + indexRecord.setLinePosition(currLine); + indexRecord.setStatus(SPOOL_FILE_STATUS.read_inprogress); + indexRecord.setLastSuccessTime(new Date()); + indexRecord.setLastAttempt(true); + saveIndexFile(); + + if (isDestDown) { + isDestDown = false; + logger.info("Destination up now. " + indexRecord.getFilePath() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + } + } + } catch (Throwable t) { + logger.error("Error while sending logs to consumer. provider=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName() + ", log=" + events, t); + } + + return ret; + } + + private void logFile(File file) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> AuditFileQueueSpool.logFile()"); + } + int currLine = 0; + int startLine = currentConsumerIndexRecord.getLinePosition(); + + if (currLine < startLine) { + currLine++; + } + + boolean ret = sendFile(file,currentConsumerIndexRecord, currLine); + if (!ret) { + throw new Exception("Destination down"); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== AuditFileQueueSpool.logFile()"); + } + } + + private boolean sendFile(File file, AuditIndexRecord indexRecord, + int currLine) { + boolean ret = true; + if (logger.isDebugEnabled()) { + logger.debug("==> AuditFileQueueSpool.sendFile()"); + } + + try { + ret = consumerProvider.logFile(file); + if (!ret) { + // Need to log error after fixed interval + logError("Error sending log file to consumer. provider=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName()+ ", logFile=" + file.getName()); + } else { + // Update index and save + indexRecord.setLinePosition(currLine); + indexRecord.setStatus(SPOOL_FILE_STATUS.read_inprogress); + indexRecord.setLastSuccessTime(new Date()); + indexRecord.setLastAttempt(true); + saveIndexFile(); + + if (isDestDown) { + isDestDown = false; + logger.info("Destination up now. " + indexRecord.getFilePath() + + ", queueName=" + FILE_QUEUE_PROVIDER_NAME + + ", consumer=" + consumerProvider.getName()); + } + } + } catch (Throwable t) { + logger.error("Error sending log file to consumer. provider=" + + FILE_QUEUE_PROVIDER_NAME + ", consumer=" + + consumerProvider.getName() + ", logFile=" + file.getName(), t); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== AuditFileQueueSpool.sendFile() " + ret ); + } + return ret; + } +} \ No newline at end of file diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileSpool.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileSpool.java index cbd819dda6..0e550ae8cb 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileSpool.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditFileSpool.java @@ -22,7 +22,6 @@ import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; -import java.io.FileFilter; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; @@ -38,26 +37,22 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.MDC; import org.apache.ranger.audit.model.AuditEventBase; +import org.apache.ranger.audit.model.AuditIndexRecord; +import org.apache.ranger.audit.model.SPOOL_FILE_STATUS; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import org.slf4j.MDC; /** * This class temporarily stores logs in file system if the destination is * overloaded or down */ public class AuditFileSpool implements Runnable { - private static final Log logger = LogFactory.getLog(AuditFileSpool.class); - - public enum SPOOL_FILE_STATUS { - pending, write_inprogress, read_inprogress, done - } + private static final Logger logger = LoggerFactory.getLogger(AuditFileSpool.class); public static final String PROP_FILE_SPOOL_LOCAL_DIR = "filespool.dir"; public static final String PROP_FILE_SPOOL_LOCAL_FILE_NAME = "filespool.filename.format"; @@ -66,14 +61,13 @@ public enum SPOOL_FILE_STATUS { public static final String PROP_FILE_SPOOL_FILENAME_PREFIX = "filespool.file.prefix"; public static final String PROP_FILE_SPOOL_FILE_ROLLOVER = "filespool.file.rollover.sec"; public static final String PROP_FILE_SPOOL_INDEX_FILE = "filespool.index.filename"; - // public static final String PROP_FILE_SPOOL_INDEX_DONE_FILE = - // "filespool.index.done_filename"; public static final String PROP_FILE_SPOOL_DEST_RETRY_MS = "filespool.destination.retry.ms"; + public static final String CONSUMER = ", consumer="; AuditQueue queueProvider = null; AuditHandler consumerProvider = null; - BlockingQueue indexQueue = new LinkedBlockingQueue(); + BlockingQueue indexQueue = new LinkedBlockingQueue<>(); // Folder and File attributes File logFolder = null; @@ -91,7 +85,7 @@ public enum SPOOL_FILE_STATUS { int errorLogIntervalMS = 30 * 1000; // Every 30 seconds long lastErrorLogMS = 0; - List indexRecords = new ArrayList(); + List indexRecords = new ArrayList<>(); boolean isPending = false; long lastAttemptTime = 0; @@ -109,8 +103,6 @@ public enum SPOOL_FILE_STATUS { boolean isDrain = false; boolean isDestDown = false; - private Gson gson = null; - public AuditFileSpool(AuditQueue queueProvider, AuditHandler consumerProvider) { this.queueProvider = queueProvider; @@ -123,9 +115,7 @@ public void init(Properties prop) { public boolean init(Properties props, String basePropertyName) { if (initDone) { - logger.error("init() called more than once. queueProvider=" - + queueProvider.getName() + ", consumerProvider=" - + consumerProvider.getName()); + logger.error("init() called more than once. queueProvider={}, consumerProvider={}", queueProvider.getName(), consumerProvider.getName()); return true; } String propPrefix = "xasecure.audit.filespool"; @@ -134,9 +124,6 @@ public boolean init(Properties props, String basePropertyName) { } try { - gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss.SSS") - .create(); - // Initial folder and file properties String logFolderProp = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_FILE_SPOOL_LOCAL_DIR); @@ -155,41 +142,29 @@ public boolean init(Properties props, String basePropertyName) { maxArchiveFiles = MiscUtil.getIntProperty(props, propPrefix + "." + PROP_FILE_SPOOL_ARCHIVE_MAX_FILES_COUNT, maxArchiveFiles); - logger.info("retryDestinationMS=" + retryDestinationMS - + ", queueName=" + queueProvider.getName()); - logger.info("fileRolloverSec=" + fileRolloverSec + ", queueName=" - + queueProvider.getName()); - logger.info("maxArchiveFiles=" + maxArchiveFiles + ", queueName=" - + queueProvider.getName()); + logger.info("retryDestinationMS={}, queueName={}", retryDestinationMS, queueProvider.getName()); + logger.info("fileRolloverSec={}, queueName={}", fileRolloverSec, queueProvider.getName()); + logger.info("maxArchiveFiles={}, queueName={}", maxArchiveFiles, queueProvider.getName()); if (logFolderProp == null || logFolderProp.isEmpty()) { - logger.fatal("Audit spool folder is not configured. Please set " - + propPrefix - + "." - + PROP_FILE_SPOOL_LOCAL_DIR - + ". queueName=" + queueProvider.getName()); + logger.error("Audit spool folder is not configured. Please set {}.{}. queueName={}", propPrefix, PROP_FILE_SPOOL_LOCAL_DIR, queueProvider.getName()); return false; } logFolder = new File(logFolderProp); if (!logFolder.isDirectory()) { logFolder.mkdirs(); if (!logFolder.isDirectory()) { - logger.fatal("File Spool folder not found and can't be created. folder=" - + logFolder.getAbsolutePath() - + ", queueName=" - + queueProvider.getName()); + logger.error("File Spool folder not found and can't be created. folder={}, queueName={}", logFolder.getAbsolutePath(), queueProvider.getName()); return false; } } - logger.info("logFolder=" + logFolder + ", queueName=" - + queueProvider.getName()); + logger.info("logFolder={}, queueName={}", logFolder, queueProvider.getName()); if (logFileNameFormat == null || logFileNameFormat.isEmpty()) { logFileNameFormat = "spool_" + "%app-type%" + "_" + "%time:yyyyMMdd-HHmm.ss%.log"; } - logger.info("logFileNameFormat=" + logFileNameFormat - + ", queueName=" + queueProvider.getName()); + logger.info("logFileNameFormat={}, queueName={}", logFileNameFormat, queueProvider.getName()); if (archiveFolderProp == null || archiveFolderProp.isEmpty()) { archiveFolder = new File(logFolder, "archive"); @@ -199,15 +174,11 @@ public boolean init(Properties props, String basePropertyName) { if (!archiveFolder.isDirectory()) { archiveFolder.mkdirs(); if (!archiveFolder.isDirectory()) { - logger.error("File Spool archive folder not found and can't be created. folder=" - + archiveFolder.getAbsolutePath() - + ", queueName=" - + queueProvider.getName()); + logger.error("File Spool archive folder not found and can't be created. folder={}, queueName={}", archiveFolder.getAbsolutePath(), queueProvider.getName()); return false; } } - logger.info("archiveFolder=" + archiveFolder + ", queueName=" - + queueProvider.getName()); + logger.info("archiveFolder={}, queueName={}", archiveFolder, queueProvider.getName()); if (indexFileName == null || indexFileName.isEmpty()) { if (fileNamePrefix == null || fileNamePrefix.isEmpty()) { @@ -224,13 +195,11 @@ public boolean init(Properties props, String basePropertyName) { if (!indexFile.exists()) { boolean ret = indexFile.createNewFile(); if (!ret) { - logger.fatal("Error creating index file. fileName=" - + indexDoneFile.getPath()); + logger.error("Error creating index file. fileName={}", indexDoneFile.getPath()); return false; } } - logger.info("indexFile=" + indexFile + ", queueName=" - + queueProvider.getName()); + logger.info("indexFile={}, queueName={}", indexFile, queueProvider.getName()); int lastDot = indexFileName.lastIndexOf('.'); if (lastDot < 0) { @@ -242,39 +211,34 @@ public boolean init(Properties props, String basePropertyName) { if (!indexDoneFile.exists()) { boolean ret = indexDoneFile.createNewFile(); if (!ret) { - logger.fatal("Error creating index done file. fileName=" - + indexDoneFile.getPath()); + logger.error("Error creating index done file. fileName={}", indexDoneFile.getPath()); return false; } } - logger.info("indexDoneFile=" + indexDoneFile + ", queueName=" - + queueProvider.getName()); + logger.info("indexDoneFile={}, queueName={}", indexDoneFile, queueProvider.getName()); // Load index file loadIndexFile(); for (AuditIndexRecord auditIndexRecord : indexRecords) { - if (!auditIndexRecord.status.equals(SPOOL_FILE_STATUS.done)) { + if (!auditIndexRecord.getStatus().equals(SPOOL_FILE_STATUS.done)) { isPending = true; } - if (auditIndexRecord.status + if (auditIndexRecord.getStatus() .equals(SPOOL_FILE_STATUS.write_inprogress)) { currentWriterIndexRecord = auditIndexRecord; - logger.info("currentWriterIndexRecord=" - + currentWriterIndexRecord.filePath - + ", queueName=" + queueProvider.getName()); + logger.info("currentWriterIndexRecord={}, queueName={}", currentWriterIndexRecord.getFilePath(), queueProvider.getName()); } - if (auditIndexRecord.status + if (auditIndexRecord.getStatus() .equals(SPOOL_FILE_STATUS.read_inprogress)) { indexQueue.add(auditIndexRecord); } } printIndex(); for (AuditIndexRecord auditIndexRecord : indexRecords) { - if (auditIndexRecord.status.equals(SPOOL_FILE_STATUS.pending)) { - File consumerFile = new File(auditIndexRecord.filePath); + if (auditIndexRecord.getStatus().equals(SPOOL_FILE_STATUS.pending)) { + File consumerFile = new File(auditIndexRecord.getFilePath()); if (!consumerFile.exists()) { - logger.error("INIT: Consumer file=" - + consumerFile.getPath() + " not found."); + logger.error("INIT: Consumer file={} not found.", consumerFile.getPath()); } else { indexQueue.add(auditIndexRecord); } @@ -282,7 +246,7 @@ public boolean init(Properties props, String basePropertyName) { } } catch (Throwable t) { - logger.fatal("Error initializing File Spooler. queue=" + logger.error("Error initializing File Spooler. queue=" + queueProvider.getName(), t); return false; } @@ -295,14 +259,11 @@ public boolean init(Properties props, String basePropertyName) { */ public void start() { if (!initDone) { - logger.error("Cannot start Audit File Spooler. Initilization not done yet. queueName=" - + queueProvider.getName()); + logger.error("Cannot start Audit File Spooler. Initilization not done yet. queueName={}", queueProvider.getName()); return; } - logger.info("Starting writerThread, queueName=" - + queueProvider.getName() + ", consumer=" - + consumerProvider.getName()); + logger.info("Starting writerThread, queueName={}, consumer={}", queueProvider.getName(), consumerProvider.getName()); // Let's start the thread to read destinationThread = new Thread(this, queueProvider.getName() + "_" @@ -313,12 +274,10 @@ public void start() { public void stop() { if (!initDone) { - logger.error("Cannot stop Audit File Spooler. Initilization not done. queueName=" - + queueProvider.getName()); + logger.error("Cannot stop Audit File Spooler. Initilization not done. queueName={}", queueProvider.getName()); return; } - logger.info("Stop called, queueName=" + queueProvider.getName() - + ", consumer=" + consumerProvider.getName()); + logger.info("Stop called, queueName={}, consumer={}", queueProvider.getName(), consumerProvider.getName()); isDrain = true; flush(); @@ -337,9 +296,7 @@ public void stop() { continue; } try { - logger.info("Closing open file, queueName=" - + queueProvider.getName() + ", consumer=" - + consumerProvider.getName()); + logger.info("Closing open file, queueName={}, consumer={}", queueProvider.getName(), consumerProvider.getName()); out.flush(); out.close(); @@ -354,15 +311,14 @@ public void stop() { destinationThread.interrupt(); } destinationThread = null; - } catch (Throwable e) { + } catch (Exception e) { // ignore } } public void flush() { if (!initDone) { - logger.error("Cannot flush Audit File Spooler. Initilization not done. queueName=" - + queueProvider.getName()); + logger.error("Cannot flush Audit File Spooler. Initilization not done. queueName={}", queueProvider.getName()); return; } PrintWriter out = getOpenLogFileStream(); @@ -379,8 +335,7 @@ public void flush() { */ public boolean isPending() { if (!initDone) { - logError("isPending(): File Spooler not initialized. queueName=" - + queueProvider.getName()); + logError("isPending(): File Spooler not initialized. queueName={}", queueProvider.getName()); return false; } @@ -399,11 +354,10 @@ public long getLastAttemptTimeDelta() { return System.currentTimeMillis() - lastAttemptTime; } - synchronized public void stashLogs(AuditEventBase event) { + public synchronized void stashLogs(AuditEventBase event) { if (isDrain) { // Stop has been called, so this method shouldn't be called - logger.error("stashLogs() is called after stop is called. event=" - + event); + logger.error("stashLogs() is called after stop is called. event={}", event); return; } try { @@ -414,25 +368,24 @@ synchronized public void stashLogs(AuditEventBase event) { logOut.println(jsonStr); isPending = true; } catch (Exception ex) { - logger.error("Error writing to file. event=" + event, ex); + logger.error("Error writing to file. event={}", event, ex); } finally { isWriting = false; } } - synchronized public void stashLogs(Collection events) { + public synchronized void stashLogs(Collection events) { for (AuditEventBase event : events) { stashLogs(event); } flush(); } - synchronized public void stashLogsString(String event) { + public synchronized void stashLogsString(String event) { if (isDrain) { // Stop has been called, so this method shouldn't be called - logger.error("stashLogs() is called after stop is called. event=" - + event); + logger.error("stashLogs() is called after stop is called. event={}", event); return; } try { @@ -440,14 +393,14 @@ synchronized public void stashLogsString(String event) { PrintWriter logOut = getLogFileStream(); logOut.println(event); } catch (Exception ex) { - logger.error("Error writing to file. event=" + event, ex); + logger.error("Error writing to file. event={}", event, ex); } finally { isWriting = false; } } - synchronized public void stashLogsString(Collection events) { + public synchronized void stashLogsString(Collection events) { for (String event : events) { stashLogsString(event); } @@ -461,7 +414,7 @@ synchronized public void stashLogsString(Collection events) { * @return * @throws Exception */ - synchronized private PrintWriter getOpenLogFileStream() { + private synchronized PrintWriter getOpenLogFileStream() { return logWriter; } @@ -469,7 +422,7 @@ synchronized private PrintWriter getOpenLogFileStream() { * @return * @throws Exception */ - synchronized private PrintWriter getLogFileStream() throws Exception { + private synchronized PrintWriter getLogFileStream() throws Exception { closeFileIfNeeded(); // Either there are no open log file or the previous one has been rolled @@ -495,19 +448,18 @@ synchronized private PrintWriter getLogFileStream() throws Exception { newFileName = baseName + "." + i + extension; } fileName = newFileName; - logger.info("Creating new file. queueName=" - + queueProvider.getName() + ", fileName=" + fileName); + logger.info("Creating new file. queueName={}, filename={}",queueProvider.getName(), fileName); // Open the file logWriter = new PrintWriter(new BufferedWriter(new FileWriter( outLogFile))); AuditIndexRecord tmpIndexRecord = new AuditIndexRecord(); - tmpIndexRecord.id = MiscUtil.generateUniqueId(); - tmpIndexRecord.filePath = outLogFile.getPath(); - tmpIndexRecord.status = SPOOL_FILE_STATUS.write_inprogress; - tmpIndexRecord.fileCreateTime = currentTime; - tmpIndexRecord.lastAttempt = true; + tmpIndexRecord.setId(MiscUtil.generateUniqueId()); + tmpIndexRecord.setFilePath(outLogFile.getPath()); + tmpIndexRecord.setStatus(SPOOL_FILE_STATUS.write_inprogress); + tmpIndexRecord.setFileCreateTime(currentTime); + tmpIndexRecord.setLastAttempt(true); currentWriterIndexRecord = tmpIndexRecord; indexRecords.add(currentWriterIndexRecord); saveIndexFile(); @@ -516,18 +468,15 @@ synchronized private PrintWriter getLogFileStream() throws Exception { if (logWriter == null) { // This means the process just started. We need to open the file // in append mode. - logger.info("Opening existing file for append. queueName=" - + queueProvider.getName() + ", fileName=" - + currentWriterIndexRecord.filePath); + logger.info("Opening existing file for append. queueName={}, filename={}", queueProvider.getName(), currentWriterIndexRecord.getFilePath()); logWriter = new PrintWriter(new BufferedWriter(new FileWriter( - currentWriterIndexRecord.filePath, true))); + currentWriterIndexRecord.getFilePath(), true))); } } return logWriter; } - synchronized private void closeFileIfNeeded() throws FileNotFoundException, - IOException { + private synchronized void closeFileIfNeeded() throws IOException { // Is there file open to write or there are no pending file, then close // the active file if (currentWriterIndexRecord != null) { @@ -537,13 +486,11 @@ synchronized private void closeFileIfNeeded() throws FileNotFoundException, closeFile = true; logger.info("Closing file. Only one open file. queueName=" + queueProvider.getName() + ", fileName=" - + currentWriterIndexRecord.filePath); + + currentWriterIndexRecord.getFilePath()); } else if (System.currentTimeMillis() - - currentWriterIndexRecord.fileCreateTime.getTime() > fileRolloverSec * 1000) { + - currentWriterIndexRecord.getFileCreateTime().getTime() > fileRolloverSec * 1000) { closeFile = true; - logger.info("Closing file. Rolling over. queueName=" - + queueProvider.getName() + ", fileName=" - + currentWriterIndexRecord.filePath); + logger.info("Closing file. Only one open file. queueName={}, filename={}",queueProvider.getName(), currentWriterIndexRecord.getFilePath()); } if (closeFile) { // Roll the file @@ -552,12 +499,10 @@ synchronized private void closeFileIfNeeded() throws FileNotFoundException, logWriter.close(); logWriter = null; } - currentWriterIndexRecord.status = SPOOL_FILE_STATUS.pending; - currentWriterIndexRecord.writeCompleteTime = new Date(); + currentWriterIndexRecord.setStatus(SPOOL_FILE_STATUS.pending); + currentWriterIndexRecord.setWriteCompleteTime( new Date()); saveIndexFile(); - logger.info("Adding file to queue. queueName=" - + queueProvider.getName() + ", fileName=" - + currentWriterIndexRecord.filePath); + logger.info("Adding file to queue. queueName={}, filename={}", queueProvider.getName(), currentWriterIndexRecord.getFilePath()); indexQueue.add(currentWriterIndexRecord); currentWriterIndexRecord = null; } @@ -570,18 +515,17 @@ synchronized private void closeFileIfNeeded() throws FileNotFoundException, * @throws IOException */ void loadIndexFile() throws IOException { - logger.info("Loading index file. fileName=" + indexFile.getPath()); - BufferedReader br = new BufferedReader(new FileReader(indexFile)); - indexRecords.clear(); - String line; - while ((line = br.readLine()) != null) { - if (!line.isEmpty() && !line.startsWith("#")) { - AuditIndexRecord record = gson.fromJson(line, - AuditIndexRecord.class); - indexRecords.add(record); + logger.info("Loading index file. fileName={}", indexFile.getPath()); + try (BufferedReader br = new BufferedReader(new FileReader(indexFile))) { + indexRecords.clear(); + String line; + while ((line = br.readLine()) != null) { + if (!line.isEmpty() && !line.startsWith("#")) { + AuditIndexRecord record = MiscUtil.fromJson(line, AuditIndexRecord.class); + indexRecords.add(record); + } } } - br.close(); } synchronized void printIndex() { @@ -589,8 +533,7 @@ synchronized void printIndex() { Iterator iter = indexRecords.iterator(); while (iter.hasNext()) { AuditIndexRecord record = iter.next(); - logger.info("INDEX=" + record + ", isFileExist=" - + (new File(record.filePath).exists())); + logger.info("INDEX={}, isFileExist={}", record, (new File(record.getFilePath()).exists())); } logger.info("INDEX printIndex() ==== END"); } @@ -600,10 +543,8 @@ synchronized void removeIndexRecord(AuditIndexRecord indexRecord) Iterator iter = indexRecords.iterator(); while (iter.hasNext()) { AuditIndexRecord record = iter.next(); - if (record.id.equals(indexRecord.id)) { - logger.info("Removing file from index. file=" + record.filePath - + ", queueName=" + queueProvider.getName() - + ", consumer=" + consumerProvider.getName()); + if (record.getId().equals(indexRecord.getId())) { + logger.info("Removing file from index. file={}, queueName={}, consumer={}", record.getFilePath(), queueProvider.getName(), consumerProvider.getName()); iter.remove(); appendToDoneFile(record); @@ -612,27 +553,23 @@ synchronized void removeIndexRecord(AuditIndexRecord indexRecord) saveIndexFile(); // If there are no more files in the index, then let's assume the // destination is now available - if (indexRecords.size() == 0) { + if (indexRecords.isEmpty()) { isPending = false; } } - synchronized void saveIndexFile() throws FileNotFoundException, IOException { - PrintWriter out = new PrintWriter(indexFile); - for (AuditIndexRecord auditIndexRecord : indexRecords) { - out.println(gson.toJson(auditIndexRecord)); + synchronized void saveIndexFile() throws IOException { + try (PrintWriter out = new PrintWriter(indexFile)) { + for (AuditIndexRecord auditIndexRecord : indexRecords) { + out.println(MiscUtil.stringify(auditIndexRecord)); + } } - out.close(); - // printIndex(); - } void appendToDoneFile(AuditIndexRecord indexRecord) - throws FileNotFoundException, IOException { - logger.info("Moving to done file. " + indexRecord.filePath - + ", queueName=" + queueProvider.getName() + ", consumer=" - + consumerProvider.getName()); - String line = gson.toJson(indexRecord); + throws IOException { + logger.info("Moving to done file. {}, queueName={}, consumer={}", indexRecord.getFilePath(), queueProvider.getName(), consumerProvider.getName()); + String line = MiscUtil.stringify(indexRecord); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter( indexDoneFile, true))); out.println(line); @@ -643,102 +580,69 @@ void appendToDoneFile(AuditIndexRecord indexRecord) File logFile = null; File archiveFile = null; try { - logFile = new File(indexRecord.filePath); + logFile = new File(indexRecord.getFilePath()); String fileName = logFile.getName(); archiveFile = new File(archiveFolder, fileName); - logger.info("Moving logFile " + logFile + " to " + archiveFile); - logFile.renameTo(archiveFile); - } catch (Throwable t) { - logger.error("Error moving log file to archive folder. logFile=" - + logFile + ", archiveFile=" + archiveFile, t); + logger.info("Moving logFile{} to {}", logFile, archiveFile); + boolean filedRenamed = logFile.renameTo(archiveFile); + if(logger.isDebugEnabled()) { + logger.debug("logFile renamed to archiveFile {}{}", archiveFile, filedRenamed ); + } + } catch (Exception t) { + logger.error("Error moving log file to archive folder. logFile={}, archiveFile={}", logFile, archiveFile, t); } archiveFile = null; try { // Remove old files - File[] logFiles = archiveFolder.listFiles(new FileFilter() { - public boolean accept(File pathname) { - return pathname.getName().toLowerCase().endsWith(".log"); - } - }); + File[] logFiles = archiveFolder.listFiles(pathname -> pathname.getName().toLowerCase().endsWith(".log")); if (logFiles != null && logFiles.length > maxArchiveFiles) { int filesToDelete = logFiles.length - maxArchiveFiles; - BufferedReader br = new BufferedReader(new FileReader( - indexDoneFile)); - try { + try (BufferedReader br = new BufferedReader(new FileReader(indexDoneFile))) { int filesDeletedCount = 0; while ((line = br.readLine()) != null) { if (!line.isEmpty() && !line.startsWith("#")) { - AuditIndexRecord record = gson.fromJson(line, - AuditIndexRecord.class); - logFile = new File(record.filePath); + try { + AuditIndexRecord record = MiscUtil.fromJson(line, + AuditIndexRecord.class); + logFile = new File(record.getFilePath()); String fileName = logFile.getName(); archiveFile = new File(archiveFolder, fileName); if (archiveFile.exists()) { - logger.info("Deleting archive file " - + archiveFile); + logger.info("Deleting archive file {}", archiveFile); boolean ret = archiveFile.delete(); if (!ret) { - logger.error("Error deleting archive file. archiveFile=" - + archiveFile); + logger.error("Error deleting archive file. archiveFile={}", archiveFile); } filesDeletedCount++; if (filesDeletedCount >= filesToDelete) { - logger.info("Deleted " + filesDeletedCount - + " files"); + logger.info("Deleted {} files", filesDeletedCount); break; } } + } catch (Exception e) { + logger.error("Error parsing following JSON: "+line, e); + } } } - } finally { - br.close(); } } - } catch (Throwable t) { + } catch (Exception t) { logger.error("Error deleting older archive file. archiveFile=" + archiveFile, t); } } - void logError(String msg) { + void logError(String msg, Object... arguments) { long currTimeMS = System.currentTimeMillis(); if (currTimeMS - lastErrorLogMS > errorLogIntervalMS) { - logger.error(msg); + logger.error(msg, arguments); lastErrorLogMS = currTimeMS; } } - class AuditIndexRecord { - String id; - String filePath; - int linePosition = 0; - SPOOL_FILE_STATUS status = SPOOL_FILE_STATUS.write_inprogress; - Date fileCreateTime; - Date writeCompleteTime; - Date doneCompleteTime; - Date lastSuccessTime; - Date lastFailedTime; - int failedAttemptCount = 0; - boolean lastAttempt = false; - - @Override - public String toString() { - return "AuditIndexRecord [id=" + id + ", filePath=" + filePath - + ", linePosition=" + linePosition + ", status=" + status - + ", fileCreateTime=" + fileCreateTime - + ", writeCompleteTime=" + writeCompleteTime - + ", doneCompleteTime=" + doneCompleteTime - + ", lastSuccessTime=" + lastSuccessTime - + ", lastFailedTime=" + lastFailedTime - + ", failedAttemptCount=" + failedAttemptCount - + ", lastAttempt=" + lastAttempt + "]"; - } - - } - class AuditFileSpoolAttempt { Date attemptTime; String status; @@ -755,22 +659,18 @@ public void run() { //This is done to clear the MDC context to avoid issue with Ranger Auditing for Knox MDC.clear(); runLogAudit(); - } catch (Throwable t) { - logger.fatal("Exited thread without abnormaly. queue=" + } catch (Exception t) { + logger.error("Exited thread without abnormaly. queue=" + consumerProvider.getName(), t); } } public void runLogAudit() { - // boolean isResumed = false; while (true) { try { if (isDestDown) { - logger.info("Destination is down. sleeping for " - + retryDestinationMS - + " milli seconds. indexQueue=" + indexQueue.size() - + ", queueName=" + queueProvider.getName() - + ", consumer=" + consumerProvider.getName()); + logger.info("Destination is down. sleeping for {} milli seconds. indexQueue={}, queueName={}, consumer={}", + retryDestinationMS, indexQueue.size(), queueProvider.getName(), consumerProvider.getName()); Thread.sleep(retryDestinationMS); } @@ -793,21 +693,18 @@ public void runLogAudit() { boolean isRemoveIndex = false; File consumerFile = new File( - currentConsumerIndexRecord.filePath); + currentConsumerIndexRecord.getFilePath()); if (!consumerFile.exists()) { - logger.error("Consumer file=" + consumerFile.getPath() - + " not found."); + logger.error("Consumer file={} not found.", consumerFile.getPath()); printIndex(); isRemoveIndex = true; } else { // Let's open the file to write - BufferedReader br = new BufferedReader(new FileReader( - currentConsumerIndexRecord.filePath)); - try { - int startLine = currentConsumerIndexRecord.linePosition; + try (BufferedReader br = new BufferedReader(new FileReader(currentConsumerIndexRecord.getFilePath()))) { + int startLine = currentConsumerIndexRecord.getLinePosition(); String line; int currLine = 0; - List lines = new ArrayList(); + List lines = new ArrayList<>(); while ((line = br.readLine()) != null) { currLine++; if (currLine < startLine) { @@ -823,7 +720,7 @@ public void runLogAudit() { lines.clear(); } } - if (lines.size() > 0) { + if (!lines.isEmpty()) { boolean ret = sendEvent(lines, currentConsumerIndexRecord, currLine); if (!ret) { @@ -831,29 +728,22 @@ public void runLogAudit() { } lines.clear(); } - logger.info("Done reading file. file=" - + currentConsumerIndexRecord.filePath - + ", queueName=" + queueProvider.getName() - + ", consumer=" + consumerProvider.getName()); + logger.info("Done reading file. file={}, queueName={}, consumer={}", currentConsumerIndexRecord.getFilePath(), queueProvider.getName(), consumerProvider.getName()); // The entire file is read - currentConsumerIndexRecord.status = SPOOL_FILE_STATUS.done; - currentConsumerIndexRecord.doneCompleteTime = new Date(); - currentConsumerIndexRecord.lastAttempt = true; + currentConsumerIndexRecord.setStatus(SPOOL_FILE_STATUS.done); + currentConsumerIndexRecord.setDoneCompleteTime(new Date()); + currentConsumerIndexRecord.setLastAttempt(true); isRemoveIndex = true; } catch (Exception ex) { isDestDown = true; - logError("Destination down. queueName=" - + queueProvider.getName() + ", consumer=" - + consumerProvider.getName()); + logError("Destination down. queueName={}, consumer={}", queueProvider.getName(), consumerProvider.getName()); lastAttemptTime = System.currentTimeMillis(); // Update the index file - currentConsumerIndexRecord.lastFailedTime = new Date(); - currentConsumerIndexRecord.failedAttemptCount++; - currentConsumerIndexRecord.lastAttempt = false; + currentConsumerIndexRecord.setLastFailedTime(new Date()); + currentConsumerIndexRecord.setFailedAttemptCount(currentConsumerIndexRecord.getFailedAttemptCount()+1); + currentConsumerIndexRecord.setLastAttempt(false); saveIndexFile(); - } finally { - br.close(); } } if (isRemoveIndex) { @@ -864,12 +754,12 @@ public void runLogAudit() { } } catch (InterruptedException e) { logger.info("Caught exception in consumer thread. Shutdown might be in progress"); - } catch (Throwable t) { + break; + } catch (Exception t) { logger.error("Exception in destination writing thread.", t); } } - logger.info("Exiting file spooler. provider=" + queueProvider.getName() - + ", consumer=" + consumerProvider.getName()); + logger.info("Exiting file spooler. provider={}, consumer={}", queueProvider.getName(), consumerProvider.getName()); } private boolean sendEvent(List lines, AuditIndexRecord indexRecord, @@ -879,28 +769,22 @@ private boolean sendEvent(List lines, AuditIndexRecord indexRecord, ret = consumerProvider.logJSON(lines); if (!ret) { // Need to log error after fixed interval - logError("Error sending logs to consumer. provider=" - + queueProvider.getName() + ", consumer=" - + consumerProvider.getName()); + logError("Error sending logs to consumer. provider={}, consumer={}", queueProvider.getName(), consumerProvider.getName()); } else { // Update index and save - indexRecord.linePosition = currLine; - indexRecord.status = SPOOL_FILE_STATUS.read_inprogress; - indexRecord.lastSuccessTime = new Date(); - indexRecord.lastAttempt = true; + indexRecord.setLinePosition(currLine); + indexRecord.setStatus(SPOOL_FILE_STATUS.read_inprogress); + indexRecord.setLastSuccessTime(new Date()); + indexRecord.setLastAttempt(true); saveIndexFile(); if (isDestDown) { isDestDown = false; - logger.info("Destination up now. " + indexRecord.filePath - + ", queueName=" + queueProvider.getName() - + ", consumer=" + consumerProvider.getName()); + logger.info("Destination up now. {}, queueName={}, consumer={}", indexRecord.getFilePath(), queueProvider.getName(), consumerProvider.getName()); } } - } catch (Throwable t) { - logger.error("Error while sending logs to consumer. provider=" - + queueProvider.getName() + ", consumer=" - + consumerProvider.getName() + ", log=" + lines, t); + } catch (Exception t) { + logger.error("Error while sending logs to consumer. provider={}, consumer={}, logEventCount={}", queueProvider.getName(), consumerProvider.getName(), lines.size(), t); } return ret; diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditQueue.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditQueue.java index e1667a47c0..e2d974121c 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditQueue.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditQueue.java @@ -21,15 +21,15 @@ import java.util.Properties; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.destination.AuditDestination; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.provider.BaseAuditHandler; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class AuditQueue extends BaseAuditHandler { - private static final Log LOG = LogFactory.getLog(AuditQueue.class); + private static final Logger LOG = LoggerFactory.getLogger(AuditQueue.class); public static final int AUDIT_MAX_QUEUE_SIZE_DEFAULT = 1024 * 1024; public static final int AUDIT_BATCH_INTERVAL_DEFAULT_MS = 3000; @@ -114,7 +114,7 @@ public void init(Properties props, String basePropertyName) { fileSpooler = new AuditFileSpool(this, consumer); if (!fileSpooler.init(props, basePropertyName)) { fileSpoolerEnabled = false; - LOG.fatal("Couldn't initialize file spooler. Disabling it. queue=" + LOG.error("Couldn't initialize file spooler. Disabling it. queue=" + getName() + ", consumer=" + consumer.getName()); } } else { diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditSummaryQueue.java b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditSummaryQueue.java index 4c250336c1..cef23db6dc 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditSummaryQueue.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/queue/AuditSummaryQueue.java @@ -28,19 +28,19 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.log4j.MDC; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; /** * This is a non-blocking queue with no limit on capacity. */ public class AuditSummaryQueue extends AuditQueue implements Runnable { - private static final Log logger = LogFactory - .getLog(AuditSummaryQueue.class); + private static final Logger logger = LoggerFactory + .getLogger(AuditSummaryQueue.class); public static final String PROP_SUMMARY_INTERVAL = "summary.interval.ms"; @@ -151,7 +151,7 @@ public void run() { MDC.clear(); runLogAudit(); } catch (Throwable t) { - logger.fatal("Exited thread without abnormaly. queue=" + getName(), + logger.error("Exited thread without abnormaly. queue=" + getName(), t); } } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/test/TestEvents.java b/agents-audit/src/main/java/org/apache/ranger/audit/test/TestEvents.java index 57f76d8150..5bc3b96c2d 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/test/TestEvents.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/test/TestEvents.java @@ -18,14 +18,13 @@ */ package org.apache.ranger.audit.test; -import org.apache.commons.logging.Log; -import org.apache.log4j.xml.DOMConfigurator; import org.apache.ranger.audit.model.AuditEventBase; import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.audit.model.EnumRepositoryType; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.provider.AuditProviderFactory; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; @@ -34,11 +33,9 @@ public class TestEvents { - private static final Log LOG = LogFactory.getLog(TestEvents.class); + private static final Logger LOG = LoggerFactory.getLogger(TestEvents.class); public static void main(String[] args) { - DOMConfigurator.configure("log4j.xml"); - LOG.info("==> TestEvents.main()"); try { @@ -48,28 +45,19 @@ public static void main(String[] args) { File propFile = new File(AUDIT_PROPERTIES_FILE); - if(propFile.exists()) { + if(!propFile.exists()) { LOG.info("Loading Audit properties file" + AUDIT_PROPERTIES_FILE); - - auditProperties.load(new FileInputStream(propFile)); + try(FileInputStream fileInputStream = new FileInputStream(propFile)) { + auditProperties.load(fileInputStream); + } } else { LOG.info("Audit properties file missing: " + AUDIT_PROPERTIES_FILE); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.url", "jdbc:mysql://localhost:3306/xa_db"); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.user", "xaaudit"); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.password", "xaaudit"); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.driver", "com.mysql.jdbc.Driver"); - auditProperties.setProperty("xasecure.audit.is.enabled", "true"); auditProperties.setProperty("xasecure.audit.log4j.is.enabled", "false"); auditProperties.setProperty("xasecure.audit.log4j.is.async", "false"); auditProperties.setProperty("xasecure.audit.log4j.async.max.queue.size", "100000"); auditProperties.setProperty("xasecure.audit.log4j.async.max.flush.interval.ms", "30000"); - auditProperties.setProperty("xasecure.audit.db.is.enabled", "false"); - auditProperties.setProperty("xasecure.audit.db.is.async", "true"); - auditProperties.setProperty("xasecure.audit.db.async.max.queue.size", "100000"); - auditProperties.setProperty("xasecure.audit.db.async.max.flush.interval.ms", "30000"); - auditProperties.setProperty("xasecure.audit.db.batch.size", "100"); } AuditProviderFactory factory = new AuditProviderFactory(); diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java new file mode 100644 index 0000000000..fd1c96e90e --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + +public abstract class AbstractKerberosUser implements KerberosUser { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractKerberosUser.class); + + static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + /** + * Percentage of the ticket window to use before we renew the TGT. + */ + static final float TICKET_RENEW_WINDOW = 0.80f; + + protected final AtomicBoolean loggedIn = new AtomicBoolean(false); + + protected Subject subject; + protected LoginContext loginContext; + + public AbstractKerberosUser() { + } + + /** + * Performs a login using the specified principal and keytab. + * + * @throws LoginException if the login fails + */ + @Override + public synchronized void login() throws LoginException { + if (isLoggedIn()) { + return; + } + + try { + // If it's the first time ever calling login then we need to initialize a new context + if (loginContext == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Initializing new login context..."); + } + if (this.subject == null) { + // only create a new subject if a current one does not exist + // other classes may be referencing an existing subject and replacing it may break functionality of those other classes after relogin + this.subject = new Subject(); + } + this.loginContext = createLoginContext(subject); + } + + loginContext.login(); + loggedIn.set(true); + if (LOG.isDebugEnabled()) { + LOG.debug("Successful login for {}", new Object[]{getPrincipal()}); + } + } catch (LoginException le) { + LoginException loginException = new LoginException("Unable to login with " + getPrincipal() + " due to: " + le.getMessage()); + loginException.setStackTrace(le.getStackTrace()); + throw loginException; + } + } + + protected abstract LoginContext createLoginContext(final Subject subject) throws LoginException; + + /** + * Performs a logout of the current user. + * + * @throws LoginException if the logout fails + */ + @Override + public synchronized void logout() throws LoginException { + if (!isLoggedIn()) { + return; + } + + try { + loginContext.logout(); + loggedIn.set(false); + LOG.debug("Successful logout for {}", new Object[]{getPrincipal()}); + + loginContext = null; + } catch (LoginException e) { + throw new LoginException("Logout failed due to: " + e.getMessage()); + } + } + + /** + * Executes the PrivilegedAction as this user. + * + * @param action the action to execute + * @param the type of result + * @return the result of the action + * @throws IllegalStateException if this method is called while not logged in + */ + @Override + public T doAs(final PrivilegedAction action) throws IllegalStateException { + if (!isLoggedIn()) { + throw new IllegalStateException("Must login before executing actions"); + } + + return Subject.doAs(subject, action); + } + + /** + * Executes the PrivilegedAction as this user. + * + * @param action the action to execute + * @param the type of result + * @return the result of the action + * @throws IllegalStateException if this method is called while not logged in + * @throws PrivilegedActionException if an exception is thrown from the action + */ + @Override + public T doAs(final PrivilegedExceptionAction action) + throws IllegalStateException, PrivilegedActionException { + if (!isLoggedIn()) { + throw new IllegalStateException("Must login before executing actions"); + } + + return Subject.doAs(subject, action); + } + + /** + * Re-login a user from keytab if TGT is expired or is close to expiry. + * + * @throws LoginException if an error happens performing the re-login + */ + @Override + public synchronized boolean checkTGTAndRelogin() throws LoginException { + final KerberosTicket tgt = getTGT(); + if (tgt == null) { + LOG.debug("TGT was not found"); + } + + if (tgt != null && System.currentTimeMillis() < getRefreshTime(tgt)) { + LOG.debug("TGT was found, but has not reached expiration window"); + return false; + } + + LOG.debug("Performing relogin for {}", new Object[]{getPrincipal()}); + logout(); + login(); + return true; + } + + /** + * Get the Kerberos TGT. + * + * @return the user's TGT or null if none was found + */ + private synchronized KerberosTicket getTGT() { + final Set tickets = subject.getPrivateCredentials(KerberosTicket.class); + + for (KerberosTicket ticket : tickets) { + if (isTGSPrincipal(ticket.getServer())) { + return ticket; + } + } + + return null; + } + + /** + * TGS must have the server principal of the form "krbtgt/FOO@FOO". + * + * @param principal the principal to check + * @return true if the principal is the TGS, false otherwise + */ + private boolean isTGSPrincipal(final KerberosPrincipal principal) { + if (principal == null) { + return false; + } + + if (principal.getName().equals("krbtgt/" + principal.getRealm() + "@" + principal.getRealm())) { + if (LOG.isTraceEnabled()) { + LOG.trace("Found TGT principal: " + principal.getName()); + } + return true; + } + + return false; + } + + private long getRefreshTime(final KerberosTicket tgt) { + long start = tgt.getStartTime().getTime(); + long end = tgt.getEndTime().getTime(); + + if (LOG.isTraceEnabled()) { + final SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); + final String startDate = dateFormat.format(new Date(start)); + final String endDate = dateFormat.format(new Date(end)); + LOG.trace("TGT valid starting at: " + startDate); + LOG.trace("TGT expires at: " + endDate); + } + + return start + (long) ((end - start) * TICKET_RENEW_WINDOW); + } + + /** + * @return true if this user is currently logged in, false otherwise + */ + @Override + public boolean isLoggedIn() { + return loggedIn.get(); + } + + @Override + public String toString() { + return "KerberosUser{" + + "principal='" + getPrincipal() + '\'' + + ", loggedIn=" + loggedIn + + '}'; + } +} + diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractRangerAuditWriter.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractRangerAuditWriter.java new file mode 100644 index 0000000000..0e74e3bd4b --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/AbstractRangerAuditWriter.java @@ -0,0 +1,392 @@ +package org.apache.ranger.audit.utils; + +/* + * 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 org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.*; +import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; +import java.util.Date; +import java.util.Map; +import java.util.Properties; + +/** + * This is Abstract class to have common properties of Ranger Audit HDFS Destination Writer. + */ +public abstract class AbstractRangerAuditWriter implements RangerAuditWriter { + private static final Logger logger = LoggerFactory.getLogger(AbstractRangerAuditWriter.class); + + public static final String PROP_FILESYSTEM_DIR = "dir"; + public static final String PROP_FILESYSTEM_SUBDIR = "subdir"; + public static final String PROP_FILESYSTEM_FILE_NAME_FORMAT = "filename.format"; + public static final String PROP_FILESYSTEM_FILE_ROLLOVER = "file.rollover.sec"; + public static final String PROP_FILESYSTEM_ROLLOVER_PERIOD = "file.rollover.period"; + public static final String PROP_FILESYSTEM_FILE_EXTENSION = ".log"; + public Configuration conf = null; + public FileSystem fileSystem = null; + public Map auditConfigs = null; + public Path auditPath = null; + public PrintWriter logWriter = null; + public RollingTimeUtil rollingTimeUtil = null; + public String auditProviderName = null; + public String fullPath = null; + public String parentFolder = null; + public String currentFileName = null; + public String logFileNameFormat = null; + public String logFolder = null; + public String fileExtension = null; + public String rolloverPeriod = null; + public String fileSystemScheme = null; + public Date nextRollOverTime = null; + public int fileRolloverSec = 24 * 60 * 60; // In seconds + public boolean rollOverByDuration = false; + public volatile FSDataOutputStream ostream = null; // output stream wrapped in logWriter + private boolean isHFlushCapableStream = false; + protected boolean reUseLastLogFile = false; + + @Override + public void init(Properties props, String propPrefix, String auditProviderName, Map auditConfigs) { + // Initialize properties for this class + // Initial folder and file properties + logger.info("==> AbstractRangerAuditWriter.init()"); + this.auditProviderName = auditProviderName; + this.auditConfigs = auditConfigs; + + init(props,propPrefix); + + logger.info("<== AbstractRangerAuditWriter.init()"); + } + + public void createFileSystemFolders() throws Exception { + + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.createFileSystemFolders()"); + } + // Create a new file + Date currentTime = new Date(); + String fileName = MiscUtil.replaceTokens(logFileNameFormat, currentTime.getTime()); + parentFolder = MiscUtil.replaceTokens(logFolder, currentTime.getTime()); + fullPath = parentFolder + Path.SEPARATOR + fileName; + String defaultPath = fullPath; + conf = createConfiguration(); + URI uri = URI.create(fullPath); + fileSystem = FileSystem.get(uri, conf); + auditPath = new Path(fullPath); + fileSystemScheme = getFileSystemScheme(); + logger.info("Checking whether log file exists. "+ fileSystemScheme + "Path= " + fullPath + ", UGI=" + MiscUtil.getUGILoginUser()); + int i = 0; + while (fileSystem.exists(auditPath)) { + i++; + int lastDot = defaultPath.lastIndexOf('.'); + String baseName = defaultPath.substring(0, lastDot); + String extension = defaultPath.substring(lastDot); + fullPath = baseName + "." + i + extension; + auditPath = new Path(fullPath); + logger.info("Checking whether log file exists. "+ fileSystemScheme + "Path= " + fullPath); + } + logger.info("Log file doesn't exists. Will create and use it. "+ fileSystemScheme + "Path= " + fullPath); + + // Create parent folders + createParents(auditPath, fileSystem); + + currentFileName = fullPath; + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.createFileSystemFolders()"); + } + } + + public Configuration createConfiguration() { + Configuration conf = new Configuration(); + for (Map.Entry entry : auditConfigs.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + // for ease of install config file may contain properties with empty value, skip those + if (StringUtils.isNotEmpty(value)) { + conf.set(key, value); + } + logger.info("Adding property to "+ fileSystemScheme + " + config: " + key + " => " + value); + } + + logger.info("Returning " + fileSystemScheme + "Filesystem Config: " + conf.toString()); + return conf; + } + + public void createParents(Path pathLogfile, FileSystem fileSystem) + throws Exception { + logger.info("Creating parent folder for " + pathLogfile); + Path parentPath = pathLogfile != null ? pathLogfile.getParent() : null; + + if (parentPath != null && fileSystem != null + && !fileSystem.exists(parentPath)) { + fileSystem.mkdirs(parentPath); + } + } + + public void init(Properties props, String propPrefix) { + + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.init()"); + } + + String logFolderProp = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_FILESYSTEM_DIR); + if (StringUtils.isEmpty(logFolderProp)) { + logger.error("File destination folder is not configured. Please set " + + propPrefix + "." + + PROP_FILESYSTEM_DIR + ". name=" + + auditProviderName); + return; + } + + String logSubFolder = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_FILESYSTEM_SUBDIR); + if (StringUtils.isEmpty(logSubFolder)) { + logSubFolder = "%app-type%/%time:yyyyMMdd%"; + } + + logFileNameFormat = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_FILESYSTEM_FILE_NAME_FORMAT); + fileRolloverSec = MiscUtil.getIntProperty(props, propPrefix + "." + PROP_FILESYSTEM_FILE_ROLLOVER, fileRolloverSec); + + if (StringUtils.isEmpty(fileExtension)) { + setFileExtension(PROP_FILESYSTEM_FILE_EXTENSION); + } + + if (logFileNameFormat == null || logFileNameFormat.isEmpty()) { + logFileNameFormat = "%app-type%_ranger_audit_%hostname%" + fileExtension; + } + + logFolder = logFolderProp + "/" + logSubFolder; + + logger.info("logFolder=" + logFolder + ", destName=" + auditProviderName); + logger.info("logFileNameFormat=" + logFileNameFormat + ", destName="+ auditProviderName); + logger.info("config=" + auditConfigs.toString()); + + rolloverPeriod = MiscUtil.getStringProperty(props, propPrefix + "." + PROP_FILESYSTEM_ROLLOVER_PERIOD); + rollingTimeUtil = RollingTimeUtil.getInstance(); + + //file.rollover.period is used for rolling over. If it could compute the next roll over time using file.rollover.period + //it fall back to use file.rollover.sec for find next rollover time. If still couldn't find default will be 1day window + //for rollover. + if(StringUtils.isEmpty(rolloverPeriod) ) { + rolloverPeriod = rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec); + } + + try { + nextRollOverTime = rollingTimeUtil.computeNextRollingTime(rolloverPeriod); + } catch ( Exception e) { + logger.warn("Rollover by file.rollover.period failed...will be using the file.rollover.sec for "+ fileSystemScheme + " audit file rollover...", e); + rollOverByDuration = true; + nextRollOverTime = rollOverByDuration(); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.init()"); + } + + } + + public void closeFileIfNeeded() { + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.closeFileIfNeeded()"); + } + + if (logWriter == null) { + if (logger.isDebugEnabled()){ + logger.debug("Log writer is null, aborting rollover condition check!"); + } + return; + } + + if ( System.currentTimeMillis() >= nextRollOverTime.getTime() ) { + logger.info("Closing file. Rolling over. name = {}, fileName = {}", auditProviderName, currentFileName); + logWriter.flush(); + closeWriter(); + resetWriter(); + currentFileName = null; + reUseLastLogFile = false; + + if (!rollOverByDuration) { + try { + if(StringUtils.isEmpty(rolloverPeriod) ) { + rolloverPeriod = rollingTimeUtil.convertRolloverSecondsToRolloverPeriod(fileRolloverSec); + } + nextRollOverTime = rollingTimeUtil.computeNextRollingTime(rolloverPeriod); + } catch ( Exception e) { + logger.warn("Rollover by file.rollover.period failed", e); + logger.warn("Using the file.rollover.sec for {} audit file rollover...", fileSystemScheme); + nextRollOverTime = rollOverByDuration(); + } + } else { + nextRollOverTime = rollOverByDuration(); + } + } + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.closeFileIfNeeded()"); + } + } + + public Date rollOverByDuration() { + long rollOverTime = rollingTimeUtil.computeNextRollingTime(fileRolloverSec,nextRollOverTime); + return new Date(rollOverTime); + } + + public PrintWriter createWriter() throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.createWriter()"); + } + + if (logWriter == null) { + boolean appendMode = false; + // if append is supported, reuse last log file + if (reUseLastLogFile && fileSystem.hasPathCapability(auditPath, CommonPathCapabilities.FS_APPEND)) { + logger.info("Appending to last log file. auditPath = {}", fullPath); + try { + ostream = fileSystem.append(auditPath); + appendMode = true; + } catch (Exception e){ + logger.error("Failed to append to file {} due to {}", fullPath, e.getMessage()); + logger.info("Falling back to create a new log file!"); + appendMode = false; + } + } + if (!appendMode) { + // Create the file to write + logger.info("Creating new log file. auditPath = {}", fullPath); + createFileSystemFolders(); + ostream = fileSystem.create(auditPath); + } + logWriter = new PrintWriter(ostream); + isHFlushCapableStream = ostream.hasCapability(StreamCapabilities.HFLUSH); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.createWriter()"); + } + + return logWriter; + } + + /** + * Closes the writer after writing audits + **/ + public void closeWriter() { + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.closeWriter()"); + } + + if (ostream != null) { + try { + ostream.close(); + } catch (IOException e) { + logger.error("Error closing the stream {}", e.getMessage()); + } + } + if (logWriter != null) + logWriter.close(); + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.closeWriter()"); + } + } + + public void resetWriter() { + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.resetWriter()"); + } + + logWriter = null; + ostream = null; + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.resetWriter()"); + } + } + + @Override + public void flush() { + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.flush() " + fileSystemScheme); + } + if (ostream != null) { + try { + synchronized (this) { + if (ostream != null) + // 1) PrinterWriter does not have bufferring of its own so + // we need to flush its underlying stream + // 2) HDFS flush() does not really flush all the way to disk. + if (isHFlushCapableStream) { + //Checking HFLUSH capability of the stream because of HADOOP-13327. + //For S3 filesysttem, hflush throws UnsupportedOperationException and hence we call flush. + ostream.hflush(); + } else { + ostream.flush(); + } + if (logger.isDebugEnabled()) { + logger.debug("Flush " + fileSystemScheme + " audit logs completed....."); + } + } + } catch (IOException e) { + logger.error("Error on flushing log writer: " + e.getMessage() + + "\nException will be ignored. name=" + auditProviderName + ", fileName=" + currentFileName); + } + } + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.flush()"); + } + } + + public boolean logFileToHDFS(File file) throws Exception { + boolean ret = false; + if (logger.isDebugEnabled()) { + logger.debug("==> AbstractRangerAuditWriter.logFileToHDFS()"); + } + + if (logWriter == null) { + // Create the file to write + createFileSystemFolders(); + logger.info("Copying the Audit File" + file.getName() + " to HDFS Path" + fullPath); + Path destPath = new Path(fullPath); + ret = FileUtil.copy(file,fileSystem,destPath,false,conf); + } + + if (logger.isDebugEnabled()) { + logger.debug("<== AbstractRangerAuditWriter.logFileToHDFS()"); + } + return ret; + } + + public String getFileSystemScheme() { + String ret = null; + ret = logFolder.substring(0, (logFolder.indexOf(":"))); + ret = ret.toUpperCase(); + return ret; + } + + public void setFileExtension(String fileExtension) { + this.fileExtension = fileExtension; + } +} \ No newline at end of file diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java index cc61fb88ac..2b59ab6802 100644 --- a/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/InMemoryJAASConfiguration.java @@ -19,7 +19,6 @@ package org.apache.ranger.audit.utils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.lang.ArrayUtils; import org.apache.hadoop.security.SecurityUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -119,23 +118,26 @@ public final class InMemoryJAASConfiguration extends Configuration { private static final Logger LOG = LoggerFactory.getLogger(InMemoryJAASConfiguration.class); - private static final String JAAS_CONFIG_PREFIX_PARAM = "xasecure.audit.jaas."; - private static final String JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM = "loginModuleName"; - private static final String JAAS_CONFIG_LOGIN_MODULE_CONTROL_FLAG_PARAM = "loginModuleControlFlag"; - private static final String JAAS_CONFIG_LOGIN_OPTIONS_PREFIX = "option"; - private static final String JAAS_PRINCIPAL_PROP = "principal"; + public static final String JAAS_CONFIG_PREFIX_PARAM = "xasecure.audit.jaas."; + public static final String JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM = "loginModuleName"; + public static final String JAAS_CONFIG_LOGIN_MODULE_CONTROL_FLAG_PARAM = "loginModuleControlFlag"; + public static final String JAAS_CONFIG_LOGIN_OPTIONS_PREFIX = "option"; + public static final String JAAS_PRINCIPAL_PROP = "principal"; - private Configuration parent = null; - private Map> applicationConfigEntryMap = new HashMap<>(); + private final Configuration parent; + private final Map> applicationConfigEntryMap = new HashMap<>(); - public static void init(String propFile) throws Exception { + public static InMemoryJAASConfiguration init(String propFile) throws Exception { LOG.debug("==> InMemoryJAASConfiguration.init( {} ) ", propFile); - InputStream in = null; + InMemoryJAASConfiguration ret = null; + InputStream in = null; try { Properties properties = new Properties(); + in = ClassLoader.getSystemResourceAsStream(propFile); + if (in == null) { if (!propFile.startsWith("/")) { in = ClassLoader.getSystemResourceAsStream("/" + propFile); @@ -144,8 +146,10 @@ public static void init(String propFile) throws Exception { in = new FileInputStream(new File(propFile)); } } + properties.load(in); - init(properties); + + ret = init(properties); } catch (IOException e) { throw new Exception("Failed to load JAAS application properties", e); } finally { @@ -157,89 +161,101 @@ public static void init(String propFile) throws Exception { } } } + LOG.debug("<== InMemoryJAASConfiguration.init( {} ) ", propFile); + + return ret; } - public static void init(Properties properties) throws Exception { + public static InMemoryJAASConfiguration init(Properties properties) throws Exception { LOG.debug("==> InMemoryJAASConfiguration.init()"); + InMemoryJAASConfiguration ret = null; + if (properties != null && MapUtils.isNotEmpty(properties)) { - InMemoryJAASConfiguration conf = new InMemoryJAASConfiguration(properties); - Configuration.setConfiguration(conf); + ret = new InMemoryJAASConfiguration(properties); } else { throw new Exception("Failed to load JAAS application properties: properties NULL or empty!"); } LOG.debug("<== InMemoryJAASConfiguration.init()"); + + return ret; } @Override public AppConfigurationEntry[] getAppConfigurationEntry(String name) { - LOG.trace("==> InMemoryJAASConfiguration.getAppConfigurationEntry( {} )", name); + LOG.debug("==> InMemoryJAASConfiguration.getAppConfigurationEntry( {} )", name); AppConfigurationEntry[] ret = null; - if (parent != null) { - ret = parent.getAppConfigurationEntry(name); - } - if (ret == null || ret.length == 0) { - List retList = applicationConfigEntryMap.get(name); - if (retList != null && retList.size() > 0) { - int sz = retList.size(); - ret = new AppConfigurationEntry[sz]; - ret = retList.toArray(ret); - } + + if (parent != null) { + ret = parent.getAppConfigurationEntry(name); + } + + if (ret == null || ret.length == 0) { + List retList = applicationConfigEntryMap.get(name); + + if (retList != null && retList.size() > 0) { + ret = retList.toArray(new AppConfigurationEntry[retList.size()]); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== InMemoryJAASConfiguration.getAppConfigurationEntry( {} ) : {}", name, toString(ret)); } - LOG.trace("<== InMemoryJAASConfiguration.getAppConfigurationEntry( {} ) : {}", name, ArrayUtils.toString(ret)); + return ret; } private InMemoryJAASConfiguration(Properties prop) { parent = Configuration.getConfiguration(); + initialize(prop); } private void initialize(Properties properties) { LOG.debug("==> InMemoryJAASConfiguration.initialize()"); - int prefixLen = JAAS_CONFIG_PREFIX_PARAM.length(); - + int prefixLen = JAAS_CONFIG_PREFIX_PARAM.length(); Map> jaasClients = new HashMap<>(); + for(String key : properties.stringPropertyNames()) { if (key.startsWith(JAAS_CONFIG_PREFIX_PARAM)) { - String jaasKey = key.substring(prefixLen); - StringTokenizer tokenizer = new StringTokenizer(jaasKey, "."); - int tokenCount =tokenizer.countTokens(); + String jaasKey = key.substring(prefixLen); + StringTokenizer tokenizer = new StringTokenizer(jaasKey, "."); + int tokenCount = tokenizer.countTokens(); + if (tokenCount > 0) { - String clientId = tokenizer.nextToken(); + String clientId = tokenizer.nextToken(); SortedSet indexList = jaasClients.get(clientId); + if (indexList == null) { - indexList = new TreeSet(); + indexList = new TreeSet<>(); + jaasClients.put(clientId, indexList); } - String indexStr = tokenizer.nextToken(); - - int indexId = isNumeric(indexStr) ? Integer.parseInt(indexStr) : -1; + String indexStr = tokenizer.nextToken(); + int indexId = isNumeric(indexStr) ? Integer.parseInt(indexStr) : -1; Integer clientIdIndex = Integer.valueOf(indexId); if (!indexList.contains(clientIdIndex)) { indexList.add(clientIdIndex); } - } } } - for(String jaasClient : jaasClients.keySet()) { + for(String jaasClient : jaasClients.keySet()) { for(Integer index : jaasClients.get(jaasClient)) { - String keyPrefix = JAAS_CONFIG_PREFIX_PARAM + jaasClient + "."; if (index > -1) { keyPrefix = keyPrefix + String.valueOf(index) + "."; } - String keyParam = keyPrefix + JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM; + String keyParam = keyPrefix + JAAS_CONFIG_LOGIN_MODULE_NAME_PARAM; String loginModuleName = properties.getProperty(keyParam); if (loginModuleName == null) { @@ -252,11 +268,14 @@ private void initialize(Properties properties) { } keyParam = keyPrefix + JAAS_CONFIG_LOGIN_MODULE_CONTROL_FLAG_PARAM; - String controlFlag = properties.getProperty(keyParam); + + String controlFlag = properties.getProperty(keyParam); AppConfigurationEntry.LoginModuleControlFlag loginControlFlag = null; + if (controlFlag != null) { controlFlag = controlFlag.trim().toLowerCase(); + if (controlFlag.equals("optional")) { loginControlFlag = AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL; } else if (controlFlag.equals("requisite")) { @@ -278,14 +297,15 @@ private void initialize(Properties properties) { loginControlFlag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED; } + Map options = new HashMap<>(); + String optionPrefix = keyPrefix + JAAS_CONFIG_LOGIN_OPTIONS_PREFIX + "."; + int optionPrefixLen = optionPrefix.length(); - Map options = new HashMap<>(); - String optionPrefix = keyPrefix + JAAS_CONFIG_LOGIN_OPTIONS_PREFIX + "."; - int optionPrefixLen = optionPrefix.length(); for(String key : properties.stringPropertyNames()) { if (key.startsWith(optionPrefix)) { String optionKey = key.substring(optionPrefixLen); String optionVal = properties.getProperty(key); + if (optionVal != null) { optionVal = optionVal.trim(); @@ -298,6 +318,7 @@ private void initialize(Properties properties) { + optionVal + "]"); } } + options.put(optionKey, optionVal); } } @@ -306,30 +327,53 @@ private void initialize(Properties properties) { if (LOG.isDebugEnabled()) { StringBuilder sb = new StringBuilder(); + sb.append("Adding client: [").append(jaasClient).append("{").append(index).append("}]\n"); sb.append("\tloginModule: [").append(loginModuleName).append("]\n"); sb.append("\tcontrolFlag: [").append(loginControlFlag).append("]\n"); + for (String key : options.keySet()) { String val = options.get(key); + sb.append("\tOptions: [").append(key).append("] => [").append(val).append("]\n"); } + LOG.debug(sb.toString()); } List retList = applicationConfigEntryMap.get(jaasClient); + if (retList == null) { - retList = new ArrayList(); + retList = new ArrayList<>(); + applicationConfigEntryMap.put(jaasClient, retList); } - retList.add(entry); - + retList.add(entry); } } + LOG.debug("<== InMemoryJAASConfiguration.initialize()"); } private static boolean isNumeric(String str) { return str.matches("-?\\d+(\\.\\d+)?"); //match a number with optional '-' and decimal. } + + private String toString(AppConfigurationEntry[] entries) { + StringBuilder sb = new StringBuilder(); + + sb.append('['); + if (entries != null) { + for (AppConfigurationEntry entry : entries) { + sb.append("{ loginModuleName=").append(entry.getLoginModuleName()) + .append(", controlFlag=").append(entry.getControlFlag()) + .append(", options=").append(entry.getOptions()) + .append("}"); + } + } + sb.append(']'); + + return sb.toString(); + } } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java new file mode 100644 index 0000000000..1bbbca8d1e --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.utils; + +import org.apache.commons.lang3.Validate; +import org.slf4j.Logger; + +import javax.security.auth.login.LoginException; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * Helper class for processors to perform an action as a KerberosUser. + */ +public class KerberosAction { + + private final KerberosUser kerberosUser; + private final PrivilegedExceptionAction action; + private final Logger logger; + + public KerberosAction(final KerberosUser kerberosUser, + final PrivilegedExceptionAction action, + final Logger logger) { + this.kerberosUser = kerberosUser; + this.action = action; + this.logger = logger; + Validate.notNull(this.kerberosUser); + Validate.notNull(this.action); + Validate.notNull(this.logger); + } + + public T execute() throws Exception { + T result; + // lazily login the first time the processor executes + if (!kerberosUser.isLoggedIn()) { + try { + kerberosUser.login(); + logger.info("Successful login for " + kerberosUser.getPrincipal()); + } catch (LoginException e) { + throw new Exception("Login failed due to: " + e.getMessage(), e); + } + } + + // check if we need to re-login, will only happen if re-login window is reached (80% of TGT life) + try { + kerberosUser.checkTGTAndRelogin(); + } catch (LoginException e) { + throw new Exception("Relogin check failed due to: " + e.getMessage(), e); + } + + // attempt to execute the action, if an exception is caught attempt to logout/login and retry + try { + result = kerberosUser.doAs(action); + } catch (SecurityException se) { + logger.info("Privileged action failed, attempting relogin and retrying..."); + logger.debug("", se); + + try { + kerberosUser.logout(); + kerberosUser.login(); + result = kerberosUser.doAs(action); + } catch (Exception e) { + throw new Exception("Retrying privileged action failed due to: " + e.getMessage(), e); + } + } catch (PrivilegedActionException pae) { + final Exception cause = pae.getException(); + throw new Exception("Privileged action failed due to: " + cause.getMessage(), cause); + } + + return result; + } +} diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java new file mode 100644 index 0000000000..2667721609 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.security.auth.Subject; +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +/** + * Used to authenticate and execute actions when Kerberos is enabled and a keytab is being used. + * + * */ +public class KerberosJAASConfigUser extends AbstractKerberosUser { + private static final Logger LOG = LoggerFactory.getLogger(KerberosJAASConfigUser.class); + + private final String configName; + private final Configuration config; + + public KerberosJAASConfigUser(final String configName, final Configuration config) { + this.configName = configName; + this.config = config; + } + + + @Override + public String getPrincipal() { + String ret = null; + AppConfigurationEntry[] entries = config.getAppConfigurationEntry(configName); + + if (entries != null) { + for (AppConfigurationEntry entry : entries) { + if (entry.getOptions().containsKey(InMemoryJAASConfiguration.JAAS_PRINCIPAL_PROP)) { + ret = (String) entry.getOptions().get(InMemoryJAASConfiguration.JAAS_PRINCIPAL_PROP); + + break; + } + } + } + + return ret; + } + + @Override + protected LoginContext createLoginContext(Subject subject) throws LoginException { + if (LOG.isDebugEnabled()) { + LOG.debug("==> KerberosJAASConfigUser.createLoginContext()"); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== KerberosJAASConfigUser.createLoginContext(), Subject: " + subject); + } + + return new LoginContext(configName, subject, null, config); + } +} + diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosUser.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosUser.java new file mode 100644 index 0000000000..fb6003e2fd --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/KerberosUser.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.utils; + +import javax.security.auth.login.LoginException; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; + +/** + * A keytab-based user that can login/logout and perform actions as the given user. + */ +public interface KerberosUser { + + /** + * Performs a login for the given user. + * + * @throws LoginException if the login fails + */ + void login() throws LoginException; + + /** + * Performs a logout for the given user. + * + * @throws LoginException if the logout fails + */ + void logout() throws LoginException; + + /** + * Executes the given action as the given user. + * + * @param action the action to execute + * @param the type of response + * @return the result of the action + * @throws IllegalStateException if attempting to execute an action before performing a login + */ + T doAs(PrivilegedAction action) throws IllegalStateException; + + /** + * Executes the given action as the given user. + * + * @param action the action to execute + * @param the type of response + * @return the result of the action + * @throws IllegalStateException if attempting to execute an action before performing a login + * @throws PrivilegedActionException if the action itself threw an exception + */ + T doAs(PrivilegedExceptionAction action) + throws IllegalStateException, PrivilegedActionException; + + /** + * Performs a re-login if the TGT is close to expiration. + * + * @return true if a relogin was performed, false otherwise + * @throws LoginException if the relogin fails + */ + boolean checkTGTAndRelogin() throws LoginException; + + /** + * @return true if this user is currently logged in, false otherwise + */ + boolean isLoggedIn(); + + /** + * @return the principal for this user + */ + String getPrincipal(); + +} + diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/ORCFileUtil.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/ORCFileUtil.java new file mode 100644 index 0000000000..c2bee8aad6 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/ORCFileUtil.java @@ -0,0 +1,449 @@ +package org.apache.ranger.audit.utils; + +/* + * 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 com.fasterxml.jackson.annotation.JsonProperty; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hive.ql.exec.vector.*; +import org.apache.orc.CompressionKind; +import org.apache.orc.OrcFile; +import org.apache.orc.OrcFile.WriterOptions; +import org.apache.orc.TypeDescription; +import org.apache.orc.Writer; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.audit.model.EnumRepositoryType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.HashMap; +import java.text.Format; +import java.text.SimpleDateFormat; + +public class ORCFileUtil { + + private static final Logger logger = LoggerFactory.getLogger(ORCFileUtil.class); + + private static volatile ORCFileUtil me = null; + protected CompressionKind defaultCompression = CompressionKind.SNAPPY; + protected CompressionKind compressionKind = CompressionKind.NONE; + protected TypeDescription schema = null; + protected VectorizedRowBatch batch = null; + protected String auditSchema = null; + protected String dateFormat = "yyyy-MM-dd HH:mm:ss"; + + protected ArrayList schemaFields = new ArrayList<>(); + protected Map vectorizedRowBatchMap = new HashMap<>(); + protected int orcBufferSize; + protected long orcStripeSize; + + public static ORCFileUtil getInstance() { + ORCFileUtil orcFileUtil = me; + if (orcFileUtil == null) { + synchronized (ORCFileUtil.class) { + orcFileUtil = me; + if (orcFileUtil == null) { + me = orcFileUtil = new ORCFileUtil(); + } + } + } + return orcFileUtil; + } + + public void init(int orcBufferSize, long orcStripeSize, String compression) throws Exception{ + if (logger.isDebugEnabled()) { + logger.debug("==> ORCFileUtil.init()"); + } + this.orcBufferSize = orcBufferSize; + this.orcStripeSize = orcStripeSize; + this.compressionKind = getORCCompression(compression); + initORCAuditSchema(); + if (logger.isDebugEnabled()) { + logger.debug("<== ORCFileUtil.init() : orcBufferSize: " + orcBufferSize + " stripeSize: " + orcStripeSize + + " compression: " + compression); + } + } + + public Writer createWriter(Configuration conf, FileSystem fs, String path) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> ORCFileUtil.createWriter()"); + } + Writer ret = null; + WriterOptions writeOptions = OrcFile.writerOptions(conf) + .fileSystem(fs) + .setSchema(schema) + .bufferSize(orcBufferSize) + .stripeSize(orcStripeSize) + .compress(compressionKind); + + ret = OrcFile.createWriter(new Path(path), writeOptions); + if (logger.isDebugEnabled()) { + logger.debug("<== ORCFileUtil.createWriter()"); + } + return ret; + } + + public void close(Writer writer) throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> ORCFileUtil.close()"); + } + + writer.close(); + + if (logger.isDebugEnabled()) { + logger.debug("<== ORCFileUtil.close()"); + } + } + + public void log(Writer writer, Collection events) throws Exception { + int eventBatchSize = events.size(); + + if (logger.isDebugEnabled()) { + logger.debug("==> ORCFileUtil.log() : EventSize: " + eventBatchSize + "ORC bufferSize:" + orcBufferSize ); + } + + try { + for(AuthzAuditEvent event : events) { + int row = batch.size++; + for (int j=0;j ORCWriter.initORCAuditSchema()"); + } + auditSchema = getAuditSchema(); + Map schemaFieldTypeMap = getSchemaFieldTypeMap(); + schema = TypeDescription.fromString(auditSchema); + batch = schema.createRowBatch(orcBufferSize); + buildVectorRowBatch(schemaFieldTypeMap); + if (logger.isDebugEnabled()) { + logger.debug("<== ORCWriter.initORCAuditSchema()"); + } + } + + protected Map getSchemaFieldTypeMap() { + Map ret = new HashMap<>(); + + int index1 = auditSchema.indexOf("<"); + int index2 = auditSchema.indexOf(">"); + String subAuditSchema = auditSchema.substring(index1+1,index2); + String[] fields = subAuditSchema.split(","); + schemaFields = new ArrayList<>(); + + for (String field: fields) { + String[] flds = field.split(":"); + schemaFields.add(flds[0]); + ret.put(flds[0],flds[1]); + } + return ret; + } + + protected void buildVectorRowBatch(Map schemaFieldTypeMap) throws Exception { + int i = 0; + for (i=0;i cls = fld.getType(); + Object value = fld.get(event); + + ret.setField(fieldName); + ret.setType(cls.getName()); + ret.setValue(value); + } catch (Exception e){ + logger.error("Error while writing into ORC File:", e); + } + return ret; + } + + protected ColumnVector getColumnVectorType(String fieldType) throws Exception { + ColumnVector ret = null; + fieldType = fieldType.toLowerCase(); + switch(fieldType) { + case "int" : + case "bigint": + case "date": + case "boolean": + ret = new LongColumnVector(); + break; + case "string": + case "varchar": + case "char": + case "binary": + ret = new BytesColumnVector(); + break; + case "decimal": + ret = new DecimalColumnVector(10,5); + break; + case "double": + case "float": + ret = new DoubleColumnVector(); + break; + case "array": + case "map": + case "uniontype": + case "struct": + throw new Exception("Unsuppoted field Type"); + } + return ret; + } + + protected Long castLongObject(Object object) { + Long ret = 0l; + try { + if (object instanceof Long) + ret = ((Long) object); + else if (object instanceof Integer) { + ret = ((Integer) object).longValue(); + } else if (object instanceof String) { + ret = Long.valueOf((String) object); + } + } catch (Exception e) { + logger.error("Error while writing into ORC File:", e); + } + return ret; + } + + protected String castStringObject(Object object) { + String ret = null; + try { + if (object instanceof String) + ret = (String) object; + else if (object instanceof Date) { + ret = (getDateString((Date) object)); + } + } catch (Exception e) { + logger.error("Error while writing into ORC File:", e); + } + return ret; + } + + protected String getAuditSchema() { + if (logger.isDebugEnabled()) { + logger.debug("==> ORCWriter.getAuditSchema()"); + } + String ret = null; + String fieldStr = "struct<"; + StringBuilder sb = new StringBuilder(fieldStr); + + Class auditEventClass = AuthzAuditEvent.class; + for(Field fld: auditEventClass.getDeclaredFields()) { + if (fld.isAnnotationPresent(JsonProperty.class)) { + String field = fld.getName(); + String fieldType = getShortFieldType(fld.getType().getName()); + if (fieldType == null) { + continue; + } + fieldStr = field + ":" + fieldType + ","; + sb.append(fieldStr); + } + } + fieldStr = sb.toString(); + if (fieldStr.endsWith(",")) { + fieldStr = fieldStr.substring(0, fieldStr.length() - 1); + } + ret = fieldStr + ">"; + + if (logger.isDebugEnabled()) { + logger.debug("<== ORCWriter.getAuditSchema() AuditSchema: " + ret); + } + return ret; + } + + protected String getShortFieldType(String type){ + String ret = null; + switch(type) { + case "java.lang.String": + ret = "string"; + break; + case "int": + ret = "int"; + break; + case "short": + ret = "string"; + break; + case "java.util.Date": + ret = "string"; + break; + case "long": + ret = "bigint"; + break; + default: + ret = null; + } + return ret; + } + + class SchemaInfo { + String field = null; + String type = null; + Object value = null; + + public String getField() { + return field; + } + + public void setField(String field) { + this.field = field; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + } + + protected CompressionKind getORCCompression(String compression) { + CompressionKind ret; + if (compression == null) { + compression = defaultCompression.name().toLowerCase(); + } + switch(compression) { + case "snappy": + ret = CompressionKind.SNAPPY; + break; + case "lzo": + ret = CompressionKind.LZO; + break; + case "zlib": + ret = CompressionKind.ZLIB; + break; + case "none": + ret = CompressionKind.NONE; + break; + default: + ret = defaultCompression; + break; + } + return ret; + } + + public static void main(String[] args) throws Exception { + ORCFileUtil auditOrcFileUtil = new ORCFileUtil(); + auditOrcFileUtil.init(10000,100000L,"snappy"); + try { + Configuration conf = new Configuration(); + FileSystem fs = FileSystem.get(conf); + Writer write = auditOrcFileUtil.createWriter(conf, fs, "/tmp/test.orc"); + Collection events = getTestEvent(); + auditOrcFileUtil.log(write, events); + write.close(); + } catch (Exception e){ + e.printStackTrace(); + } + } + + protected static Collection getTestEvent() { + Collection events = new ArrayList<>(); + for (int idx=0;idx<20;idx++) { + AuthzAuditEvent event = new AuthzAuditEvent(); + event.setEventId(Integer.toString(idx)); + event.setClientIP("127.0.0.1"); + event.setAccessResult((short) 1); + event.setAclEnforcer("ranger-acl"); + event.setRepositoryName("hdfsdev"); + event.setRepositoryType(EnumRepositoryType.HDFS); + event.setResourcePath("/tmp/test-audit.log" +idx+idx+1); + event.setResourceType("file"); + event.setAccessType("read"); + event.setEventTime(new Date()); + event.setResultReason(Integer.toString(1)); + events.add(event); + } + return events; + } +} \ No newline at end of file diff --git a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerAuditWriter.java similarity index 64% rename from ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java rename to agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerAuditWriter.java index a2cfa7bcc4..fbe9301f47 100644 --- a/ugsync/src/main/java/org/apache/ranger/unixusersync/model/GroupUserInfo.java +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerAuditWriter.java @@ -1,3 +1,5 @@ +package org.apache.ranger.audit.utils; + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -17,24 +19,21 @@ * under the License. */ -package org.apache.ranger.unixusersync.model; - -import java.util.List; - -public class GroupUserInfo { - XGroupInfo xgroupInfo; - List xuserInfo; - - public XGroupInfo getXgroupInfo() { - return xgroupInfo; - } - public void setXgroupInfo(XGroupInfo xgroupInfo) { - this.xgroupInfo = xgroupInfo; - } - public List getXuserInfo() { - return xuserInfo; - } - public void setXuserInfo(List xuserInfo) { - this.xuserInfo = xuserInfo; - } +import java.io.File; +import java.util.Collection; +import java.util.Map; +import java.util.Properties; + +public interface RangerAuditWriter { + void init(Properties prop, String propPrefix, String auditProviderName, Map auditConfigs); + + boolean log(Collection events) throws Exception; + + boolean logFile(File file) throws Exception; + + void start(); + + void flush(); + + void stop(); } diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerJSONAuditWriter.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerJSONAuditWriter.java new file mode 100644 index 0000000000..f74f0cbd32 --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerJSONAuditWriter.java @@ -0,0 +1,245 @@ +package org.apache.ranger.audit.utils; + +/* + * 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 org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.PrintWriter; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.Collection; +import java.util.Map; +import java.util.Properties; + +/** + * Writes the Ranger audit to HDFS as JSON text + */ +public class RangerJSONAuditWriter extends AbstractRangerAuditWriter { + + private static final Logger logger = LoggerFactory.getLogger(RangerJSONAuditWriter.class); + public static final String PROP_HDFS_ROLLOVER_ENABLE_PERIODIC_ROLLOVER = "file.rollover.enable.periodic.rollover"; + public static final String PROP_HDFS_ROLLOVER_PERIODIC_ROLLOVER_CHECK_TIME = "file.rollover.periodic.rollover.check.sec"; + + protected String JSON_FILE_EXTENSION = ".log"; + + /* + * When enableAuditFilePeriodicRollOver is enabled, Audit File in HDFS would be closed by the defined period in + * xasecure.audit.destination.hdfs.file.rollover.sec. By default xasecure.audit.destination.hdfs.file.rollover.sec = 86400 sec + * and file will be closed midnight. Custom rollover time can be set by defining file.rollover.sec to desire time in seconds. + */ + private boolean enableAuditFilePeriodicRollOver = false; + + /* + Time frequency of next occurrence of periodic rollover check. By Default every 60 seconds the check is done. + */ + private long periodicRollOverCheckTimeinSec; + + public void init(Properties props, String propPrefix, String auditProviderName, Map auditConfigs) { + if (logger.isDebugEnabled()) { + logger.debug("==> RangerJSONAuditWriter.init()"); + } + init(); + super.init(props,propPrefix,auditProviderName,auditConfigs); + + // start AuditFilePeriodicRollOverTask if enabled. + enableAuditFilePeriodicRollOver = MiscUtil.getBooleanProperty(props, propPrefix + "." + PROP_HDFS_ROLLOVER_ENABLE_PERIODIC_ROLLOVER, false); + if (enableAuditFilePeriodicRollOver) { + periodicRollOverCheckTimeinSec = MiscUtil.getLongProperty(props, propPrefix + "." + PROP_HDFS_ROLLOVER_PERIODIC_ROLLOVER_CHECK_TIME, 60L); + try { + if (logger.isDebugEnabled()) { + logger.debug("rolloverPeriod: " + rolloverPeriod + " nextRollOverTime: " + nextRollOverTime + " periodicRollOverTimeinSec: " + periodicRollOverCheckTimeinSec); + } + startAuditFilePeriodicRollOverTask(); + } catch (Exception e) { + logger.warn("Error enabling audit file perodic rollover..! Default behavior will be"); + } + } + + if (logger.isDebugEnabled()) { + logger.debug("<== RangerJSONAuditWriter.init()"); + } + } + + public void init() { + setFileExtension(JSON_FILE_EXTENSION); + } + + synchronized public boolean logJSON(final Collection events) throws Exception { + PrintWriter out = null; + try { + if (logger.isDebugEnabled()) { + logger.debug("UGI = {}, will write to HDFS file = {}", MiscUtil.getUGILoginUser(), currentFileName); + } + out = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { + @Override + public PrintWriter run() throws Exception { + PrintWriter out = getLogFileStream(); + for (String event : events) { + out.println(event); + } + return out; + }; + }); + // flush and check the stream for errors + if (out.checkError()) { + // In theory, this count may NOT be accurate as part of the messages may have been successfully written. + // However, in practice, since client does buffering, either all or none would succeed. + logger.error("Stream encountered errors while writing audits to HDFS!"); + closeWriter(); + resetWriter(); + reUseLastLogFile = true; + return false; + } + } catch (Exception e) { + logger.error("Exception encountered while writing audits to HDFS!", e); + closeWriter(); + resetWriter(); + reUseLastLogFile = true; + return false; + } finally { + if (logger.isDebugEnabled()) { + logger.debug("Flushing HDFS audit. Event Size:" + events.size()); + } + if (out != null) { + out.flush(); + } + //closeWriter(); + } + + return true; + } + + @Override + public boolean log(Collection events) throws Exception { + return logJSON(events); + } + + synchronized public boolean logAsFile(final File file) throws Exception { + boolean ret = false; + if (logger.isDebugEnabled()) { + logger.debug("UGI=" + MiscUtil.getUGILoginUser() + + ". Will write to HDFS file=" + currentFileName); + } + Boolean retVal = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { + @Override + public Boolean run() throws Exception { + boolean ret = logFileToHDFS(file); + return Boolean.valueOf(ret); + }; + }); + ret = retVal.booleanValue(); + logger.info("Flushing HDFS audit File :" + file.getAbsolutePath() + file.getName()); + return ret; + } + + @Override + public boolean logFile(File file) throws Exception { + return logAsFile(file); + } + + synchronized public PrintWriter getLogFileStream() throws Exception { + if (!enableAuditFilePeriodicRollOver) { + // when periodic rollover is enabled closing of file is done by the file rollover monitoring task and hence don't need to + // close the file inline with audit logging. + closeFileIfNeeded(); + } + // Either there are no open log file or the previous one has been rolled over + PrintWriter logWriter = createWriter(); + return logWriter; + } + + + public void flush() { + if (logger.isDebugEnabled()) { + logger.debug("==> JSONWriter.flush() called. name=" + auditProviderName); + } + super.flush(); + if (logger.isDebugEnabled()) { + logger.debug("<== JSONWriter.flush()"); + } + } + + @Override + public void start() { + // nothing to start + } + + @Override + synchronized public void stop() { + if (logger.isDebugEnabled()) { + logger.debug("==> JSONWriter.stop()"); + } + if (logWriter != null) { + try { + logWriter.flush(); + logWriter.close(); + } catch (Throwable t) { + logger.error("Error on closing log writter. Exception will be ignored. name=" + + auditProviderName + ", fileName=" + currentFileName); + } + logWriter = null; + ostream = null; + } + if (logger.isDebugEnabled()) { + logger.debug("<== JSONWriter.stop()"); + } + } + + private void startAuditFilePeriodicRollOverTask() { + ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(new AuditFilePeriodicRollOverTaskThreadFactory()); + + if (logger.isDebugEnabled()) { + logger.debug("HDFSAuditDestination.startAuditFilePeriodicRollOverTask() strated.." + "Audit File rollover happens every " + rolloverPeriod ); + } + + executorService.scheduleAtFixedRate(new AuditFilePeriodicRollOverTask(), 0, periodicRollOverCheckTimeinSec, TimeUnit.SECONDS); + } + + class AuditFilePeriodicRollOverTaskThreadFactory implements ThreadFactory { + //Threadfactory to create a daemon Thread. + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "AuditFilePeriodicRollOverTask"); + t.setDaemon(true); + return t; + } + } + + private class AuditFilePeriodicRollOverTask implements Runnable { + public void run() { + if (logger.isDebugEnabled()) { + logger.debug("==> AuditFilePeriodicRollOverTask.run()"); + } + try { + closeFileIfNeeded(); + } catch (Exception excp) { + logger.error("AuditFilePeriodicRollOverTask Failed. Aborting..", excp); + } + if (logger.isDebugEnabled()) { + logger.debug("<== AuditFilePeriodicRollOverTask.run()"); + } + } + } +} \ No newline at end of file diff --git a/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerORCAuditWriter.java b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerORCAuditWriter.java new file mode 100644 index 0000000000..b7cddfc2fd --- /dev/null +++ b/agents-audit/src/main/java/org/apache/ranger/audit/utils/RangerORCAuditWriter.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.audit.utils; + +import org.apache.orc.Writer; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.audit.provider.MiscUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; +import java.util.Properties; + +/** + * This class writes the Ranger audits to HDFS as ORC files + * Refer README.TXT for enabling ORCWriter. + */ +public class RangerORCAuditWriter extends AbstractRangerAuditWriter { + private static final Logger logger = LoggerFactory.getLogger(RangerORCAuditWriter.class); + + protected static final String ORC_FILE_EXTENSION = ".orc"; + protected volatile ORCFileUtil orcFileUtil = null; + protected Writer orcLogWriter = null; + protected String fileType = "orc"; + protected String compression = null; + protected int orcBufferSize = 0; + protected int defaultbufferSize = 100000; + protected long orcStripeSize = 0; + protected long defaultStripeSize = 100000L; + + @Override + public void init(Properties props, String propPrefix, String auditProviderName, Map auditConfigs) { + if (logger.isDebugEnabled()) { + logger.debug("==> RangerORCAuditWriter.init()"); + } + init(props,propPrefix,auditProviderName); + super.init(props, propPrefix, auditProviderName, auditConfigs); + if (logger.isDebugEnabled()) { + logger.debug("<== RangerORCAuditWriter.init()"); + } + } + + synchronized public boolean logAuditAsORC(final Collection events) throws Exception { + boolean ret = false; + Writer out = null; + try { + if (logger.isDebugEnabled()) { + logger.debug("UGI=" + MiscUtil.getUGILoginUser() + + ". Will write to HDFS file=" + currentFileName); + } + + out = MiscUtil.executePrivilegedAction(new PrivilegedExceptionAction() { + @Override + public Writer run() throws Exception { + Writer out = getORCFileWrite(); + orcFileUtil.log(out,events); + return out; + } + }); + } catch (Exception e) { + orcLogWriter = null; + logger.error("Error while writing into ORC FileWriter", e); + throw e; + } finally { + if (logger.isDebugEnabled()) { + logger.debug("Flushing HDFS audit in ORC Format. Event Size:" + events.size()); + } + if (out != null) { + try { + //flush and close the ORC batch file + orcFileUtil.close(out); + ret = true; + } catch (Exception e) { + logger.error("Error while closing the ORC FileWriter", e); + throw e; + } + orcLogWriter = null; + } + } + return ret; + } + + @Override + public void flush() { + //For HDFSAuditDestionation with ORC format each file is flushed immediately after writing the ORC batch. + //So nothing to flush. + } + + @Override + public boolean log(Collection events) throws Exception { + return logAsORC(events); + } + + @Override + public void start() { + // Nothing to do here. We will open the file when the first log request comes + } + + @Override + public synchronized void stop() { + if (orcLogWriter != null) { + try { + orcFileUtil.close(orcLogWriter); + } catch (Throwable t) { + logger.error("Error on closing log ORC Writer. Exception will be ignored. name=" + + auditProviderName + ", fileName=" + currentFileName); + } + orcLogWriter = null; + } + } + + @Override + public boolean logFile(File file) throws Exception { + return false; + } + + // Creates ORC Write file + protected synchronized Writer getORCFileWrite() throws Exception { + if (logger.isDebugEnabled()) { + logger.debug("==> RangerORCAuditWriter.getORCFileWrite()"); + } + if (orcLogWriter == null) { + // Create the file to write + createFileSystemFolders(); + logger.info("Creating new log file. hdfPath=" + fullPath); + orcLogWriter = orcFileUtil.createWriter(conf, fileSystem, fullPath); + currentFileName = fullPath; + } + if (logger.isDebugEnabled()) { + logger.debug("<== RangerORCAuditWriter.getORCFileWrite()"); + } + return orcLogWriter; + } + + public boolean logAsORC(Collection events) throws Exception { + boolean ret = false; + Collection authzAuditEvents = getAuthzAuditEvents(events); + ret = logAuditAsORC(authzAuditEvents); + return ret; + } + + public Collection getAuthzAuditEvents(Collection events) throws Exception { + Collection ret = new ArrayList<>(); + for (String event : events) { + try { + AuthzAuditEvent authzAuditEvent = MiscUtil.fromJson(event, AuthzAuditEvent.class); + ret.add(authzAuditEvent); + } catch (Exception e) { + logger.error("Error converting to From JSON to AuthzAuditEvent=" + event); + throw e; + } + } + return ret; + } + + public void init(Properties props, String propPrefix, String auditProviderName) { + compression = MiscUtil.getStringProperty(props, propPrefix + "." + fileType +".compression"); + orcBufferSize = MiscUtil.getIntProperty(props, propPrefix + "." + fileType +".buffersize",defaultbufferSize); + orcStripeSize = MiscUtil.getLongProperty(props, propPrefix + "." + fileType +".stripesize",defaultStripeSize); + setFileExtension(ORC_FILE_EXTENSION); + try { + orcFileUtil = ORCFileUtil.getInstance(); + orcFileUtil.init(orcBufferSize, orcStripeSize, compression); + } catch ( Exception e) { + logger.error("Error while doing ORCWriter.init() ", e); + } + } +} \ No newline at end of file diff --git a/agents-audit/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java b/agents-audit/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java new file mode 100644 index 0000000000..3d65790d50 --- /dev/null +++ b/agents-audit/src/test/java/org/apache/ranger/audit/utils/RangerJSONAuditWriterTest.java @@ -0,0 +1,123 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.audit.utils; + +import org.apache.hadoop.fs.CommonPathCapabilities; +import org.apache.hadoop.fs.FileSystem; +import org.junit.Test; + +import java.io.IOException; +import java.util.Map; +import java.util.HashMap; +import java.util.Properties; +import java.util.Collections; +import java.io.PrintWriter; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class RangerJSONAuditWriterTest { + + public Properties props; + public Map auditConfigs; + + public void setup(){ + props = new Properties(); + props.setProperty("test.dir", "/tmp"); + auditConfigs = new HashMap<>(); + auditConfigs.put(FileSystem.FS_DEFAULT_NAME_KEY, FileSystem.DEFAULT_FS); + } + + @Test + public void checkReUseFlagInStreamErrors() throws Exception { + + RangerJSONAuditWriter jsonAuditWriter = spy(new RangerJSONAuditWriter()); + PrintWriter out = mock(PrintWriter.class); + + setup(); + jsonAuditWriter.init(props, "test", "localfs", auditConfigs); + + assertFalse(jsonAuditWriter.reUseLastLogFile); + when(jsonAuditWriter.getLogFileStream()).thenReturn(out); + when(out.checkError()).thenReturn(true); + assertFalse(jsonAuditWriter.logJSON(Collections.singleton("This event will not be logged!"))); + assertTrue(jsonAuditWriter.reUseLastLogFile); + + // cleanup + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + jsonAuditWriter.logJSON(Collections.singleton("cleaning up!")); + jsonAuditWriter.closeWriter(); + } + + @Test + public void checkAppendtoFileWhenExceptionsOccur() throws Exception { + RangerJSONAuditWriter jsonAuditWriter = spy(new RangerJSONAuditWriter()); + + setup(); + jsonAuditWriter.init(props, "test", "localfs", auditConfigs); + jsonAuditWriter.createFileSystemFolders(); + // File creation should fail with an exception which will trigger append next time. + when(jsonAuditWriter.fileSystem.create(jsonAuditWriter.auditPath)) + .thenThrow(new IOException("Creation not allowed!")); + jsonAuditWriter.logJSON(Collections.singleton("This event will not be logged!")); + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + + assertTrue(jsonAuditWriter.reUseLastLogFile); + assertNull(jsonAuditWriter.ostream); + assertNull(jsonAuditWriter.logWriter); + + jsonAuditWriter.fileSystem = mock(FileSystem.class); + when(jsonAuditWriter.fileSystem + .hasPathCapability(jsonAuditWriter.auditPath, CommonPathCapabilities.FS_APPEND)).thenReturn(true); + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); + // this will lead to an exception since append is called on mocks + jsonAuditWriter.logJSON(Collections.singleton( + "This event should be appended but won't be as appended we use mocks.")); + } + + + @Test + public void checkFileRolloverAfterThreshold() throws Exception { + RangerJSONAuditWriter jsonAuditWriter = spy(new RangerJSONAuditWriter()); + + setup(); + props.setProperty("test.file.rollover.enable.periodic.rollover", "true"); + props.setProperty("test.file.rollover.periodic.rollover.check.sec", "2"); + // rollover log file after this interval + jsonAuditWriter.fileRolloverSec = 5; // in seconds + jsonAuditWriter.init(props, "test", "localfs", auditConfigs); + + + assertTrue(jsonAuditWriter.logJSON(Collections.singleton("First file created and added this line!"))); + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); // cleanup + Thread.sleep(6000); + + assertFalse(jsonAuditWriter.reUseLastLogFile); + assertNull(jsonAuditWriter.ostream); + assertNull(jsonAuditWriter.logWriter); + + assertTrue(jsonAuditWriter.logJSON(Collections.singleton("Second file created since rollover happened!"))); + jsonAuditWriter.fileSystem.deleteOnExit(jsonAuditWriter.auditPath); // cleanup + jsonAuditWriter.closeWriter(); + } +} diff --git a/agents-common/conf/log4j.properties b/agents-common/conf/log4j.properties deleted file mode 100644 index dd22c6d428..0000000000 --- a/agents-common/conf/log4j.properties +++ /dev/null @@ -1,33 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -log4j.logger.xaaudit.org.apache.ranger.audit.provider.Log4jAuditProvider=INFO, hdfsAppender - - -log4j.appender.hdfsAppender=org.apache.log4j.HdfsRollingFileAppender -log4j.appender.hdfsAppender.hdfsDestinationDirectory=hdfs://%hostname%:8020/logs/application/%file-open-time:yyyyMMdd% -log4j.appender.hdfsAppender.hdfsDestinationFile=%hostname%-audit.log -log4j.appender.hdfsAppender.hdfsDestinationRolloverIntervalSeconds=86400 - -log4j.appender.hdfsAppender.localFileBufferDirectory=/tmp/logs/application/%hostname% -log4j.appender.hdfsAppender.localFileBufferFile=%file-open-time:yyyyMMdd-HHmm.ss%.log -log4j.appender.hdfsAppender.localFileBufferRolloverIntervalSeconds=15 -log4j.appender.hdfsAppender.localFileBufferArchiveDirectory=/tmp/logs/archive/application/%hostname% -log4j.appender.hdfsAppender.localFileBufferArchiveFileCount=12 - - -log4j.appender.hdfsAppender.layout=org.apache.log4j.PatternLayout -log4j.appender.hdfsAppender.layout.ConversionPattern=%d{yy/MM/dd HH:mm:ss} [%t]: %p %c{2}: %m%n -log4j.appender.hdfsAppender.encoding=UTF-8 diff --git a/agents-common/conf/logback.xml b/agents-common/conf/logback.xml new file mode 100644 index 0000000000..4e991c3f2b --- /dev/null +++ b/agents-common/conf/logback.xml @@ -0,0 +1,32 @@ + + + + + + System.out + + %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n + + + INFO + + + + + + diff --git a/agents-common/dev-support/spotbugsIncludeFile.xml b/agents-common/dev-support/spotbugsIncludeFile.xml new file mode 100644 index 0000000000..9a0a9261a9 --- /dev/null +++ b/agents-common/dev-support/spotbugsIncludeFile.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/agents-common/pom.xml b/agents-common/pom.xml index e44b7f6a87..502a1f716d 100644 --- a/agents-common/pom.xml +++ b/agents-common/pom.xml @@ -39,7 +39,7 @@ org.apache.ranger ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT .. @@ -47,6 +47,12 @@ com.sun.jersey jersey-bundle ${jersey-bundle.version} + + + javax.ws.rs + jsr311-api + + commons-lang @@ -54,32 +60,95 @@ ${commons.lang.version} - commons-logging - commons-logging - ${commons.logging.version} + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt.version} + + + net.minidev + json-smart + ${jsonsmart.version} org.apache.hadoop hadoop-common ${hadoop.version} + + + net.minidev + json-smart + + + io.netty + netty-handler + + + io.netty + netty-transport-native-epoll + + + org.apache.commons + commons-text + + + org.apache.commons + commons-configuration2 + + + log4j + * + + + org.slf4j + * + + + org.apache.commons + commons-compress + + + org.apache.zookeeper + zookeeper + + - log4j - log4j - ${log4j.version} + org.apache.commons + commons-text + ${commons.text.version} - junit - junit + org.apache.commons + commons-configuration2 + ${commons.configuration.version} - org.mockito - mockito-core + com.fasterxml.jackson.core + jackson-databind + ${fasterxml.jackson.databind.version} - org.codehaus.jackson - jackson-jaxrs - ${codehaus.jackson.version} + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + ${fasterxml.jackson.version} + + + jakarta.activation + jakarta.activation-api + + + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-base + ${fasterxml.jackson.version} + + + + + org.apache.ranger + ranger-plugins-cred + ${project.version} org.apache.ranger @@ -108,8 +177,48 @@ org.apache.ranger ranger-plugin-classloader - 2.1.0-SNAPSHOT + ${project.version} compile + + com.fasterxml.jackson.core + jackson-core + ${fasterxml.jackson.version} + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.vintage + junit-vintage-engine + ${junit.jupiter.version} + test + + + org.mockito + mockito-core + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test + diff --git a/agents-common/scripts/enable-agent.sh b/agents-common/scripts/enable-agent.sh index ad021feee5..d4c7816b5d 100755 --- a/agents-common/scripts/enable-agent.sh +++ b/agents-common/scripts/enable-agent.sh @@ -125,6 +125,8 @@ PROJ_LIB_PLUGIN_DIR=${PROJ_INSTALL_DIR}/${PLUGIN_DEPENDENT_LIB_DIR} HCOMPONENT_INSTALL_DIR_NAME=$(getInstallProperty 'COMPONENT_INSTALL_DIR_NAME') +# Install Environment property used for trino plugin. +INSTALL_ENV=$(getInstallProperty 'INSTALL_ENV') CUSTOM_USER=$(getInstallProperty 'CUSTOM_USER') CUSTOM_USER=${CUSTOM_USER// } @@ -214,6 +216,17 @@ elif [ "${HCOMPONENT_NAME}" = "elasticsearch" ]; then HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/plugins elif [ "${HCOMPONENT_NAME}" = "presto" ]; then HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/plugin/ranger + if [ ! -d "${HCOMPONENT_LIB_DIR}" ]; then + echo "INFO: Creating ${HCOMPONENT_LIB_DIR}" + mkdir -p ${HCOMPONENT_LIB_DIR} + fi +elif [ "${HCOMPONENT_NAME}" = "trino" ]; then + HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/plugin/ranger + #Configure ranger plugin location for trino docker environment + if [ "${INSTALL_ENV}" = "docker" ];then + HCOMPONENT_LIB_DIR=/usr/lib/trino/plugin/ranger + fi + if [ ! -d "${HCOMPONENT_LIB_DIR}" ]; then echo "INFO: Creating ${HCOMPONENT_LIB_DIR}" mkdir -p ${HCOMPONENT_LIB_DIR} @@ -248,6 +261,11 @@ elif [ "${HCOMPONENT_NAME}" = "elasticsearch" ]; then fi elif [ "${HCOMPONENT_NAME}" = "presto" ]; then HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/etc +elif [ "${HCOMPONENT_NAME}" = "trino" ]; then + HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/etc + if [ "${INSTALL_ENV}" = "docker" ];then + HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR} + fi fi HCOMPONENT_ARCHIVE_CONF_DIR=${HCOMPONENT_CONF_DIR}/.archive @@ -808,6 +826,40 @@ then ln -sf ${HCOMPONENT_CONF_DIR} conf fi +if [ "${HCOMPONENT_NAME}" = "trino" ] +then + # Configure logback file using System Property logback.configurationFile in jvm.config + jvm_config_file=`ls ${HCOMPONENT_CONF_DIR}/jvm.config 2> /dev/null` + cp -n ${PROJ_INSTALL_DIR}/trino-ranger-plugin-logback.xml ${HCOMPONENT_CONF_DIR}/trino-ranger-plugin-logback.xml + logback_file=`ls ${HCOMPONENT_CONF_DIR}/trino-ranger-plugin-logback.xml 2> /dev/null` + + if [ "${action}" = "enable" ] + then + controlName="ranger" + addOrUpdatePropertyToFile -Dlogback.configurationFile ${logback_file} ${jvm_config_file} + else + controlName="allow-all" + sed -i '/-Dlogback.configurationFile/d' ${HCOMPONENT_CONF_DIR}/jvm.config + rm ${HCOMPONENT_CONF_DIR}/trino-ranger-plugin-logback.xml + fi + dt=`date '+%Y%m%d%H%M%S'` + fn=`ls ${HCOMPONENT_CONF_DIR}/access-control.properties 2> /dev/null` + if [ -f "${fn}" ] + then + dn=`dirname ${fn}` + bn=`basename ${fn}` + bf=${dn}/.${bn}.${dt} + echo "backup of ${fn} to ${bf} ..." + cp ${fn} ${bf} + else + fn=${HCOMPONENT_CONF_DIR}/access-control.properties + fi + echo "Add or Update properties file: [${fn}] ... " + addOrUpdatePropertyToFile access-control.name $controlName ${fn} + echo "Linking config files" + cd ${HCOMPONENT_LIB_DIR}/ranger-trino-plugin-impl/ + ln -sf ${HCOMPONENT_CONF_DIR} conf +fi # # Set notice to restart the ${HCOMPONENT_NAME} diff --git a/agents-common/src/main/java/org/apache/hadoop/security/SecureClientLogin.java b/agents-common/src/main/java/org/apache/hadoop/security/SecureClientLogin.java index e4d6a399fc..8c2a0babba 100644 --- a/agents-common/src/main/java/org/apache/hadoop/security/SecureClientLogin.java +++ b/agents-common/src/main/java/org/apache/hadoop/security/SecureClientLogin.java @@ -32,15 +32,15 @@ import javax.security.auth.login.LoginContext; import javax.security.auth.login.LoginException; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod; import org.apache.hadoop.security.authentication.util.KerberosUtil; import org.apache.hadoop.security.authentication.util.KerberosName; import org.apache.hadoop.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class SecureClientLogin { - private static final Log LOG = LogFactory.getLog(SecureClientLogin.class); + private static final Logger LOG = LoggerFactory.getLogger(SecureClientLogin.class); public static final String HOSTNAME_PATTERN = "_HOST"; public synchronized static Subject loginUserFromKeytab(String user, String path) throws IOException { diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java b/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java index 1ad5ec01e9..90e3e6eb8c 100644 --- a/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java +++ b/agents-common/src/main/java/org/apache/ranger/admin/client/AbstractRangerAdminClient.java @@ -22,6 +22,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.security.UserGroupInformation; import org.apache.ranger.plugin.model.RangerRole; import org.apache.ranger.plugin.util.*; import org.slf4j.Logger; @@ -34,6 +35,8 @@ public abstract class AbstractRangerAdminClient implements RangerAdminClient { protected Gson gson; + private boolean forceNonKerberos = false; + @Override public void init(String serviceName, String appId, String configPropertyPrefix, Configuration config) { Gson gson = null; @@ -45,6 +48,7 @@ public void init(String serviceName, String appId, String configPropertyPrefix, } this.gson = gson; + this.forceNonKerberos = config.getBoolean(configPropertyPrefix + ".forceNonKerberos", false); } @Override @@ -116,4 +120,22 @@ public List getTagTypes(String tagTypePattern) throws Exception { public RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, long lastActivationTimeInMillis) throws Exception { return null; } + + @Override + public ServiceGdsInfo getGdsInfoIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception { + return null; + } + + + public boolean isKerberosEnabled(UserGroupInformation user) { + final boolean ret; + + if (forceNonKerberos) { + ret = false; + } else { + ret = user != null && UserGroupInformation.isSecurityEnabled() && user.hasKerberosCredentials(); + } + + return ret; + } } diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java index 22a8121cab..4f08d40f6c 100644 --- a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java +++ b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminClient.java @@ -25,6 +25,7 @@ import org.apache.ranger.plugin.util.GrantRevokeRequest; import org.apache.ranger.plugin.util.GrantRevokeRoleRequest; import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.ServiceGdsInfo; import org.apache.ranger.plugin.util.ServicePolicies; import org.apache.ranger.plugin.util.ServiceTags; import org.apache.ranger.plugin.util.RangerUserStore; @@ -64,4 +65,5 @@ public interface RangerAdminClient { RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, long lastActivationTimeInMillis) throws Exception; + ServiceGdsInfo getGdsInfoIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception; } diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java index 479a50c913..653ae63d57 100644 --- a/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java +++ b/agents-common/src/main/java/org/apache/ranger/admin/client/RangerAdminRESTClient.java @@ -20,31 +20,35 @@ package org.apache.ranger.admin.client; +import com.fasterxml.jackson.core.type.TypeReference; import com.sun.jersey.api.client.ClientResponse; -import com.sun.jersey.api.client.GenericType; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.AccessControlException; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.http.HttpStatus; import org.apache.ranger.admin.client.datatype.RESTResponse; import org.apache.ranger.audit.provider.MiscUtil; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.model.RangerRole; import org.apache.ranger.plugin.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.Cookie; +import javax.ws.rs.core.NewCookie; import java.io.UnsupportedEncodingException; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.List; import java.util.Map; public class RangerAdminRESTClient extends AbstractRangerAdminClient { - private static final Log LOG = LogFactory.getLog(RangerAdminRESTClient.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminRESTClient.class); + + private static final TypeReference> TYPE_LIST_STRING = new TypeReference>() {}; private String serviceName; private String serviceNameUrlParam; @@ -52,28 +56,12 @@ public class RangerAdminRESTClient extends AbstractRangerAdminClient { private String clusterName; private RangerRESTClient restClient; private RangerRESTUtils restUtils = new RangerRESTUtils(); - private String supportsPolicyDeltas; - private String supportsTagDeltas; - private final String pluginCapabilities = Long.toHexString(new RangerPluginCapability().getPluginCapabilities()); - - public static GenericType> getGenericType(final T clazz) { - - ParameterizedType parameterizedGenericType = new ParameterizedType() { - public Type[] getActualTypeArguments() { - return new Type[] { clazz.getClass() }; - } - - public Type getRawType() { - return List.class; - } - - public Type getOwnerType() { - return List.class; - } - }; - - return new GenericType>(parameterizedGenericType) {}; - } + private boolean supportsPolicyDeltas; + private boolean supportsTagDeltas; + private boolean isRangerCookieEnabled; + private String rangerAdminCookieName; + private Cookie sessionId = null; + private final String pluginCapabilities = Long.toHexString(new RangerPluginCapability().getPluginCapabilities()); @Override public void init(String serviceName, String appId, String propertyPrefix, Configuration config) { @@ -88,11 +76,21 @@ public void init(String serviceName, String appId, String propertyPrefix, Config clusterName = config.get(propertyPrefix + ".access.cluster.name", ""); if(StringUtil.isEmpty(clusterName)){ clusterName =config.get(propertyPrefix + ".ambari.cluster.name", ""); + if (StringUtil.isEmpty(clusterName)) { + if (config instanceof RangerPluginConfig) { + clusterName = ((RangerPluginConfig)config).getClusterName(); + } + } } int restClientConnTimeOutMs = config.getInt(propertyPrefix + ".policy.rest.client.connection.timeoutMs", 120 * 1000); int restClientReadTimeOutMs = config.getInt(propertyPrefix + ".policy.rest.client.read.timeoutMs", 30 * 1000); - supportsPolicyDeltas = config.get(propertyPrefix + ".policy.rest.supports.policy.deltas", "false"); - supportsTagDeltas = config.get(propertyPrefix + ".tag.rest.supports.tag.deltas", "false"); + int restClientMaxRetryAttempts = config.getInt(propertyPrefix + ".policy.rest.client.max.retry.attempts", 3); + int restClientRetryIntervalMs = config.getInt(propertyPrefix + ".policy.rest.client.retry.interval.ms", 1 * 1000); + + supportsPolicyDeltas = config.getBoolean(propertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA_DEFAULT); + supportsTagDeltas = config.getBoolean(propertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA_DEFAULT); + isRangerCookieEnabled = config.getBoolean(propertyPrefix + ".policy.rest.client.cookie.enabled", RangerCommonConstants.POLICY_REST_CLIENT_SESSION_COOKIE_ENABLED); + rangerAdminCookieName = config.get(propertyPrefix + ".policy.rest.client.session.cookie.name", RangerCommonConstants.DEFAULT_COOKIE_NAME); if (!StringUtil.isEmpty(tmpUrl)) { url = tmpUrl.trim(); @@ -100,14 +98,8 @@ public void init(String serviceName, String appId, String propertyPrefix, Config if (url.endsWith("/")) { url = url.substring(0, url.length() - 1); } - if (!"true".equalsIgnoreCase(supportsPolicyDeltas)) { - supportsPolicyDeltas = "false"; - } - if (!"true".equalsIgnoreCase(supportsTagDeltas)) { - supportsTagDeltas = "false"; - } - init(url, sslConfigFileName, restClientConnTimeOutMs , restClientReadTimeOutMs, config); + init(url, sslConfigFileName, restClientConnTimeOutMs , restClientReadTimeOutMs, restClientMaxRetryAttempts, restClientRetryIntervalMs, config); try { this.serviceNameUrlParam = URLEncoderUtil.encodeURIParam(serviceName); @@ -123,63 +115,67 @@ public ServicePolicies getServicePoliciesIfUpdated(final long lastKnownVersion, LOG.debug("==> RangerAdminRESTClient.getServicePoliciesIfUpdated(" + lastKnownVersion + ", " + lastActivationTimeInMillis + ")"); } - final ServicePolicies ret; - - final UserGroupInformation user = MiscUtil.getUGILoginUser(); - final boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); - final ClientResponse response; + final ServicePolicies ret; + final UserGroupInformation user = MiscUtil.getUGILoginUser(); + final boolean isSecureMode = isKerberosEnabled(user); + final Cookie sessionId = this.sessionId; + final ClientResponse response; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_POLICY_VERSION, Long.toString(lastKnownVersion)); queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, clusterName); - queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_POLICY_DELTAS, supportsPolicyDeltas); + queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_POLICY_DELTAS, Boolean.toString(supportsPolicyDeltas)); queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); if (isSecureMode) { if (LOG.isDebugEnabled()) { LOG.debug("Checking Service policy if updated as user : " + user); } - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; + + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { String relativeURL = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SECURE_SERVICE_IF_UPDATED + serviceNameUrlParam; - try { - clientRes = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientRes; + + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); } - }; - response = user.doAs(action); + + return null; + }); } else { if (LOG.isDebugEnabled()) { LOG.debug("Checking Service policy if updated with old api call"); } String relativeURL = RangerRESTUtils.REST_URL_POLICY_GET_FOR_SERVICE_IF_UPDATED + serviceNameUrlParam; - response = restClient.get(relativeURL, queryParams); + response = restClient.get(relativeURL, queryParams, sessionId); } - if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) { + checkAndResetSessionCookie(response); + + if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED || response.getStatus() == HttpServletResponse.SC_NO_CONTENT) { if (response == null) { LOG.error("Error getting policies; Received NULL response!!. secureMode=" + isSecureMode + ", user=" + user + ", serviceName=" + serviceName); } else { RESTResponse resp = RESTResponse.fromClientResponse(response); if (LOG.isDebugEnabled()) { - LOG.debug("No change in policies. secureMode=" + isSecureMode + ", user=" + user + ", response=" + resp + ", serviceName=" + serviceName); + LOG.debug("No change in policies. secureMode=" + isSecureMode + ", user=" + user + + ", response=" + resp + ", serviceName=" + serviceName + + ", " + "lastKnownVersion=" + lastKnownVersion + + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); } } ret = null; } else if (response.getStatus() == HttpServletResponse.SC_OK) { - ret = response.getEntity(ServicePolicies.class); + ret = JsonUtilsV2.readResponse(response, ServicePolicies.class); } else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) { - LOG.error("Error getting policies; service not found. secureMode=" + isSecureMode + ", user=" + user - + ", response=" + response.getStatus() + ", serviceName=" + serviceName - + ", " + "lastKnownVersion=" + lastKnownVersion - + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); ret = null; + LOG.error("Error getting policies; service not found. secureMode=" + isSecureMode + ", user=" + user + + ", response=" + response.getStatus() + ", serviceName=" + serviceName + + ", " + "lastKnownVersion=" + lastKnownVersion + + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); String exceptionMsg = response.hasEntity() ? response.getEntity(String.class) : null; RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, exceptionMsg); @@ -205,8 +201,10 @@ public RangerRoles getRolesIfUpdated(final long lastKnownRoleVersion, final long } final RangerRoles ret; + final UserGroupInformation user = MiscUtil.getUGILoginUser(); - final boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + final boolean isSecureMode = isKerberosEnabled(user); + final Cookie sessionId = this.sessionId; final ClientResponse response; Map queryParams = new HashMap(); @@ -220,48 +218,48 @@ public RangerRoles getRolesIfUpdated(final long lastKnownRoleVersion, final long if (LOG.isDebugEnabled()) { LOG.debug("Checking Roles updated as user : " + user); } - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USER_GROUP_ROLES + serviceNameUrlParam; - try { - clientRes = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientRes; + + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); } - }; - response = user.doAs(action); + + return null; + }); } else { if (LOG.isDebugEnabled()) { LOG.debug("Checking Roles updated as user : " + user); } String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_USER_GROUP_ROLES + serviceNameUrlParam; - response = restClient.get(relativeURL, queryParams); + response = restClient.get(relativeURL, queryParams, sessionId); } - if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) { + checkAndResetSessionCookie(response); + + if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED || response.getStatus() == HttpServletResponse.SC_NO_CONTENT) { if (response == null) { LOG.error("Error getting Roles; Received NULL response!!. secureMode=" + isSecureMode + ", user=" + user + ", serviceName=" + serviceName); } else { RESTResponse resp = RESTResponse.fromClientResponse(response); if (LOG.isDebugEnabled()) { LOG.debug("No change in Roles. secureMode=" + isSecureMode + ", user=" + user - + ", response=" + resp + ", serviceName=" + serviceName - + ", " + "lastKnownRoleVersion=" + lastKnownRoleVersion - + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); + + ", response=" + resp + ", serviceName=" + serviceName + + ", " + "lastKnownRoleVersion=" + lastKnownRoleVersion + + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); } } ret = null; } else if (response.getStatus() == HttpServletResponse.SC_OK) { - ret = response.getEntity(RangerRoles.class); + ret = JsonUtilsV2.readResponse(response, RangerRoles.class); } else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) { ret = null; LOG.error("Error getting Roles; service not found. secureMode=" + isSecureMode + ", user=" + user - + ", response=" + response.getStatus() + ", serviceName=" + serviceName - + ", " + "lastKnownRoleVersion=" + lastKnownRoleVersion - + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); + + ", response=" + response.getStatus() + ", serviceName=" + serviceName + + ", " + "lastKnownRoleVersion=" + lastKnownRoleVersion + + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); String exceptionMsg = response.hasEntity() ? response.getEntity(String.class) : null; RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, exceptionMsg); @@ -288,34 +286,35 @@ public RangerRole createRole(final RangerRole request) throws Exception { RangerRole ret = null; - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); String relativeURL = RangerRESTUtils.REST_URL_SERVICE_CREATE_ROLE; + Cookie sessionId = this.sessionId; Map queryParams = new HashMap (); queryParams.put(RangerRESTUtils.SERVICE_NAME_PARAM, serviceNameUrlParam); if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; + if (LOG.isDebugEnabled()) { + LOG.debug("create role as user " + user); + } + + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { try { - clientRes = restClient.post(relativeURL, queryParams, request); + return restClient.post(relativeURL, queryParams, request, sessionId); } catch (Exception e) { LOG.error("Failed to get response, Error is : "+e.getMessage()); } - return clientRes; - } - }; - if (LOG.isDebugEnabled()) { - LOG.debug("create role as user " + user); - } - response = user.doAs(action); + + return null; + }); } else { - response = restClient.post(relativeURL, queryParams, request); + response = restClient.post(relativeURL, queryParams, request, sessionId); } + checkAndResetSessionCookie(response); + if(response != null && response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); LOG.error("createRole() failed: HTTP status=" + response.getStatus() + ", message=" + resp.getMessage() + ", isSecure=" + isSecureMode + (isSecureMode ? (", user=" + user) : "")); @@ -328,7 +327,7 @@ public ClientResponse run() { } else if(response == null) { throw new Exception("unknown error during createRole. roleName=" + request.getName()); } else { - ret = response.getEntity(RangerRole.class); + ret = JsonUtilsV2.readResponse(response, RangerRole.class); } if(LOG.isDebugEnabled()) { @@ -343,9 +342,10 @@ public void dropRole(final String execUser, final String roleName) throws Except LOG.debug("==> RangerAdminRESTClient.dropRole(" + roleName + ")"); } - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); + Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.SERVICE_NAME_PARAM, serviceNameUrlParam); @@ -354,24 +354,24 @@ public void dropRole(final String execUser, final String roleName) throws Except String relativeURL = RangerRESTUtils.REST_URL_SERVICE_DROP_ROLE + roleName; if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; + if (LOG.isDebugEnabled()) { + LOG.debug("drop role as user " + user); + } + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { try { - clientRes = restClient.delete(relativeURL, queryParams); + return restClient.delete(relativeURL, queryParams, sessionId); } catch (Exception e) { LOG.error("Failed to get response, Error is : "+e.getMessage()); } - return clientRes; - } - }; - if (LOG.isDebugEnabled()) { - LOG.debug("drop role as user " + user); - } - response = user.doAs(action); + + return null; + }); } else { - response = restClient.delete(relativeURL, queryParams); + response = restClient.delete(relativeURL, queryParams, sessionId); } + + checkAndResetSessionCookie(response); + if(response == null) { throw new Exception("unknown error during deleteRole. roleName=" + roleName); } else if(response.getStatus() != HttpServletResponse.SC_OK && response.getStatus() != HttpServletResponse.SC_NO_CONTENT) { @@ -398,30 +398,31 @@ public List getUserRoles(final String execUser) throws Exception { List ret = null; String emptyString = ""; - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_USER_ROLES + execUser; + Cookie sessionId = this.sessionId; if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; - try { - clientRes = restClient.get(relativeURL, null); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientRes; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("get roles as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + return restClient.get(relativeURL, null, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { - response = restClient.get(relativeURL, null); + response = restClient.get(relativeURL, null, sessionId); } + + checkAndResetSessionCookie(response); + if(response != null) { if (response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); @@ -433,7 +434,7 @@ public ClientResponse run() { throw new Exception("HTTP " + response.getStatus() + " Error: " + resp.getMessage()); } else { - ret = response.getEntity(getGenericType(emptyString)); + ret = JsonUtilsV2.readResponse(response, TYPE_LIST_STRING); } } else { throw new Exception("unknown error during getUserRoles. execUser=" + execUser); @@ -453,34 +454,35 @@ public List getAllRoles(final String execUser) throws Exception { List ret = null; String emptyString = ""; - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_ALL_ROLES; + Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.SERVICE_NAME_PARAM, serviceNameUrlParam); queryParams.put(RangerRESTUtils.REST_PARAM_EXEC_USER, execUser); if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; - try { - clientRes = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientRes; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("get roles as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { - response = restClient.get(relativeURL, queryParams); + response = restClient.get(relativeURL, queryParams, sessionId); } + + checkAndResetSessionCookie(response); + if(response != null) { if (response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); @@ -492,7 +494,7 @@ public ClientResponse run() { throw new Exception("HTTP " + response.getStatus() + " Error: " + resp.getMessage()); } else { - ret = response.getEntity(getGenericType(emptyString)); + ret = JsonUtilsV2.readResponse(response, TYPE_LIST_STRING); } } else { throw new Exception("unknown error during getAllRoles."); @@ -511,34 +513,35 @@ public RangerRole getRole(final String execUser, final String roleName) throws E } RangerRole ret = null; - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_ROLE_INFO + roleName; + Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.SERVICE_NAME_PARAM, serviceNameUrlParam); queryParams.put(RangerRESTUtils.REST_PARAM_EXEC_USER, execUser); if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientResp = null; - try { - clientResp = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("get role info as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { - response = restClient.get(relativeURL, queryParams); + response = restClient.get(relativeURL, queryParams, sessionId); } + + checkAndResetSessionCookie(response); + if(response != null) { if (response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); @@ -550,7 +553,7 @@ public ClientResponse run() { throw new Exception("HTTP " + response.getStatus() + " Error: " + resp.getMessage()); } else { - ret = response.getEntity(RangerRole.class); + ret = JsonUtilsV2.readResponse(response, RangerRole.class); } } else { throw new Exception("unknown error during getPrincipalsForRole. roleName=" + roleName); @@ -569,30 +572,31 @@ public void grantRole(final GrantRevokeRoleRequest request) throws Exception { LOG.debug("==> RangerAdminRESTClient.grantRole(" + request + ")"); } - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GRANT_ROLE + serviceNameUrlParam; + Cookie sessionId = this.sessionId; if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientResp = null; - try { - clientResp = restClient.put(relativeURL, null, request); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("grant role as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + return restClient.put(relativeURL, request, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { - response = restClient.put(relativeURL, null, request); + response = restClient.put(relativeURL, request, sessionId); } + + checkAndResetSessionCookie(response); + if(response != null && response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); LOG.error("grantRole() failed: HTTP status=" + response.getStatus() + ", message=" + resp.getMessage() + ", isSecure=" + isSecureMode + (isSecureMode ? (", user=" + user) : "")); @@ -617,30 +621,31 @@ public void revokeRole(final GrantRevokeRoleRequest request) throws Exception { LOG.debug("==> RangerAdminRESTClient.revokeRole(" + request + ")"); } - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); String relativeURL = RangerRESTUtils.REST_URL_SERVICE_REVOKE_ROLE + serviceNameUrlParam; + Cookie sessionId = this.sessionId; if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientResp = null; - try { - clientResp = restClient.put(relativeURL, null, request); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("revoke role as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + return restClient.put(relativeURL, request, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { - response = restClient.put(relativeURL, null, request); + response = restClient.put(relativeURL, request, sessionId); } + + checkAndResetSessionCookie(response); + if(response != null && response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); LOG.error("revokeRole() failed: HTTP status=" + response.getStatus() + ", message=" + resp.getMessage() + ", isSecure=" + isSecureMode + (isSecureMode ? (", user=" + user) : "")); @@ -665,34 +670,36 @@ public void grantAccess(final GrantRevokeRequest request) throws Exception { LOG.debug("==> RangerAdminRESTClient.grantAccess(" + request + ")"); } - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); + Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - String relativeURL = RangerRESTUtils.REST_URL_SECURE_SERVICE_GRANT_ACCESS + serviceNameUrlParam; - ClientResponse clientResp = null; - try { - clientResp = restClient.post(relativeURL, queryParams, request); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("grantAccess as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + String relativeURL = RangerRESTUtils.REST_URL_SECURE_SERVICE_GRANT_ACCESS + serviceNameUrlParam; + + return restClient.post(relativeURL, queryParams, request, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GRANT_ACCESS + serviceNameUrlParam; - response = restClient.post(relativeURL, queryParams, request); + response = restClient.post(relativeURL, queryParams, request, sessionId); } + + checkAndResetSessionCookie(response); + if(response != null && response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); LOG.error("grantAccess() failed: HTTP status=" + response.getStatus() + ", message=" + resp.getMessage() + ", isSecure=" + isSecureMode + (isSecureMode ? (", user=" + user) : "")); @@ -717,35 +724,36 @@ public void revokeAccess(final GrantRevokeRequest request) throws Exception { LOG.debug("==> RangerAdminRESTClient.revokeAccess(" + request + ")"); } - ClientResponse response = null; + final ClientResponse response; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); + Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - String relativeURL = RangerRESTUtils.REST_URL_SECURE_SERVICE_REVOKE_ACCESS + serviceNameUrlParam; - ClientResponse clientResp = null; - try { - clientResp = restClient.post(relativeURL, queryParams, request); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("revokeAccess as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + String relativeURL = RangerRESTUtils.REST_URL_SECURE_SERVICE_REVOKE_ACCESS + serviceNameUrlParam; + + return restClient.post(relativeURL, queryParams, request, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { String relativeURL = RangerRESTUtils.REST_URL_SERVICE_REVOKE_ACCESS + serviceNameUrlParam; - response = restClient.post(relativeURL, queryParams, request); + response = restClient.post(relativeURL, queryParams, request, sessionId); } + checkAndResetSessionCookie(response); + if(response != null && response.getStatus() != HttpServletResponse.SC_OK) { RESTResponse resp = RESTResponse.fromClientResponse(response); LOG.error("revokeAccess() failed: HTTP status=" + response.getStatus() + ", message=" + resp.getMessage() + ", isSecure=" + isSecureMode + (isSecureMode ? (", user=" + user) : "")); @@ -764,7 +772,7 @@ public ClientResponse run() { } } - private void init(String url, String sslConfigFileName, int restClientConnTimeOutMs , int restClientReadTimeOutMs, Configuration config) { + private void init(String url, String sslConfigFileName, int restClientConnTimeOutMs , int restClientReadTimeOutMs, int restClientMaxRetryAttempts, int restClientRetryIntervalMs, Configuration config) { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerAdminRESTClient.init(" + url + ", " + sslConfigFileName + ")"); } @@ -772,6 +780,8 @@ private void init(String url, String sslConfigFileName, int restClientConnTimeOu restClient = new RangerRESTClient(url, sslConfigFileName, config); restClient.setRestClientConnTimeOutMs(restClientConnTimeOutMs); restClient.setRestClientReadTimeOutMs(restClientReadTimeOutMs); + restClient.setMaxRetryAttempts(restClientMaxRetryAttempts); + restClient.setRetryIntervalMs(restClientRetryIntervalMs); if(LOG.isDebugEnabled()) { LOG.debug("<== RangerAdminRESTClient.init(" + url + ", " + sslConfigFileName + ")"); @@ -784,40 +794,42 @@ public ServiceTags getServiceTagsIfUpdated(final long lastKnownVersion, final lo LOG.debug("==> RangerAdminRESTClient.getServiceTagsIfUpdated(" + lastKnownVersion + ", " + lastActivationTimeInMillis + "): "); } - ServiceTags ret = null; - ClientResponse response = null; - UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + final ServiceTags ret; + + final UserGroupInformation user = MiscUtil.getUGILoginUser(); + final boolean isSecureMode = isKerberosEnabled(user); + final ClientResponse response; + final Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.LAST_KNOWN_TAG_VERSION_PARAM, Long.toString(lastKnownVersion)); queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); - queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_TAG_DELTAS, supportsTagDeltas); + queryParams.put(RangerRESTUtils.REST_PARAM_SUPPORTS_TAG_DELTAS, Boolean.toString(supportsTagDeltas)); queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - String relativeURL = RangerRESTUtils.REST_URL_GET_SECURE_SERVICE_TAGS_IF_UPDATED + serviceNameUrlParam; - ClientResponse clientResp = null; - try { - clientResp = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("getServiceTagsIfUpdated as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + String relativeURL = RangerRESTUtils.REST_URL_GET_SECURE_SERVICE_TAGS_IF_UPDATED + serviceNameUrlParam; + + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { String relativeURL = RangerRESTUtils.REST_URL_GET_SERVICE_TAGS_IF_UPDATED + serviceNameUrlParam; - response = restClient.get(relativeURL, queryParams); + response = restClient.get(relativeURL, queryParams, sessionId); } + checkAndResetSessionCookie(response); + if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) { if (response == null) { LOG.error("Error getting tags; Received NULL response!!. secureMode=" + isSecureMode + ", user=" + user + ", serviceName=" + serviceName); @@ -825,23 +837,23 @@ public ClientResponse run() { RESTResponse resp = RESTResponse.fromClientResponse(response); if (LOG.isDebugEnabled()) { LOG.debug("No change in tags. secureMode=" + isSecureMode + ", user=" + user - + ", response=" + resp + ", serviceName=" + serviceName - + ", " + "lastKnownVersion=" + lastKnownVersion - + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); + + ", response=" + resp + ", serviceName=" + serviceName + + ", " + "lastKnownVersion=" + lastKnownVersion + + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); } } ret = null; } else if (response.getStatus() == HttpServletResponse.SC_OK) { - ret = response.getEntity(ServiceTags.class); + ret = JsonUtilsV2.readResponse(response, ServiceTags.class); } else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) { + ret = null; LOG.error("Error getting tags; service not found. secureMode=" + isSecureMode + ", user=" + user - + ", response=" + response.getStatus() + ", serviceName=" + serviceName - + ", " + "lastKnownVersion=" + lastKnownVersion - + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); - String exceptionMsg = response.hasEntity() ? response.getEntity(String.class) : null; + + ", response=" + response.getStatus() + ", serviceName=" + serviceName + + ", " + "lastKnownVersion=" + lastKnownVersion + + ", " + "lastActivationTimeInMillis=" + lastActivationTimeInMillis); + String exceptionMsg = response.hasEntity() ? response.getEntity(String.class) : null; RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, exceptionMsg); - LOG.warn("Received 404 error code with body:[" + exceptionMsg + "], Ignoring"); } else { RESTResponse resp = RESTResponse.fromClientResponse(response); @@ -865,36 +877,36 @@ public List getTagTypes(String pattern) throws Exception { List ret = null; String emptyString = ""; UserGroupInformation user = MiscUtil.getUGILoginUser(); - boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + boolean isSecureMode = isKerberosEnabled(user); + Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.SERVICE_NAME_PARAM, serviceNameUrlParam); queryParams.put(RangerRESTUtils.PATTERN_PARAM, pattern); String relativeURL = RangerRESTUtils.REST_URL_LOOKUP_TAG_NAMES; - ClientResponse response = null; + final ClientResponse response; if (isSecureMode) { - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientResp = null; - try { - clientResp = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientResp; - } - }; if (LOG.isDebugEnabled()) { LOG.debug("getTagTypes as user " + user); } - response = user.doAs(action); + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); + } + + return null; + }); } else { - response = restClient.get(relativeURL, queryParams); + response = restClient.get(relativeURL, queryParams, sessionId); } + checkAndResetSessionCookie(response); + if(response != null && response.getStatus() == HttpServletResponse.SC_OK) { - ret = response.getEntity(getGenericType(emptyString)); + ret = JsonUtilsV2.readResponse(response, TYPE_LIST_STRING); } else { RESTResponse resp = RESTResponse.fromClientResponse(response); LOG.error("Error getting tags. response=" + resp + ", serviceName=" + serviceName + ", " + "pattern=" + pattern); @@ -916,8 +928,9 @@ public RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, lon final RangerUserStore ret; final UserGroupInformation user = MiscUtil.getUGILoginUser(); - final boolean isSecureMode = user != null && UserGroupInformation.isSecurityEnabled(); + final boolean isSecureMode = isKerberosEnabled(user); final ClientResponse response; + final Cookie sessionId = this.sessionId; Map queryParams = new HashMap(); queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_USERSTORE_VERSION, Long.toString(lastKnownUserStoreVersion)); @@ -930,27 +943,27 @@ public RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, lon if (LOG.isDebugEnabled()) { LOG.debug("Checking UserStore updated as user : " + user); } - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USERSTORE + serviceNameUrlParam; - try { - clientRes = restClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientRes; + + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); } - }; - response = user.doAs(action); + + return null; + }); } else { if (LOG.isDebugEnabled()) { LOG.debug("Checking UserStore updated as user : " + user); } - String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USERSTORE + serviceNameUrlParam; - response = restClient.get(relativeURL, queryParams); + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_USERSTORE + serviceNameUrlParam; + response = restClient.get(relativeURL, queryParams, sessionId); } + checkAndResetSessionCookie(response); + if (response == null || response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) { if (response == null) { LOG.error("Error getting UserStore; Received NULL response!!. secureMode=" + isSecureMode + ", user=" + user + ", serviceName=" + serviceName); @@ -965,7 +978,7 @@ public ClientResponse run() { } ret = null; } else if (response.getStatus() == HttpServletResponse.SC_OK) { - ret = response.getEntity(RangerUserStore.class); + ret = JsonUtilsV2.readResponse(response, RangerUserStore.class); } else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) { ret = null; LOG.error("Error getting UserStore; service not found. secureMode=" + isSecureMode + ", user=" + user @@ -990,4 +1003,114 @@ public ClientResponse run() { return ret; } + @Override + public ServiceGdsInfo getGdsInfoIfUpdated(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception { + LOG.debug("==> RangerAdminRESTClient.getGdsInfoIfUpdated({}, {})", lastKnownVersion, lastActivationTimeInMillis); + + final ServiceGdsInfo ret; + final UserGroupInformation user = MiscUtil.getUGILoginUser(); + final boolean isSecureMode = isKerberosEnabled(user); + final Map queryParams = new HashMap<>(); + final ClientResponse response; + Cookie sessionId = this.sessionId; + + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_KNOWN_GDS_VERSION, Long.toString(lastKnownVersion)); + queryParams.put(RangerRESTUtils.REST_PARAM_LAST_ACTIVATION_TIME, Long.toString(lastActivationTimeInMillis)); + queryParams.put(RangerRESTUtils.REST_PARAM_PLUGIN_ID, pluginId); + queryParams.put(RangerRESTUtils.REST_PARAM_CLUSTER_NAME, clusterName); + queryParams.put(RangerRESTUtils.REST_PARAM_CAPABILITIES, pluginCapabilities); + + LOG.debug("Checking for updated GdsInfo: secureMode={}, user={}, serviceName={}" , isSecureMode, user, serviceName); + + if (isSecureMode) { + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SECURE_GET_GDSINFO + serviceNameUrlParam; + + return restClient.get(relativeURL, queryParams, sessionId); + } catch (Exception e) { + LOG.error("Failed to get response", e); + } + + return null; + }); + } else { + String relativeURL = RangerRESTUtils.REST_URL_SERVICE_GET_GDSINFO + serviceNameUrlParam; + + response = restClient.get(relativeURL, queryParams, sessionId); + } + + checkAndResetSessionCookie(response); + + if (response == null) { + ret = null; + + LOG.error("Error getting GdsInfo - received NULL response: secureMode={}, user={}, serviceName={}", isSecureMode, user, serviceName); + } else if (response.getStatus() == HttpServletResponse.SC_NOT_MODIFIED) { + ret = null; + + RESTResponse resp = RESTResponse.fromClientResponse(response); + + LOG.debug("No change in GdsInfo: secureMode={}, user={}, response={}, serviceName={}, lastKnownGdsVersion={}, lastActivationTimeInMillis={}", + isSecureMode, user, resp, serviceName, lastKnownVersion, lastActivationTimeInMillis); + } else if (response.getStatus() == HttpServletResponse.SC_OK) { + ret = JsonUtilsV2.readResponse(response, ServiceGdsInfo.class); + } else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) { + ret = null; + + LOG.error("Error getting GdsInfo - service not found: secureMode={}, user={}, response={}, serviceName={}, lastKnownGdsVersion={},lastActivationTimeInMillis={}", + isSecureMode, user, response.getStatus(), serviceName, lastKnownVersion, lastActivationTimeInMillis); + + String exceptionMsg = response.hasEntity() ? response.getEntity(String.class) : null; + + RangerServiceNotFoundException.throwExceptionIfServiceNotFound(serviceName, exceptionMsg); + + LOG.warn("Received 404 error code with body:[{}], Ignoring", exceptionMsg); + } else { + ret = null; + + RESTResponse resp = RESTResponse.fromClientResponse(response); + + LOG.warn("Error getting GdsInfo: unexpected status code {}: secureMode={}, user={}, response={}, serviceName={}", + response.getStatus(), isSecureMode, user, resp, serviceName); + } + + LOG.debug("<== RangerAdminRESTClient.getGdsInfoIfUpdated({}, {}): ret={}", lastKnownVersion, lastActivationTimeInMillis, ret); + + return ret; + } + + private void checkAndResetSessionCookie(ClientResponse response) { + if (isRangerCookieEnabled) { + if (response == null) { + LOG.debug("checkAndResetSessionCookie(): RESETTING sessionId - response is null"); + + sessionId = null; + } else { + int status = response.getStatus(); + + if (status == HttpStatus.SC_OK || status == HttpStatus.SC_NO_CONTENT || status == HttpStatus.SC_NOT_MODIFIED) { + Cookie newCookie = null; + + for (NewCookie cookie : response.getCookies()) { + if (cookie.getName().equalsIgnoreCase(rangerAdminCookieName)) { + newCookie = cookie; + + break; + } + } + + if (sessionId == null || newCookie != null) { + LOG.debug("checkAndResetSessionCookie(): status={}, sessionIdCookie={}, newCookie={}", status, sessionId, newCookie); + + sessionId = newCookie; + } + } else { + LOG.debug("checkAndResetSessionCookie(): RESETTING sessionId - status={}", status); + + sessionId = null; + } + } + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/GrantRevokeData.java b/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/GrantRevokeData.java index d55667c578..6d094e0ce0 100644 --- a/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/GrantRevokeData.java +++ b/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/GrantRevokeData.java @@ -23,16 +23,16 @@ import java.util.List; import java.util.ArrayList; +import com.fasterxml.jackson.annotation.JsonInclude; import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.util.JsonUtilsV2; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown = true) public class GrantRevokeData implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -162,7 +162,7 @@ public String toString() { } @JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown = true) public static class PermMap implements java.io.Serializable { private List userList = new ArrayList<>(); diff --git a/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/RESTResponse.java b/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/RESTResponse.java index 75c2c9f38d..574f84f50e 100644 --- a/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/RESTResponse.java +++ b/agents-common/src/main/java/org/apache/ranger/admin/client/datatype/RESTResponse.java @@ -20,22 +20,23 @@ import java.util.List; -import org.apache.log4j.Logger; +import com.fasterxml.jackson.annotation.JsonInclude; import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.util.JsonUtilsV2; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.sun.jersey.api.client.ClientResponse; @JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) -@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown = true) public class RESTResponse implements java.io.Serializable { - private static final Logger LOG = Logger.getLogger(RESTResponse.class); + private static final Logger LOG = LoggerFactory.getLogger(RESTResponse.class); /** * values for statusCode @@ -139,7 +140,7 @@ public String toString() { } @JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown = true) public static class Message implements java.io.Serializable { private String name; diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAdminConfig.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAdminConfig.java index 5cd539aebe..30a80f2621 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAdminConfig.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAdminConfig.java @@ -19,12 +19,17 @@ package org.apache.ranger.authorization.hadoop.config; -import org.apache.log4j.Logger; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.security.KeyStore; public class RangerAdminConfig extends RangerConfiguration { - private static final Logger LOG = Logger.getLogger(RangerAdminConfig.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminConfig.class); private static volatile RangerAdminConfig sInstance = null; + private final boolean isFipsEnabled; public static RangerAdminConfig getInstance() { RangerAdminConfig ret = RangerAdminConfig.sInstance; @@ -44,11 +49,12 @@ public static RangerAdminConfig getInstance() { private RangerAdminConfig() { super(); - addAdminResources(); + String storeType = get(RangerConfigConstants.RANGER_KEYSTORE_TYPE, KeyStore.getDefaultType()); + isFipsEnabled = StringUtils.equalsIgnoreCase("bcfks", storeType) ? true : false; + } - private boolean addAdminResources() { if (LOG.isDebugEnabled()) { LOG.debug("==> addAdminResources()"); @@ -82,4 +88,8 @@ private boolean addAdminResources() { return ret; } + + public boolean isFipsEnabled() { + return isFipsEnabled; + } } diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAuditConfig.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAuditConfig.java index d6ba00ab55..b1322074b4 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAuditConfig.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerAuditConfig.java @@ -19,10 +19,11 @@ package org.apache.ranger.authorization.hadoop.config; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAuditConfig extends RangerConfiguration { - private static final Logger LOG = Logger.getLogger(RangerAuditConfig.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAuditConfig.class); private final boolean initSuccess; diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerChainedPluginConfig.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerChainedPluginConfig.java new file mode 100644 index 0000000000..ae8f01fed8 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerChainedPluginConfig.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.authorization.hadoop.config; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RangerChainedPluginConfig extends RangerPluginConfig { + + private static final Logger LOG = LoggerFactory.getLogger(RangerChainedPluginConfig.class); + + private final String[] legacySSLProperties = new String[] {"xasecure.policymgr.clientssl.keystore", "xasecure.policymgr.clientssl.keystore.type", "xasecure.policymgr.clientssl.keystore.credential.file","xasecure.policymgr.clientssl.truststore", "xasecure.policymgr.clientssl.truststore.credential.file", "hadoop.security.credential.provider.path"}; + private final String[] chainedPluginPropertyPrefixes = new String[] { ".chained.services"}; + + public RangerChainedPluginConfig(String serviceType, String serviceName, String appId, RangerPluginConfig sourcePluginConfig) { + super(serviceType, serviceName, appId, sourcePluginConfig); + + // Copy all of properties from sourcePluginConfig except chained properties but with converted propertyPrefix + copyProperties(sourcePluginConfig, sourcePluginConfig.getPropertyPrefix()); + + // Copy SSL configurations from sourcePluginConfig + copyLegacySSLProperties(sourcePluginConfig); + + // Override copied properties from those in sourcePluginConfig with getPropertyPrefix() + copyProperties(sourcePluginConfig, getPropertyPrefix()); + + // Copy chained properties + copyChainedProperties(sourcePluginConfig, getPropertyPrefix()); + + set(getPropertyPrefix() + ".service.name", serviceName); + } + + private void copyProperties(RangerPluginConfig sourcePluginConfig, String propertyPrefix) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> copyProperties: propertyPrefix:[" + propertyPrefix + "]"); + } + for (String propName : sourcePluginConfig.getProperties().stringPropertyNames()) { + String value = sourcePluginConfig.get(propName); + + if (value != null && propName.startsWith(propertyPrefix)) { + String suffix = propName.substring(propertyPrefix.length()); + if (!isExcludedSuffix(suffix)) { + set(getPropertyPrefix() + suffix, value); + if (LOG.isDebugEnabled()) { + LOG.debug("set property:[" + getPropertyPrefix() + suffix + "] to value:[" + value + "]"); + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Not copying property :[" + propName + "] value from sourcePluginConfig"); + } + } + } + } + if (LOG.isDebugEnabled()) { + LOG.debug("<== copyProperties: propertyPrefix:[" + propertyPrefix + "]"); + } + } + + private void copyLegacySSLProperties(RangerPluginConfig sourcePluginConfig) { + for (String legacyPropertyName : legacySSLProperties) { + String value = sourcePluginConfig.get(legacyPropertyName); + if (value != null) { + set(legacyPropertyName, value); + } + } + } + + private void copyChainedProperties(RangerPluginConfig sourcePluginConfig, String propertyPrefix) { + for (String propName : sourcePluginConfig.getProperties().stringPropertyNames()) { + String value = sourcePluginConfig.get(propName); + + if (value != null && propName.startsWith(propertyPrefix)) { + String suffix = propName.substring(propertyPrefix.length()); + for (String chainedPropertyPrefix : chainedPluginPropertyPrefixes) { + if (StringUtils.startsWith(suffix, chainedPropertyPrefix)) { + set(getPropertyPrefix() + suffix, value); + } + } + } + } + } + + private boolean isExcludedSuffix(String suffix) { + for (String excludedSuffix : chainedPluginPropertyPrefixes) { + if (StringUtils.startsWith(suffix, excludedSuffix)) { + return true; + } + } + return false; + } + + private String printProperties() { + StringBuilder sb = new StringBuilder(); + boolean seenOneProp = false; + for (String propName : this.getProperties().stringPropertyNames()) { + String value = this.get(propName); + if (!seenOneProp) { + seenOneProp = true; + } else { + sb.append(",\n"); + } + sb.append("{ propertyName:[").append(propName).append("], propertyValue:[").append(value).append("] }"); + } + return sb.toString(); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + " : { " + printProperties() + " }"; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfigConstants.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfigConstants.java index 1ad34efa75..374c78c5e3 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfigConstants.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfigConstants.java @@ -29,6 +29,7 @@ public class RangerConfigConstants { public static final String RANGER_PLUGIN_POLICY_POLLINVETERVALMS = "ranger.plugin..policy.pollIntervalMs"; public static final String RANGER_PLUGIN_POLICY_CACHE_DIR = "ranger.plugin..policy.cache.dir"; public static final String RANGER_PLUGIN_ADD_HADDOOP_AUTHORIZATION = "xasecure.add-hadoop-authorization"; + public static final String RANGER_KEYSTORE_TYPE = "ranger.keystore.file.type"; //CHANGE MAP CONSTANTS public static final String XASECURE_POLICYMGR_URL = "xasecure..policymgr.url"; diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfiguration.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfiguration.java index 43ddf0bc23..e8937edd5a 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfiguration.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerConfiguration.java @@ -20,15 +20,19 @@ package org.apache.ranger.authorization.hadoop.config; +import java.io.File; +import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; +import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerConfiguration extends Configuration { - private static final Logger LOG = Logger.getLogger(RangerConfiguration.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerConfiguration.class); protected RangerConfiguration() { super(false); @@ -43,10 +47,9 @@ public boolean addResourceIfReadable(String aResourceName) { URL fUrl = getFileLocation(aResourceName); if (fUrl != null) { - if(LOG.isInfoEnabled()) { - LOG.info("addResourceIfReadable(" + aResourceName + "): resource file is " + fUrl); + if(LOG.isDebugEnabled()) { + LOG.debug("addResourceIfReadable(" + aResourceName + "): resource file is " + fUrl); } - try { addResource(fUrl); ret = true; @@ -57,7 +60,9 @@ public boolean addResourceIfReadable(String aResourceName) { } } } else { - LOG.error("addResourceIfReadable(" + aResourceName + "): couldn't find resource file location"); + if(LOG.isDebugEnabled()) { + LOG.debug("addResourceIfReadable(" + aResourceName + "): couldn't find resource file location"); + } } if(LOG.isDebugEnabled()) { @@ -70,12 +75,29 @@ public Properties getProperties() { return getProps(); } - private URL getFileLocation(String fileName) { - URL lurl = RangerConfiguration.class.getClassLoader().getResource(fileName); - - if (lurl == null ) { - lurl = RangerConfiguration.class.getClassLoader().getResource("/" + fileName); + URL lurl = null; + if (!StringUtils.isEmpty(fileName)) { + lurl = RangerConfiguration.class.getClassLoader().getResource(fileName); + + if (lurl == null ) { + lurl = RangerConfiguration.class.getClassLoader().getResource("/" + fileName); + } + + if (lurl == null ) { + File f = new File(fileName); + if (f.exists()) { + try { + lurl=f.toURI().toURL(); + } catch (MalformedURLException e) { + LOG.error("Unable to load the resource name [" + fileName + "]. Ignoring the resource:" + f.getPath()); + } + } else { + if(LOG.isDebugEnabled()) { + LOG.debug("Conf file path " + fileName + " does not exists"); + } + } + } } return lurl; } diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerLegacyConfigBuilder.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerLegacyConfigBuilder.java index 8b6382cfab..95aa57c528 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerLegacyConfigBuilder.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerLegacyConfigBuilder.java @@ -25,12 +25,13 @@ import java.util.Map; import org.apache.hadoop.conf.Configuration; -import org.apache.log4j.Logger; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerLegacyConfigBuilder { - private static final Logger LOG = Logger.getLogger(RangerLegacyConfigBuilder.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerLegacyConfigBuilder.class); static String serviceType; static String legacyResource; diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerPluginConfig.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerPluginConfig.java index 43004cb538..2a783331e6 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerPluginConfig.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/config/RangerPluginConfig.java @@ -22,9 +22,10 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; -import org.apache.log4j.Logger; import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.net.URL; @@ -34,7 +35,7 @@ public class RangerPluginConfig extends RangerConfiguration { - private static final Logger LOG = Logger.getLogger(RangerPluginConfig.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPluginConfig.class); private static final char RANGER_TRUSTED_PROXY_IPADDRESSES_SEPARATOR_CHAR = ','; @@ -47,11 +48,18 @@ public class RangerPluginConfig extends RangerConfiguration { private final boolean useForwardedIPAddress; private final String[] trustedProxyAddresses; private final String propertyPrefix; + private final boolean useRangerGroups; + private final boolean useOnlyRangerGroups; + private final boolean convertEmailToUsername; + private final boolean enableImplicitUserStoreEnricher; + private final boolean enableImplicitGdsInfoEnricher; + private boolean isFallbackSupported; private Set auditExcludedUsers = Collections.emptySet(); private Set auditExcludedGroups = Collections.emptySet(); private Set auditExcludedRoles = Collections.emptySet(); private Set superUsers = Collections.emptySet(); private Set superGroups = Collections.emptySet(); + private Set serviceAdmins = Collections.emptySet(); public RangerPluginConfig(String serviceType, String serviceName, String appId, String clusterName, String clusterType, RangerPolicyEngineOptions policyEngineOptions) { @@ -113,7 +121,36 @@ public RangerPluginConfig(String serviceType, String serviceName, String appId, this.policyEngineOptions = policyEngineOptions; - LOG.info(policyEngineOptions); + useRangerGroups = this.getBoolean(propertyPrefix + ".use.rangerGroups", false); + useOnlyRangerGroups = this.getBoolean(propertyPrefix + ".use.only.rangerGroups", false); + convertEmailToUsername = this.getBoolean(propertyPrefix + ".convert.emailToUser", false); + enableImplicitUserStoreEnricher = useRangerGroups || convertEmailToUsername || this.getBoolean(propertyPrefix + ".enable.implicit.userstore.enricher", false); + enableImplicitGdsInfoEnricher = this.getBoolean(propertyPrefix + ".enable.implicit.gdsinfo.enricher", true); + + LOG.info("" + policyEngineOptions); + } + + protected RangerPluginConfig(String serviceType, String serviceName, String appId, RangerPluginConfig sourcePluginConfig) { + super(); + + this.serviceType = serviceType; + this.appId = StringUtils.isEmpty(appId) ? serviceType : appId; + this.propertyPrefix = "ranger.plugin." + serviceType; + this.serviceName = serviceName; + + this.clusterName = sourcePluginConfig.getClusterName(); + this.clusterType = sourcePluginConfig.getClusterType(); + this.useForwardedIPAddress = sourcePluginConfig.isUseForwardedIPAddress(); + this.trustedProxyAddresses = sourcePluginConfig.getTrustedProxyAddresses(); + this.isFallbackSupported = sourcePluginConfig.getIsFallbackSupported(); + + this.policyEngineOptions = sourcePluginConfig.getPolicyEngineOptions(); + + this.useRangerGroups = sourcePluginConfig.useRangerGroups; + this.useOnlyRangerGroups = sourcePluginConfig.useOnlyRangerGroups; + this.convertEmailToUsername = sourcePluginConfig.convertEmailToUsername; + this.enableImplicitUserStoreEnricher = sourcePluginConfig.enableImplicitUserStoreEnricher; + this.enableImplicitGdsInfoEnricher = sourcePluginConfig.enableImplicitGdsInfoEnricher; } public String getServiceType() { @@ -148,6 +185,34 @@ public String getPropertyPrefix() { return propertyPrefix; } + public boolean isUseRangerGroups() { + return useRangerGroups; + } + + public boolean isUseOnlyRangerGroups() { + return useOnlyRangerGroups; + } + + public boolean isConvertEmailToUsername() { + return convertEmailToUsername; + } + + public boolean isEnableImplicitUserStoreEnricher() { + return enableImplicitUserStoreEnricher; + } + + public boolean isEnableImplicitGdsInfoEnricher() { + return enableImplicitGdsInfoEnricher; + } + + public boolean getIsFallbackSupported() { + return isFallbackSupported; + } + + public void setIsFallbackSupported(boolean isFallbackSupported) { + this.isFallbackSupported = isFallbackSupported; + } + public RangerPolicyEngineOptions getPolicyEngineOptions() { return policyEngineOptions; } @@ -155,7 +220,7 @@ public RangerPolicyEngineOptions getPolicyEngineOptions() { public void setAuditExcludedUsersGroupsRoles(Set users, Set groups, Set roles) { auditExcludedUsers = CollectionUtils.isEmpty(users) ? Collections.emptySet() : new HashSet<>(users); auditExcludedGroups = CollectionUtils.isEmpty(groups) ? Collections.emptySet() : new HashSet<>(groups); - auditExcludedRoles = CollectionUtils.isEmpty(groups) ? Collections.emptySet() : new HashSet<>(roles); + auditExcludedRoles = CollectionUtils.isEmpty(roles) ? Collections.emptySet() : new HashSet<>(roles); if (LOG.isDebugEnabled()) { LOG.debug("auditExcludedUsers=" + auditExcludedUsers + ", auditExcludedGroups=" + auditExcludedGroups + ", auditExcludedRoles=" + auditExcludedRoles); @@ -171,6 +236,10 @@ public void setSuperUsersGroups(Set users, Set groups) { } } + public void setServiceAdmins(Set users) { + serviceAdmins = CollectionUtils.isEmpty(users) ? Collections.emptySet() : new HashSet<>(users); + } + public boolean isAuditExcludedUser(String userName) { return auditExcludedUsers.contains(userName); } @@ -191,6 +260,10 @@ public boolean hasSuperGroup(Set userGroups) { return userGroups != null && userGroups.size() > 0 && superGroups.size() > 0 && CollectionUtils.containsAny(userGroups, superGroups); } + public boolean isServiceAdmin(String userName) { + return serviceAdmins.contains(userName); + } + private void addResourcesForServiceType(String serviceType) { String auditCfg = "ranger-" + serviceType + "-audit.xml"; String securityCfg = "ranger-" + serviceType + "-security.xml"; diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java index 6d9fe26a47..fcd9ebd4da 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/hadoop/constants/RangerHadoopConstants.java @@ -24,10 +24,19 @@ public class RangerHadoopConstants { public static final String RANGER_OPTIMIZE_SUBACCESS_AUTHORIZATION_PROP = "ranger.optimize-subaccess-authorization" ; public static final boolean RANGER_ADD_HDFS_PERMISSION_DEFAULT = false; public static final boolean RANGER_OPTIMIZE_SUBACCESS_AUTHORIZATION_DEFAULT = false ; + + public static final String RANGER_USE_LEGACY_SUBACCESS_AUTHORIZATION_PROP = "ranger.plugin.hdfs.use.legacy.subaccess.authorization"; + public static final boolean RANGER_USE_LEGACY_SUBACCESS_AUTHORIZATION_DEFAULT = true; + public static final String READ_ACCCESS_TYPE = "read"; public static final String WRITE_ACCCESS_TYPE = "write"; public static final String EXECUTE_ACCCESS_TYPE = "execute"; + public static final String READ_EXECUTE_PERM = "READ_EXECUTE"; + public static final String WRITE_EXECUTE_PERM = "WRITE_EXECUTE"; + public static final String READ_WRITE_PERM = "READ_WRITE"; + public static final String ALL_PERM = "ALL"; + public static final String HDFS_ROOT_FOLDER_PATH_ALT = ""; public static final String HDFS_ROOT_FOLDER_PATH = "/"; @@ -37,6 +46,8 @@ public class RangerHadoopConstants { public static final boolean HIVE_BLOCK_UPDATE_IF_ROWFILTER_COLUMNMASK_SPECIFIED_DEFAULT_VALUE = true; public static final String HIVE_DESCRIBE_TABLE_SHOW_COLUMNS_AUTH_OPTION_PROP = "xasecure.hive.describetable.showcolumns.authorization.option"; public static final String HIVE_DESCRIBE_TABLE_SHOW_COLUMNS_AUTH_OPTION_PROP_DEFAULT_VALUE = "NONE"; + public static final String HIVE_URI_PERMISSION_COARSE_CHECK = "xasecure.hive.uri.permission.coarse.check"; + public static final boolean HIVE_URI_PERMISSION_COARSE_CHECK_DEFAULT_VALUE = false; public static final String HBASE_UPDATE_RANGER_POLICIES_ON_GRANT_REVOKE_PROP = "xasecure.hbase.update.xapolicies.on.grant.revoke"; public static final boolean HBASE_UPDATE_RANGER_POLICIES_ON_GRANT_REVOKE_DEFAULT_VALUE = true; @@ -81,4 +92,5 @@ public class RangerHadoopConstants { public static final String AUDITLOG_IS_ENABLED_PROP = "xasecure.audit.is.enabled"; public static final String KEYMGR_URL_PROP = "hdfs.keymanager.url"; + public static final String ACCESS_TYPE_MONITOR_HEALTH = "monitorHealth"; } diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java b/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java old mode 100644 new mode 100755 index 74555eefdf..1e1dc67f7b --- a/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/utils/JsonUtils.java @@ -17,39 +17,69 @@ package org.apache.ranger.authorization.utils; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.model.AuditFilter; +import org.apache.ranger.plugin.model.RangerGds.RangerTagDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPrincipal; +import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerValidityRecurrence; import org.apache.ranger.plugin.model.RangerValiditySchedule; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.lang.reflect.Type; -import java.util.HashMap; +import java.io.Reader; +import java.io.Writer; +import java.text.SimpleDateFormat; import java.util.List; import java.util.Map; +import java.util.Set; + +import static com.fasterxml.jackson.databind.DeserializationFeature.*; public class JsonUtils { - private static final Log LOG = LogFactory.getLog(JsonUtils.class); + private static final Logger LOG = LoggerFactory.getLogger(JsonUtils.class); - private static final HashMap MAP_STRING_STRING = new HashMap<>(); + private static final TypeReference TYPE_MAP_STRING_STRING = new TypeReference>() {}; + private static final TypeReference TYPE_SET_STRING = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_STRING = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_RANGER_VALIDITY_SCHEDULE = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_AUDIT_FILTER = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_RANGER_VALIDITY_RECURRENCE = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_RANGER_PRINCIPAL = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_RANGER_TAG_MASK_INFO = new TypeReference>() {}; + private static final TypeReference TYPE_MAP_RANGER_MASK_INFO = new TypeReference>() {}; + private static final TypeReference TYPE_MAP_RANGER_POLICY_RESOURCE = new TypeReference>() {}; + private static final TypeReference TYPE_LIST_RANGER_TAG = new TypeReference>() {}; - private static final Gson gson; + static private final ThreadLocal MAPPER = new ThreadLocal() { + @Override + protected ObjectMapper initialValue() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setDateFormat(new SimpleDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z")); + objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true); + objectMapper.configure(FAIL_ON_UNKNOWN_PROPERTIES, false); + return objectMapper; + } + }; - static { - gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z") - .create(); + static public ObjectMapper getMapper() { + return MAPPER.get(); } public static String mapToJson(Map map) { String ret = null; if (MapUtils.isNotEmpty(map)) { try { - ret = gson.toJson(map); + ret = getMapper().writeValueAsString(map); } catch (Exception e) { LOG.error("Invalid input data: ", e); } @@ -61,7 +91,19 @@ public static String listToJson(List list) { String ret = null; if (CollectionUtils.isNotEmpty(list)) { try { - ret = gson.toJson(list); + ret = getMapper().writeValueAsString(list); + } catch (Exception e) { + LOG.error("Invalid input data: ", e); + } + } + return ret; + } + + public static String setToJson(Set set) { + String ret = null; + if (CollectionUtils.isNotEmpty(set)) { + try { + ret = getMapper().writeValueAsString(set); } catch (Exception e) { LOG.error("Invalid input data: ", e); } @@ -74,7 +116,7 @@ public static String objectToJson(Object object) { if(object != null) { try { - ret = gson.toJson(object); + ret = getMapper().writeValueAsString(object); } catch(Exception excp) { LOG.warn("objectToJson() failed to convert object to Json", excp); } @@ -83,28 +125,93 @@ public static String objectToJson(Object object) { return ret; } - public static T jsonToObject(String jsonStr, Class clz) { + public static T jsonToObject(Reader reader, Class clz) { + T ret = null; + + if(null != reader) { + try { + ret = getMapper().readValue(reader, clz); + } catch(Exception excp) { + LOG.warn("jsonToObject() failed to convert json to object: class " + clz + " reader ", excp); + } + } + + return ret; + } + + public static void objectToWriter(Writer writer, T object) { + if(null != writer) { + try { + getMapper().writeValue(writer, object); + } catch(Exception excp) { + LOG.warn("objectToWriter() failed to write oject to writer: class " + object + " writer ", excp); + } + } + } + + public static T jsonToObject(String jsonStr, Class clz) { T ret = null; if(StringUtils.isNotEmpty(jsonStr)) { try { - ret = gson.fromJson(jsonStr, clz); + ret = getMapper().readValue(jsonStr, clz); } catch(Exception excp) { - LOG.warn("jsonToObject() failed to convert json to object: " + jsonStr, excp); + LOG.warn("jsonToObject() failed to convert json to object: class " +clz + " JSON "+ jsonStr, excp); } } return ret; } + public static T jsonToObject(String jsonStr, TypeReference valueTypeRef) throws JsonProcessingException, JsonMappingException { + T ret = null; + if(StringUtils.isNotEmpty(jsonStr)) { + try { + ret = getMapper().readValue(jsonStr, valueTypeRef); + } catch(Exception excp) { + LOG.warn("jsonToObject() failed to convert json to object: " + jsonStr, excp); + } + } + + return (T) ret; + } + public static Map jsonToMapStringString(String jsonStr) { Map ret = null; if(StringUtils.isNotEmpty(jsonStr)) { try { - ret = gson.fromJson(jsonStr, MAP_STRING_STRING.getClass()); + ret = (Map) getMapper().readValue(jsonStr, TYPE_MAP_STRING_STRING); } catch(Exception excp) { - LOG.warn("jsonToObject() failed to convert json to object: " + jsonStr, excp); + LOG.warn("jsonToMapStringString() failed to convert json to object: " + jsonStr, excp); + } + } + + return ret; + } + + public static Set jsonToSetString(String jsonStr) { + Set ret = null; + + if (StringUtils.isNotEmpty(jsonStr)) { + try { + ret = (Set) getMapper().readValue(jsonStr, TYPE_SET_STRING); + } catch(Exception excp) { + LOG.warn("jsonToSetString() failed to convert json to object: " + jsonStr, excp); + } + } + + return ret; + } + + public static List jsonToListString(String jsonStr) { + List ret = null; + + if (StringUtils.isNotEmpty(jsonStr)) { + try { + ret = (List) getMapper().readValue(jsonStr, TYPE_LIST_STRING); + } catch(Exception excp) { + LOG.warn("jsonToListString() failed to convert json to object: " + jsonStr, excp); } } @@ -113,22 +220,73 @@ public static Map jsonToMapStringString(String jsonStr) { public static List jsonToRangerValiditySchedule(String jsonStr) { try { - Type listType = new TypeToken>() { - }.getType(); - return gson.fromJson(jsonStr, listType); + return (List) getMapper().readValue(jsonStr, TYPE_LIST_RANGER_VALIDITY_SCHEDULE); } catch (Exception e) { LOG.error("Cannot get List from " + jsonStr, e); return null; } } + + public static List jsonToAuditFilterList(String jsonStr) { + try { + return (List) getMapper().readValue(jsonStr, TYPE_LIST_AUDIT_FILTER); + } catch (Exception e) { + LOG.error("Cannot get List from " + jsonStr, e); + return null; + } + } + public static List jsonToRangerValidityRecurringSchedule(String jsonStr) { try { - Type listType = new TypeToken>() { - }.getType(); - return gson.fromJson(jsonStr, listType); + return (List) getMapper().readValue(jsonStr, TYPE_LIST_RANGER_VALIDITY_RECURRENCE); } catch (Exception e) { LOG.error("Cannot get List from " + jsonStr, e); return null; } } + + public static List jsonToRangerPrincipalList(String jsonStr) { + try { + return (List) getMapper().readValue(jsonStr, TYPE_LIST_RANGER_PRINCIPAL); + } catch (Exception e) { + LOG.error("Cannot get List from " + jsonStr, e); + return null; + } + } + + public static List jsonToRangerTagList(String jsonStr) { + try { + return (List) getMapper().readValue(jsonStr, TYPE_LIST_RANGER_TAG); + } catch (Exception e) { + LOG.error("Cannot get List from " + jsonStr, e); + return null; + } + } + + public static Map jsonToMapMaskInfo(String jsonStr) { + try { + return (Map) getMapper().readValue(jsonStr, TYPE_MAP_RANGER_MASK_INFO); + } catch (Exception e) { + LOG.error("Cannot get Map from " + jsonStr, e); + return null; + } + } + + public static List jsonToListTagMaskInfo(String jsonStr) { + try { + return (List) getMapper().readValue(jsonStr, TYPE_LIST_RANGER_TAG_MASK_INFO); + } catch (Exception e) { + LOG.error("Cannot get List from " + jsonStr, e); + return null; + } + } + + public static Map jsonToMapPolicyResource(String jsonStr) { + try { + return (Map) getMapper().readValue(jsonStr, TYPE_MAP_RANGER_POLICY_RESOURCE); + } catch (Exception e) { + LOG.error("Cannot get Map from " + jsonStr, e); + return null; + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/authorization/utils/StringUtil.java b/agents-common/src/main/java/org/apache/ranger/authorization/utils/StringUtil.java index 4e959bccdf..db04e5539b 100644 --- a/agents-common/src/main/java/org/apache/ranger/authorization/utils/StringUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/authorization/utils/StringUtil.java @@ -19,9 +19,14 @@ package org.apache.ranger.authorization.utils; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; +import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; @@ -30,13 +35,18 @@ import java.util.Map; import java.util.Set; import java.util.TimeZone; +import java.util.zip.GZIPInputStream; +import java.util.zip.GZIPOutputStream; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; public class StringUtil { - private static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT+0"); + public static boolean equals(String str1, String str2) { boolean ret = false; @@ -307,6 +317,21 @@ public static Set toSet(String str) { return values; } + public static List toList(String str) { + List values; + if (StringUtils.isNotBlank(str)) { + values = new ArrayList<>(); + for (String item : str.split(",")) { + if (StringUtils.isNotBlank(item)) { + values.add(StringUtils.trim(item)); + } + } + } else { + values = Collections.emptyList(); + } + return values; + } + public static List getURLs(String configURLs) { List configuredURLs = new ArrayList<>(); if(configURLs!=null) { @@ -322,4 +347,229 @@ public static List getURLs(String configURLs) { } return configuredURLs; } + + public static Map> dedupStringsMapOfMap(Map> value, Map strTbl) { + final Map> ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry> entry : value.entrySet()) { + ret.put(dedupString(entry.getKey(), strTbl), dedupStringsMap(entry.getValue(), strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static Map> dedupStringsMapOfSet(Map> value, Map strTbl) { + final Map> ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry> entry : value.entrySet()) { + ret.put(dedupString(entry.getKey(), strTbl), dedupStringsSet(entry.getValue(), strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static Map> dedupStringsMapOfList(Map> value, Map strTbl) { + final Map> ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry> entry : value.entrySet()) { + ret.put(dedupString(entry.getKey(), strTbl), dedupStringsList(entry.getValue(), strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static HashMap> dedupStringsHashMapOfList(HashMap> value, Map strTbl) { + final HashMap> ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry> entry : value.entrySet()) { + ret.put(dedupString(entry.getKey(), strTbl), dedupStringsList(entry.getValue(), strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static Map dedupStringsMapOfObject(Map value, Map strTbl) { + final Map ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry entry : value.entrySet()) { + ret.put(dedupString(entry.getKey(), strTbl), entry.getValue()); + } + } else { + ret = value; + } + + return ret; + } + + public static Map dedupStringsMapOfPolicyResource(Map value, Map strTbl) { + final Map ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry entry : value.entrySet()) { + RangerPolicyResource resource = entry.getValue(); + + resource.dedupStrings(strTbl); + + ret.put(dedupString(entry.getKey(), strTbl), resource); + } + } else { + ret = value; + } + + return ret; + } + + public static Map dedupStringsMap(Map value, Map strTbl) { + final Map ret; + + if (MapUtils.isNotEmpty(value)) { + ret = new HashMap<>(value.size()); + + for (Map.Entry entry : value.entrySet()) { + ret.put(dedupString(entry.getKey(), strTbl), dedupString(entry.getValue(), strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static Set dedupStringsSet(Set value, Map strTbl) { + final Set ret; + + if (CollectionUtils.isNotEmpty(value)) { + ret = new HashSet<>(value.size()); + + for (String val : value) { + ret.add(dedupString(val, strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static List dedupStringsList(List value, Map strTbl) { + final List ret; + + if (CollectionUtils.isNotEmpty(value)) { + ret = new ArrayList<>(value.size()); + + for (String val : value) { + ret.add(dedupString(val, strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static Collection dedupStringsCollection(Collection value, Map strTbl) { + final Collection ret; + + if (CollectionUtils.isNotEmpty(value)) { + ret = value instanceof Set ? new HashSet<>(value.size()) : new ArrayList<>(value.size()); + + for (String val : value) { + ret.add(dedupString(val, strTbl)); + } + } else { + ret = value; + } + + return ret; + } + + public static String dedupString(String str, Map strTbl) { + String ret = str != null ? strTbl.putIfAbsent(str, str) : null; + + return ret == null ? str : ret; + } + + public static String compressString(String input) throws IOException { + final String ret; + + if (StringUtils.isEmpty(input)) { + ret = input; + } else { + ret = new String(gzipCompress(input), StandardCharsets.ISO_8859_1); + } + + return ret; + } + + public static String decompressString(String input) throws IOException { + final String ret; + + if (StringUtils.isEmpty(input)) { + ret = input; + } else { + ret = gzipDecompress(input.getBytes(StandardCharsets.ISO_8859_1)); + } + + return ret; + } + + public static byte[] gzipCompress(String input) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + GZIPOutputStream gos = new GZIPOutputStream(out); + + gos.write(input.getBytes(StandardCharsets.ISO_8859_1)); + gos.close(); + + return out.toByteArray(); + } + + public static String gzipDecompress(byte[] input) throws IOException { + ByteArrayInputStream in = new ByteArrayInputStream(input); + GZIPInputStream gis = new GZIPInputStream(in); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[1024]; + + while (true) { + int bytesRead = gis.read(buf, 0, buf.length); + + if (bytesRead == -1) { + break; + } + + out.write(buf, 0, bytesRead); + } + + gis.close(); + + return new String(out.toByteArray(), StandardCharsets.ISO_8859_1); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerDefaultAuditHandler.java b/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerDefaultAuditHandler.java index 4273726437..8efea1fbac 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerDefaultAuditHandler.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerDefaultAuditHandler.java @@ -25,23 +25,25 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.ranger.audit.model.AuthzAuditEvent; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.provider.MiscUtil; import org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; import org.apache.ranger.plugin.service.RangerBasePlugin; import org.apache.ranger.plugin.util.JsonUtilsV2; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.RangerRESTUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerDefaultAuditHandler implements RangerAccessResultProcessor { - private static final Log LOG = LogFactory.getLog(RangerDefaultAuditHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultAuditHandler.class); private static final String CONF_AUDIT_ID_STRICT_UUID = "xasecure.audit.auditid.strict.uuid"; private static final boolean DEFAULT_AUDIT_ID_STRICT_UUID = false; @@ -131,10 +133,14 @@ public AuthzAuditEvent getAuthzEvents(RangerAccessResult result) { ret.setClientType(request.getClientType()); ret.setSessionId(request.getSessionId()); ret.setAclEnforcer(moduleName); + Set tags = getTags(request); if (tags != null) { ret.setTags(tags); } + + ret.setDatasets(getDatasets(request)); + ret.setProjects(getProjects(request)); ret.setAdditionalInfo(getAdditionalInfo(request)); ret.setClusterName(request.getClusterName()); ret.setZoneName(result.getZoneName()); @@ -262,15 +268,28 @@ protected final Set getTags(RangerAccessRequest request) { return ret; } + public final Set getDatasets(RangerAccessRequest request) { + GdsAccessResult gdsResult = RangerAccessRequestUtil.getGdsResultFromContext(request.getContext()); + + return gdsResult != null ? gdsResult.getDatasets() : null; + } + + public final Set getProjects(RangerAccessRequest request) { + GdsAccessResult gdsResult = RangerAccessRequestUtil.getGdsResultFromContext(request.getContext()); + + return gdsResult != null ? gdsResult.getProjects() : null; + } + public String getAdditionalInfo(RangerAccessRequest request) { if (StringUtils.isBlank(request.getRemoteIPAddress()) && CollectionUtils.isEmpty(request.getForwardedAddresses())) { return null; } - StringBuilder sb = new StringBuilder(); - sb.append("{\"remote-ip-address\":").append(request.getRemoteIPAddress()) - .append(", \"forwarded-ip-addresses\":[").append(StringUtils.join(request.getForwardedAddresses(), ", ")).append("]"); + Map addInfomap=new HashMap(); + addInfomap.put("forwarded-ip-addresses", "[" + StringUtils.join(request.getForwardedAddresses(), ", ") + "]"); + addInfomap.put("remote-ip-address", request.getRemoteIPAddress()); + String addInfojsonStr = JsonUtils.mapToJson(addInfomap); + return addInfojsonStr; - return sb.toString(); } private String generateNextAuditEventId() { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerMultiResourceAuditHandler.java b/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerMultiResourceAuditHandler.java index d7e6376f02..c657cce8f1 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerMultiResourceAuditHandler.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/audit/RangerMultiResourceAuditHandler.java @@ -22,12 +22,12 @@ import java.util.ArrayList; import java.util.Collection; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerMultiResourceAuditHandler extends RangerDefaultAuditHandler { - private static final Log LOG = LogFactory.getLog(RangerMultiResourceAuditHandler.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerMultiResourceAuditHandler.class); Collection auditEvents = new ArrayList<>(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java b/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java index ed002ef747..b1628f2c83 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/client/BaseClient.java @@ -27,13 +27,13 @@ import javax.security.auth.Subject; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.SecureClientLogin; import org.apache.ranger.plugin.util.PasswordUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class BaseClient { - private static final Log LOG = LogFactory.getLog(BaseClient.class); + private static final Logger LOG = LoggerFactory.getLogger(BaseClient.class); private static final String DEFAULT_NAME_RULE = "DEFAULT"; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/client/HadoopConfigHolder.java b/agents-common/src/main/java/org/apache/ranger/plugin/client/HadoopConfigHolder.java index a065a8db74..492dbcdcf9 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/client/HadoopConfigHolder.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/client/HadoopConfigHolder.java @@ -28,12 +28,12 @@ import java.util.Set; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.SecureClientLogin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class HadoopConfigHolder { - private static final Log LOG = LogFactory.getLog(HadoopConfigHolder.class); + private static final Logger LOG = LoggerFactory.getLogger(HadoopConfigHolder.class); public static final String GLOBAL_LOGIN_PARAM_PROP_FILE = "hadoop-login.properties"; public static final String DEFAULT_DATASOURCE_PARAM_PROP_FILE = "datasource.properties"; public static final String RESOURCEMAP_PROP_FILE = "resourcenamemap.properties"; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterCondition.java index e5cc916806..3538b87681 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterCondition.java @@ -20,13 +20,13 @@ package org.apache.ranger.plugin.conditionevaluator; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAccessedFromClusterCondition extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerAccessedFromClusterCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAccessedFromClusterCondition.class); private boolean isAlwaysTrue = false; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterTypeCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterTypeCondition.java index 50a92bd6ff..9e0a01f5d0 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterTypeCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedFromClusterTypeCondition.java @@ -19,11 +19,12 @@ package org.apache.ranger.plugin.conditionevaluator; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class RangerAccessedFromClusterTypeCondition extends RangerAbstractConditionEvaluator{ - private static final Log LOG = LogFactory.getLog(RangerAccessedFromClusterTypeCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAccessedFromClusterTypeCondition.class); private boolean isAlwaysTrue = false; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterCondition.java index d8e966c3d1..c0850f08ba 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterCondition.java @@ -20,13 +20,13 @@ package org.apache.ranger.plugin.conditionevaluator; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAccessedNotFromClusterCondition extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerAccessedNotFromClusterCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAccessedNotFromClusterCondition.class); private boolean isAlwaysTrue = false; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterTypeCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterTypeCondition.java index eb6c45c0df..fe882960ef 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterTypeCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAccessedNotFromClusterTypeCondition.java @@ -19,12 +19,12 @@ package org.apache.ranger.plugin.conditionevaluator; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAccessedNotFromClusterTypeCondition extends RangerAbstractConditionEvaluator{ - private static final Log LOG = LogFactory.getLog(RangerAccessedNotFromClusterTypeCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAccessedNotFromClusterTypeCondition.class); private boolean isAlwaysTrue = false; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java index 3221f79d51..c7b78908a1 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerAnyOfExpectedTagsPresentConditionEvaluator.java @@ -19,19 +19,19 @@ package org.apache.ranger.plugin.conditionevaluator; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.Collections; import java.util.HashSet; import java.util.Set; // Policy Condition to check if resource Tags does contain any of the policy Condition Tags - public class RangerAnyOfExpectedTagsPresentConditionEvaluator extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerAnyOfExpectedTagsPresentConditionEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAnyOfExpectedTagsPresentConditionEvaluator.class); private final Set policyConditionTags = new HashSet<>(); @@ -61,15 +61,17 @@ public boolean isMatched(RangerAccessRequest request) { LOG.debug("==> RangerAnyOfExpectedTagsPresentConditionEvaluator.isMatched(" + request + ")"); } - boolean matched = false; - - RangerAccessRequest readOnlyRequest = request.getReadOnlyCopy(); - RangerScriptExecutionContext context = new RangerScriptExecutionContext(readOnlyRequest); - Set resourceTags = context.getAllTagTypes(); + boolean matched = false; + Set resourceTags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); if (resourceTags != null) { // check if resource Tags does contain any of the policy Condition Tags - matched = (!Collections.disjoint(resourceTags, policyConditionTags)); + for (RangerTagForEval tag : resourceTags) { + if (policyConditionTags.contains(tag.getType())) { + matched = true; + break; + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueInCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueInCondition.java index 316cdb5d10..5346fbd06c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueInCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueInCondition.java @@ -21,14 +21,14 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; public class RangerContextAttributeValueInCondition extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerContextAttributeValueInCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerContextAttributeValueInCondition.class); protected String attributeName; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueNotInCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueNotInCondition.java index 7df73dfd21..f11eb7edcd 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueNotInCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerContextAttributeValueNotInCondition.java @@ -21,14 +21,14 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; public class RangerContextAttributeValueNotInCondition extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerContextAttributeValueNotInCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerContextAttributeValueNotInCondition.class); protected String attributeName; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesAccessedTogetherCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesAccessedTogetherCondition.java index 960efbe87d..3ea5de6e3c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesAccessedTogetherCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesAccessedTogetherCondition.java @@ -21,8 +21,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; @@ -30,6 +28,8 @@ import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.RangerRequestedResources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; @@ -37,7 +37,7 @@ import java.util.Map; public class RangerHiveResourcesAccessedTogetherCondition extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerHiveResourcesAccessedTogetherCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerHiveResourcesAccessedTogetherCondition.class); private List matchers = new ArrayList<>(); private boolean isInitialized; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesNotAccessedTogetherCondition.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesNotAccessedTogetherCondition.java index fb2ba2ea9c..7a82191d2f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesNotAccessedTogetherCondition.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerHiveResourcesNotAccessedTogetherCondition.java @@ -21,8 +21,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; @@ -30,6 +28,8 @@ import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.RangerRequestedResources; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; @@ -37,7 +37,7 @@ import java.util.Map; public class RangerHiveResourcesNotAccessedTogetherCondition extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerHiveResourcesNotAccessedTogetherCondition.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerHiveResourcesNotAccessedTogetherCondition.class); private List matchers = new ArrayList<>(); private boolean isInitialized; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerIpMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerIpMatcher.java index 03d09a4ef7..f2a0773242 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerIpMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerIpMatcher.java @@ -27,16 +27,16 @@ import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Credits: Large parts of this file have been lifted as is from org.apache.ranger.pdp.knox.URLBasedAuthDB. Credits for those are due to Dilli Arumugam. * @author alal */ public class RangerIpMatcher extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerIpMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerIpMatcher.class); private List _exactIps = new ArrayList<>(); private List _wildCardIps = new ArrayList<>(); private boolean _allowAny; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java index d04f4b388d..9f1b60c982 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerNoneOfExpectedTagsPresentConditionEvaluator.java @@ -19,11 +19,12 @@ package org.apache.ranger.plugin.conditionevaluator; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -31,7 +32,7 @@ public class RangerNoneOfExpectedTagsPresentConditionEvaluator extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerNoneOfExpectedTagsPresentConditionEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerNoneOfExpectedTagsPresentConditionEvaluator.class); private final Set policyConditionTags = new HashSet<>(); @@ -61,15 +62,17 @@ public boolean isMatched(RangerAccessRequest request) { LOG.debug("==> RangerNoneOfExpectedTagsPresentConditionEvaluator.isMatched(" + request + ")"); } - boolean matched = true; - - RangerAccessRequest readOnlyRequest = request.getReadOnlyCopy(); - RangerScriptExecutionContext context = new RangerScriptExecutionContext(readOnlyRequest); - Set resourceTags = context.getAllTagTypes(); + boolean matched = true; + Set resourceTags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); if (resourceTags != null) { // check if resource Tags does not contain any tags in the policy condition - matched = (Collections.disjoint(resourceTags, policyConditionTags)); + for (RangerTagForEval tag : resourceTags) { + if (policyConditionTags.contains(tag.getType())) { + matched = false; + break; + } + } } if(LOG.isDebugEnabled()) { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java index b306f5150e..6eb192270d 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptConditionEvaluator.java @@ -22,41 +22,44 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ranger.plugin.classloader.RangerPluginClassLoader; -import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator; +import org.apache.ranger.plugin.util.ScriptEngineUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import javax.script.Bindings; import javax.script.ScriptEngine; -import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import java.util.Collections; import java.util.List; import java.util.Map; +import static org.apache.ranger.plugin.util.RangerCommonConstants.SCRIPT_OPTION_ENABLE_JSON_CTX; + + public class RangerScriptConditionEvaluator extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerScriptConditionEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerScriptConditionEvaluator.class); private ScriptEngine scriptEngine; + private Boolean enableJsonCtx = null; @Override public void init() { - if (LOG.isDebugEnabled()) { LOG.debug("==> RangerScriptConditionEvaluator.init(" + condition + ")"); } super.init(); - String engineName = "JavaScript"; - + String engineName = "JavaScript"; Map evalOptions = conditionDef. getEvaluatorOptions(); if (MapUtils.isNotEmpty(evalOptions)) { engineName = evalOptions.get("engineName"); + + String strEnableJsonCtx = evalOptions.get(SCRIPT_OPTION_ENABLE_JSON_CTX); + + if (StringUtils.isNotEmpty(strEnableJsonCtx)) { + enableJsonCtx = Boolean.parseBoolean(strEnableJsonCtx); + } } if (StringUtils.isBlank(engineName)) { @@ -67,51 +70,11 @@ public void init() { LOG.debug("RangerScriptConditionEvaluator.init() - engineName=" + engineName); } - String conditionType = condition != null ? condition.getType() : null; - - try { - ScriptEngineManager manager = new ScriptEngineManager(); - - if (LOG.isDebugEnabled()) { - List factories = manager.getEngineFactories(); - - if (CollectionUtils.isEmpty(factories)) { - LOG.debug("List of scriptEngineFactories is empty!!"); - } else { - for (ScriptEngineFactory factory : factories) { - LOG.debug("engineName=" + factory.getEngineName() + ", language=" + factory.getLanguageName()); - } - } - } - - scriptEngine = manager.getEngineByName(engineName); - } catch (Exception exp) { - LOG.error("RangerScriptConditionEvaluator.init() failed with exception=" + exp); - } + scriptEngine = ScriptEngineUtil.createScriptEngine(serviceDef.getName()); if (scriptEngine == null) { - LOG.warn("failed to initialize condition '" + conditionType + "': script engine '" + engineName + "' was not created in a default manner"); - LOG.info("Will try to get script-engine from plugin-class-loader"); - - - RangerPluginClassLoader pluginClassLoader; - - try { - - pluginClassLoader = RangerPluginClassLoader.getInstance(serviceDef.getName(), null); - - if (pluginClassLoader != null) { - scriptEngine = pluginClassLoader.getScriptEngine(engineName); - } else { - LOG.error("Cannot get script-engine from null pluginClassLoader"); - } - - } catch (Throwable exp) { - LOG.error("RangerScriptConditionEvaluator.init() failed with exception=", exp); - } - } + String conditionType = condition != null ? condition.getType() : null; - if (scriptEngine == null) { LOG.error("failed to initialize condition '" + conditionType + "': script engine '" + engineName + "' was not created"); } else { LOG.info("ScriptEngine for engineName=[" + engineName + "] is successfully created"); @@ -127,54 +90,34 @@ public boolean isMatched(RangerAccessRequest request) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerScriptConditionEvaluator.isMatched()"); } + boolean result = true; if (scriptEngine != null) { - String script = getScript(); if (StringUtils.isNotBlank(script)) { - - RangerAccessRequest readOnlyRequest = request.getReadOnlyCopy(); - - RangerScriptExecutionContext context = new RangerScriptExecutionContext(readOnlyRequest); - RangerTagForEval currentTag = context.getCurrentTag(); - Map tagAttribs = currentTag != null ? currentTag.getAttributes() : Collections.emptyMap(); - - Bindings bindings = scriptEngine.createBindings(); - - bindings.put("ctx", context); - bindings.put("tag", currentTag); - bindings.put("tagAttr", tagAttribs); - if (LOG.isDebugEnabled()) { LOG.debug("RangerScriptConditionEvaluator.isMatched(): script={" + script + "}"); } - try { - Object ret = scriptEngine.eval(script, bindings); + if (enableJsonCtx == null) { // if not specified in evaluatorOptions, set it on first call to isMatched() + enableJsonCtx = RangerRequestScriptEvaluator.needsJsonCtxEnabled(script); + } - if (ret == null) { - ret = context.getResult(); - } - if (ret instanceof Boolean) { - result = (Boolean) ret; - } + RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request, scriptEngine, enableJsonCtx); - } catch (NullPointerException nullp) { - LOG.error("RangerScriptConditionEvaluator.isMatched(): eval called with NULL argument(s)", nullp); + evaluator.evaluateConditionScript(script); - } catch (ScriptException exception) { - LOG.error("RangerScriptConditionEvaluator.isMatched(): failed to evaluate script," + - " exception=" + exception); - } + result = evaluator.getResult(); } else { String conditionType = condition != null ? condition.getType() : null; + LOG.error("failed to evaluate condition '" + conditionType + "': script is empty"); } - } else { String conditionType = condition != null ? condition.getType() : null; + LOG.error("failed to evaluate condition '" + conditionType + "': script engine not found"); } @@ -187,13 +130,12 @@ public boolean isMatched(RangerAccessRequest request) { } protected String getScript() { - String ret = null; - + String ret = null; List values = condition.getValues(); if (CollectionUtils.isNotEmpty(values)) { - String value = values.get(0); + if (StringUtils.isNotBlank(value)) { ret = value.trim(); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptExecutionContext.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptExecutionContext.java deleted file mode 100644 index dc4ede9a78..0000000000 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptExecutionContext.java +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.ranger.plugin.conditionevaluator; - -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; -import org.apache.ranger.authorization.utils.StringUtil; -import org.apache.ranger.plugin.contextenricher.RangerTagForEval; -import org.apache.ranger.plugin.policyengine.RangerAccessRequest; -import org.apache.ranger.plugin.policyengine.RangerAccessResource; -import org.apache.ranger.plugin.util.RangerAccessRequestUtil; - -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TimeZone; - -public final class RangerScriptExecutionContext { - private static final Log LOG = LogFactory.getLog(RangerScriptExecutionContext.class); - private static final String TAG_ATTR_DATE_FORMAT_PROP = "ranger.plugin.tag.attr.additional.date.formats"; - private static final String TAG_ATTR_DATE_FORMAT_SEPARATOR = "||"; - private static final String TAG_ATTR_DATE_FORMAT_SEPARATOR_REGEX = "\\|\\|"; - private static final String DEFAULT_RANGER_TAG_ATTRIBUTE_DATE_FORMAT = "yyyy/MM/dd"; - private static final String DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME = "ATLAS_DATE_FORMAT"; - private static final String DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; - - private final RangerAccessRequest accessRequest; - private Boolean result = false; - - private static String[] dateFormatStrings = null; - - static { - init(null); - } - - private static final ThreadLocal> THREADLOCAL_DATE_FORMATS = - new ThreadLocal>() { - @Override protected List initialValue() { - List ret = new ArrayList<>(); - - for (String dateFormatString : dateFormatStrings) { - try { - if (StringUtils.isNotBlank(dateFormatString)) { - if (StringUtils.equalsIgnoreCase(dateFormatString, DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME)) { - dateFormatString = DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT; - } - SimpleDateFormat df = new SimpleDateFormat(dateFormatString); - df.setLenient(false); - ret.add(df); - } - } catch (Exception exception) { - // Ignore - } - } - - return ret; - } - }; - - RangerScriptExecutionContext(final RangerAccessRequest accessRequest) { - this.accessRequest = accessRequest; - } - - public static void init(Configuration config) { - StringBuilder sb = new StringBuilder(DEFAULT_RANGER_TAG_ATTRIBUTE_DATE_FORMAT); - - sb.append(TAG_ATTR_DATE_FORMAT_SEPARATOR).append(DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME); - - String additionalDateFormatsValue = config != null ? config.get(TAG_ATTR_DATE_FORMAT_PROP) : null; - - if (StringUtils.isNotBlank(additionalDateFormatsValue)) { - sb.append(TAG_ATTR_DATE_FORMAT_SEPARATOR).append(additionalDateFormatsValue); - } - - String[] formatStrings = sb.toString().split(TAG_ATTR_DATE_FORMAT_SEPARATOR_REGEX); - - Arrays.sort(formatStrings, new Comparator() { - @Override - public int compare(String first, String second) { - return Integer.compare(second.length(), first.length()); - } - }); - - RangerScriptExecutionContext.dateFormatStrings = formatStrings; - } - - public String getResource() { - String ret = null; - Object val = getRequestContext().get(RangerAccessRequestUtil.KEY_CONTEXT_RESOURCE); - - if(val != null) { - if(val instanceof RangerAccessResource) { - ret = ((RangerAccessResource)val).getAsString(); - } else { - ret = val.toString(); - } - } - - return ret; - } - - public Map getRequestContext() { - return accessRequest.getContext(); - } - - public String getRequestContextAttribute(String attributeName) { - String ret = null; - - if (StringUtils.isNotBlank(attributeName)) { - Object val = getRequestContext().get(attributeName); - - if(val != null) { - ret = val.toString(); - } - } - - return ret; - } - - public boolean isAccessTypeAny() { return accessRequest.isAccessTypeAny(); } - - public boolean isAccessTypeDelegatedAdmin() { return accessRequest.isAccessTypeDelegatedAdmin(); } - - public String getUser() { return accessRequest.getUser(); } - - public Set getUserGroups() { return accessRequest.getUserGroups(); } - - public Date getAccessTime() { return accessRequest.getAccessTime() != null ? accessRequest.getAccessTime() : new Date(); } - - public String getClientIPAddress() { return accessRequest.getClientIPAddress(); } - - public String getClientType() { return accessRequest.getClientType(); } - - public String getAction() { return accessRequest.getAction(); } - - public String getRequestData() { return accessRequest.getRequestData(); } - - public String getSessionId() { return accessRequest.getSessionId(); } - - public RangerTagForEval getCurrentTag() { - RangerTagForEval ret = null; - Object val = getRequestContext().get(RangerAccessRequestUtil.KEY_CONTEXT_TAG_OBJECT); - - if(val instanceof RangerTagForEval) { - ret = (RangerTagForEval)val; - } else { - if (LOG.isDebugEnabled()) { - logDebug("RangerScriptExecutionContext.getCurrentTag() - No current TAG object. Script execution must be for resource-based policy."); - } - } - return ret; - } - - public String getCurrentTagType() { - RangerTagForEval tagObject = getCurrentTag(); - return (tagObject != null) ? tagObject.getType() : null; - } - - public Set getAllTagTypes() { - Set allTagTypes = null; - Set tagObjectList = getAllTags(); - - if (CollectionUtils.isNotEmpty(tagObjectList)) { - for (RangerTagForEval tag : tagObjectList) { - String tagType = tag.getType(); - if (allTagTypes == null) { - allTagTypes = new HashSet<>(); - } - allTagTypes.add(tagType); - } - } - - return allTagTypes; - } - - public Map getTagAttributes(final String tagType) { - Map ret = null; - - if (StringUtils.isNotBlank(tagType)) { - Set tagObjectList = getAllTags(); - - // Assumption: There is exactly one tag with given tagType in the list of tags - may not be true ***TODO*** - // This will get attributes of the first tagType that matches - if (CollectionUtils.isNotEmpty(tagObjectList)) { - for (RangerTagForEval tag : tagObjectList) { - if (tag.getType().equals(tagType)) { - ret = tag.getAttributes(); - break; - } - } - } - } - - return ret; - } - - public Set getAttributeNames(final String tagType) { - Set ret = null; - Map attributes = getTagAttributes(tagType); - - if (attributes != null) { - ret = attributes.keySet(); - } - - return ret; - } - - public String getAttributeValue(final String tagType, final String attributeName) { - String ret = null; - - if (StringUtils.isNotBlank(tagType) || StringUtils.isNotBlank(attributeName)) { - Map attributes = getTagAttributes(tagType); - - if (attributes != null) { - ret = attributes.get(attributeName); - } - } - return ret; - } - - public String getAttributeValue(final String attributeName) { - String ret = null; - - if (StringUtils.isNotBlank(attributeName)) { - RangerTagForEval tag = getCurrentTag(); - Map attributes = null; - if (tag != null) { - attributes = tag.getAttributes(); - } - if (attributes != null) { - ret = attributes.get(attributeName); - } - } - - return ret; - } - - public boolean getResult() { - return result; - - } - - public void setResult(final boolean result) { - this.result = result; - } - - private Date getAsDate(String value, SimpleDateFormat df) { - Date ret = null; - - TimeZone savedTimeZone = df.getTimeZone(); - try { - ret = df.parse(value); - } catch (ParseException exception) { - // Ignore - } finally { - df.setTimeZone(savedTimeZone); - } - - return ret; - } - - public Date getAsDate(String value) { - Date ret = null; - - if (StringUtils.isNotBlank(value)) { - for (SimpleDateFormat simpleDateFormat : THREADLOCAL_DATE_FORMATS.get()) { - ret = getAsDate(value, simpleDateFormat); - if (ret != null) { - if (LOG.isDebugEnabled()) { - logDebug("RangerScriptExecutionContext.getAsDate() -The best match found for Format-String:[" + simpleDateFormat.toPattern() + "], date:[" + ret +"]"); - } - break; - } - } - } - - if (ret == null) { - logError("RangerScriptExecutionContext.getAsDate() - Could not convert [" + value + "] to Date using any of the Format-Strings: " + Arrays.toString(dateFormatStrings)); - } else { - ret = StringUtil.getUTCDateForLocalDate(ret); - } - - return ret; - } - - public Date getTagAttributeAsDate(String tagType, String attributeName) { - String attrValue = getAttributeValue(tagType, attributeName); - - return getAsDate(attrValue); - } - - public boolean isAccessedAfter(String tagType, String attributeName) { - boolean ret = false; - Date accessDate = getAccessTime(); - Date expiryDate = getTagAttributeAsDate(tagType, attributeName); - - if (expiryDate == null || accessDate.after(expiryDate) || accessDate.equals(expiryDate)) { - ret = true; - } - - return ret; - } - - public boolean isAccessedAfter(String attributeName) { - boolean ret = false; - Date accessDate = getAccessTime(); - Date expiryDate = getAsDate(getAttributeValue(attributeName)); - - if (expiryDate == null || accessDate.after(expiryDate) || accessDate.equals(expiryDate)) { - ret = true; - } - - return ret; - } - - public boolean isAccessedBefore(String tagType, String attributeName) { - boolean ret = true; - Date accessDate = getAccessTime(); - Date expiryDate = getTagAttributeAsDate(tagType, attributeName); - - if (expiryDate == null || accessDate.after(expiryDate)) { - ret = false; - } - - return ret; - } - - public boolean isAccessedBefore(String attributeName) { - boolean ret = true; - Date accessDate = getAccessTime(); - Date expiryDate = getAsDate(getAttributeValue(attributeName)); - - if (expiryDate == null || accessDate.after(expiryDate)) { - ret = false; - } - - return ret; - } - - private Set getAllTags() { - Set ret = RangerAccessRequestUtil.getRequestTagsFromContext(accessRequest.getContext()); - if(ret == null) { - if (LOG.isDebugEnabled()) { - String resource = accessRequest.getResource().getAsString(); - - logDebug("RangerScriptExecutionContext.getAllTags() - No TAGS. No TAGS for the RangerAccessResource=" + resource); - } - } - - return ret; - } - - public void logDebug(Object msg) { - LOG.debug(msg); - } - - public void logInfo(Object msg) { - LOG.info(msg); - } - - public void logWarn(Object msg) { - LOG.warn(msg); - } - - public void logError(Object msg) { - LOG.error(msg); - } - - public void logFatal(Object msg) { - LOG.fatal(msg); - } -} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptTemplateConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptTemplateConditionEvaluator.java index e885920473..745b90622e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptTemplateConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerScriptTemplateConditionEvaluator.java @@ -22,12 +22,12 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerScriptTemplateConditionEvaluator extends RangerScriptConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerScriptTemplateConditionEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerScriptTemplateConditionEvaluator.class); protected String script; private boolean reverseResult; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java index 8616c6660b..6dcfe7ac4a 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTagsAllPresentConditionEvaluator.java @@ -20,9 +20,11 @@ package org.apache.ranger.plugin.conditionevaluator; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Set; @@ -30,7 +32,7 @@ public class RangerTagsAllPresentConditionEvaluator extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerTagsAllPresentConditionEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerTagsAllPresentConditionEvaluator.class); private final Set policyConditionTags = new HashSet<>(); @@ -62,13 +64,21 @@ public boolean isMatched(RangerAccessRequest request) { boolean matched = true; - if (CollectionUtils.isNotEmpty(policyConditionTags)) { - RangerAccessRequest readOnlyRequest = request.getReadOnlyCopy(); - RangerScriptExecutionContext context = new RangerScriptExecutionContext(readOnlyRequest); - Set resourceTags = context.getAllTagTypes(); + if (CollectionUtils.isNotEmpty(policyConditionTags)) { + Set resourceTags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); // check if resource Tags atleast have to have all the tags in policy Condition - matched = resourceTags != null && resourceTags.containsAll(policyConditionTags); + if (CollectionUtils.isNotEmpty(resourceTags)) { + Set tags = new HashSet<>(resourceTags.size()); + + for (RangerTagForEval tag : resourceTags) { + tags.add(tag.getType()); + } + + matched = tags.containsAll(policyConditionTags); + } else { + matched = false; + } } if(LOG.isDebugEnabled()) { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcher.java index aa8f9c6703..652005b832 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcher.java @@ -30,12 +30,12 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerTimeOfDayMatcher extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerTimeOfDayMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerTimeOfDayMatcher.class); boolean _allowAny; List _durations = new ArrayList<>(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerValidityScheduleConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerValidityScheduleConditionEvaluator.java new file mode 100644 index 0000000000..2c4a756a3a --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/conditionevaluator/RangerValidityScheduleConditionEvaluator.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.conditionevaluator; + +import org.apache.ranger.authorization.utils.JsonUtils; +import org.apache.ranger.plugin.model.RangerValiditySchedule; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +public class RangerValidityScheduleConditionEvaluator extends RangerAbstractConditionEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(RangerValidityScheduleConditionEvaluator.class); + + private List evaluators = Collections.emptyList(); + + @Override + public void init() { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerValidityScheduleConditionEvaluator.init({})", condition); + } + + super.init(); + + if (condition != null && condition.getValues() != null && !condition.getValues().isEmpty()) { + evaluators = new ArrayList<>(condition.getValues().size()); + + for (String scheduleStr : condition.getValues()) { + try { + RangerValiditySchedule schedule = JsonUtils.jsonToObject(scheduleStr, RangerValiditySchedule.class); + + if (schedule != null) { + evaluators.add(new RangerValidityScheduleEvaluator(schedule)); + } + } catch (Exception excp) { + LOG.error("RangerValidityScheduleConditionEvaluator.init({}): failed to initialize schedule {}", condition, scheduleStr, excp); + } + + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerValidityScheduleConditionEvaluator.init({}): evaluator={}", condition, evaluators); + } + } + + @Override + public boolean isMatched(RangerAccessRequest request) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerValidityScheduleConditionEvaluator.isMatched({})", request); + } + + final boolean ret; + + if (evaluators.isEmpty() || request.getAccessTime() == null) { + ret = true; + } else { + final long accessTime = request.getAccessTime().getTime(); + + ret = evaluators.stream().filter(evaluator -> evaluator.isApplicable(accessTime)).findFirst().orElse(null) != null; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerValidityScheduleConditionEvaluator.isMatched({}): condition={}, ret={}", request, condition, ret); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractContextEnricher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractContextEnricher.java index fa84760c62..359ee59938 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractContextEnricher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractContextEnricher.java @@ -22,32 +22,34 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.InputStream; -import java.net.MalformedURLException; import java.net.URL; import java.util.Map; import java.util.Properties; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; import org.apache.ranger.plugin.service.RangerAuthContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class RangerAbstractContextEnricher implements RangerContextEnricher { - private static final Log LOG = LogFactory.getLog(RangerAbstractContextEnricher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAbstractContextEnricher.class); protected RangerContextEnricherDef enricherDef; protected String serviceName; protected String appId; protected RangerServiceDef serviceDef; private RangerPluginContext pluginContext; + protected RangerPolicyEngineOptions options = new RangerPolicyEngineOptions(); @Override public void setEnricherDef(RangerContextEnricherDef enricherDef) { @@ -176,12 +178,24 @@ final public void setPluginContext(RangerPluginContext pluginContext) { this.pluginContext = pluginContext; } + public RangerPluginContext getPluginContext() { + return this.pluginContext; + } + + final public void setPolicyEngineOptions(RangerPolicyEngineOptions options) { + this.options = options; + } + public RangerPluginConfig getPluginConfig() { RangerPluginContext pluginContext = this.pluginContext; return pluginContext != null ? pluginContext.getConfig() : null; } + public RangerPolicyEngineOptions getPolicyEngineOptions() { + return options; + } + public void notifyAuthContextChanged() { RangerPluginContext pluginContext = this.pluginContext; @@ -190,6 +204,12 @@ public void notifyAuthContextChanged() { } } + public String getPropertyPrefix() { + RangerPluginConfig pluginConfig = getPluginConfig(); + + return pluginConfig != null ? pluginConfig.getPropertyPrefix() : "ranger.plugin." + serviceDef.getName(); + } + public String getConfig(String configName, String defaultValue) { RangerPluginContext pluginContext = this.pluginContext; String ret = defaultValue; @@ -272,17 +292,15 @@ public long getLongOption(String name, long defaultValue) { public Properties readProperties(String fileName) { Properties ret = null; - InputStream inStr = null; URL fileURL = null; File f = new File(fileName); if (f.exists() && f.isFile() && f.canRead()) { - try { - inStr = new FileInputStream(f); + try (InputStream inStr = new FileInputStream(f);) { fileURL = f.toURI().toURL(); } catch (FileNotFoundException exception) { LOG.error("Error processing input file:" + fileName + " or no privilege for reading file " + fileName, exception); - } catch (MalformedURLException malformedException) { + } catch (IOException malformedException) { LOG.error("Error processing input file:" + fileName + " cannot be converted to URL " + fileName, malformedException); } } else { @@ -302,8 +320,7 @@ public Properties readProperties(String fileName) { } if (fileURL != null) { - try { - inStr = fileURL.openStream(); + try(InputStream inStr = fileURL.openStream()) { Properties prop = new Properties(); @@ -312,14 +329,6 @@ public Properties readProperties(String fileName) { ret = prop; } catch (Exception excp) { LOG.error("failed to load properties from file '" + fileName + "'", excp); - } finally { - if (inStr != null) { - try { - inStr.close(); - } catch (Exception excp) { - // ignore - } - } } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractGeolocationProvider.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractGeolocationProvider.java index d6a0412cf5..eb2e50705d 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractGeolocationProvider.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAbstractGeolocationProvider.java @@ -20,18 +20,18 @@ package org.apache.ranger.plugin.contextenricher; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.geo.RangerGeolocationData; import org.apache.ranger.plugin.geo.RangerGeolocationDatabase; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.store.GeolocationStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; public abstract class RangerAbstractGeolocationProvider extends RangerAbstractContextEnricher { - private static final Log LOG = LogFactory.getLog(RangerAbstractGeolocationProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAbstractGeolocationProvider.class); public static final String ENRICHER_OPTION_GEOLOCATION_META_PREFIX = "geolocation.meta.prefix"; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminGdsInfoRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminGdsInfoRetriever.java new file mode 100644 index 0000000000..0126a11605 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminGdsInfoRetriever.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.contextenricher; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.channels.ClosedByInterruptException; +import java.util.Map; + +public class RangerAdminGdsInfoRetriever extends RangerGdsInfoRetriever { + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminGdsInfoRetriever.class); + + private static final String OPTION_DEDUP_TAGS = "deDupTags"; + private static final Boolean OPTION_DEDUP_TAGS_DEFAULT = true; + + + private RangerAdminClient adminClient; + private boolean deDupTags; + + @Override + public void init(Map options) { + try { + if (StringUtils.isNotBlank(serviceName) && serviceDef != null && StringUtils.isNotBlank(appId)) { + RangerPluginConfig pluginConfig = super.pluginConfig; + + if (pluginConfig == null) { + pluginConfig = new RangerPluginConfig(serviceDef.getName(), serviceName, appId, null, null, null); + } + + String deDupTagsVal = options != null ? options.get(OPTION_DEDUP_TAGS) : null; + RangerPluginContext pluginContext = getPluginContext(); + RangerAdminClient rangerAdmin = pluginContext.getAdminClient(); + + this.deDupTags = StringUtils.isNotBlank(deDupTagsVal) ? Boolean.parseBoolean(deDupTagsVal) : OPTION_DEDUP_TAGS_DEFAULT; + this.adminClient = (rangerAdmin != null) ? rangerAdmin : pluginContext.createAdminClient(pluginConfig); + } else { + LOG.error("FATAL: Cannot find service/serviceDef to use for retrieving tags. Will NOT be able to retrieve GdsInfo."); + } + } catch (Exception excp) { + LOG.error("FATAL: Failed to initialize GDS retriever. Will not be able to enforce GDS policies", excp); + } + } + + @Override + public ServiceGdsInfo retrieveGdsInfo(long lastKnownVersion, long lastActivationTimeInMillis) throws InterruptedException { + ServiceGdsInfo ret = null; + + if (adminClient != null) { + try { + ret = adminClient.getGdsInfoIfUpdated(lastKnownVersion, lastActivationTimeInMillis); + } catch (ClosedByInterruptException excp) { + LOG.error("gdsInfo retriever thread was interrupted while blocked on I/O", excp); + + throw new InterruptedException(); + } catch (Exception e) { + LOG.error("gdsInfo retriever encountered exception. Returning null gdsInfo", e); + } + } + + if (ret != null && deDupTags) { + ret.dedupStrings(); + } + + return ret; + } +} + diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminTagRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminTagRetriever.java index 7b49a99068..bae31e6cca 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminTagRetriever.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminTagRetriever.java @@ -20,18 +20,18 @@ package org.apache.ranger.plugin.contextenricher; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.admin.client.RangerAdminClient; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; -import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.util.ServiceTags; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.channels.ClosedByInterruptException; import java.util.Map; public class RangerAdminTagRetriever extends RangerTagRetriever { - private static final Log LOG = LogFactory.getLog(RangerAdminTagRetriever.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminTagRetriever.class); private RangerAdminClient adminClient; @@ -45,7 +45,10 @@ public void init(Map options) { pluginConfig = new RangerPluginConfig(serviceDef.getName(), serviceName, appId, null, null, null); } - adminClient = RangerBasePlugin.createAdminClient(pluginConfig); + RangerPluginContext pluginContext = getPluginContext(); + RangerAdminClient rangerAdmin = pluginContext.getAdminClient(); + + this.adminClient = (rangerAdmin != null) ? rangerAdmin : pluginContext.createAdminClient(pluginConfig); } else { LOG.error("FATAL: Cannot find service/serviceDef to use for retrieving tags. Will NOT be able to retrieve tags."); } @@ -67,6 +70,7 @@ public ServiceTags retrieveTags(long lastKnownVersion, long lastActivationTimeIn LOG.error("Returning null service tags"); } } + return serviceTags; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminUserStoreRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminUserStoreRetriever.java index ed963363d2..e288649aeb 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminUserStoreRetriever.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerAdminUserStoreRetriever.java @@ -20,18 +20,18 @@ package org.apache.ranger.plugin.contextenricher; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.admin.client.RangerAdminClient; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; -import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.util.RangerUserStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.nio.channels.ClosedByInterruptException; import java.util.Map; public class RangerAdminUserStoreRetriever extends RangerUserStoreRetriever { - private static final Log LOG = LogFactory.getLog(RangerAdminUserStoreRetriever.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAdminUserStoreRetriever.class); private RangerAdminClient adminClient; @@ -45,7 +45,10 @@ public void init(Map options) { pluginConfig = new RangerPluginConfig(serviceDef.getName(), serviceName, appId, null, null, null); } - adminClient = RangerBasePlugin.createAdminClient(pluginConfig); + RangerPluginContext pluginContext = getPluginContext(); + RangerAdminClient rangerAdmin = pluginContext.getAdminClient(); + this.adminClient = (rangerAdmin != null) ? rangerAdmin : pluginContext.createAdminClient(pluginConfig); + } else { LOG.error("FATAL: Cannot find service/serviceDef to use for retrieving userstore. Will NOT be able to retrieve userstore."); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerFileBasedTagRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerFileBasedTagRetriever.java index b858879ba2..1e39d93faf 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerFileBasedTagRetriever.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerFileBasedTagRetriever.java @@ -19,27 +19,27 @@ package org.apache.ranger.plugin.contextenricher; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.util.ServiceTags; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.*; import java.net.MalformedURLException; import java.net.URL; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; public class RangerFileBasedTagRetriever extends RangerTagRetriever { - private static final Log LOG = LogFactory.getLog(RangerFileBasedTagRetriever.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerFileBasedTagRetriever.class); private URL serviceTagsFileURL; private String serviceTagsFileName; - private Gson gsonBuilder; - + int tagFilesCount = 0; + int currentTagFileIndex = 0; + boolean isInitial = true; @Override public void init(Map options) { @@ -47,67 +47,32 @@ public void init(Map options) { LOG.debug("==> init()" ); } - gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z") - .setPrettyPrinting() - .create(); - String serviceTagsFileNameProperty = "serviceTagsFileName"; String serviceTagsDefaultFileName = "/testdata/test_servicetags_hive.json"; + String tagFilesCountProperty = "tagFileCount"; if (StringUtils.isNotBlank(serviceName) && serviceDef != null && StringUtils.isNotBlank(appId)) { - InputStream serviceTagsFileStream = null; - - // Open specified file from options- it should contain service-tags serviceTagsFileName = options != null? options.get(serviceTagsFileNameProperty) : null; serviceTagsFileName = serviceTagsFileName == null ? serviceTagsDefaultFileName : serviceTagsFileName; - - File f = new File(serviceTagsFileName); - - if (f.exists() && f.isFile() && f.canRead()) { - try { - serviceTagsFileStream = new FileInputStream(f); - serviceTagsFileURL = f.toURI().toURL(); - } catch (FileNotFoundException exception) { - LOG.error("Error processing input file:" + serviceTagsFileName + " or no privilege for reading file " + serviceTagsFileName, exception); - } catch (MalformedURLException malformedException) { - LOG.error("Error processing input file:" + serviceTagsFileName + " cannot be converted to URL " + serviceTagsFileName, malformedException); - } - } else { - URL fileURL = getClass().getResource(serviceTagsFileName); - if (fileURL == null && !serviceTagsFileName.startsWith("/")) { - fileURL = getClass().getResource("/" + serviceTagsFileName); - } - - if (fileURL == null) { - fileURL = ClassLoader.getSystemClassLoader().getResource(serviceTagsFileName); - if (fileURL == null && !serviceTagsFileName.startsWith("/")) { - fileURL = ClassLoader.getSystemClassLoader().getResource("/" + serviceTagsFileName); - } - } - - if (fileURL != null) { + if (options != null) { + String tagFilesCountStr = options.get(tagFilesCountProperty); + if (!StringUtils.isNotEmpty(tagFilesCountStr)) { try { - serviceTagsFileStream = fileURL.openStream(); - serviceTagsFileURL = fileURL; - } catch (Exception exception) { - LOG.error(serviceTagsFileName + " is not a file", exception); + tagFilesCount = Integer.parseInt(tagFilesCountStr); + } catch (Exception e) { + LOG.error("Exception while parsing tagFileCount option value:[" + tagFilesCountStr + "]"); + LOG.error("Setting tagFilesCount to 0"); } - } else { - LOG.warn("Error processing input file: URL not found for " + serviceTagsFileName + " or no privilege for reading file " + serviceTagsFileName); } } - if (serviceTagsFileStream != null) { - try { - serviceTagsFileStream.close(); - } catch (Exception e) { - // Ignore - } + if (StringUtils.isNotBlank(serviceTagsFileName)) { + serviceTagsFileURL = getTagFileURL(serviceTagsFileName); } - + isInitial = true; } else { LOG.error("FATAL: Cannot find service/serviceDef/serviceTagsFile to use for retrieving tags. Will NOT be able to retrieve tags."); } @@ -119,39 +84,140 @@ public void init(Map options) { @Override public ServiceTags retrieveTags(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception { - if (LOG.isDebugEnabled()) { LOG.debug("==> retrieveTags(lastKnownVersion=" + lastKnownVersion + ", lastActivationTimeInMillis=" + lastActivationTimeInMillis + ", serviceTagsFilePath=" + serviceTagsFileName); } - ServiceTags serviceTags = null; + ServiceTags serviceTags = readFromFile(); - if (serviceTagsFileURL != null) { - try ( - InputStream serviceTagsFileStream = serviceTagsFileURL.openStream(); - Reader reader = new InputStreamReader(serviceTagsFileStream, Charset.forName("UTF-8")) - ) { + if (LOG.isDebugEnabled()) { + LOG.debug("<== retrieveTags(lastKnownVersion=" + lastKnownVersion + ", lastActivationTimeInMillis=" + lastActivationTimeInMillis); + } - serviceTags = gsonBuilder.fromJson(reader, ServiceTags.class); + return serviceTags; + } - if (serviceTags.getTagVersion() <= lastKnownVersion) { - // No change in serviceTags - serviceTags = null; - } - } catch (IOException e) { - LOG.warn("Error processing input file: or no privilege for reading file " + serviceTagsFileName); - throw e; + URL getTagFileURL(String fileName) { + URL fileURL = null; + + InputStream tagFileStream = null; + + File f = new File(fileName); + + if (f.exists() && f.isFile() && f.canRead()) { + try { + tagFileStream = new FileInputStream(f); + fileURL = f.toURI().toURL(); + } catch (FileNotFoundException exception) { + LOG.error("Error processing input file:" + fileName + " or no privilege for reading file " + fileName, exception); + } catch (MalformedURLException malformedException) { + LOG.error("Error processing input file:" + fileName + " cannot be converted to URL " + fileName, malformedException); } } else { - LOG.error("Error reading file: " + serviceTagsFileName); - throw new Exception("serviceTagsFileURL is null!"); + + fileURL = getClass().getResource(fileName); + if (fileURL == null) { + if (!fileName.startsWith("/")) { + fileURL = getClass().getResource("/" + fileName); + } + } + + if (fileURL == null) { + fileURL = ClassLoader.getSystemClassLoader().getResource(fileName); + if (fileURL == null) { + if (!fileName.startsWith("/")) { + fileURL = ClassLoader.getSystemClassLoader().getResource("/" + fileName); + } + } + } + + if (fileURL != null) { + + try { + tagFileStream = fileURL.openStream(); + } catch (Exception exception) { + fileURL = null; + LOG.error(fileName + " is not a file", exception); + } + } else { + LOG.warn("Error processing input file: URL not found for " + fileName + " or no privilege for reading file " + fileName); + } } + if (tagFileStream != null) { + try { + tagFileStream.close(); + } catch (Exception e) { + // Ignore + } + } + return fileURL; + } + + private ServiceTags readFromFile() { + if (LOG.isDebugEnabled()) { - LOG.debug("<== retrieveTags(lastKnownVersion=" + lastKnownVersion + ", lastActivationTimeInMillis=" + lastActivationTimeInMillis); + LOG.debug("==> RangerFileBasedTagRetriever.readFromFile: sourceFileName=" + serviceTagsFileName); } - return serviceTags; + ServiceTags ret = null; + + String fileName; + + fileName = serviceTagsFileName; + + if (isInitial) { + isInitial = false; + if (serviceTagsFileURL != null) { + try ( + InputStream fileStream = serviceTagsFileURL.openStream(); + Reader reader = new InputStreamReader(fileStream, StandardCharsets.UTF_8) + ) { + + ret = JsonUtils.jsonToObject(reader, ServiceTags.class); + if (ret.getIsTagsDeduped()) { + final int countOfDuplicateTags = ret.dedupTags(); + LOG.info("Number of duplicate tags removed from the received serviceTags:[" + countOfDuplicateTags + "]. Number of tags in the de-duplicated serviceTags :[" + ret.getTags().size() + "]."); + } + + } catch (IOException e) { + LOG.warn("Error processing input file: or no privilege for reading file " + fileName, e); + } + } else { + LOG.error("Error reading file: " + fileName); + } + + } else if (tagFilesCount > 0) { + + currentTagFileIndex = currentTagFileIndex % tagFilesCount; + fileName = serviceTagsFileName + "_" + currentTagFileIndex + ".json"; + URL fileURL = getTagFileURL(fileName); + if (fileURL != null) { + try ( + InputStream fileStream = fileURL.openStream(); + Reader reader = new InputStreamReader(fileStream, StandardCharsets.UTF_8) + ) { + + ret = JsonUtils.jsonToObject(reader, ServiceTags.class); + currentTagFileIndex++; + if (ret.getIsTagsDeduped()) { + final int countOfDuplicateTags = ret.dedupTags(); + LOG.info("Number of duplicate tags removed from the received serviceTags:[" + countOfDuplicateTags + "]. Number of tags in the de-duplicated serviceTags :[" + ret.getTags().size() + "]."); + } + } catch (IOException e) { + LOG.warn("Error processing input file: or no privilege for reading file " + fileName, e); + } + } else { + LOG.error("Error reading file: " + fileName); + } + + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerFileBasedTagRetriever.readFromFile: sourceFileName=" + fileName); + } + + return ret; } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java new file mode 100644 index 0000000000..19193b7f56 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsEnricher.java @@ -0,0 +1,353 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.contextenricher; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; +import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine; +import org.apache.ranger.plugin.service.RangerAuthContext; +import org.apache.ranger.plugin.util.DownloadTrigger; +import org.apache.ranger.plugin.util.DownloaderTask; +import org.apache.ranger.plugin.util.JsonUtilsV2; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class RangerGdsEnricher extends RangerAbstractContextEnricher { + private static final Logger LOG = LoggerFactory.getLogger(RangerGdsEnricher.class); + + public static final String REFRESHER_POLLINGINTERVAL_OPTION = "refresherPollingInterval"; + public static final String RETRIEVER_CLASSNAME_OPTION = "retrieverClassName"; + + private RangerGdsInfoRetriever gdsInfoRetriever; + private RangerGdsInfoRefresher gdsInfoRefresher; + private RangerServiceDefHelper serviceDefHelper; + private GdsPolicyEngine gdsPolicyEngine = null; + + @Override + public void init() { + LOG.debug("==> RangerGdsEnricher.init()"); + + super.init(); + + String propertyPrefix = "ranger.plugin." + serviceDef.getName(); + String retrieverClassName = getOption(RETRIEVER_CLASSNAME_OPTION); + long pollingIntervalMs = getLongOption(REFRESHER_POLLINGINTERVAL_OPTION, 60 * 1000L); + String cacheFile = null; + + serviceDefHelper = new RangerServiceDefHelper(serviceDef, false); + + if (StringUtils.isNotBlank(retrieverClassName)) { + try { + @SuppressWarnings("unchecked") + Class retriverClass = (Class) Class.forName(retrieverClassName); + + gdsInfoRetriever = retriverClass.newInstance(); + } catch (ClassNotFoundException | ClassCastException | IllegalAccessException | InstantiationException excp) { + LOG.error("Failed to instantiate retriever (className={})", retrieverClassName, excp); + } + } + + if (gdsInfoRetriever != null) { + String cacheDir = getConfig(propertyPrefix + ".policy.cache.dir", null); + String cacheFilename = String.format("%s_%s_gds.json", appId, serviceName); + + cacheFilename = cacheFilename.replace(File.separatorChar, '_'); + cacheFilename = cacheFilename.replace(File.pathSeparatorChar, '_'); + + cacheFile = cacheDir == null ? null : (cacheDir + File.separator + cacheFilename); + + gdsInfoRetriever.setServiceName(serviceName); + gdsInfoRetriever.setServiceDef(serviceDef); + gdsInfoRetriever.setAppId(appId); + gdsInfoRetriever.setPluginConfig(getPluginConfig()); + gdsInfoRetriever.setPluginContext(getPluginContext()); + gdsInfoRetriever.init(enricherDef.getEnricherOptions()); + + gdsInfoRefresher = new RangerGdsInfoRefresher(gdsInfoRetriever, pollingIntervalMs, cacheFile, -1L); + + try { + gdsInfoRefresher.populateGdsInfo(); + } catch (Throwable excp) { + LOG.error("RangerGdsEnricher.init(): failed to retrieve gdsInfo", excp); + } + + gdsInfoRefresher.setDaemon(true); + gdsInfoRefresher.startRefresher(); + } else { + LOG.error("No value specified for {} in the RangerGdsEnricher options", RETRIEVER_CLASSNAME_OPTION); + } + + LOG.info("RangerGdsEnricher.init(): retrieverClassName={}, pollingIntervalMs={}, cacheFile={}", retrieverClassName, pollingIntervalMs, cacheFile); + + LOG.debug("<== RangerGdsEnricher.init()"); + } + + @Override + public boolean preCleanup() { + LOG.debug("==> RangerGdsEnricher.preCleanup()"); + + super.preCleanup(); + + RangerGdsInfoRefresher gdsInfoRefresher = this.gdsInfoRefresher; + + this.gdsInfoRefresher = null; + + if (gdsInfoRefresher != null) { + LOG.debug("Trying to clean up RangerGdsInfoRefresher({})", gdsInfoRefresher.getName()); + + gdsInfoRefresher.cleanup(); + } + + LOG.debug("<== RangerGdsEnricher.preCleanup(): result={}", true); + + return true; + } + + @Override + public void enrich(RangerAccessRequest request) { + LOG.debug("==> RangerGdsEnricher.enrich({})", request); + + enrich(request, null); + + LOG.debug("<== RangerGdsEnricher.enrich({})", request); + } + + @Override + public void enrich(RangerAccessRequest request, Object dataStore) { + LOG.debug("==> RangerGdsEnricher.enrich({}, {})", request, dataStore); + + GdsPolicyEngine policyEngine = (dataStore instanceof GdsPolicyEngine) ? (GdsPolicyEngine) dataStore : this.gdsPolicyEngine; + + LOG.debug("RangerGdsEnricher.enrich(): using policyEngine={}", policyEngine); + + GdsAccessResult result = policyEngine != null ? policyEngine.evaluate(request) : null; + + RangerAccessRequestUtil.setGdsResultInContext(request, result); + + LOG.debug("<== RangerGdsEnricher.enrich({}, {})", request, dataStore); + } + + public void setGdsInfo(ServiceGdsInfo gdsInfo) { + this.gdsPolicyEngine = new GdsPolicyEngine(gdsInfo, serviceDefHelper, getPluginContext()); + + setGdsInfoInPlugin(); + } + + public RangerServiceDefHelper getServiceDefHelper() { return serviceDefHelper; } + + public GdsPolicyEngine getGdsPolicyEngine() { return gdsPolicyEngine; } + + private void setGdsInfoInPlugin() { + LOG.debug("==> setGdsInfoInPlugin()"); + + RangerAuthContext authContext = getAuthContext(); + + if (authContext != null) { + authContext.addOrReplaceRequestContextEnricher(this, gdsPolicyEngine); + + notifyAuthContextChanged(); + } + + LOG.debug("<== setGdsInfoInPlugin()"); + } + + private class RangerGdsInfoRefresher extends Thread { + private final RangerGdsInfoRetriever retriever; + private final long pollingIntervalMs; + private final String cacheFile; + private Long lastKnownVersion; + private long lastActivationTimeInMillis; + private Timer downloadTimer; + private BlockingQueue downloadQueue; + private boolean gdsInfoSetInPlugin = false; + + public RangerGdsInfoRefresher(RangerGdsInfoRetriever retriever, long pollingIntervalMs, String cacheFile, long lastKnownVersion) { + super("RangerGdsInfoRefresher"); + + this.retriever = retriever; + this.pollingIntervalMs = pollingIntervalMs; + this.cacheFile = cacheFile; + this.lastKnownVersion = lastKnownVersion; + } + + final void startRefresher() { + try { + downloadTimer = new Timer("gdsInfoDownloadTimer", true); + downloadQueue = new LinkedBlockingQueue<>(); + + super.start(); + + downloadTimer.schedule(new DownloaderTask(downloadQueue), pollingIntervalMs, pollingIntervalMs); + + if (LOG.isDebugEnabled()) { + LOG.debug("Scheduled timer to download gdsInfo every " + pollingIntervalMs + " milliseconds"); + } + } catch (IllegalStateException exception) { + LOG.error("Error scheduling gdsInfo download", exception); + LOG.error("*** GdsInfo will NOT be downloaded every " + pollingIntervalMs + " milliseconds ***"); + + stopRefresher(); + } + } + + private void stopRefresher() { + Timer downloadTimer = this.downloadTimer; + + this.downloadTimer = null; + this.downloadQueue = null; + + if (downloadTimer != null) { + downloadTimer.cancel(); + } + + if (super.isAlive()) { + super.interrupt(); + + boolean setInterrupted = false; + boolean isJoined = false; + + while (!isJoined) { + try { + super.join(); + + isJoined = true; + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerGdsInfoRefresher({}) is stopped", getName()); + } + } catch (InterruptedException excp) { + LOG.warn("RangerGdsInfoRefresher({}).stopRefresher(): Error while waiting for thread to exit", getName(), excp); + LOG.warn("Retrying Thread.join(). Current thread will be marked as 'interrupted' after Thread.join() returns"); + + setInterrupted = true; + } + } + + if (setInterrupted) { + Thread.currentThread().interrupt(); + } + } + } + + @Override + public void run() { + while (true) { + BlockingQueue downloadQueue = this.downloadQueue; + DownloadTrigger trigger = null; + + try { + if (downloadQueue == null) { + LOG.error("RangerGdsInfoRefresher(serviceName=" + serviceName + ").run(): downloadQueue is null"); + + break; + } + + trigger = downloadQueue.take(); + + populateGdsInfo(); + } catch(InterruptedException excp) { + LOG.info("RangerGdsInfoRefresher(serviceName=" + serviceName + ").run() interrupted! Exiting thread", excp); + break; + } catch (Exception excp) { + LOG.error("RangerGdsInfoRefresher(serviceName=" + serviceName + ").run() failed to download gdsInfo. Will retry again", excp); + } finally { + if (trigger != null) { + trigger.signalCompletion(); + } + } + } + } + + private void populateGdsInfo() throws Exception { + ServiceGdsInfo gdsInfo = retriever.retrieveGdsInfo(lastKnownVersion != null ? lastKnownVersion : -1, lastActivationTimeInMillis); + + if (gdsInfo == null && !gdsInfoSetInPlugin) { + gdsInfo = loadFromCache(); + } + + if (gdsInfo != null) { + setGdsInfo(gdsInfo); + saveToCache(gdsInfo); + + gdsInfoSetInPlugin = true; + lastKnownVersion = gdsInfo.getGdsVersion(); + lastActivationTimeInMillis = System.currentTimeMillis(); + } + } + + void cleanup() { + LOG.debug("==> RangerGdsInfoRefresher.cleanup()"); + + stopRefresher(); + + LOG.debug("<== RangerGdsInfoRefresher.cleanup()"); + } + + ServiceGdsInfo loadFromCache() { + LOG.debug("==> RangerGdsInfoRefresher(serviceName={}).loadFromCache()", getServiceName()); + + ServiceGdsInfo ret = null; + File cacheFile = org.apache.commons.lang.StringUtils.isEmpty(this.cacheFile) ? null : new File(this.cacheFile); + + if (cacheFile != null && cacheFile.isFile() && cacheFile.canRead()) { + try (Reader reader = new FileReader(cacheFile)){ + ret = JsonUtilsV2.readValue(reader, ServiceGdsInfo.class); + } catch (Exception excp) { + LOG.error("failed to load gdsInfo from cache file {}", cacheFile.getAbsolutePath(), excp); + } + } else { + LOG.warn("cache file does not exist or not readable '{}'", (cacheFile == null ? null : cacheFile.getAbsolutePath())); + } + + LOG.debug("<== RangerGdsInfoRefresher(serviceName={}).loadFromCache()", getServiceName()); + + return ret; + } + + void saveToCache(ServiceGdsInfo gdsInfo) { + LOG.debug("==> RangerGdsInfoRefresher(serviceName={}).saveToCache()", getServiceName()); + + if (gdsInfo != null) { + File cacheFile = StringUtils.isEmpty(this.cacheFile) ? null : new File(this.cacheFile); + + if (cacheFile != null) { + try (Writer writer = new FileWriter(cacheFile)) { + JsonUtilsV2.writeValue(writer, gdsInfo); + } catch (Exception excp) { + LOG.error("failed to save gdsInfo to cache file '{}'", cacheFile.getAbsolutePath(), excp); + } + } + } else { + LOG.info("gdsInfo is null for service={}. Nothing to save in cache", getServiceName()); + } + + LOG.debug("<== RangerGdsInfoRefresher(serviceName={}).saveToCache()", getServiceName()); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsInfoRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsInfoRetriever.java new file mode 100644 index 0000000000..2fd3634c5c --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerGdsInfoRetriever.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.contextenricher; + +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.util.ServiceGdsInfo; + +import java.util.Map; + +public abstract class RangerGdsInfoRetriever { + protected String serviceName; + protected RangerServiceDef serviceDef; + protected String appId; + protected RangerPluginConfig pluginConfig; + protected RangerPluginContext pluginContext; + + public abstract void init(Map options); + + public abstract ServiceGdsInfo retrieveGdsInfo(long lastKnownVersion, long lastActivationTimeInMillis) throws Exception; + + public String getServiceName() { return serviceName; } + + public void setServiceName(String serviceName) { this.serviceName = serviceName; } + + public RangerServiceDef getServiceDef() { return serviceDef; } + + public void setServiceDef(RangerServiceDef serviceDef) { this.serviceDef = serviceDef; } + + public String getAppId() { return appId; } + + public void setAppId(String appId) { this.appId = appId; } + + public void setPluginConfig(RangerPluginConfig pluginConfig) { this.pluginConfig = pluginConfig; } + + public RangerPluginContext getPluginContext() { return pluginContext; } + + public void setPluginContext(RangerPluginContext pluginContext) { this.pluginContext = pluginContext; } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerServiceResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerServiceResourceMatcher.java index 7b02dd6e13..44cf3dcdf5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerServiceResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerServiceResourceMatcher.java @@ -19,12 +19,14 @@ package org.apache.ranger.plugin.contextenricher; +import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; -import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerServiceResource; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; import org.apache.ranger.plugin.util.ServiceDefUtil; @@ -32,17 +34,17 @@ import java.util.Comparator; import java.util.Map; -public class RangerServiceResourceMatcher implements RangerPolicyResourceEvaluator { +public class RangerServiceResourceMatcher implements RangerResourceEvaluator { public static final Comparator ID_COMPARATOR = new IdComparator(); private final RangerServiceResource serviceResource; private final RangerPolicyResourceMatcher policyResourceMatcher; - private RangerServiceDef.RangerResourceDef leafResourceDef; + private final RangerResourceDef leafResourceDef; public RangerServiceResourceMatcher(final RangerServiceResource serviceResource, RangerPolicyResourceMatcher policyResourceMatcher) { this.serviceResource = serviceResource; this.policyResourceMatcher = policyResourceMatcher; - this.leafResourceDef = ServiceDefUtil.getLeafResourceDef(policyResourceMatcher.getServiceDef(), getPolicyResource()); + this.leafResourceDef = ServiceDefUtil.getLeafResourceDef(policyResourceMatcher.getServiceDef(), getPolicyResource(), true); } public RangerServiceResource getServiceResource() { return serviceResource; } @@ -66,12 +68,12 @@ public RangerResourceMatcher getResourceMatcher(String resourceName) { } @Override - public boolean isAncestorOf(RangerServiceDef.RangerResourceDef resourceDef) { + public boolean isAncestorOf(RangerResourceDef resourceDef) { return ServiceDefUtil.isAncestorOf(policyResourceMatcher.getServiceDef(), leafResourceDef, resourceDef); } - public RangerPolicyResourceMatcher.MatchType getMatchType(RangerAccessResource requestedResource, Map evalContext) { - return policyResourceMatcher != null ? policyResourceMatcher.getMatchType(requestedResource, evalContext) : RangerPolicyResourceMatcher.MatchType.NONE; + public RangerPolicyResourceMatcher.MatchType getMatchType(RangerAccessResource requestedResource, Map scopes, Map evalContext) { + return policyResourceMatcher != null ? policyResourceMatcher.getMatchType(requestedResource, scopes, evalContext) : RangerPolicyResourceMatcher.MatchType.NONE; } static class IdComparator implements Comparator, Serializable { @@ -80,4 +82,12 @@ public int compare(RangerServiceResourceMatcher me, RangerServiceResourceMatcher return Long.compare(me.getId(), other.getId()); } } + + @Override + public String toString() { + return String.valueOf(getId()); + } + + @Override + public boolean isLeaf(String resourceName) { return StringUtils.equals(resourceName, leafResourceDef.getName()); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java index 4c60efecdc..822e20fd43 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagEnricher.java @@ -19,32 +19,39 @@ package org.apache.ranger.plugin.contextenricher; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.model.RangerPolicy; -import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerServiceResource; import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.policyengine.RangerResourceTrie; import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; import org.apache.ranger.plugin.util.DownloadTrigger; import org.apache.ranger.plugin.util.DownloaderTask; import org.apache.ranger.plugin.service.RangerAuthContext; +import org.apache.ranger.plugin.util.CachedResourceEvaluators; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerCommonConstants; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerReadWriteLock; import org.apache.ranger.plugin.util.RangerServiceNotFoundException; import org.apache.ranger.plugin.util.RangerServiceTagsDeltaUtil; import org.apache.ranger.plugin.util.ServiceTags; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileReader; @@ -54,9 +61,11 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Set; import java.util.Timer; @@ -64,15 +73,14 @@ import java.util.concurrent.LinkedBlockingQueue; public class RangerTagEnricher extends RangerAbstractContextEnricher { - private static final Log LOG = LogFactory.getLog(RangerTagEnricher.class); - - private static final Log PERF_CONTEXTENRICHER_INIT_LOG = RangerPerfTracer.getPerfLogger("contextenricher.init"); - private static final Log PERF_TRIE_OP_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.retrieval"); - private static final Log PERF_SET_SERVICETAGS_LOG = RangerPerfTracer.getPerfLogger("tagenricher.setservicetags"); + private static final Logger LOG = LoggerFactory.getLogger(RangerTagEnricher.class); + private static final Logger PERF_CONTEXTENRICHER_INIT_LOG = RangerPerfTracer.getPerfLogger("contextenricher.init"); + private static final Logger PERF_SET_SERVICETAGS_LOG = RangerPerfTracer.getPerfLogger("tagenricher.setservicetags"); + private static final Logger PERF_SERVICETAGS_RETRIEVAL_LOG = RangerPerfTracer.getPerfLogger("tagenricher.tags.retrieval"); private static final String TAG_REFRESHER_POLLINGINTERVAL_OPTION = "tagRefresherPollingInterval"; - private static final String TAG_RETRIEVER_CLASSNAME_OPTION = "tagRetrieverClassName"; + public static final String TAG_RETRIEVER_CLASSNAME_OPTION = "tagRetrieverClassName"; private static final String TAG_DISABLE_TRIE_PREFILTER_OPTION = "disableTrieLookupPrefilter"; private RangerTagRefresher tagRefresher; @@ -80,9 +88,14 @@ public class RangerTagEnricher extends RangerAbstractContextEnricher { private boolean disableTrieLookupPrefilter; private EnrichedServiceTags enrichedServiceTags; private boolean disableCacheIfServiceNotFound = true; + private boolean dedupStrings = true; + private Timer tagDownloadTimer; + private RangerServiceDefHelper serviceDefHelper; private final BlockingQueue tagDownloadQueue = new LinkedBlockingQueue<>(); - private Timer tagDownloadTimer; + private final RangerReadWriteLock lock = new RangerReadWriteLock(false); + private final CachedResourceEvaluators cache = new CachedResourceEvaluators(); + @Override public void init() { @@ -92,11 +105,13 @@ public void init() { super.init(); + String propertyPrefix = getPropertyPrefix(); String tagRetrieverClassName = getOption(TAG_RETRIEVER_CLASSNAME_OPTION); + long pollingIntervalMs = getLongOption(TAG_REFRESHER_POLLINGINTERVAL_OPTION, 60 * 1000L); - long pollingIntervalMs = getLongOption(TAG_REFRESHER_POLLINGINTERVAL_OPTION, 60 * 1000); - + dedupStrings = getBooleanConfig(propertyPrefix + ".dedup.strings", true); disableTrieLookupPrefilter = getBooleanOption(TAG_DISABLE_TRIE_PREFILTER_OPTION, false); + serviceDefHelper = new RangerServiceDefHelper(serviceDef, false); if (StringUtils.isNotBlank(tagRetrieverClassName)) { @@ -117,7 +132,6 @@ public void init() { } if (tagRetriever != null) { - String propertyPrefix = "ranger.plugin." + serviceDef.getName(); disableCacheIfServiceNotFound = getBooleanConfig(propertyPrefix + ".disable.cache.if.servicenotfound", true); String cacheDir = getConfig(propertyPrefix + ".policy.cache.dir", null); String cacheFilename = String.format("%s_%s_tag.json", appId, serviceName); @@ -126,10 +140,14 @@ public void init() { cacheFilename = cacheFilename.replace(File.pathSeparatorChar, '_'); String cacheFile = cacheDir == null ? null : (cacheDir + File.separator + cacheFilename); + + createLock(); + tagRetriever.setServiceName(serviceName); tagRetriever.setServiceDef(serviceDef); tagRetriever.setAppId(appId); tagRetriever.setPluginConfig(getPluginConfig()); + tagRetriever.setPluginContext(getPluginContext()); tagRetriever.init(enricherDef.getEnricherOptions()); tagRefresher = new RangerTagRefresher(tagRetriever, this, -1L, tagDownloadQueue, cacheFile); @@ -183,21 +201,33 @@ public void enrich(RangerAccessRequest request, Object dataStore) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerTagEnricher.enrich(" + request + ") with dataStore:[" + dataStore + "]"); } - final EnrichedServiceTags enrichedServiceTags; - if (dataStore instanceof EnrichedServiceTags) { - enrichedServiceTags = (EnrichedServiceTags) dataStore; - } else { - enrichedServiceTags = this.enrichedServiceTags; + final Set matchedTags; + + try (RangerReadWriteLock.RangerLock readLock = this.lock.getReadLock()) { + + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + + final EnrichedServiceTags enrichedServiceTags; + + if (dataStore instanceof EnrichedServiceTags) { + enrichedServiceTags = (EnrichedServiceTags) dataStore; + } else { + enrichedServiceTags = this.enrichedServiceTags; - if (dataStore != null) { - LOG.warn("Incorrect type of dataStore :[" + dataStore.getClass().getName() + "], falling back to original enrich"); + if (dataStore != null) { + LOG.warn("Incorrect type of dataStore :[" + dataStore.getClass().getName() + "], falling back to original enrich"); + } } - } - final Set matchedTags = enrichedServiceTags == null ? null : findMatchingTags(request, enrichedServiceTags); + matchedTags = enrichedServiceTags == null ? null : findMatchingTags(request, enrichedServiceTags); - RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), matchedTags); + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), matchedTags); + } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerTagEnricher.enrich(" + request + ") with dataStore:[" + dataStore + "]): tags count=" + (matchedTags == null ? 0 : matchedTags.size())); @@ -213,7 +243,7 @@ public void enrich(RangerAccessRequest request, Object dataStore) { * combinations in a service-def will be much smaller than the number of service-resources. */ - static class ResourceHierarchies { + static public class ResourceHierarchies { private final Map, Boolean> accessHierarchies = new HashMap<>(); private final Map, Boolean> dataMaskHierarchies = new HashMap<>(); private final Map, Boolean> rowFilterHierarchies = new HashMap<>(); @@ -260,60 +290,103 @@ protected void setServiceTags(final ServiceTags serviceTags, final boolean rebui LOG.debug("==> RangerTagEnricher.setServiceTags(serviceTags=" + serviceTags + ", rebuildOnlyIndex=" + rebuildOnlyIndex + ")"); } - if (serviceTags == null) { - LOG.info("ServiceTags is null for service " + serviceName); - enrichedServiceTags = null; - } else { + final EnrichedServiceTags localEnrichedServiceTags; + final Set keysToRemoveFromCache = new HashSet<>(); + + try (RangerReadWriteLock.RangerLock writeLock = this.lock.getWriteLock()) { + + if (LOG.isDebugEnabled()) { + if (writeLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + writeLock); + } + } RangerPerfTracer perf = null; - if(RangerPerfTracer.isPerfTraceEnabled(PERF_SET_SERVICETAGS_LOG)) { + if (RangerPerfTracer.isPerfTraceEnabled(PERF_SET_SERVICETAGS_LOG)) { perf = RangerPerfTracer.getPerfTracer(PERF_SET_SERVICETAGS_LOG, "RangerTagEnricher.setServiceTags(newTagVersion=" + serviceTags.getTagVersion() + ",isDelta=" + serviceTags.getIsDelta() + ")"); } - if (!serviceTags.getIsDelta()) { - processServiceTags(serviceTags); + if (serviceTags == null) { + LOG.info("ServiceTags is null for service " + serviceName); + localEnrichedServiceTags = null; } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Received service-tag deltas:" + serviceTags); + if (dedupStrings) { + serviceTags.dedupStrings(); } - ServiceTags oldServiceTags = enrichedServiceTags != null ? enrichedServiceTags.getServiceTags() : new ServiceTags(); - ServiceTags allServiceTags = rebuildOnlyIndex ? oldServiceTags : RangerServiceTagsDeltaUtil.applyDelta(oldServiceTags, serviceTags); + if (!serviceTags.getIsDelta()) { + if (serviceTags.getIsTagsDeduped()) { + final int countOfDuplicateTags = serviceTags.dedupTags(); - if (serviceTags.getTagsChangeExtent() == ServiceTags.TagsChangeExtent.NONE) { - if (LOG.isDebugEnabled()) { - LOG.debug("No change to service-tags other than version change"); + LOG.info("Number of duplicate tags removed from the received serviceTags:[" + countOfDuplicateTags + "]. Number of tags in the de-duplicated serviceTags :[" + serviceTags.getTags().size() + "]."); } + localEnrichedServiceTags = processServiceTags(serviceTags); } else { - if (serviceTags.getTagsChangeExtent() != ServiceTags.TagsChangeExtent.TAGS) { - processServiceTagDeltas(serviceTags, allServiceTags); - } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Received service-tag deltas:" + serviceTags); + } + ServiceTags oldServiceTags = enrichedServiceTags != null ? enrichedServiceTags.getServiceTags() : new ServiceTags(); + ServiceTags allServiceTags = rebuildOnlyIndex ? oldServiceTags : RangerServiceTagsDeltaUtil.applyDelta(oldServiceTags, serviceTags, serviceTags.getIsTagsDeduped()); + + if (serviceTags.getTagsChangeExtent() == ServiceTags.TagsChangeExtent.NONE) { if (LOG.isDebugEnabled()) { - LOG.debug("Delta contains only tag attribute changes"); + LOG.debug("No change to service-tags other than version change"); + } + localEnrichedServiceTags = enrichedServiceTags; + } else { + if (serviceTags.getTagsChangeExtent() != ServiceTags.TagsChangeExtent.TAGS) { + Map> trieMap; + + if (enrichedServiceTags == null) { + trieMap = new HashMap<>(); + } else { + trieMap = writeLock.isLockingEnabled() ? enrichedServiceTags.getServiceResourceTrie() : copyServiceResourceTrie(); + } + + localEnrichedServiceTags = processServiceTagDeltas(serviceTags, allServiceTags, trieMap, keysToRemoveFromCache); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Delta contains only tag attribute changes"); + } + List resourceMatchers = enrichedServiceTags != null ? enrichedServiceTags.getServiceResourceMatchers() : new ArrayList<>(); + Map> serviceResourceTrie = enrichedServiceTags != null ? enrichedServiceTags.getServiceResourceTrie() : new HashMap<>(); + localEnrichedServiceTags = new EnrichedServiceTags(allServiceTags, resourceMatchers, serviceResourceTrie); } - List resourceMatchers = enrichedServiceTags != null ? enrichedServiceTags.getServiceResourceMatchers() : new ArrayList<>(); - Map> serviceResourceTrie = enrichedServiceTags != null ? enrichedServiceTags.getServiceResourceTrie() : new HashMap<>(); - enrichedServiceTags = new EnrichedServiceTags(allServiceTags, resourceMatchers, serviceResourceTrie); } } } + + synchronized (RangerTagEnricher.class) { + enrichedServiceTags = localEnrichedServiceTags; + + if (serviceTags != null) { + if (serviceTags.getIsDelta()) { + cache.removeCacheEvaluators(keysToRemoveFromCache); + keysToRemoveFromCache.clear(); + } else { + cache.clearCache(); + } + } + setEnrichedServiceTagsInPlugin(); + } + RangerPerfTracer.logAlways(perf); } - setEnrichedServiceTagsInPlugin(); - if (LOG.isDebugEnabled()) { LOG.debug("<== RangerTagEnricher.setServiceTags(serviceTags=" + serviceTags + ", rebuildOnlyIndex=" + rebuildOnlyIndex + ")"); } } - protected Long getServiceTagsVersion() { - return enrichedServiceTags != null ? enrichedServiceTags.getServiceTags().getTagVersion() : -1L; + public Long getServiceTagsVersion() { + EnrichedServiceTags localEnrichedServiceTags = enrichedServiceTags; + return localEnrichedServiceTags != null ? localEnrichedServiceTags.getServiceTags().getTagVersion() : -1L; } protected Long getResourceTrieVersion() { - return enrichedServiceTags != null ? enrichedServiceTags.getResourceTrieVersion() : -1L; + EnrichedServiceTags localEnrichedServiceTags = enrichedServiceTags; + return localEnrichedServiceTags != null ? localEnrichedServiceTags.getResourceTrieVersion() : -1L; } @Override @@ -335,6 +408,9 @@ public boolean preCleanup() { this.tagRefresher = null; if (tagRefresher != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Trying to clean up RangerTagRefresher(" + tagRefresher.getName() + ")"); + } tagRefresher.cleanup(); } @@ -353,97 +429,105 @@ public EnrichedServiceTags getEnrichedServiceTags() { return enrichedServiceTags; } - private void processServiceTags(ServiceTags serviceTags) { + protected RangerReadWriteLock createLock() { + String propertyPrefix = getPropertyPrefix(); + RangerPluginConfig config = getPluginConfig(); + boolean deltasEnabled = config != null && config.getBoolean(propertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_TAG_DELTA_DEFAULT); + boolean inPlaceUpdatesEnabled = config != null && config.getBoolean(propertyPrefix + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_IN_PLACE_TAG_UPDATES, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_IN_PLACE_TAG_UPDATES_DEFAULT); + boolean useReadWriteLock = deltasEnabled && inPlaceUpdatesEnabled; + + LOG.info("Policy-Engine will" + (useReadWriteLock ? " " : " not ") + "use read-write locking to update tags in place when tag-deltas are provided"); + + return new RangerReadWriteLock(useReadWriteLock); + + } + private EnrichedServiceTags processServiceTags(ServiceTags serviceTags) { if (LOG.isDebugEnabled()) { LOG.debug("Processing all service-tags"); } - boolean isInError = false; + final EnrichedServiceTags ret; if (CollectionUtils.isEmpty(serviceTags.getServiceResources())) { LOG.info("There are no tagged resources for service " + serviceName); - enrichedServiceTags = null; + ret = null; } else { - - RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef, false); - ResourceHierarchies hierarchies = new ResourceHierarchies(); - + ResourceHierarchies hierarchies = new ResourceHierarchies(); List resourceMatchers = new ArrayList<>(); List serviceResources = serviceTags.getServiceResources(); - for (RangerServiceResource serviceResource : serviceResources) { - RangerServiceResourceMatcher serviceResourceMatcher = createRangerServiceResourceMatcher(serviceResource, serviceDefHelper, hierarchies); + for (ListIterator iter = serviceResources.listIterator(); iter.hasNext(); ) { + RangerServiceResource serviceResource = iter.next(); + RangerServiceResourceMatcher serviceResourceMatcher = createRangerServiceResourceMatcher(serviceResource, serviceDefHelper, hierarchies, getPluginContext()); + if (serviceResourceMatcher != null) { resourceMatchers.add(serviceResourceMatcher); } else { - LOG.error("Could not create service-resource-matcher for service-resource:[" + serviceResource + "]"); - isInError = true; - break; + iter.remove(); + + List tags = serviceTags.getResourceToTagIds().remove(serviceResource.getId()); + + LOG.warn("Invalid resource [" + serviceResource + "]: failed to create resource-matcher. Ignoring " + (tags != null ? tags.size() : 0) + " tags associated with the resource"); } } - if (isInError) { - serviceTags.setTagVersion(-1L); - LOG.error("Error in processing tag-deltas. Will continue to use old tags"); - } else { - Map> serviceResourceTrie = null; + Map> serviceResourceTrie = null; - if (!disableTrieLookupPrefilter) { - serviceResourceTrie = new HashMap<>(); + if (!disableTrieLookupPrefilter) { + serviceResourceTrie = new HashMap<>(); - for (RangerServiceDef.RangerResourceDef resourceDef : serviceDef.getResources()) { - serviceResourceTrie.put(resourceDef.getName(), new RangerResourceTrie<>(resourceDef, resourceMatchers)); - } + for (RangerResourceDef resourceDef : serviceDef.getResources()) { + serviceResourceTrie.put(resourceDef.getName(), new RangerResourceTrie(resourceDef, resourceMatchers, getPolicyEngineOptions().optimizeTagTrieForRetrieval, getPolicyEngineOptions().optimizeTagTrieForSpace, null)); } - enrichedServiceTags = new EnrichedServiceTags(serviceTags, resourceMatchers, serviceResourceTrie); } + ret = new EnrichedServiceTags(serviceTags, resourceMatchers, serviceResourceTrie); } + return ret; } - private void processServiceTagDeltas(ServiceTags deltas, ServiceTags allServiceTags) { + private EnrichedServiceTags processServiceTagDeltas(ServiceTags deltas, ServiceTags allServiceTags, Map> serviceResourceTrie, Set keysToRemoveFromCache) { if (LOG.isDebugEnabled()) { LOG.debug("Delta contains changes other than tag attribute changes, [" + deltas.getTagsChangeExtent() + "]"); } - boolean isInError = false; + final EnrichedServiceTags ret; - RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef, false); - ResourceHierarchies hierarchies = new ResourceHierarchies(); + boolean isInError = false; - List resourceMatchers = new ArrayList<>(); - Map> serviceResourceTrie = new HashMap<>(); + ResourceHierarchies hierarchies = new ResourceHierarchies(); + List resourceMatchers = new ArrayList<>(); if (enrichedServiceTags != null) { resourceMatchers.addAll(enrichedServiceTags.getServiceResourceMatchers()); - for (Map.Entry> entry : enrichedServiceTags.getServiceResourceTrie().entrySet()) { - RangerResourceTrie resourceTrie = new RangerResourceTrie<>(entry.getValue()); - serviceResourceTrie.put(entry.getKey(), resourceTrie); - } } List changedServiceResources = deltas.getServiceResources(); for (RangerServiceResource serviceResource : changedServiceResources) { + final RangerAccessResource removedAccessResource = MapUtils.isEmpty(serviceResource.getResourceElements()) ? null : removeOldServiceResource(serviceResource, resourceMatchers, serviceResourceTrie); - final boolean removedOldServiceResource = MapUtils.isEmpty(serviceResource.getResourceElements()) || removeOldServiceResource(serviceResource, resourceMatchers, serviceResourceTrie); - if (removedOldServiceResource) { - + if (removedAccessResource != null) { if (!StringUtils.isEmpty(serviceResource.getResourceSignature())) { - - RangerServiceResourceMatcher resourceMatcher = createRangerServiceResourceMatcher(serviceResource, serviceDefHelper, hierarchies); + RangerServiceResourceMatcher resourceMatcher = createRangerServiceResourceMatcher(serviceResource, serviceDefHelper, hierarchies, getPluginContext()); if (resourceMatcher != null) { - for (RangerServiceDef.RangerResourceDef resourceDef : serviceDef.getResources()) { + for (RangerResourceDef resourceDef : serviceDef.getResources()) { + RangerPolicyResource policyResource = serviceResource.getResourceElements().get(resourceDef.getName()); + RangerResourceTrie trie = serviceResourceTrie.get(resourceDef.getName()); - RangerResourceTrie trie = serviceResourceTrie.get(resourceDef.getName()); + if (LOG.isDebugEnabled()) { + LOG.debug("Trying to add resource-matcher to " + (trie == null ? "new" : "existing") + " trie for " + resourceDef.getName()); + } if (trie != null) { - trie.add(serviceResource.getResourceElements().get(resourceDef.getName()), resourceMatcher); + trie.add(policyResource, resourceMatcher); + trie.wrapUpUpdate(); + if (LOG.isDebugEnabled()) { - LOG.debug("Added resource-matcher for service-resource:[" + serviceResource + "]"); + LOG.debug("Added resource-matcher for policy-resource:[" + policyResource + "]"); } } else { - trie = new RangerResourceTrie<>(resourceDef, Collections.singletonList(resourceMatcher)); + trie = new RangerResourceTrie<>(resourceDef, Collections.singletonList(resourceMatcher), getPolicyEngineOptions().optimizeTagTrieForRetrieval, getPolicyEngineOptions().optimizeTagTrieForSpace, null); serviceResourceTrie.put(resourceDef.getName(), trie); } } @@ -458,6 +542,7 @@ private void processServiceTagDeltas(ServiceTags deltas, ServiceTags allServiceT LOG.debug("Service-resource:[id=" + serviceResource.getId() + "] is deleted as its resource-signature is empty. No need to create it!"); } } + keysToRemoveFromCache.add(removedAccessResource.getCacheKey()); } else { isInError = true; } @@ -465,72 +550,108 @@ private void processServiceTagDeltas(ServiceTags deltas, ServiceTags allServiceT break; } } + if (isInError) { LOG.error("Error in processing tag-deltas. Will continue to use old tags"); deltas.setTagVersion(-1L); + keysToRemoveFromCache.clear(); + ret = enrichedServiceTags; } else { for (Map.Entry> entry : serviceResourceTrie.entrySet()) { entry.getValue().wrapUpUpdate(); } - enrichedServiceTags = new EnrichedServiceTags(allServiceTags, resourceMatchers, serviceResourceTrie); + ret = new EnrichedServiceTags(allServiceTags, resourceMatchers, serviceResourceTrie); } - + return ret; } - private boolean removeOldServiceResource(RangerServiceResource serviceResource, List resourceMatchers, Map> resourceTries) { - boolean ret = true; + private RangerAccessResource removeOldServiceResource(RangerServiceResource serviceResource, List resourceMatchers, Map> resourceTries) { + final RangerAccessResource ret; + boolean result = true; if (enrichedServiceTags != null) { - if (LOG.isDebugEnabled()) { LOG.debug("Removing service-resource:[" + serviceResource + "] from trie-map"); } - // Remove existing serviceResource from the copy - RangerAccessResourceImpl accessResource = new RangerAccessResourceImpl(); - for (Map.Entry entry : serviceResource.getResourceElements().entrySet()) { - accessResource.setValue(entry.getKey(), entry.getValue()); + for (Map.Entry entry : serviceResource.getResourceElements().entrySet()) { + accessResource.setValue(entry.getKey(), entry.getValue().getValues()); } + + accessResource.setServiceDef(serviceDef); + if (LOG.isDebugEnabled()) { LOG.debug("RangerAccessResource:[" + accessResource + "] created to represent service-resource[" + serviceResource + "] to find evaluators from trie-map"); } - List oldMatchers = getEvaluators(accessResource, enrichedServiceTags); + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + request.setResource(accessResource); + + Collection oldMatchers = CachedResourceEvaluators.getEvaluators(request, enrichedServiceTags.getServiceResourceTrie(), cache); if (LOG.isDebugEnabled()) { - LOG.debug("Found [" + oldMatchers.size() + "] matchers for service-resource[" + serviceResource + "]"); + LOG.debug("Found [" + oldMatchers + "] matchers for service-resource[" + serviceResource + "]"); + } + + if (CollectionUtils.isNotEmpty(oldMatchers)) { + List notMatched = new ArrayList<>(); + + for (RangerServiceResourceMatcher resourceMatcher : oldMatchers) { + final RangerPolicyResourceMatcher.MatchType matchType = resourceMatcher.getMatchType(accessResource, request.getResourceElementMatchingScopes(), request.getContext()); + + if (LOG.isDebugEnabled()) { + LOG.debug("resource:[" + accessResource + ", MatchType:[" + matchType + "]"); + } + + if (matchType != RangerPolicyResourceMatcher.MatchType.SELF) { + notMatched.add(resourceMatcher); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("oldMatchers : [" + notMatched + "] do not match resource:[" + accessResource + "] exactly and will be discarded"); + } + + oldMatchers.removeAll(notMatched); } for (RangerServiceResourceMatcher matcher : oldMatchers) { + for (RangerResourceDef resourceDef : serviceDef.getResources()) { + String resourceDefName = resourceDef.getName(); + RangerResourceTrie trie = resourceTries.get(resourceDefName); - for (String resourceDefName : serviceResource.getResourceElements().keySet()) { - RangerResourceTrie trie = resourceTries.get(resourceDefName); if (trie != null) { trie.delete(serviceResource.getResourceElements().get(resourceDefName), matcher); } else { LOG.error("Cannot find resourceDef with name:[" + resourceDefName + "]. Should NOT happen!!"); LOG.error("Setting tagVersion to -1 to ensure that in the next download all tags are downloaded"); - ret = false; + result = false; break; } } } - // Remove old resource matchers - if (ret) { + if (result) { resourceMatchers.removeAll(oldMatchers); if (LOG.isDebugEnabled()) { - LOG.debug("Found and removed [" + oldMatchers.size() + "] matchers for service-resource[" + serviceResource + "] from trie-map"); + LOG.debug("Found and removed [" + oldMatchers + "] matchers for service-resource[" + serviceResource + "] from trie-map"); } + + ret = accessResource; + } else { + ret = null; } + } else { + ret = null; } + return ret; } - static public RangerServiceResourceMatcher createRangerServiceResourceMatcher(RangerServiceResource serviceResource, RangerServiceDefHelper serviceDefHelper, ResourceHierarchies hierarchies) { + static public RangerServiceResourceMatcher createRangerServiceResourceMatcher(RangerServiceResource serviceResource, RangerServiceDefHelper serviceDefHelper, ResourceHierarchies hierarchies, RangerPluginContext pluginContext) { if (LOG.isDebugEnabled()) { LOG.debug("==> createRangerServiceResourceMatcher(serviceResource=" + serviceResource + ")"); @@ -545,7 +666,7 @@ static public RangerServiceResourceMatcher createRangerServiceResourceMatcher(Ra if (isValidHierarchy == null) { // hierarchy not yet validated isValidHierarchy = Boolean.FALSE; - for (List hierarchy : serviceDefHelper.getResourceHierarchies(policyType)) { + for (List hierarchy : serviceDefHelper.getResourceHierarchies(policyType)) { if (serviceDefHelper.hierarchyHasAllResources(hierarchy, resourceKeys)) { isValidHierarchy = Boolean.TRUE; @@ -561,6 +682,7 @@ static public RangerServiceResourceMatcher createRangerServiceResourceMatcher(Ra matcher.setServiceDef(serviceDefHelper.getServiceDef()); matcher.setPolicyResources(serviceResource.getResourceElements(), policyType); + matcher.setPluginContext(pluginContext); if (LOG.isDebugEnabled()) { LOG.debug("RangerTagEnricher.setServiceTags() - Initializing matcher with (resource=" + serviceResource @@ -611,39 +733,49 @@ private Set findMatchingTags(final RangerAccessRequest request RangerAccessResource resource = request.getResource(); + RangerPerfTracer perf = null; + + if (RangerPerfTracer.isPerfTraceEnabled(PERF_SERVICETAGS_RETRIEVAL_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_SERVICETAGS_RETRIEVAL_LOG, "RangerTagEnricher.findMatchingTags=" + resource.getAsString() + ")"); + } + if ((resource == null || resource.getKeys() == null || resource.getKeys().isEmpty()) && request.isAccessTypeAny()) { ret = enrichedServiceTags.getTagsForEmptyResourceAndAnyAccess(); } else { - final List serviceResourceMatchers = getEvaluators(resource, enrichedServiceTags); + final Collection serviceResourceMatchers = CachedResourceEvaluators.getEvaluators(request, enrichedServiceTags.getServiceResourceTrie(), cache); if (CollectionUtils.isNotEmpty(serviceResourceMatchers)) { - for (RangerServiceResourceMatcher resourceMatcher : serviceResourceMatchers) { - final RangerPolicyResourceMatcher.MatchType matchType = resourceMatcher.getMatchType(resource, request.getContext()); + final RangerPolicyResourceMatcher.MatchType matchType = resourceMatcher.getMatchType(resource, request.getResourceElementMatchingScopes(), request.getContext()); + + if (LOG.isDebugEnabled()) { + LOG.debug("resource:[" + resource + ", MatchType:[" + matchType + "]"); + } - final boolean isMatched; + final ResourceMatchingScope resourceMatchingScope = request.getResourceMatchingScope() != null ? request.getResourceMatchingScope() : ResourceMatchingScope.SELF; + final boolean isMatched; - if (request.isAccessTypeAny()) { - isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; - } else if (request.getResourceMatchingScope() == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) { - isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; + if (request.isAccessTypeAny() || resourceMatchingScope == ResourceMatchingScope.SELF_OR_DESCENDANTS) { + isMatched = matchType == RangerPolicyResourceMatcher.MatchType.ANCESTOR || matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS || matchType == RangerPolicyResourceMatcher.MatchType.DESCENDANT; } else { - isMatched = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.ANCESTOR; + isMatched = matchType == RangerPolicyResourceMatcher.MatchType.ANCESTOR || matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS; } if (isMatched) { if (ret == null) { ret = new HashSet<>(); } - ret.addAll(getTagsForServiceResource(enrichedServiceTags.getServiceTags(), resourceMatcher.getServiceResource(), matchType)); + ret.addAll(getTagsForServiceResource(request.getAccessTime(), enrichedServiceTags.getServiceTags(), resourceMatcher.getServiceResource(), matchType)); } } } } + RangerPerfTracer.logAlways(perf); + if (CollectionUtils.isEmpty(ret)) { if (LOG.isDebugEnabled()) { LOG.debug("RangerTagEnricher.findMatchingTags(" + resource + ") - No tags Found "); @@ -661,92 +793,7 @@ private Set findMatchingTags(final RangerAccessRequest request return ret; } - private List getEvaluators(RangerAccessResource resource, EnrichedServiceTags enrichedServiceTags) { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerTagEnricher.getEvaluators(" + (resource != null ? resource.getAsString() : null) + ")"); - } - List ret = Collections.EMPTY_LIST; - - final Map> serviceResourceTrie = enrichedServiceTags.getServiceResourceTrie(); - - if (resource == null || resource.getKeys() == null || resource.getKeys().isEmpty() || serviceResourceTrie == null) { - ret = enrichedServiceTags.getServiceResourceMatchers(); - } else { - Set evaluators = null; - - RangerPerfTracer perf = null; - - if (RangerPerfTracer.isPerfTraceEnabled(PERF_TRIE_OP_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_OP_LOG, "RangerTagEnricher.getEvaluators(resource=" + resource.getAsString() + ")"); - } - - Set resourceKeys = resource.getKeys(); - List> serviceResourceMatchersList = null; - Set smallestList = null; - - if (CollectionUtils.isNotEmpty(resourceKeys)) { - - for (String resourceName : resourceKeys) { - RangerResourceTrie trie = serviceResourceTrie.get(resourceName); - - if (trie == null) { // if no trie exists for this resource level, ignore and continue to next level - continue; - } - - Set serviceResourceMatchers = trie.getEvaluatorsForResource(resource.getValue(resourceName)); - - if (CollectionUtils.isEmpty(serviceResourceMatchers)) { // no tags for this resource, bail out - serviceResourceMatchersList = null; - smallestList = null; - break; - } - - if (smallestList == null) { - smallestList = serviceResourceMatchers; - } else { - if (serviceResourceMatchersList == null) { - serviceResourceMatchersList = new ArrayList<>(); - serviceResourceMatchersList.add(smallestList); - } - serviceResourceMatchersList.add(serviceResourceMatchers); - - if (smallestList.size() > serviceResourceMatchers.size()) { - smallestList = serviceResourceMatchers; - } - } - } - if (serviceResourceMatchersList != null) { - evaluators = new HashSet<>(smallestList); - for (Set serviceResourceMatchers : serviceResourceMatchersList) { - if (serviceResourceMatchers != smallestList) { - // remove other serviceResourceMatchers from ret that are not in serviceResourceMatchers - evaluators.retainAll(serviceResourceMatchers); - if (CollectionUtils.isEmpty(evaluators)) { // if no policy exists, bail out and return empty list - evaluators = null; - break; - } - } - } - } else { - evaluators = smallestList; - } - } - - if (evaluators != null) { - ret = new ArrayList<>(evaluators); - } - - RangerPerfTracer.logAlways(perf); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerTagEnricher.getEvaluators(" + (resource != null ? resource.getAsString() : null) + "): evaluatorCount=" + ret.size()); - } - - return ret; - } - - private static Set getTagsForServiceResource(final ServiceTags serviceTags, final RangerServiceResource serviceResource, final RangerPolicyResourceMatcher.MatchType matchType) { + private static Set getTagsForServiceResource(Date accessTime, final ServiceTags serviceTags, final RangerServiceResource serviceResource, final RangerPolicyResourceMatcher.MatchType matchType) { Set ret = new HashSet<>(); final Long resourceId = serviceResource.getId(); @@ -763,12 +810,17 @@ private static Set getTagsForServiceResource(final ServiceTags if (CollectionUtils.isNotEmpty(tagIds)) { + accessTime = accessTime == null ? new Date() : accessTime; + for (Long tagId : tagIds) { RangerTag tag = tags.get(tagId); if (tag != null) { - ret.add(new RangerTagForEval(tag, matchType)); + RangerTagForEval tagForEval = new RangerTagForEval(tag, matchType); + if (tagForEval.isApplicable(accessTime)) { + ret.add(tagForEval); + } } } } else { @@ -785,6 +837,18 @@ private static Set getTagsForServiceResource(final ServiceTags return ret; } + private Map> copyServiceResourceTrie() { + Map> ret = new HashMap<>(); + + if (enrichedServiceTags != null) { + for (Map.Entry> entry : enrichedServiceTags.getServiceResourceTrie().entrySet()) { + RangerResourceTrie resourceTrie = new RangerResourceTrie<>(entry.getValue()); + ret.put(entry.getKey(), resourceTrie); + } + } + return ret; + } + static public final class EnrichedServiceTags { final private ServiceTags serviceTags; final private List serviceResourceMatchers; @@ -815,7 +879,7 @@ private Set createTagsForEmptyResourceAndAnyAccess() { } static class RangerTagRefresher extends Thread { - private static final Log LOG = LogFactory.getLog(RangerTagRefresher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerTagRefresher.class); private final RangerTagRetriever tagRetriever; private final RangerTagEnricher tagEnricher; @@ -824,8 +888,7 @@ static class RangerTagRefresher extends Thread { private long lastActivationTimeInMillis; private final String cacheFile; - private boolean hasProvidedTagsToReceiver; - private Gson gson; + private boolean hasProvidedTagsToReceiver; RangerTagRefresher(RangerTagRetriever tagRetriever, RangerTagEnricher tagEnricher, long lastKnownVersion, BlockingQueue tagDownloadQueue, String cacheFile) { this.tagRetriever = tagRetriever; @@ -833,11 +896,6 @@ static class RangerTagRefresher extends Thread { this.lastKnownVersion = lastKnownVersion; this.tagDownloadQueue = tagDownloadQueue; this.cacheFile = cacheFile; - try { - gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create(); - } catch(Throwable excp) { - LOG.fatal("failed to create GsonBuilder object", excp); - } setName("RangerTagRefresher(serviceName=" + tagRetriever.getServiceName() + ")-" + getId()); } @@ -871,7 +929,7 @@ public void run() { RangerPerfTracer.log(perf); } catch (InterruptedException excp) { - LOG.debug("RangerTagRefresher(" + getName() + ").run(): Interrupted! Exiting thread", excp); + LOG.info("RangerTagRefresher(" + getName() + ").run(): Interrupted! Exiting thread", excp); break; } finally { if (trigger != null) { @@ -969,6 +1027,9 @@ private void stopRefresher() { try { super.join(); isJoined = true; + if (LOG.isDebugEnabled()) { + LOG.debug("RangerTagRefresher(" + getName() + ") is stopped"); + } } catch (InterruptedException excp) { LOG.warn("RangerTagRefresher(" + getName() + ").stopRefresher(): Error while waiting for thread to exit", excp); LOG.warn("Retrying Thread.join(). Current thread will be marked as 'interrupted' after Thread.join() returns"); @@ -997,7 +1058,7 @@ final ServiceTags loadFromCache() { try { reader = new FileReader(cacheFile); - serviceTags = gson.fromJson(reader, ServiceTags.class); + serviceTags = JsonUtils.jsonToObject(reader, ServiceTags.class); if (serviceTags != null && !StringUtils.equals(tagEnricher.getServiceName(), serviceTags.getServiceName())) { LOG.warn("ignoring unexpected serviceName '" + serviceTags.getServiceName() + "' in cache file '" + cacheFile.getAbsolutePath() + "'"); @@ -1040,7 +1101,7 @@ final void saveToCache(ServiceTags serviceTags) { try { writer = new FileWriter(cacheFile); - gson.toJson(serviceTags, writer); + JsonUtils.objectToWriter(writer, serviceTags); } catch (Exception excp) { LOG.error("failed to save service-tags to cache file '" + cacheFile.getAbsolutePath() + "'", excp); } finally { @@ -1087,4 +1148,5 @@ final void disableCache() { } } } + } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java index 3f0db31ac0..8557a47046 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagForEval.java @@ -21,21 +21,18 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerValiditySchedule; import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnore; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; @@ -44,16 +41,14 @@ import java.util.Map; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) // This class needs above annotations for policy-engine unit tests involving RangerTagForEval objects that are initialized // from JSON specification public class RangerTagForEval implements Serializable { - private static final Log LOG = LogFactory.getLog(RangerTagForEval.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerTagForEval.class); private String type; private Map attributes; @@ -69,10 +64,10 @@ private RangerTagForEval() {} public RangerTagForEval(RangerTag tag, RangerPolicyResourceMatcher.MatchType matchType) { this.type = tag.getType(); - this.attributes = tag.getAttributes(); - this.options = tag.getOptions(); + this.attributes = tag.getAttributes() != null ? tag.getAttributes() : Collections.emptyMap(); + this.options = tag.getOptions() != null ? tag.getOptions() : Collections.emptyMap(); this.matchType = matchType; - this.validityPeriods = tag.getValidityPeriods(); + this.validityPeriods = tag.getValidityPeriods() != null ? tag.getValidityPeriods() : Collections.emptyList(); this.validityPeriodEvaluators = createValidityPeriodEvaluators(); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagRetriever.java index 0f09b64d2f..584959e02f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagRetriever.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerTagRetriever.java @@ -21,6 +21,7 @@ import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.util.ServiceTags; import java.util.Map; @@ -31,6 +32,7 @@ public abstract class RangerTagRetriever { protected RangerServiceDef serviceDef; protected String appId; protected RangerPluginConfig pluginConfig; + protected RangerPluginContext pluginContext; public abstract void init(Map options); @@ -61,4 +63,13 @@ public void setAppId(String appId) { } public void setPluginConfig(RangerPluginConfig pluginConfig) { this.pluginConfig = pluginConfig; } + + public RangerPluginContext getPluginContext() { + return pluginContext; + } + + public void setPluginContext(RangerPluginContext pluginContext) { + this.pluginContext = pluginContext; + } + } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreEnricher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreEnricher.java index cc892d8fdd..ec5ca7b4f5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreEnricher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreEnricher.java @@ -20,8 +20,6 @@ package org.apache.ranger.plugin.contextenricher; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.service.RangerAuthContext; import org.apache.ranger.plugin.util.DownloaderTask; @@ -29,27 +27,27 @@ import org.apache.ranger.plugin.util.RangerUserStore; import org.apache.ranger.plugin.util.RangerPerfTracer; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.util.Timer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class RangerUserStoreEnricher extends RangerAbstractContextEnricher { - private static final Log LOG = LogFactory.getLog(RangerUserStoreEnricher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerUserStoreEnricher.class); + private static final Logger PERF_SET_USERSTORE_LOG = RangerPerfTracer.getPerfLogger("userstoreenricher.setuserstore"); - private static final Log PERF_SET_USERSTORE_LOG = RangerPerfTracer.getPerfLogger("userstoreenricher.setuserstore"); + public static final String USERSTORE_REFRESHER_POLLINGINTERVAL_OPTION = "userStoreRefresherPollingInterval"; + public static final String USERSTORE_RETRIEVER_CLASSNAME_OPTION = "userStoreRetrieverClassName"; - - private static final String USERSTORE_REFRESHER_POLLINGINTERVAL_OPTION = "userStoreRefresherPollingInterval"; - private static final String USERSTORE_RETRIEVER_CLASSNAME_OPTION = "userStoreRetrieverClassName"; - - private RangerUserStoreRefresher userStoreRefresher; - private RangerUserStoreRetriever userStoreRetriever; - private RangerUserStore rangerUserStore; - private boolean disableCacheIfServiceNotFound = true; - - private final BlockingQueue userStoreDownloadQueue = new LinkedBlockingQueue<>(); - private Timer userStoreDownloadTimer; + private RangerUserStoreRefresher userStoreRefresher; + private RangerUserStoreRetriever userStoreRetriever; + private RangerUserStore rangerUserStore; + private boolean disableCacheIfServiceNotFound = true; + private boolean dedupStrings = true; + private final BlockingQueue userStoreDownloadQueue = new LinkedBlockingQueue<>(); + private Timer userStoreDownloadTimer; @Override public void init() { @@ -59,9 +57,11 @@ public void init() { super.init(); + String propertyPrefix = getPropertyPrefix(); String userStoreRetrieverClassName = getOption(USERSTORE_RETRIEVER_CLASSNAME_OPTION); + long pollingIntervalMs = getLongOption(USERSTORE_REFRESHER_POLLINGINTERVAL_OPTION, 3600 * 1000L); - long pollingIntervalMs = getLongOption(USERSTORE_REFRESHER_POLLINGINTERVAL_OPTION, 3600 * 1000); + dedupStrings = getBooleanConfig(propertyPrefix + ".dedup.strings", true); if (StringUtils.isNotBlank(userStoreRetrieverClassName)) { @@ -82,7 +82,6 @@ public void init() { } if (userStoreRetriever != null) { - String propertyPrefix = "ranger.plugin." + serviceDef.getName(); disableCacheIfServiceNotFound = getBooleanConfig(propertyPrefix + ".disable.cache.if.servicenotfound", true); String cacheDir = getConfig(propertyPrefix + ".policy.cache.dir", null); String cacheFilename = String.format("%s_%s_userstore.json", appId, serviceName); @@ -96,9 +95,11 @@ public void init() { userStoreRetriever.setServiceDef(serviceDef); userStoreRetriever.setAppId(appId); userStoreRetriever.setPluginConfig(getPluginConfig()); + userStoreRetriever.setPluginContext(getPluginContext()); userStoreRetriever.init(enricherDef.getEnricherOptions()); userStoreRefresher = new RangerUserStoreRefresher(userStoreRetriever, this, null, -1L, userStoreDownloadQueue, cacheFile); + LOG.info("Created Thread(RangerUserStoreRefresher(" + getName() + ")"); try { userStoreRefresher.populateUserStoreInfo(); @@ -192,7 +193,12 @@ public void setRangerUserStore(final RangerUserStore rangerUserStore) { perf = RangerPerfTracer.getPerfTracer(PERF_SET_USERSTORE_LOG, "RangerUserStoreEnricher.setRangerUserStore(newUserStoreVersion=" + rangerUserStore.getUserStoreVersion() + ")"); } + if (dedupStrings) { + rangerUserStore.dedupStrings(); + } + this.rangerUserStore = rangerUserStore; + RangerPerfTracer.logAlways(perf); } @@ -203,6 +209,12 @@ public void setRangerUserStore(final RangerUserStore rangerUserStore) { } + public Long getUserStoreVersion() { + RangerUserStore localUserStore = this.rangerUserStore; + + return localUserStore != null ? localUserStore.getUserStoreVersion() : null; + } + @Override public boolean preCleanup() { if (LOG.isDebugEnabled()) { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRefresher.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRefresher.java index 7d0521daee..97fe181573 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRefresher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRefresher.java @@ -18,21 +18,21 @@ */ package org.apache.ranger.plugin.contextenricher; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.sun.jersey.api.client.ClientResponse; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.UserGroupInformation; import org.apache.ranger.admin.client.datatype.RESTResponse; import org.apache.ranger.audit.provider.MiscUtil; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.util.RangerPerfTracer; import org.apache.ranger.plugin.util.DownloadTrigger; +import org.apache.ranger.plugin.util.JsonUtilsV2; import org.apache.ranger.plugin.util.RangerRESTClient; import org.apache.ranger.plugin.util.RangerUserStore; import org.apache.ranger.plugin.util.RangerServiceNotFoundException; import org.apache.ranger.plugin.util.RangerRESTUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.servlet.http.HttpServletResponse; import java.io.File; @@ -41,14 +41,14 @@ import java.io.FileWriter; import java.io.FileReader; import java.nio.channels.ClosedByInterruptException; -import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; import java.util.HashMap; import java.util.Map; import java.util.concurrent.BlockingQueue; public class RangerUserStoreRefresher extends Thread { - private static final Log LOG = LogFactory.getLog(RangerUserStoreRefresher.class); - private static final Log PERF_REFRESHER_INIT_LOG = RangerPerfTracer.getPerfLogger("userstore.init"); + private static final Logger LOG = LoggerFactory.getLogger(RangerUserStoreRefresher.class); + private static final Logger PERF_REFRESHER_INIT_LOG = RangerPerfTracer.getPerfLogger("userstore.init"); private final RangerUserStoreRetriever userStoreRetriever; private final RangerUserStoreEnricher userStoreEnricher; @@ -57,8 +57,7 @@ public class RangerUserStoreRefresher extends Thread { private long lastActivationTimeInMillis; private final String cacheFile; - private boolean hasProvidedUserStoreToReceiver; - private Gson gson; + private boolean hasProvidedUserStoreToReceiver; private RangerRESTClient rangerRESTClient; public RangerUserStoreRefresher(RangerUserStoreRetriever userStoreRetriever, RangerUserStoreEnricher userStoreEnricher, @@ -70,11 +69,6 @@ public RangerUserStoreRefresher(RangerUserStoreRetriever userStoreRetriever, Ran this.lastKnownVersion = lastKnownVersion; this.userStoreDownloadQueue = userStoreDownloadQueue; this.cacheFile = cacheFile; - try { - gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create(); - } catch(Throwable excp) { - LOG.fatal("failed to create GsonBuilder object", excp); - } setName("RangerUserStoreRefresher(serviceName=" + userStoreRetriever.getServiceName() + ")-" + getId()); } @@ -265,7 +259,7 @@ private RangerUserStore loadFromCache() { try { reader = new FileReader(cacheFile); - rangerUserStore = gson.fromJson(reader, RangerUserStore.class); + rangerUserStore = JsonUtils.jsonToObject(reader, RangerUserStore.class); } catch (Exception excp) { LOG.error("failed to load userstore information from cache file " + cacheFile.getAbsolutePath(), excp); @@ -303,7 +297,7 @@ public void saveToCache(RangerUserStore rangerUserStore) { try { writer = new FileWriter(cacheFile); - gson.toJson(rangerUserStore, writer); + JsonUtils.objectToWriter(writer, rangerUserStore); } catch (Exception excp) { LOG.error("failed to save userstore information to cache file '" + cacheFile.getAbsolutePath() + "'", excp); } finally { @@ -384,19 +378,17 @@ private RangerUserStore getUserStoreIfUpdated(long lastKnownUserStoreVersion, lo if (LOG.isDebugEnabled()) { LOG.debug("Checking UserStore updated as user : " + user); } - PrivilegedAction action = new PrivilegedAction() { - public ClientResponse run() { - ClientResponse clientRes = null; + response = MiscUtil.executePrivilegedAction((PrivilegedExceptionAction) () -> { + try { String relativeURL = RangerRESTUtils.REST_URL_SERVICE_SERCURE_GET_USERSTORE; - try { - clientRes = rangerRESTClient.get(relativeURL, queryParams); - } catch (Exception e) { - LOG.error("Failed to get response, Error is : "+e.getMessage()); - } - return clientRes; + + return rangerRESTClient.get(relativeURL, queryParams); + } catch (Exception e) { + LOG.error("Failed to get response, Error is : "+e.getMessage()); } - }; - response = user.doAs(action); + + return null; + }); } else { if (LOG.isDebugEnabled()) { LOG.debug("Checking UserStore updated as user : " + user); @@ -419,7 +411,7 @@ public ClientResponse run() { } ret = null; } else if (response.getStatus() == HttpServletResponse.SC_OK) { - ret = response.getEntity(RangerUserStore.class); + ret = JsonUtilsV2.readResponse(response, RangerUserStore.class); } else if (response.getStatus() == HttpServletResponse.SC_NOT_FOUND) { ret = null; LOG.error("Error getting UserStore; service not found. secureMode=" + isSecureMode + ", user=" + user diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRetriever.java index 1addbc49db..c9a9492ed5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRetriever.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/RangerUserStoreRetriever.java @@ -21,6 +21,7 @@ import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.util.RangerUserStore; import java.util.Map; @@ -31,6 +32,7 @@ public abstract class RangerUserStoreRetriever { protected RangerServiceDef serviceDef; protected String appId; protected RangerPluginConfig pluginConfig; + protected RangerPluginContext pluginContext; public abstract void init(Map options); @@ -61,4 +63,12 @@ public void setAppId(String appId) { } public void setPluginConfig(RangerPluginConfig pluginConfig) { this.pluginConfig = pluginConfig; } + + public RangerPluginContext getPluginContext() { + return pluginContext; + } + + public void setPluginContext(RangerPluginContext pluginContext) { + this.pluginContext = pluginContext; + } } \ No newline at end of file diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/GetFromDataFile.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/GetFromDataFile.java new file mode 100644 index 0000000000..93cf38aace --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/GetFromDataFile.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.contextenricher.externalretrievers; + +import org.apache.ranger.plugin.contextenricher.RangerAbstractContextEnricher; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class GetFromDataFile { + private static final Logger LOG = LoggerFactory.getLogger(GetFromDataFile.class); + + public Map> getFromDataFile(String dataFile, String attrName) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> getFromDataFile(dataFile={}, attrName={})", dataFile, attrName); + } + + Map> ret = new HashMap<>(); + + // create an instance so that readProperties() can be used! + RangerAbstractContextEnricher ce = new RangerAbstractContextEnricher() { + @Override + public void enrich(RangerAccessRequest rangerAccessRequest) { + } + }; + + Properties prop = ce.readProperties(dataFile); + + if (prop == null) { + LOG.warn("getFromDataFile({}, {}): failed to read file", dataFile, attrName); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("read from datafile {}: {}", dataFile, prop); + } + + // reformat UserAttrsProp into UserStore format: + // format of UserAttrsProp: Map + // format of UserStore: Map> + for (String user : prop.stringPropertyNames()) { + Map userAttrs = new HashMap<>(); + + userAttrs.put(attrName, prop.getProperty(user)); + + ret.put(user, userAttrs); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== getFromDataFile(dataFile={}, attrName={}): ret={}", dataFile, attrName, ret); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/GetFromURL.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/GetFromURL.java new file mode 100644 index 0000000000..0589561580 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/GetFromURL.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.contextenricher.externalretrievers; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.hadoop.util.StringUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpStatus; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.apache.ranger.authorization.utils.JsonUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GetFromURL { + private static final Logger LOG = LoggerFactory.getLogger(GetFromURL.class); + + public Map> getFromURL(String url, String configFile) throws Exception { + if (LOG.isDebugEnabled()) { + LOG.debug("==> getFromURL(url={}, configFile={})", url, configFile); + } + + String token = getBearerToken(configFile); + HttpUriRequest request = RequestBuilder.get().setUri(url) + .setHeader(HttpHeaders.AUTHORIZATION, token) + .setHeader(HttpHeaders.CONTENT_TYPE, "text/plain") + .build(); + Map> ret; + + try (CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = httpClient.execute(request)) { + if (response == null) { + throw new IOException("getFromURL(" + url + ") failed: null response"); + } + + int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode != HttpStatus.SC_OK) { + throw new IOException("getFromURL(" + url + ") failed: http status=" + response.getStatusLine()); + } + + HttpEntity httpEntity = response.getEntity(); + String stringResult = EntityUtils.toString(httpEntity); + Map resultMap = JsonUtils.jsonToObject(stringResult, Map.class); + Map>> userAttrValues = (Map>>) resultMap.get("body"); + + ret = toUserAttributes(userAttrValues); + + // and ensure response body is fully consumed + EntityUtils.consume(httpEntity); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== getFromURL(url={}, configFile={}): ret={}", url, configFile, ret); + } + + return ret; + } + + private String getBearerToken(String configFile) throws Exception { + String secrets = getSecretsFromFile(configFile); + JsonNode jsonObject = JsonUtils.getMapper().readTree(secrets); + String tokenURL = jsonObject.get("tokenUrl").asText(); + List> headers = JsonUtils.getMapper().convertValue(jsonObject.get("headers"), new TypeReference>>(){}); + List> params = JsonUtils.getMapper().convertValue(jsonObject.get("params"), new TypeReference>>(){}); + + List nvPairs = new ArrayList<>(); + HttpPost httpPost = new HttpPost(tokenURL); + + // add headers to httpPost object: + for (Map header : headers) { + for (Map.Entry e : header.entrySet()) { + httpPost.setHeader(e.getKey(), e.getValue()); + } + } + + // add params to httpPost entity: + for (Map param : params) { + for (Map.Entry e : param.entrySet()) { + nvPairs.add(new BasicNameValuePair(e.getKey(), e.getValue())); + } + } + + httpPost.setEntity(new UrlEncodedFormEntity(nvPairs, StandardCharsets.UTF_8)); + + String ret; + + // execute httpPost: + try (CloseableHttpClient httpClient = HttpClients.createDefault(); + CloseableHttpResponse response = httpClient.execute(httpPost)) { + if (response == null) { + throw new IOException("getBearerToken(" + configFile + ") failed: null response"); + } + + int statusCode = response.getStatusLine().getStatusCode(); + + if (statusCode != HttpStatus.SC_OK) { + throw new IOException("getBearerToken(" + configFile + ") failed: http status=" + response.getStatusLine()); + } + + HttpEntity httpEntity = response.getEntity(); + String stringResult = EntityUtils.toString(httpEntity); + Map resultMap = JsonUtils.jsonToObject(stringResult, Map.class); + String token = resultMap.get("access_token").toString(); + + ret = "Bearer " + token; + + // and ensure response body is fully consumed + EntityUtils.consume(httpEntity); + } + + return ret; + } + + private Map> toUserAttributes(Map>> userAttrValues){ + if (LOG.isDebugEnabled()) { + LOG.debug("==> toUserAttributes(userAttrValues={})", userAttrValues); + } + + Map> ret = new HashMap<>(); + + for (Map.Entry>> userEntry : userAttrValues.entrySet()) { + String user = userEntry.getKey(); + Map> attrValues = userEntry.getValue(); + Map userAttrs = new HashMap<>(); + + for (Map.Entry> attrEntry : attrValues.entrySet()) { + String attrName = attrEntry.getKey(); + List values = attrEntry.getValue(); + + userAttrs.put(attrName, String.join(",", values)); + } + + ret.put(user, userAttrs); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== toUserAttributes(userAttrValues={}): ret={}", userAttrValues, ret); + } + + return ret; + } + + private String getSecretsFromFile(String configFile) throws IOException { + String ret = decodeSecrets(new String(Files.readAllBytes(Paths.get(configFile)))); + + verifyToken(ret); + + return ret; + } + + private String decodeSecrets(String encodedSecrets) { + return new String(Base64.getDecoder().decode(encodedSecrets)); + } + + private void verifyToken(String secrets) throws IOException { + String errorMessage = ""; + JsonNode jsonObject = JsonUtils.getMapper().readTree(secrets); + + // verify all necessary items are there + if (jsonObject.get("tokenUrl") == null) { + errorMessage += "tokenUrl must be specified in the config file; "; + } + + if (jsonObject.get("headers") == null) { + errorMessage += "headers must be specified in the config file; "; + } else { // verify that Content-type, if included, is application/x-www-form-urlencoded + JsonNode headers = jsonObject.get("headers"); + + for (JsonNode header : headers) { + if (header.has("Content-Type") && !StringUtils.equalsIgnoreCase(header.get("Content-Type").textValue(), "application/x-www-form-urlencoded")) { + errorMessage += "Content-Type, if specified, must be \"application/x-www-form-urlencoded\"; "; + } + } + } + + if (jsonObject.get("params") == null) { + errorMessage += "params must be specified in the config file; "; + } + + if (!errorMessage.equals("")) { + throw new IOException(errorMessage); + } + } +} + + diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/LICENSE b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/LICENSE @@ -0,0 +1,202 @@ + + 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. diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/NOTICE b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/NOTICE new file mode 100644 index 0000000000..b5c81eb4d4 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/NOTICE @@ -0,0 +1,18 @@ +Apache Ranger External User Store Retriever and Apache Ranger Role User Store Retriever + +Copyright 2022 Comcast Cable Communications Management, LLC + +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. + +PDX-License-Identifier: Apache-2.0 + +This product includes software developed at Comcast (http://www.comcast.com/). diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/README.md b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/README.md new file mode 100644 index 0000000000..c874419eb0 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/README.md @@ -0,0 +1,137 @@ + +````text +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. +```` + +# Ranger External User Store Retrievers + +A library to retrieve userStore entries from sources external to the Ranger Admin User Store. The top level class is called RangerMultiSourceUserStoreRetriever. + +## Business Value + +A counterpart of RangerAdminUserStoreRetriever, instead of retrieving items from the internal Ranger userStore, +RangerMultiSourceUserStoreRetriever retrieves userStore entries from other sources. The userStore entries will persist +while the plugin is up, and will be refreshed at configured intervals. + +This enables ABAC (Attribute-based Access Control) based on the user's attributes that are retrieved. +For example, the API could return the set of business partners that the user has been granted access to. +Thus, a row filter policy can be built using a condition like this: +````text +${{USER.partner}}.includes(partner) +```` +where partner is the name of a column in a hive table. This enables +row filter policies in which a user might match multiple conditions, which is not possible with out-of-the-box Ranger. + +## Currently Supported Sources + +### Arbitrary API calls (source name: "api") + +This code enables additions to the UserStore to be retrieved simply by creating an API which +returns data in userStore format, and including attrName, userStoreURL, and optionally +configFile and dataFile as contextEnricher options in the host plugin's service definition. + +#### Configuration Items + +Configurations are specified in the host plugin's service definition, as enricherOptions in the contextEnricher +definition. Configurations specific to this source type: + +**attrName** is the attribute whose values are mapped to the user, i.e., the key to be used in the userStore: +user -> **attrName** -> attrValues. +In Ranger policies it appears in ${{USER.**attrName**}} syntax, eg ${{USER.partner}} + +**userStoreURL** is the URL from which to retrieve the user-to-attribute mapping. + +**dataFile** is a local java properties file from which the user-to-attribute mapping can be retrieved. It is optional, +and intended to be used primarily in development. + +**configFile** is the name of the file containing the Base64-encoded secrets needed as inputs to retrieve +the Bearer Token needed for access to the userStoreURL. It is optional, for security reasons. If configFile doesn't +appear in the EnricherOptions, a default value is constructed as "/var/ranger/security/"+attrName+".conf". + +The config file is a required JSON file which must contain: +- **tokenUrl** : the name of the url to call to retrieve the Bearer Token +- **headers**: list of key-value pairs representing names and values of http headers for the call to the tokenUrl. + Note: Content-Type is assumed to be "application/x-www-form-urlencoded". Inclusion of a different content-type header in the config file will cause a 400 error. +- **params**: name-value pairs to be added as parameters to the URI's query portion + +Here are the contents of an **example configFile**: +```json +{ + "tokenUrl": "https://security.mycompany.com/token.oauth2", + "headers": [ + { "Content-Type": "application/x-www-form-urlencoded" } , + { "Accept": "application/json" } + ], + "params": [ + { "client_id": "my_user_name" }, + { "client_secret": "***************" }, + { "grant_type": "client_credentials" }, + { "scope": "my_project" } + ] +} +``` + +### RangerRoles (source name: "role") + +In this case, attributes are retrieved internally from Ranger, based on the +roles of which the user is a member. No additional coding is needed. + +#### Configuration Items + +Configurations are specified in the host plugin's service definition, as enricherOptions in the contextEnricher +definition. Instead of external storage configurations (eg URL, datafile), configurations +specify how to retrieve the roles of interest: + +**attrName** is the attribute whose values are mapped to the user, i.e., the key to be used in the userStore: +user -> **attrName** -> attrValues. It is also the string used to identify the role of interest. By convention, +role names are assumed to have this structure: _attrName.attrValue_, e.g., salesRegion.northeast. + +## Service Definition Configurations +In order to ensure that all new userStore entries are retained, there must be a single userStoreRetrieverClass +and a single userStore for all retrievers. + +**Options at the Context Enricher Level:** + +**userStoreRetrieverClassName** is the name of the context enricher that calls all subsequent retriever methods. +**userStoreRefresherPollingInterval** defines the interval at which the userStoreRefresher polls its source, seeking data changes since it was last refreshed. + +Within the options for this enricher, configurations for the individual retrievers are given in a special format. +The option key is "retrieverX_*sourceType*", where X is a sequential integer and sourceType is (currently) +either "api" or "role". The option value is a string containing configurations for the individual retrievers, as outlined above, +specified in a comma-separated Java Property-like format. + +Here is the relevant section of a **sample host plugin's service definition**. Two api retrievers and two role retrievers +are specified. + +```json +{ + "contextEnrichers": [ + { + "itemId": 1, + "name": "RangerMultiSourceUserStoreRetriever", + "enricher": "org.apache.ranger.plugin.contextenricher.RangerUserStoreEnricher", + "enricherOptions": { + "userStoreRetrieverClassName": "org.apache.ranger.plugin.contextenricher.externalretrievers.RangerMultiSourceUserStoreRetriever", + "userStoreRefresherPollingInterval": "60000", + "retriever0_api": "attrName=partner,userStoreURL=http://localhost:8000/security/getPartnersByUser", + "retriever1_api": "attrName=ownedResources,dataFile=/var/ranger/data/userOwnerResource.txt", + "retriever2_role": "attrName=salesRegion", + "retriever3_role": "attrName=sensitivityLevel" + } + } + ] +} +``` diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/RangerMultiSourceUserStoreRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/RangerMultiSourceUserStoreRetriever.java new file mode 100644 index 0000000000..7e24628148 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/contextenricher/externalretrievers/RangerMultiSourceUserStoreRetriever.java @@ -0,0 +1,365 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.contextenricher.externalretrievers; + +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.plugin.contextenricher.RangerUserStoreRetriever; +import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.RangerRolesUtil; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +// Options examples for individual retrievers: +// "retriever0_api": "attrName=partner,userStoreURL=http://localhost:8000/security/getPartnersByUser", +// "retriever1_role": "attrName=employee" + +public class RangerMultiSourceUserStoreRetriever extends RangerUserStoreRetriever { + private static final Logger LOG = LoggerFactory.getLogger(RangerMultiSourceUserStoreRetriever.class); + + private static final Pattern PATTERN_ROLE_RETRIEVER_NAME = Pattern.compile("\\d+_role"); + + private Map> retrieverOptions = Collections.emptyMap(); + private RangerAdminClient adminClient = null; + private RangerUserStore userStore = null; + private RangerRolesUtil rolesUtil = new RangerRolesUtil(new RangerRoles()); + + // options come from service-def + @Override + public void init(Map options) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> init(options={})", options); + } + + try { + retrieverOptions = toRetrieverOptions(options); + + if (hasAnyRoleRetriever()) { + adminClient = pluginContext.createAdminClient(pluginConfig); + } + } catch (Exception e) { + LOG.error("init() failed", e); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== init(options={})", options); + } + } + + @Override + public RangerUserStore retrieveUserStoreInfo(long lastKnownVersion, long lastActivationTimeInMillis) { + if (LOG.isDebugEnabled()) { + LOG.debug("=> retrieveUserStoreInfo(lastKnownVersion={}, lastActivationTimeInMillis={})", lastKnownVersion, lastActivationTimeInMillis); + } + + // if there are any role type retrievers, get rangerRoles; otherwise don't bother + if (adminClient != null) { + try { + RangerRoles roles = rolesUtil.getRoles(); + long rolesVersion = roles.getRoleVersion() != null ? roles.getRoleVersion() : -1; + RangerRoles updatedRoles = adminClient.getRolesIfUpdated(rolesVersion, lastActivationTimeInMillis); + + if (updatedRoles != null) { + rolesUtil = new RangerRolesUtil(updatedRoles); + } + } catch (Exception e) { + LOG.error("retrieveUserStoreInfo(lastKnownVersion={}) failed to retrieve roles", lastKnownVersion, e); + } + } + + Map> userAttrs = null; + + try { + userAttrs = retrieveAll(); + } catch (Exception e) { + LOG.error("retrieveUserStoreInfo(lastKnownVersion={}) failed", lastKnownVersion, e); + } + + if (userAttrs != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("retrieveUserStoreInfo(lastKnownVersion={}): user-attributes={}", lastKnownVersion, userAttrs); + } + + userStore = new RangerUserStore(); + + userStore.setUserStoreVersion(System.currentTimeMillis()); + userStore.setUserAttrMapping(userAttrs); + } else { + LOG.error("retrieveUserStoreInfo(lastKnownVersion={}): failed to retrieve user-attributes", lastKnownVersion); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== retrieveUserStoreInfo(lastKnownVersion={}, lastActivationTimeInMillis={}): ret={}", lastKnownVersion, lastActivationTimeInMillis, userStore); + } + + return userStore; + } + + private Map> toRetrieverOptions(Map enricherOptions) throws Exception { + if (LOG.isDebugEnabled()) { + LOG.debug("==> toRetrieverOptions({})", enricherOptions); + } + + Map> ret = new HashMap<>(); + + for (Map.Entry entry : enricherOptions.entrySet()) { + String retrieverName = entry.getKey(); + + if (retrieverName.startsWith("retriever")) { + String retrieverOptions = entry.getValue(); + + ret.put(retrieverName, toRetrieverOptions(retrieverName, retrieverOptions)); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== toRetrieverOptions({}): ret={}", enricherOptions, ret); + } + + return ret; + } + + // Managing options for various retrievals + private Map toRetrieverOptions(String name, String options) throws Exception { + if (LOG.isDebugEnabled()) { + LOG.debug("==> toRetrieverOptions(name={}, options={})", name, options); + } + + Properties prop = new Properties(); + + options = options.replaceAll("\\s", ""); + options = options.replaceAll(",", "\n"); + + try { + prop.load(new StringReader(options)); + } catch (Exception e) { + LOG.error("toRetrieverOptions(name={}, options={}): failed to parse retriever options", name, options, e); + + throw new Exception(name + ": failed to parse retriever options: " + options, e); + } + + Map ret = new HashMap<>(); + + for (String key : prop.stringPropertyNames()) { + ret.put(key, prop.getProperty(key)); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== toRetrieverOptions(name={}, options={}): ret={}", name, options, ret); + } + + return ret; + } + + private boolean hasAnyRoleRetriever() { + if (LOG.isDebugEnabled()) { + LOG.debug("==> hasAnyRoleRetriever()"); + } + + boolean ret = false; + + for (String retrieverName : retrieverOptions.keySet()) { + Matcher matcher = PATTERN_ROLE_RETRIEVER_NAME.matcher(retrieverName); + + if (matcher.find()) { + ret = true; + + break; + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== hasAnyRoleRetriever(): ret={}", ret); + } + + return ret; + } + + // top-level retrieval management + private Map> retrieveAll() throws Exception { + if (LOG.isDebugEnabled()) { + LOG.debug("==> retrieveAll()"); + } + + Map> ret = new HashMap<>(); + + for (Map.Entry> entry : retrieverOptions.entrySet()) { + String name = entry.getKey(); + Map options = entry.getValue(); + String source = name.replaceAll("\\w+_",""); + Map> userAttrs; + + switch (source) { + case "api": + userAttrs = retrieveUserAttributes(name, options); + break; + + case "role": + userAttrs = retrieveUserAttrFromRoles(name, options); + break; + + default: + throw new Exception("unrecognized retriever source '" + source + "'. Valid values: api, role"); + } + + mergeUserAttributes(userAttrs, ret); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== retrieveAll(): ret={}", ret); + } + + return ret; + } + + // external retrieval + private Map> retrieveUserAttributes(String retrieverName, Map options) throws Exception { + if (LOG.isDebugEnabled()) { + LOG.debug("==> retrieveUserAttributes(name={}, options={})", retrieverName, options); + } + + String attrName = options.get("attrName"); + String url = options.get("userStoreURL"); + String dataFile = options.get("dataFile"); + + if (attrName == null) { + throw new Exception(retrieverName + ": attrName must be specified in retriever options"); + } + + if (url == null && dataFile == null) { + throw new Exception(retrieverName + ": url or dataFile must be specified in retriever options"); + } + + Map> ret; + + if (url != null) { + GetFromURL gu = new GetFromURL(); + + String configFile = options.getOrDefault("configFile", "/var/ranger/security/" + attrName + ".conf"); + + if (LOG.isDebugEnabled()) { + LOG.debug("{}: configFile={}", retrieverName, configFile); + } + + ret = gu.getFromURL(url, configFile); // get user-Attrs mapping in UserStore format from an API call + + if (LOG.isDebugEnabled()) { + LOG.debug("loaded attribute {} from URL {}: {}", attrName, url, ret); + } + } else { + GetFromDataFile gf = new GetFromDataFile(); + + ret = gf.getFromDataFile(dataFile, attrName); + + if (LOG.isDebugEnabled()) { + LOG.debug("loaded attribute {} from file {}: {}", attrName, dataFile, ret); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== retrieveUserAttributes(name={}, options={}): ret={}", retrieverName, options, ret); + } + + return ret; + } + + // role-based retrieval + /** retrieveSingleRoleUserAttrMapping: + * + * @param options includes the attribute name of interest, from which to create the UserStore attribute name + * and to identify the role of interest. + * @return In UserStore format, maps from user to attrName to attribute values + * + * rangerRoles: one object for each role; contains set of users who are members. The important feature here + * * is that it maps roles to users. rolesUtil.getUserRoleMapping() returns the reverse: + * * maps users to roles that they are members of. This is closer to the UserStore format. + */ + public Map> retrieveUserAttrFromRoles(String retrieverName, Map options) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> retrieveUserAttrFromRoles(name={}, options={})", retrieverName, options); + } + + Map> ret = new HashMap<>(); + Map> userToRoles = rolesUtil.getUserRoleMapping(); + String attrName = options.get("attrName"); + String rolePrefix = attrName + "."; + Pattern pattern = Pattern.compile("^.*" + rolePrefix + ".*$"); + + for (Map.Entry> entry : userToRoles.entrySet()) { + String user = entry.getKey(); + Set roles = entry.getValue(); + List attrValues = new ArrayList<>(); + + for (String role : roles) { + Matcher matcher = pattern.matcher(role); + + if (matcher.find()) { + String value = matcher.group().replace(rolePrefix, ""); + + attrValues.add(value); + } + } + + if (!attrValues.isEmpty()) { + Map userAttrs = new HashMap<>(); + + userAttrs.put(attrName, String.join(",", attrValues)); + + ret.put(user, userAttrs); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== retrieveUserAttrFromRoles(name={}, options={}): ret={}", retrieverName, options, ret); + } + + return ret; + } + + private void mergeUserAttributes(Map> source, Map> dest) { + if (dest.size() == 0) { + dest.putAll(source); + } else { + for (Map.Entry> e : source.entrySet()) { + String userName = e.getKey(); + Map userAttrs = e.getValue(); + + if (dest.containsKey(userName)) { + Map existingAttrs = dest.get(userName); + + existingAttrs.putAll(userAttrs); + } else { + dest.put(userName, userAttrs); + } + } + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java b/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java index 971fd507b7..00855458d3 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/errors/ValidationErrorCode.java @@ -19,8 +19,8 @@ package org.apache.ranger.plugin.errors; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.text.MessageFormat; import java.util.Arrays; @@ -66,7 +66,7 @@ public enum ValidationErrorCode { SERVICE_DEF_VALIDATION_ERR_ENUM_DEF_INVALID_DEFAULT_INDEX(2019, "default index[{0}] for enum [{1}] is invalid"), SERVICE_DEF_VALIDATION_ERR_ENUM_DEF_NULL_ENUM_ELEMENT(2020, "An enum element in enum element collection of enum [{0}] is null"), SERVICE_DEF_VALIDATION_ERR_INVALID_SERVICE_RESOURCE_LEVELS(2021, "Resource-def levels are not in increasing order in an hierarchy"), - SERVICE_DEF_VALIDATION_ERR_NOT_LOWERCASE_NAME(2022, "{0}:[{1}] Invalid resource name. Resource name should consist of only lowercase, hyphen or underscore characters"), + SERVICE_DEF_VALIDATION_ERR_NOT_LOWERCASE_NAME(2022, "{0}:[{1}] Invalid resource name. Resource name should consist of only lowercase, hyphen or underscore characters"), SERVICE_DEF_VALIDATION_ERR_INVALID_MANADORY_VALUE_FOR_SERVICE_RESOURCE(2023, "{0} cannot be mandatory because {1}(parent) is not mandatory"), // POLICY VALIDATION @@ -104,13 +104,18 @@ public enum ValidationErrorCode { POLICY_VALIDATION_ERR_NONEXISTANT_ZONE_NAME(3033, "Non-existent Zone name={0} in policy create"), POLICY_VALIDATION_ERR_SERVICE_NOT_ASSOCIATED_TO_ZONE(3048, "Service name = {0} is not associated to Zone name = {1}"), POLICY_VALIDATION_ERR_UNSUPPORTED_POLICY_ITEM_TYPE(3049, "Deny or deny-exceptions are not supported if policy has isDenyAllElse flag set to true"), + POLICY_VALIDATION_ERR_NULL_POLICY_ITEM_USER(3053, "policy items user was null"), + POLICY_VALIDATION_ERR_NULL_POLICY_ITEM_GROUP(3054, "policy items group was null"), + POLICY_VALIDATION_ERR_NULL_POLICY_ITEM_ROLE(3055, "policy items role was null"), + POLICY_VALIDATION_ERR_DUPLICATE_VALUES_FOR_RESOURCE(3056, "Values for the resource={0} contained a duplicate value={1}. Ensure all values for a resource are unique"), + POLICY_VALIDATION_ERR_INVALID_SERVICE_TYPE(4009," Invalid service type [{0}] provided for service [{1}]"), // SECURITY_ZONE Validations SECURITY_ZONE_VALIDATION_ERR_UNSUPPORTED_ACTION(3034, "Internal error: unsupported action[{0}]; isValid() is only supported for DELETE"), SECURITY_ZONE_VALIDATION_ERR_MISSING_FIELD(3035, "Internal error: missing field[{0}]"), SECURITY_ZONE_VALIDATION_ERR_ZONE_NAME_CONFLICT(3036, "Another security zone already exists for this name: zone-id=[{0}]]"), SECURITY_ZONE_VALIDATION_ERR_INVALID_ZONE_ID(3037, "No security zone found for [{0}]"), - SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS(3038, "both users and user-groups collections for the security zone were null/empty"), + SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS_AND_ROLES(3038, "users, user-groups and roles collections for the security zone were null/empty"), SECURITY_ZONE_VALIDATION_ERR_MISSING_RESOURCES(3039, "No resources specified for service [{0}]"), SECURITY_ZONE_VALIDATION_ERR_INVALID_SERVICE_NAME(3040, "Invalid service [{0}]"), SECURITY_ZONE_VALIDATION_ERR_INVALID_SERVICE_TYPE(3041, "Invalid service-type [{0}]"), @@ -120,6 +125,7 @@ public enum ValidationErrorCode { SECURITY_ZONE_VALIDATION_ERR_INTERNAL_ERROR(3045, "Internal Error:[{0}]"), SECURITY_ZONE_VALIDATION_ERR_ZONE_RESOURCE_CONFLICT(3046, "Multiple zones:[{0}] match resource:[{1}]"), SECURITY_ZONE_VALIDATION_ERR_UNEXPECTED_RESOURCES(3047, "Tag service [{0}], with non-empty resources, is associated with security zone"), + SECURITY_ZONE_VALIDATION_ERR_DUPLICATE_RESOURCE_ENTRY(3052, "Resource [{0}] specified more than once in service [{1}]"), //RANGER ROLE Validations ROLE_VALIDATION_ERR_NULL_RANGER_ROLE_OBJECT(4001, "Internal error: RangerRole object passed in was null"), @@ -131,11 +137,42 @@ public enum ValidationErrorCode { ROLE_VALIDATION_ERR_INVALID_ROLE_NAME(4007, "No RangerRole found for name[{0}]"), ROLE_VALIDATION_ERR_UNSUPPORTED_ACTION(4008, "Internal error: method signature isValid(Long) is only supported for DELETE"), - + GDS_VALIDATION_ERR_NON_EXISTING_USER(4101, "User [{0}] does not exist"), + GDS_VALIDATION_ERR_NON_EXISTING_GROUP(4102, "Group [{0}] does not exist"), + GDS_VALIDATION_ERR_NON_EXISTING_ROLE(4103, "Role [{0}] does not exist"), + GDS_VALIDATION_ERR_NON_EXISTING_SERVICE(4104, "Service [{0}] does not exist"), + GDS_VALIDATION_ERR_NON_EXISTING_ZONE(4105, "Zone [{0}] does not exist"), + GDS_VALIDATION_ERR_NOT_ADMIN(4106, "User [{0}] is not an admin for {1} [{2}]"), + GDS_VALIDATION_ERR_SERVICE_NAME_MISSING(4107, "Service name not provided"), + GDS_VALIDATION_ERR_DATASET_NAME_CONFLICT(4108, "Dataset with name [{0}] already exists. ID=[{1}]"), + GDS_VALIDATION_ERR_DATASET_NAME_NOT_FOUND(4109, "Dataset with name [{0}] does not exist"), + GDS_VALIDATION_ERR_DATASET_ID_NOT_FOUND(4110, "Dataset with ID [{0}] does not exist"), + GDS_VALIDATION_ERR_PROJECT_NAME_CONFLICT(4111, "Project with name [{0}] already exists. ID=[{1}]"), + GDS_VALIDATION_ERR_PROJECT_NAME_NOT_FOUND(4112, "Project with name [{0}] does not exist"), + GDS_VALIDATION_ERR_PROJECT_ID_NOT_FOUND(4113, "Project with ID [{0}] does not exist"), + GDS_VALIDATION_ERR_DATA_SHARE_NAME_CONFLICT(4114, "Data share with name [{0}] already exists. ID=[{1}]"), + GDS_VALIDATION_ERR_DATA_SHARE_NAME_NOT_FOUND(4115, "Data share with name [{0}] does not exist"), + GDS_VALIDATION_ERR_DATA_SHARE_ID_NOT_FOUND(4116, "Data share with ID [{0}] does not exist"), + GDS_VALIDATION_ERR_DATA_SHARE_NOT_SERVICE_ADMIN(4117, "Not a admin for service [{0}]"), + GDS_VALIDATION_ERR_DATA_SHARE_NOT_SERVICE_OR_ZONE_ADMIN(4118, "Not a admin for service [{0}] or zone [{1}]"), + GDS_VALIDATION_ERR_INVALID_ACCESS_TYPE(4119, "Not a valid access-type [{0}]"), + GDS_VALIDATION_ERR_INVALID_MASK_TYPE(4120, "Not a valid mask-type [{0}]"), + GDS_VALIDATION_ERR_SHARED_RESOURCE_NAME_CONFLICT(4121, "Shared resource with name [{0}] already exists in data share [{1}]. ID=[{2}]"), + GDS_VALIDATION_ERR_SHARED_RESOURCE_ID_NOT_FOUND(4122, "Shared resource with ID [{0}] does not exist"), + GDS_VALIDATION_ERR_ADD_DATA_SHARE_IN_DATASET_INVALID_STATUS(4123, "[{0}]: invalid status while adding data share into a dataset"), + GDS_VALIDATION_ERR_DATA_SHARE_IN_DATASET_ID_NOT_FOUND(4124, "Data share-in-dataset with ID [{0}] does not exist"), + GDS_VALIDATION_ERR_INVALID_STATUS_CHANGE(4125, "invalid status change from [{0}] to [{1}]"), + GDS_VALIDATION_ERR_UPDATE_IMMUTABLE_FIELD(4126, "[{0}] can't be updated"), + GDS_VALIDATION_ERR_DATASET_IN_PROJECT_ID_NOT_FOUND(4127, "Dataset-in-project with ID [{0}] does not exist"), + GDS_VALIDATION_ERR_SHARED_RESOURCE_CONFLICT(4128, "Shared resource with resources [{0}] already exists for data share [{1}]"), + GDS_DATASET_NAME_TOO_LONG(4129, "Invalid dataset name=[{0}]. Dataset name should not be longer than 512 characters"), + GDS_DATASHARE_NAME_TOO_LONG(4130, "Invalid datashare name=[{0}]. Datashare name should not be longer than 512 characters"), + GDS_PROJECT_NAME_TOO_LONG(4131, "Invalid project name=[{0}]. Project name should not be longer than 512 characters"), + GDS_VALIDATION_ERR_SHARED_RESOURCE_RESOURCE_NULL(4132, "Resource value in SharedResource [{0}] is null"), ; - private static final Log LOG = LogFactory.getLog(ValidationErrorCode.class); + private static final Logger LOG = LoggerFactory.getLogger(ValidationErrorCode.class); final int _errorCode; final String _template; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/geo/GeolocationMetadata.java b/agents-common/src/main/java/org/apache/ranger/plugin/geo/GeolocationMetadata.java index 1142dccd45..80001c01e7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/geo/GeolocationMetadata.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/geo/GeolocationMetadata.java @@ -20,11 +20,11 @@ package org.apache.ranger.plugin.geo; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class GeolocationMetadata { - private static final Log LOG = LogFactory.getLog(GeolocationMetadata.class); + private static final Logger LOG = LoggerFactory.getLogger(GeolocationMetadata.class); private String[] locationDataItemNames = new String[0]; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationData.java b/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationData.java index 869d31678b..3ea88a34b8 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationData.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationData.java @@ -20,15 +20,15 @@ package org.apache.ranger.plugin.geo; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Objects; public class RangerGeolocationData implements Comparable, RangeChecker { - private static final Log LOG = LogFactory.getLog(RangerGeolocationData.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerGeolocationData.class); private static final Character IPSegmentsSeparator = '.'; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationDatabase.java b/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationDatabase.java index 98c55cc853..59e9d635a1 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationDatabase.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/geo/RangerGeolocationDatabase.java @@ -20,11 +20,11 @@ package org.apache.ranger.plugin.geo; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerGeolocationDatabase { - private static final Log LOG = LogFactory.getLog(RangerGeolocationDatabase.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerGeolocationDatabase.class); private BinarySearchTree data = new BinarySearchTree<>(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/geo/ValuePrinter.java b/agents-common/src/main/java/org/apache/ranger/plugin/geo/ValuePrinter.java index cc429a2d65..3ed0772b97 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/geo/ValuePrinter.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/geo/ValuePrinter.java @@ -20,13 +20,13 @@ package org.apache.ranger.plugin.geo; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.*; class ValuePrinter implements ValueProcessor { - private static final Log LOG = LogFactory.getLog(ValuePrinter.class); + private static final Logger LOG = LoggerFactory.getLogger(ValuePrinter.class); private Writer writer; private String fileName; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/AuditFilter.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/AuditFilter.java new file mode 100644 index 0000000000..f90f0af315 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/AuditFilter.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; + +import java.util.List; +import java.util.Map; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonAutoDetect(fieldVisibility=Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class AuditFilter { + public enum AccessResult { DENIED, ALLOWED, NOT_DETERMINED } + + private AccessResult accessResult; + private Map resources; + private List accessTypes; + private List actions; + private List users; + private List groups; + private List roles; + private Boolean isAudited; + + public AuditFilter() { } + + public AccessResult getAccessResult() { + return accessResult; + } + + public void setAccessResult(AccessResult accessResult) { + this.accessResult = accessResult; + } + + public Map getResources() { + return resources; + } + + public void setResources(Map resources) { + this.resources = resources; + } + + public List getAccessTypes() { + return accessTypes; + } + + public void setAccessTypes(List accessTypes) { + this.accessTypes = accessTypes; + } + + public List getActions() { + return actions; + } + + public void setActions(List actions) { + this.actions = actions; + } + + public List getUsers() { + return users; + } + + public void setUsers(List users) { + this.users = users; + } + + public List getGroups() { + return groups; + } + + public void setGroups(List groups) { + this.groups = groups; + } + + public List getRoles() { + return roles; + } + + public void setRoles(List roles) { + this.roles = roles; + } + + public Boolean getIsAudited() { + return isAudited; + } + + public void setAction(Boolean isAudited) { + this.isAudited = isAudited; + } + + @Override + public String toString() { + return "{accessResult=" + accessResult + + ", resources=" + resources + + ", accessTypes=" + accessTypes + + ", actions=" + actions + + ", users=" + users + + ", groups=" + groups + + ", roles=" + roles + + ", isAudited=" + isAudited + + "}"; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java index 5925014dae..e98eff9c86 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/GroupInfo.java @@ -20,21 +20,16 @@ package org.apache.ranger.plugin.model; import org.apache.ranger.plugin.util.RangerUserStoreUtil; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.util.HashMap; import java.util.Map; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class GroupInfo extends RangerBaseModelObject implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -84,4 +79,4 @@ public String toString() { + "}"; } -} \ No newline at end of file +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerBaseModelObject.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerBaseModelObject.java index 0273f7e8ff..72757d2763 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerBaseModelObject.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerBaseModelObject.java @@ -19,25 +19,31 @@ package org.apache.ranger.plugin.model; +import java.util.Collections; +import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.apache.ranger.authorization.utils.StringUtil; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(getterVisibility=Visibility.NONE, setterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL ) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerBaseModelObject implements java.io.Serializable { private static final long serialVersionUID = 1L; + public static final String NULL_SAFE_SUPPLIER_V2 = "v2"; + + private static NullSafeSupplier NULL_SAFE_SUPPLIER = NullSafeSupplierV1.INSTANCE; + private Long id; private String guid; private Boolean isEnabled; @@ -152,6 +158,59 @@ public void setVersion(Long version) { this.version = version; } + public void dedupStrings(Map strTbl) { + createdBy = StringUtil.dedupString(createdBy, strTbl); + updatedBy = StringUtil.dedupString(updatedBy, strTbl); + } + + public static List nullSafeList(List coll) { + return NULL_SAFE_SUPPLIER.toList(coll); + } + + public static Set nullSafeSet(Set coll) { + return NULL_SAFE_SUPPLIER.toSet(coll); + } + + public static Map nullSafeMap(Map coll) { + return NULL_SAFE_SUPPLIER.toMap(coll); + } + + public static List getUpdatableList(List curr) { + final List ret; + + if (curr instanceof ArrayList) { + ret = curr; + } else { + ret = curr != null ? new ArrayList<>(curr) : new ArrayList<>(); + } + + return ret; + } + + public static Set getUpdatableSet(Set curr) { + final Set ret; + + if (curr instanceof HashSet) { + ret = curr; + } else { + ret = curr != null ? new HashSet<>(curr) : new HashSet<>(); + } + + return ret; + } + + public static Map getUpdatableMap(Map curr) { + final Map ret; + + if (curr instanceof HashMap) { + ret = curr; + } else { + ret = curr != null ? new HashMap<>(curr) : new HashMap<>(); + } + + return ret; + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -173,4 +232,69 @@ public StringBuilder toString(StringBuilder sb) { return sb; } + + protected NullSafeSupplier getNullSafeSupplier() { return NULL_SAFE_SUPPLIER; } + + public static void setNullSafeSupplier(NullSafeSupplier supplier) { + NULL_SAFE_SUPPLIER = supplier == null ? NullSafeSupplierV1.INSTANCE : supplier; + } + + public static void setNullSafeSupplier(String supplier) { + if (NULL_SAFE_SUPPLIER_V2.equalsIgnoreCase(supplier)) { + NULL_SAFE_SUPPLIER = NullSafeSupplierV2.INSTANCE; + } else { + NULL_SAFE_SUPPLIER = NullSafeSupplierV1.INSTANCE; + } + } + + public static abstract class NullSafeSupplier { + public abstract List toList(List coll); + + public abstract Set toSet(Set coll); + + public abstract Map toMap(Map coll); + } + + + // each call creates a new collection object + // 1. for a null/empty collection, return a new collection object + // 2. for a non-null collection, return a copy of the collection + public static class NullSafeSupplierV1 extends NullSafeSupplier { + public static final NullSafeSupplierV1 INSTANCE = new NullSafeSupplierV1(); + + private NullSafeSupplierV1() { } + + public List toList(List coll) { + return (coll == null || coll.isEmpty()) ? new ArrayList<>() : new ArrayList<>(coll); + } + + public Set toSet(Set coll) { + return (coll == null || coll.isEmpty()) ? new HashSet<>() : new HashSet<>(coll); + } + + public Map toMap(Map coll) { + return (coll == null || coll.isEmpty()) ? new HashMap<>() : new HashMap<>(coll); + } + } + + // calls do not create collection objects + // 1. for a null/empty collection, return Collections.empty*() + // 2. for a non-null collection, return that collection itself + public static class NullSafeSupplierV2 extends NullSafeSupplier { + public static final NullSafeSupplierV2 INSTANCE = new NullSafeSupplierV2(); + + private NullSafeSupplierV2() { } + + public List toList(List coll) { + return (coll == null || coll.isEmpty()) ? Collections.emptyList() : coll; + } + + public Set toSet(Set coll) { + return (coll == null || coll.isEmpty()) ? Collections.emptySet() : coll; + } + + public Map toMap(Map coll) { + return (coll == null || coll.isEmpty()) ? Collections.emptyMap() : coll; + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerDatasetHeader.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerDatasetHeader.java new file mode 100644 index 0000000000..4641b119a3 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerDatasetHeader.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Map; + +public class RangerDatasetHeader { + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerDatasetHeaderInfo extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private Map dataSharesCountByStatus; + private Map principalsCountByType; + private Long projectsCount; + private String permissionForCaller; + private Long resourceCount; + + public RangerDatasetHeaderInfo() { + super(); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Map getDataSharesCountByStatus() { + return dataSharesCountByStatus; + } + + public void setDataSharesCountByStatus(Map dataSharesCountByStatus) { + this.dataSharesCountByStatus = dataSharesCountByStatus; + } + + public Map getPrincipalsCountByType() { + return principalsCountByType; + } + + public void setPrincipalsCountByType(Map principalsCountByType) { + this.principalsCountByType = principalsCountByType; + } + + public Long getProjectsCount() { + return projectsCount; + } + + public void setProjectsCount(Long projectsCount) { + this.projectsCount = projectsCount; + } + + public String getPermissionForCaller() { + return permissionForCaller; + } + + public void setPermissionForCaller(String permissionForCaller) { + this.permissionForCaller = permissionForCaller; + } + + public Long getResourceCount() { + return resourceCount; + } + + public void setResourceCount(Long resourceCount) { + this.resourceCount = resourceCount; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerGds.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerGds.java new file mode 100644 index 0000000000..68216fd45e --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerGds.java @@ -0,0 +1,886 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerPrincipal.PrincipalType; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class RangerGds { + + public enum GdsPermission { NONE, LIST, VIEW, AUDIT, POLICY_ADMIN, ADMIN } + + public enum GdsShareStatus { NONE, REQUESTED, GRANTED, DENIED, ACTIVE } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerGdsBaseModelObject extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String description; + private Map options; + private Map additionalInfo; + + + public RangerGdsBaseModelObject() { } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + public Map getOptions() { return options; } + + public void setOptions(Map options) { this.options = options; } + + public Map getAdditionalInfo() { return additionalInfo; } + + public void setAdditionalInfo(Map additionalInfo) { this.additionalInfo = additionalInfo; } + + @Override + public StringBuilder toString(StringBuilder sb) { + super.toString(sb); + + sb.append("description={").append(description).append("} ") + .append("options={").append(options).append("} ") + .append("additionalInfo={").append(additionalInfo).append("} "); + + return sb; + } + + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerDataset extends RangerGdsBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private RangerGdsObjectACL acl; + private RangerValiditySchedule validitySchedule; + private String termsOfUse; + private List labels; + private List keywords; + + public RangerDataset() { } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public RangerGdsObjectACL getAcl() { return acl; } + + public void setAcl(RangerGdsObjectACL acl) { this.acl = acl; } + + public RangerValiditySchedule getValiditySchedule() { return validitySchedule; } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { this.validitySchedule = validitySchedule; } + + public String getTermsOfUse() { return termsOfUse; } + + public void setTermsOfUse(String termsOfUse) { this.termsOfUse = termsOfUse; } + + public List getLabels() { + return labels; + } + + public void setLabels(List labels) { + this.labels = labels; + } + + public List getKeywords() { + return keywords; + } + + public void setKeywords(List keywords) { + this.keywords = keywords; + } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerDataset={"); + + super.toString(sb); + + sb.append("name={").append(name).append("} ") + .append("acl={").append(acl).append("} ") + .append("validitySchedule={").append(validitySchedule).append("} ") + .append("termsOfUse={").append(termsOfUse).append("} ") + .append("labels={").append(validitySchedule).append("} ") + .append("keywords={").append(termsOfUse).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerProject extends RangerGdsBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private RangerGdsObjectACL acl; + private RangerValiditySchedule validitySchedule; + private String termsOfUse; + + public RangerProject() { } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public RangerGdsObjectACL getAcl() { return acl; } + + public void setAcl(RangerGdsObjectACL acl) { this.acl = acl; } + + public RangerValiditySchedule getValiditySchedule() { return validitySchedule; } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { this.validitySchedule = validitySchedule; } + + public String getTermsOfUse() { return termsOfUse; } + + public void setTermsOfUse(String termsOfUse) { this.termsOfUse = termsOfUse; } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerProject={"); + + super.toString(sb); + + sb.append("name={").append(name).append("} ") + .append("acl={").append(acl).append("} ") + .append("validitySchedule={").append(validitySchedule).append("} ") + .append("termsOfUse={").append(termsOfUse).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerDataShare extends RangerGdsBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private RangerGdsObjectACL acl; + private String service; + private String zone; + private String conditionExpr; + private Set defaultAccessTypes; + private List defaultTagMasks; + private String termsOfUse; + + public RangerDataShare() { } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public RangerGdsObjectACL getAcl() { return acl; } + + public void setAcl(RangerGdsObjectACL acl) { this.acl = acl; } + + public String getService() { return service; } + + public void setService(String service) { this.service = service; } + + public String getZone() { return zone; } + + public void setZone(String zone) { this.zone = zone; } + + public String getConditionExpr() { return conditionExpr; } + + public void setConditionExpr(String conditionExpr) { this.conditionExpr = conditionExpr; } + + public Set getDefaultAccessTypes() { + return defaultAccessTypes; + } + + public void setDefaultAccessTypes(Set defaultAccessTypes) { + this.defaultAccessTypes = defaultAccessTypes; + } + + public List getDefaultTagMasks() { + return defaultTagMasks; + } + + public void setDefaultTagMasks(List defaultTagMasks) { + this.defaultTagMasks = defaultTagMasks; + } + + public String getTermsOfUse() { return termsOfUse; } + + public void setTermsOfUse(String termsOfUse) { this.termsOfUse = termsOfUse; } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerDataShare={"); + + super.toString(sb); + + sb.append("name={").append(name).append("} ") + .append("acl={").append(acl).append("} ") + .append("service={").append(service).append("} ") + .append("zone={").append(zone).append("} ") + .append("conditionExpr={").append(conditionExpr).append("} ") + .append("defaultAccessTypes={").append(defaultAccessTypes).append("} ") + .append("defaultTagMasks={").append(defaultTagMasks).append("} ") + .append("termsOfUse={").append(termsOfUse).append("} ") + .append("}"); + + return sb; + } + } + + public static class RangerSharedResource extends RangerGdsBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private Long dataShareId; + private Map resource; + private RangerPolicyResource subResource; + private String subResourceType; + private String conditionExpr; + private Set accessTypes; + private RangerPolicyItemRowFilterInfo rowFilter; + private Map subResourceMasks; + private Set profiles; + + public RangerSharedResource() { } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public Long getDataShareId() { return dataShareId; } + + public void setDataShareId(Long dataShareId) { this.dataShareId = dataShareId; } + + public Map getResource() { return resource; } + + public void setResource(Map resource) { this.resource = resource; } + + public RangerPolicyResource getSubResource() { return subResource; } + + public void setSubResource(RangerPolicyResource subResource) { this.subResource = subResource; } + + public String getSubResourceType() { return subResourceType; } + + public void setSubResourceType(String subResourceType) { this.subResourceType = subResourceType; } + + public String getConditionExpr() { return conditionExpr; } + + public void setConditionExpr(String conditionExpr) { this.conditionExpr = conditionExpr; } + + public Set getAccessTypes() { + return accessTypes; + } + + public void setAccessTypes(Set accessTypes) { + this.accessTypes = accessTypes; + } + + public RangerPolicyItemRowFilterInfo getRowFilter() { return rowFilter; } + + public void setRowFilter(RangerPolicyItemRowFilterInfo rowFilter) { this.rowFilter = rowFilter; } + + public Map getSubResourceMasks() { return subResourceMasks; } + + public void setSubResourceMasks(Map subResourceMasks) { this.subResourceMasks = subResourceMasks; } + + public Set getProfiles() { return profiles; } + + public void setProfiles(Set profiles) { this.profiles = profiles; } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerSharedResource={"); + + super.toString(sb); + + sb.append("name").append(name).append("} ") + .append("dataShareId={").append(dataShareId).append("} ") + .append("resource={").append(resource).append("} ") + .append("subResource={").append(subResource).append("} ") + .append("subResourceType={").append(subResourceType).append("} ") + .append("conditionExpr={").append(conditionExpr).append("} ") + .append("accessTypes={").append(accessTypes).append("} ") + .append("rowFilterInfo={").append(rowFilter).append("} ") + .append("subResourceMasks={").append(subResourceMasks).append("} ") + .append("profiles={").append(profiles).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerDataShareInDataset extends RangerGdsBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long dataShareId; + private Long datasetId; + private GdsShareStatus status; + private RangerValiditySchedule validitySchedule; + private Set profiles; + private String approver; + + + public RangerDataShareInDataset() { } + + public Long getDataShareId() { return dataShareId; } + + public void setDataShareId(Long dataShareId) { this.dataShareId = dataShareId; } + + public Long getDatasetId() { return datasetId; } + + public void setDatasetId(Long datasetId) { this.datasetId = datasetId; } + + public GdsShareStatus getStatus() { return status; } + + public void setStatus(GdsShareStatus status) { this.status = status; } + + public RangerValiditySchedule getValiditySchedule() { return validitySchedule; } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { this.validitySchedule = validitySchedule; } + + public Set getProfiles() { return profiles; } + + public void setProfiles(Set profiles) { this.profiles = profiles; } + + public String getApprover() { return approver; } + + public void setApprover(String approver) { this.approver = approver; } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerDataShareInDataset={"); + + super.toString(sb); + + sb.append("dataShareId={").append(dataShareId).append("} ") + .append("datasetId={").append(datasetId).append("} ") + .append("status={").append(status).append("} ") + .append("validitySchedule={").append(validitySchedule).append("} ") + .append("profiles={").append(profiles).append("} ") + .append("approver={").append(approver).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerDatasetInProject extends RangerGdsBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long datasetId; + private Long projectId; + private GdsShareStatus status; + private RangerValiditySchedule validitySchedule; + private Set profiles; + private String approver; + + + public RangerDatasetInProject() { } + + public Long getDatasetId() { return datasetId; } + + public void setDatasetId(Long datasetId) { this.datasetId = datasetId; } + + public Long getProjectId() { return projectId; } + + public void setProjectId(Long projectId) { this.projectId = projectId; } + + public GdsShareStatus getStatus() { return status; } + + public void setStatus(GdsShareStatus status) { this.status = status; } + + public RangerValiditySchedule getValiditySchedule() { return validitySchedule; } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { this.validitySchedule = validitySchedule; } + + public Set getProfiles() { return profiles; } + + public void setProfiles(Set profiles) { this.profiles = profiles; } + + public String getApprover() { return approver; } + + public void setApprover(String approver) { this.approver = approver; } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerDatasetInProject={"); + + super.toString(sb); + + sb.append("datasetGuid={").append(datasetId).append("} ") + .append("projectGuid={").append(projectId).append("} ") + .append("status={").append(status).append("} ") + .append("validitySchedule={").append(validitySchedule).append("} ") + .append("profiles={").append(profiles).append("} ") + .append("approver={").append(approver).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerGdsObjectACL implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Map users; + private Map groups; + private Map roles; + + + public RangerGdsObjectACL() { } + + public Map getUsers() { return users; } + + public void setUsers(Map users) { this.users = users; } + + public Map getGroups() { return groups; } + + public void setGroups(Map groups) { this.groups = groups; } + + public Map getRoles() { return roles; } + + public void setRoles(Map roles) { this.roles = roles; } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerGdsObjectACL={"); + + sb.append("users={").append(users).append("} ") + .append("groups={").append(groups).append("} ") + .append("roles={").append(roles).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + @XmlRootElement + @XmlAccessorType(XmlAccessType.FIELD) + public static class RangerTagDataMaskInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String tagName; + private String conditionExpr; + private RangerPolicyItemDataMaskInfo maskInfo; + + public RangerTagDataMaskInfo() { } + + public String getTagName() { + return tagName; + } + + public void setTagName(String tagName) { + this.tagName = tagName; + } + + public String getConditionExpr() { + return conditionExpr; + } + + public void setConditionExpr(String conditionExpr) { + this.conditionExpr = conditionExpr; + } + + public RangerPolicyItemDataMaskInfo getMaskInfo() { + return maskInfo; + } + + public void setMaskInfo(RangerPolicyItemDataMaskInfo maskInfo) { + this.maskInfo = maskInfo; + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerTagDataMaskInfo={"); + + sb.append("tagName={").append(tagName).append("} ") + .append("conditionExpr={").append(conditionExpr).append("} ") + .append("maskInfo={").append(maskInfo).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DatasetSummary extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private GdsPermission permissionForCaller; + private Map principalsCount; + private Map aclPrincipalsCount; + private Long projectsCount; + private Long totalResourceCount; + private List dataShares; + private RangerValiditySchedule validitySchedule; + private List labels; + private List keywords; + + public DatasetSummary() { + super(); + } + + public String getName() { + return name; + } + + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + public GdsPermission getPermissionForCaller() { + return permissionForCaller; + } + + public void setPermissionForCaller(GdsPermission permissionForCaller) { + this.permissionForCaller = permissionForCaller; + } + + public Map getPrincipalsCount() { + return principalsCount; + } + + public void setPrincipalsCount(Map principalsCount) { + this.principalsCount = principalsCount; + } + + public Long getProjectsCount() { + return projectsCount; + } + + public void setProjectsCount(Long projectsCount) { + this.projectsCount = projectsCount; + } + + public Long getTotalResourceCount() { + return totalResourceCount; + } + + public void setTotalResourceCount(Long totalResourceCount) { + this.totalResourceCount = totalResourceCount; + } + + public List getDataShares() { + return dataShares; + } + + public void setDataShares(List dataShares) { + this.dataShares = dataShares; + } + + public Map getAclPrincipalsCount() { + return aclPrincipalsCount; + } + + public void setAclPrincipalsCount(Map aclPrincipalsCount) { + this.aclPrincipalsCount = aclPrincipalsCount; + } + + public RangerValiditySchedule getValiditySchedule() { + return validitySchedule; + } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { + this.validitySchedule = validitySchedule; + } + + public List getLabels() { + return labels; + } + + public void setLabels(List labels) { + this.labels = labels; + } + + public List getKeywords() { + return keywords; + } + + public void setKeywords(List keywords) { + this.keywords = keywords; + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DatasetSummary={"); + + super.toString(sb); + + sb.append("name={").append(name).append("} ") + .append("description={").append(description).append("} ") + .append("permissionForCaller={").append(permissionForCaller).append("} ") + .append("principalsCount={").append(principalsCount).append("} ") + .append("projectsCount={").append(projectsCount).append("} ") + .append("aclPrincipalsCount={").append(aclPrincipalsCount).append("} ") + .append("totalResourceCount={").append(totalResourceCount).append("} ") + .append("dataShares={").append(dataShares).append("} ") + .append("validitySchedule={").append(totalResourceCount).append("} ") + .append("labels={").append(totalResourceCount).append("} ") + .append("keywords={").append(totalResourceCount).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DataShareSummary extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private GdsPermission permissionForCaller; + private Long resourceCount; + private Long serviceId; + private String serviceName; + private String serviceType; + private Long zoneId; + private String zoneName; + private List datasets; + + public DataShareSummary() { + super(); + } + + public String getName() { return name; } + + public void setName(String name) { this.name = name; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + public GdsPermission getPermissionForCaller() { return permissionForCaller; } + + public void setPermissionForCaller(GdsPermission permissionForCaller) { this.permissionForCaller = permissionForCaller; } + + public Long getResourceCount() { return resourceCount; } + + public void setResourceCount(Long resourceCount) { this.resourceCount = resourceCount; } + + public Long getServiceId() { return serviceId; } + + public void setServiceId(Long serviceId) { this.serviceId = serviceId; } + + public String getServiceName() { return serviceName; } + + public void setServiceName(String serviceName) { this.serviceName = serviceName; } + + public String getServiceType() { return serviceType; } + + public void setServiceType(String serviceType) { this.serviceType = serviceType; } + + public Long getZoneId() { return zoneId; } + + public void setZoneId(Long zoneId) { this.zoneId = zoneId; } + + public String getZoneName() { return zoneName; } + + public void setZoneName(String zoneName) { this.zoneName = zoneName; } + + public List getDatasets() { return datasets; } + + public void setDatasets(List datasets) { this.datasets = datasets; } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DataShareSummary={"); + + super.toString(sb); + + sb.append("name={").append(name).append("} ") + .append("description={").append(description).append("} ") + .append("permissionForCaller={").append(permissionForCaller).append("} ") + .append("resourceCount={").append(resourceCount).append("} ") + .append("serviceId={").append(serviceId).append("} ") + .append("serviceName={").append(serviceName).append("} ") + .append("serviceType={").append(serviceType).append("} ") + .append("zoneName={").append(zoneName).append("} ") + .append("zoneId={").append(zoneId).append("} ") + .append("datasets={").append(datasets).append("} ") + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown = true) + public static class DataShareInDatasetSummary extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long datasetId; + private String datasetName; + private Long dataShareId; + private String dataShareName; + private Long serviceId; + private String serviceName; + private Long zoneId; + private String zoneName; + private Long resourceCount; + private GdsShareStatus shareStatus; + private String approver; + + public DataShareInDatasetSummary() { + super(); + } + + public String getDatasetName() { return datasetName; } + + public void setDatasetName(String datasetName) { this.datasetName = datasetName; } + + public Long getDatasetId() { return datasetId; } + + public void setDatasetId(Long datasetId) { this.datasetId = datasetId; } + + public Long getDataShareId() { return dataShareId; } + + public void setDataShareId(Long dataShareId) { this.dataShareId = dataShareId; } + + public String getDataShareName() { return dataShareName; } + + public void setDataShareName(String dataShareName) { this.dataShareName = dataShareName; } + + public Long getServiceId() { return serviceId; } + + public void setServiceId(Long serviceId) { this.serviceId = serviceId; } + + public String getServiceName() { return serviceName; } + + public void setServiceName(String serviceName) { this.serviceName = serviceName; } + + public Long getZoneId() { return zoneId; } + + public void setZoneId(Long zoneId) { this.zoneId = zoneId; } + + public String getZoneName() { return zoneName; } + + public void setZoneName(String zoneName) { this.zoneName = zoneName; } + + public Long getResourceCount() { return resourceCount; } + + public void setResourceCount(Long resourceCount) { this.resourceCount = resourceCount; } + + public GdsShareStatus getShareStatus() { return shareStatus; } + + public void setShareStatus(GdsShareStatus shareStatus) { this.shareStatus = shareStatus; } + + public String getApprover() { return approver; } + + public void setApprover(String approver) { this.approver = approver; } + + @Override + public String toString() {return toString(new StringBuilder()).toString(); } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DataShareInDatasetSummary={"); + + super.toString(sb); + + sb.append("name={").append(datasetName).append("} ") + .append("datasetId={").append(datasetId).append("} ") + .append("datasetName={").append(datasetName).append("} ") + .append("dataShareId={").append(dataShareId).append("} ") + .append("dataShareName={").append(dataShareName).append("} ") + .append("serviceId={").append(serviceId).append("} ") + .append("serviceName={").append(serviceName).append("} ") + .append("zoneId={").append(zoneId).append("} ") + .append("zoneName={").append(zoneName).append("} ") + .append("resourceCount={").append(resourceCount).append("} ") + .append("shareStatus={").append(shareStatus).append("} ") + .append("approver={").append(approver).append("} ") + .append("}"); + + return sb; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerGrant.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerGrant.java new file mode 100644 index 0000000000..a8c63b759c --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerGrant.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.List; +import java.util.Objects; + +@JsonAutoDetect(getterVisibility= JsonAutoDetect.Visibility.NONE, setterVisibility= JsonAutoDetect.Visibility.NONE, fieldVisibility= JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerGrant implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private RangerPrincipal principal; + private List accessTypes; + private List conditions; + + public RangerGrant() { + this(null, null, null); + } + + public RangerGrant(RangerPrincipal principal, List accessTypes, List conditions) { + this.principal = principal; + this.accessTypes = accessTypes; + this.conditions = conditions; + } + + public RangerPrincipal getPrincipal() { + return principal; + } + + public void setPrincipal(RangerPrincipal principal) { + this.principal = principal; + } + + public List getAccessTypes() { + return accessTypes; + } + + public void setAccessTypes(List accessTypes) { + this.accessTypes = accessTypes; + } + + public List getConditions() { + return conditions; + } + + public void setConditions(List conditions) { + this.conditions = conditions; + } + + @Override + public int hashCode() { + return Objects.hash(principal, accessTypes, conditions); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } + + RangerGrant other = (RangerGrant) obj; + + return Objects.equals(principal, other.principal) && + Objects.equals(accessTypes, other.accessTypes) && + Objects.equals(conditions, other.conditions); + } + + @Override + public String toString() { + return "RangerGrant{" + + "principal='" + principal.toString() + + ", accessTypes=" + accessTypes + + ", conditions=" + conditions + + '}'; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerMetrics.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerMetrics.java index 2ee756f006..3584d6c739 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerMetrics.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerMetrics.java @@ -21,20 +21,14 @@ import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerMetrics { private Map data; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java index 5b92566201..8d46b99f7c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPluginInfo.java @@ -20,24 +20,21 @@ package org.apache.ranger.plugin.model; import org.apache.commons.lang.StringUtils; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnore; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + + import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerPluginInfo implements Serializable { private static final long serialVersionUID = 1L; @@ -45,11 +42,13 @@ public class RangerPluginInfo implements Serializable { public static final int ENTITY_TYPE_TAGS = 1; public static final int ENTITY_TYPE_ROLES = 2; public static final int ENTITY_TYPE_USERSTORE = 3; + public static final int ENTITY_TYPE_GDS = 4; public static final String PLUGIN_INFO_POLICY_DOWNLOAD_TIME = "policyDownloadTime"; public static final String PLUGIN_INFO_POLICY_DOWNLOADED_VERSION = "policyDownloadedVersion"; public static final String PLUGIN_INFO_POLICY_ACTIVATION_TIME = "policyActivationTime"; public static final String PLUGIN_INFO_POLICY_ACTIVE_VERSION = "policyActiveVersion"; + public static final String PLUGIN_INFO_TAG_DOWNLOAD_TIME = "tagDownloadTime"; public static final String PLUGIN_INFO_TAG_DOWNLOADED_VERSION = "tagDownloadedVersion"; public static final String PLUGIN_INFO_TAG_ACTIVATION_TIME = "tagActivationTime"; @@ -65,10 +64,17 @@ public class RangerPluginInfo implements Serializable { public static final String PLUGIN_INFO_USERSTORE_ACTIVATION_TIME = "userstoreActivationTime"; public static final String PLUGIN_INFO_USERSTORE_ACTIVE_VERSION = "userstoreActiveVersion"; + public static final String PLUGIN_INFO_GDS_DOWNLOAD_TIME = "gdsDownloadTime"; + public static final String PLUGIN_INFO_GDS_DOWNLOADED_VERSION = "gdsDownloadedVersion"; + public static final String PLUGIN_INFO_GDS_ACTIVATION_TIME = "gdsActivationTime"; + public static final String PLUGIN_INFO_GDS_ACTIVE_VERSION = "gdsActiveVersion"; + public static final String RANGER_ADMIN_LAST_POLICY_UPDATE_TIME = "lastPolicyUpdateTime"; public static final String RANGER_ADMIN_LATEST_POLICY_VERSION = "latestPolicyVersion"; public static final String RANGER_ADMIN_LAST_TAG_UPDATE_TIME = "lastTagUpdateTime"; public static final String RANGER_ADMIN_LATEST_TAG_VERSION = "latestTagVersion"; + public static final String RANGER_ADMIN_LAST_GDS_UPDATE_TIME = "lastGdsUpdateTime"; + public static final String RANGER_ADMIN_LATEST_GDS_VERSION = "latestGdsVersion"; public static final String RANGER_ADMIN_CAPABILITIES = "adminCapabilities"; public static final String PLUGIN_INFO_CAPABILITIES = "pluginCapabilities"; @@ -345,7 +351,7 @@ public void setRoleActiveVersion(Long roleActiveVersion) { @JsonIgnore public Long getRoleActiveVersion() { - String activeVersionString = getInfo().get(PLUGIN_INFO_POLICY_ACTIVE_VERSION); + String activeVersionString = getInfo().get(PLUGIN_INFO_ROLE_ACTIVE_VERSION); return StringUtils.isNotBlank(activeVersionString) ? Long.valueOf(activeVersionString) : null; } @@ -393,6 +399,50 @@ public Long getUserStoreActiveVersion() { return StringUtils.isNotBlank(activeVersionString) ? Long.valueOf(activeVersionString) : null; } + @JsonIgnore + public void setGdsDownloadTime(Long gdsDownloadTime) { + getInfo().put(PLUGIN_INFO_GDS_DOWNLOAD_TIME, gdsDownloadTime == null ? null : Long.toString(gdsDownloadTime)); + } + + @JsonIgnore + public Long getGdsDownloadTime() { + String downloadTimeString = getInfo().get(PLUGIN_INFO_GDS_DOWNLOAD_TIME); + return StringUtils.isNotBlank(downloadTimeString) ? Long.valueOf(downloadTimeString) : null; + } + + @JsonIgnore + public void setGdsDownloadedVersion(Long gdsDownloadedVersion) { + getInfo().put(PLUGIN_INFO_GDS_DOWNLOADED_VERSION, gdsDownloadedVersion == null ? null : Long.toString(gdsDownloadedVersion)); + } + + @JsonIgnore + public Long getGdsDownloadedVersion() { + String downloadedVersionString = getInfo().get(PLUGIN_INFO_GDS_DOWNLOADED_VERSION); + return StringUtils.isNotBlank(downloadedVersionString) ? Long.valueOf(downloadedVersionString) : null; + } + + @JsonIgnore + public void setGdsActivationTime(Long gdsActivationTime) { + getInfo().put(PLUGIN_INFO_GDS_ACTIVATION_TIME, gdsActivationTime == null ? null : Long.toString(gdsActivationTime)); + } + + @JsonIgnore + public Long getGdsActivationTime() { + String activationTimeString = getInfo().get(PLUGIN_INFO_GDS_ACTIVATION_TIME); + return StringUtils.isNotBlank(activationTimeString) ? Long.valueOf(activationTimeString) : null; + } + + @JsonIgnore + public void setGdsActiveVersion(Long gdsActiveVersion) { + getInfo().put(PLUGIN_INFO_GDS_ACTIVE_VERSION, gdsActiveVersion == null ? null : Long.toString(gdsActiveVersion)); + } + + @JsonIgnore + public Long getGdsActiveVersion() { + String activeVersionString = getInfo().get(PLUGIN_INFO_GDS_ACTIVE_VERSION); + return StringUtils.isNotBlank(activeVersionString) ? Long.valueOf(activeVersionString) : null; + } + @JsonIgnore public void setPluginCapabilities(String capabilities) { setCapabilities(PLUGIN_INFO_CAPABILITIES, capabilities); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java index 04c6e75c49..279ff69b49 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicy.java @@ -20,31 +20,29 @@ package org.apache.ranger.plugin.model; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.List; -import java.util.HashMap; import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - import org.apache.commons.collections.CollectionUtils; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.apache.commons.collections.MapUtils; +import org.apache.ranger.authorization.utils.StringUtil; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerPolicy extends RangerBaseModelObject implements java.io.Serializable { public static final int POLICY_TYPE_ACCESS = 0; public static final int POLICY_TYPE_DATAMASK = 1; public static final int POLICY_TYPE_ROWFILTER = 2; + public static final int POLICY_TYPE_AUDIT = 3; public static final int[] POLICY_TYPES = new int[] { POLICY_TYPE_ACCESS, @@ -67,27 +65,28 @@ public class RangerPolicy extends RangerBaseModelObject implements java.io.Seria // For future use private static final long serialVersionUID = 1L; - private String service; - private String name; - private Integer policyType; - private Integer policyPriority; - private String description; - private String resourceSignature; - private Boolean isAuditEnabled; - private Map resources; - private List conditions; - private List policyItems; - private List denyPolicyItems; - private List allowExceptions; - private List denyExceptions; - private List dataMaskPolicyItems; - private List rowFilterPolicyItems; - private String serviceType; - private Map options; - private List validitySchedules; - private List policyLabels; - private String zoneName; - private Boolean isDenyAllElse; + private String service; + private String name; + private Integer policyType; + private Integer policyPriority; + private String description; + private String resourceSignature; + private Boolean isAuditEnabled; + private Map resources; + private List> additionalResources; + private List conditions; + private List policyItems; + private List denyPolicyItems; + private List allowExceptions; + private List denyExceptions; + private List dataMaskPolicyItems; + private List rowFilterPolicyItems; + private String serviceType; + private Map options; + private List validitySchedules; + private List policyLabels; + private String zoneName; + private Boolean isDenyAllElse; public RangerPolicy() { this(null, null, null, null, null, null, null, null, null, null, null); @@ -137,7 +136,6 @@ public RangerPolicy(String service, String name, Integer policyType, Integer pol setZoneName(zoneName); setConditions(conditions); setIsDenyAllElse(isDenyAllElse); - } /** @@ -154,17 +152,19 @@ public void updateFrom(RangerPolicy other) { setResourceSignature(other.getResourceSignature()); setIsAuditEnabled(other.getIsAuditEnabled()); setResources(other.getResources()); + setAdditionalResources(other.getAdditionalResources()); + setConditions(other.getConditions()); setPolicyItems(other.getPolicyItems()); setDenyPolicyItems(other.getDenyPolicyItems()); setAllowExceptions(other.getAllowExceptions()); setDenyExceptions(other.getDenyExceptions()); setDataMaskPolicyItems(other.getDataMaskPolicyItems()); setRowFilterPolicyItems(other.getRowFilterPolicyItems()); + setServiceType(other.getServiceType()); setOptions(other.getOptions()); setValiditySchedules(other.getValiditySchedules()); setPolicyLabels(other.getPolicyLabels()); setZoneName(other.getZoneName()); - setConditions(other.getConditions()); setIsDenyAllElse(other.getIsDenyAllElse()); } @@ -279,19 +279,13 @@ public List getPolicyLabels() { } public void setPolicyLabels(List policyLabels) { - if (this.policyLabels == null) { - this.policyLabels = new ArrayList<>(); - } - - if (this.policyLabels == policyLabels) { - return; - } + this.policyLabels = nullSafeList(policyLabels); + } - this.policyLabels.clear(); + public boolean addPolicyLabel(String policyLabel) { + policyLabels = getUpdatableList(policyLabels); - if (policyLabels != null) { - this.policyLabels.addAll(policyLabels); - } + return policyLabels.add(policyLabel); } /** @@ -305,19 +299,31 @@ public Map getResources() { * @param resources the resources to set */ public void setResources(Map resources) { - if(this.resources == null) { - this.resources = new HashMap<>(); - } + this.resources = nullSafeMap(resources); + } - if(this.resources == resources) { - return; - } + public RangerPolicyResource setResource(String resourceName, RangerPolicyResource value) { + resources = getUpdatableMap(resources); - this.resources.clear(); + return resources.put(resourceName, value); + } - if(resources != null) { - for(Map.Entry e : resources.entrySet()) { - this.resources.put(e.getKey(), e.getValue()); + public List> getAdditionalResources() { + return additionalResources; + } + + public void setAdditionalResources(List> additionalResources) { + this.additionalResources = additionalResources; + } + + public void addResource(Map resources) { + if (MapUtils.isNotEmpty(resources)) { + if (MapUtils.isEmpty(this.resources)) { + this.resources = nullSafeMap(resources); + } else { + this.additionalResources = getUpdatableList(this.additionalResources); + + this.additionalResources.add(resources); } } } @@ -333,21 +339,14 @@ public List getPolicyItems() { * @param policyItems the policyItems to set */ public void setPolicyItems(List policyItems) { - if(this.policyItems == null) { - this.policyItems = new ArrayList<>(); - } - - if(this.policyItems == policyItems) { - return; - } + this.policyItems = nullSafeList(policyItems); + } - this.policyItems.clear(); + public boolean addPolicyItem(RangerPolicyItem policyItem) { + policyItems = getUpdatableList(policyItems); - if(policyItems != null) { - this.policyItems.addAll(policyItems); - } + return policyItems.add(policyItem); } - /** * @return the denyPolicyItems */ @@ -359,19 +358,13 @@ public List getDenyPolicyItems() { * @param denyPolicyItems the denyPolicyItems to set */ public void setDenyPolicyItems(List denyPolicyItems) { - if(this.denyPolicyItems == null) { - this.denyPolicyItems = new ArrayList<>(); - } - - if(this.denyPolicyItems == denyPolicyItems) { - return; - } + this.denyPolicyItems = nullSafeList(denyPolicyItems); + } - this.denyPolicyItems.clear(); + public boolean addDenyPolicyItem(RangerPolicyItem policyItem) { + denyPolicyItems = getUpdatableList(denyPolicyItems); - if(denyPolicyItems != null) { - this.denyPolicyItems.addAll(denyPolicyItems); - } + return denyPolicyItems.add(policyItem); } /** @@ -385,19 +378,13 @@ public List getAllowExceptions() { * @param allowExceptions the allowExceptions to set */ public void setAllowExceptions(List allowExceptions) { - if(this.allowExceptions == null) { - this.allowExceptions = new ArrayList<>(); - } - - if(this.allowExceptions == allowExceptions) { - return; - } + this.allowExceptions = nullSafeList(allowExceptions); + } - this.allowExceptions.clear(); + public boolean addAllowException(RangerPolicyItem policyItem) { + allowExceptions = getUpdatableList(allowExceptions); - if(allowExceptions != null) { - this.allowExceptions.addAll(allowExceptions); - } + return allowExceptions.add(policyItem); } /** @@ -411,19 +398,13 @@ public List getDenyExceptions() { * @param denyExceptions the denyExceptions to set */ public void setDenyExceptions(List denyExceptions) { - if(this.denyExceptions == null) { - this.denyExceptions = new ArrayList<>(); - } - - if(this.denyExceptions == denyExceptions) { - return; - } + this.denyExceptions = nullSafeList(denyExceptions); + } - this.denyExceptions.clear(); + public boolean addDenyException(RangerPolicyItem policyItem) { + denyExceptions = getUpdatableList(denyExceptions); - if(denyExceptions != null) { - this.denyExceptions.addAll(denyExceptions); - } + return denyExceptions.add(policyItem); } public List getDataMaskPolicyItems() { @@ -431,19 +412,13 @@ public List getDataMaskPolicyItems() { } public void setDataMaskPolicyItems(List dataMaskPolicyItems) { - if(this.dataMaskPolicyItems == null) { - this.dataMaskPolicyItems = new ArrayList<>(); - } - - if(this.dataMaskPolicyItems == dataMaskPolicyItems) { - return; - } + this.dataMaskPolicyItems = nullSafeList(dataMaskPolicyItems); + } - this.dataMaskPolicyItems.clear(); + public boolean addDataMaskPolicyItem(RangerDataMaskPolicyItem policyItem) { + dataMaskPolicyItems = getUpdatableList(dataMaskPolicyItems); - if(dataMaskPolicyItems != null) { - this.dataMaskPolicyItems.addAll(dataMaskPolicyItems); - } + return dataMaskPolicyItems.add(policyItem); } public List getRowFilterPolicyItems() { @@ -451,54 +426,33 @@ public List getRowFilterPolicyItems() { } public void setRowFilterPolicyItems(List rowFilterPolicyItems) { - if(this.rowFilterPolicyItems == null) { - this.rowFilterPolicyItems = new ArrayList<>(); - } - - if(this.rowFilterPolicyItems == rowFilterPolicyItems) { - return; - } + this.rowFilterPolicyItems = nullSafeList(rowFilterPolicyItems); + } - this.rowFilterPolicyItems.clear(); + public boolean addRowFilterPolicyItem(RangerRowFilterPolicyItem policyItem) { + rowFilterPolicyItems = getUpdatableList(rowFilterPolicyItems); - if(rowFilterPolicyItems != null) { - this.rowFilterPolicyItems.addAll(rowFilterPolicyItems); - } + return rowFilterPolicyItems.add(policyItem); } public Map getOptions() { return options; } public void setOptions(Map options) { - if (this.options == null) { - this.options = new HashMap<>(); - } - if (this.options == options) { - return; - } - this.options.clear(); - - if(options != null) { - for(Map.Entry e : options.entrySet()) { - this.options.put(e.getKey(), e.getValue()); - } - } + this.options = nullSafeMap(options); } public List getValiditySchedules() { return validitySchedules; } public void setValiditySchedules(List validitySchedules) { - if (this.validitySchedules == null) { - this.validitySchedules = new ArrayList<>(); - } - if (this.validitySchedules == validitySchedules) { - return; - } - this.validitySchedules.clear(); - - if(validitySchedules != null) { - this.validitySchedules.addAll(validitySchedules); - } + this.validitySchedules = nullSafeList(validitySchedules); } + + public boolean addValiditySchedule(RangerValiditySchedule validitySchedule) { + validitySchedules = getUpdatableList(validitySchedules); + + return validitySchedules.add(validitySchedule); + } + public String getZoneName() { return zoneName; } public void setZoneName(String zoneName) { @@ -516,6 +470,12 @@ public void setConditions(List conditions) { this.conditions = conditions; } + public boolean addCondition(RangerPolicyItemCondition condition) { + conditions = getUpdatableList(conditions); + + return conditions.add(condition); + } + public Boolean getIsDenyAllElse() { return isDenyAllElse; } @@ -524,6 +484,71 @@ public void setIsDenyAllElse(Boolean isDenyAllElse) { this.isDenyAllElse = isDenyAllElse == null ? Boolean.FALSE : isDenyAllElse; } + public void dedupStrings(Map strTbl) { + super.dedupStrings(strTbl); + + service = StringUtil.dedupString(service, strTbl); + serviceType = StringUtil.dedupString(serviceType, strTbl); + zoneName = StringUtil.dedupString(zoneName, strTbl); + name = StringUtil.dedupString(name, strTbl); + description = StringUtil.dedupString(description, strTbl); + policyLabels = StringUtil.dedupStringsList(policyLabels, strTbl); + resources = StringUtil.dedupStringsMapOfPolicyResource(resources, strTbl); + options = StringUtil.dedupStringsMapOfObject(options, strTbl); + + if (CollectionUtils.isNotEmpty(additionalResources)) { + List> updated = new ArrayList<>(additionalResources.size()); + + for (Map additionalResource : additionalResources) { + updated.add(StringUtil.dedupStringsMapOfPolicyResource(additionalResource, strTbl)); + } + + additionalResources = updated; + } + + if (CollectionUtils.isNotEmpty(conditions)) { + for (RangerPolicyItemCondition condition : conditions) { + condition.dedupStrings(strTbl); + } + } + + if (CollectionUtils.isNotEmpty(policyItems)) { + for (RangerPolicyItem policyItem : policyItems) { + policyItem.dedupStrings(strTbl); + } + } + + if (CollectionUtils.isNotEmpty(denyPolicyItems)) { + for (RangerPolicyItem policyItem : denyPolicyItems) { + policyItem.dedupStrings(strTbl); + } + } + + if (CollectionUtils.isNotEmpty(allowExceptions)) { + for (RangerPolicyItem policyItem : allowExceptions) { + policyItem.dedupStrings(strTbl); + } + } + + if (CollectionUtils.isNotEmpty(denyExceptions)) { + for (RangerPolicyItem policyItem : denyExceptions) { + policyItem.dedupStrings(strTbl); + } + } + + if (CollectionUtils.isNotEmpty(dataMaskPolicyItems)) { + for (RangerPolicyItem policyItem : dataMaskPolicyItems) { + policyItem.dedupStrings(strTbl); + } + } + + if (CollectionUtils.isNotEmpty(rowFilterPolicyItems)) { + for (RangerPolicyItem policyItem : rowFilterPolicyItems) { + policyItem.dedupStrings(strTbl); + } + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -556,6 +581,19 @@ public StringBuilder toString(StringBuilder sb) { } } sb.append("} "); + sb.append("additionalResources={"); + if(additionalResources != null) { + for(Map additionalResource : additionalResources) { + sb.append("{"); + for(Map.Entry e : additionalResource.entrySet()) { + sb.append(e.getKey()).append("={"); + e.getValue().toString(sb); + sb.append("} "); + } + sb.append("} "); + } + } + sb.append("} "); sb.append("policyLabels={"); if(policyLabels != null) { for(String policyLabel : policyLabels) { @@ -648,7 +686,7 @@ public StringBuilder toString(StringBuilder sb) { //sb.append("validitySchedules={").append(validitySchedules).append("} "); sb.append("validitySchedules={"); - if (CollectionUtils.isNotEmpty(validitySchedules)) { + if (validitySchedules != null) { for (RangerValiditySchedule schedule : validitySchedules) { if (schedule != null) { sb.append("schedule={").append(schedule).append("}"); @@ -674,10 +712,8 @@ public int compare(RangerPolicy me, RangerPolicy other) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyResource implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -718,32 +754,36 @@ public List getValues() { * @param values the values to set */ public void setValues(List values) { - if(this.values == null) { - this.values = new ArrayList<>(); - } + this.values = nullSafeList(values); + } - if(this.values == values) { - return; - } + public boolean addValue(String value) { + this.values = getUpdatableList(this.values); + + return values.add(value); + } - this.values.clear(); + public boolean addValues(Collection values) { + this.values = getUpdatableList(this.values); - if(values != null) { - this.values.addAll(values); - } + return this.values.addAll(values); + } + + public boolean addValues(String[] values) { + this.values = getUpdatableList(this.values); + + return Collections.addAll(this.values, values); } /** * @param value the value to set */ public void setValue(String value) { - if(this.values == null) { - this.values = new ArrayList<>(); - } + ArrayList values = new ArrayList<>(); - this.values.clear(); + values.add(value); - this.values.add(value); + this.values = values; } /** @@ -774,6 +814,10 @@ public void setIsRecursive(Boolean isRecursive) { this.isRecursive = isRecursive == null ? Boolean.FALSE : isRecursive; } + public void dedupStrings(Map strTbl) { + values = StringUtil.dedupStringsList(values, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -842,10 +886,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyItem implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -860,6 +902,10 @@ public RangerPolicyItem() { this(null, null, null, null, null, null); } + public RangerPolicyItem(RangerPolicyItem other) { + this(other.accesses, other.users, other.groups, other.roles, other.conditions, other.delegateAdmin); + } + public RangerPolicyItem(List accessTypes, List users, List groups, List roles, List conditions, Boolean delegateAdmin) { setAccesses(accessTypes); setUsers(users); @@ -879,20 +925,21 @@ public List getAccesses() { * @param accesses the accesses to set */ public void setAccesses(List accesses) { - if(this.accesses == null) { - this.accesses = new ArrayList<>(); - } + this.accesses = nullSafeList(accesses); + } - if(this.accesses == accesses) { - return; - } + public boolean addAccess(RangerPolicyItemAccess access) { + this.accesses = getUpdatableList(this.accesses); + + return accesses.add(access); + } - this.accesses.clear(); + public boolean addAccesses(Collection accesses) { + this.accesses = getUpdatableList(this.accesses); - if(accesses != null) { - this.accesses.addAll(accesses); - } + return this.accesses.addAll(accesses); } + /** * @return the users */ @@ -903,20 +950,25 @@ public List getUsers() { * @param users the users to set */ public void setUsers(List users) { - if(this.users == null) { - this.users = new ArrayList<>(); - } + this.users = nullSafeList(users); + } - if(this.users == users) { - return; - } + public boolean addUser(String user) { + this.users = getUpdatableList(this.users); - this.users.clear(); + return users.add(user); + } - if(users != null) { - this.users.addAll(users); - } + public boolean addUsers(Collection users) { + this.users = getUpdatableList(this.users); + + return this.users.addAll(users); + } + + public boolean removeUser(String user) { + return CollectionUtils.isNotEmpty(users) && users.remove(user); } + /** * @return the groups */ @@ -927,20 +979,25 @@ public List getGroups() { * @param groups the groups to set */ public void setGroups(List groups) { - if(this.groups == null) { - this.groups = new ArrayList<>(); - } + this.groups = nullSafeList(groups); + } - if(this.groups == groups) { - return; - } + public boolean addGroup(String group) { + this.groups = getUpdatableList(this.groups); - this.groups.clear(); + return groups.add(group); + } - if(groups != null) { - this.groups.addAll(groups); - } + public boolean addGroups(Collection groups) { + this.groups = getUpdatableList(this.groups); + + return this.groups.addAll(groups); } + + public boolean removeGroup(String group) { + return CollectionUtils.isNotEmpty(groups) && groups.remove(group); + } + /** * @return the roles */ @@ -951,20 +1008,25 @@ public List getRoles() { * @param roles the roles to set */ public void setRoles(List roles) { - if(this.roles == null) { - this.roles = new ArrayList<>(); - } + this.roles = nullSafeList(roles); + } - if(this.roles == roles) { - return; - } + public boolean addRole(String role) { + this.roles = getUpdatableList(this.roles); - this.roles.clear(); + return roles.add(role); + } - if(roles != null) { - this.roles.addAll(roles); - } + public boolean addRoles(Collection roles) { + this.roles = getUpdatableList(this.roles); + + return this.roles.addAll(roles); + } + + public boolean removeRole(String role) { + return CollectionUtils.isNotEmpty(roles) && roles.remove(role); } + /** * @return the conditions */ @@ -975,19 +1037,19 @@ public List getConditions() { * @param conditions the conditions to set */ public void setConditions(List conditions) { - if(this.conditions == null) { - this.conditions = new ArrayList<>(); - } + this.conditions = nullSafeList(conditions); + } - if(this.conditions == conditions) { - return; - } + public boolean addCondition(RangerPolicyItemCondition condition) { + this.conditions = getUpdatableList(this.conditions); - this.conditions.clear(); + return conditions.add(condition); + } - if(conditions != null) { - this.conditions.addAll(conditions); - } + public boolean addConditions(Collection conditions) { + this.conditions = getUpdatableList(this.conditions); + + return this.conditions.addAll(conditions); } /** @@ -1004,6 +1066,24 @@ public void setDelegateAdmin(Boolean delegateAdmin) { this.delegateAdmin = delegateAdmin == null ? Boolean.FALSE : delegateAdmin; } + public void dedupStrings(Map strTbl) { + if (accesses != null) { + for (RangerPolicyItemAccess access : accesses) { + access.dedupStrings(strTbl); + } + } + + users = StringUtil.dedupStringsList(users, strTbl); + groups = StringUtil.dedupStringsList(groups, strTbl); + roles = StringUtil.dedupStringsList(roles, strTbl); + + if (conditions != null) { + for (RangerPolicyItemCondition condition : conditions) { + condition.dedupStrings(strTbl); + } + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -1135,10 +1215,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerDataMaskPolicyItem extends RangerPolicyItem implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1222,10 +1300,8 @@ public StringBuilder toString(StringBuilder sb) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerRowFilterPolicyItem extends RangerPolicyItem implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1309,10 +1385,8 @@ public StringBuilder toString(StringBuilder sb) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyItemAccess implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1343,7 +1417,7 @@ public String getType() { * @param type the type to set */ public void setType(String type) { - this.type = type; + this.type = type == null ? "" : type; } /** @@ -1360,6 +1434,10 @@ public void setIsAllowed(Boolean isAllowed) { this.isAllowed = isAllowed == null ? Boolean.TRUE : isAllowed; } + public void dedupStrings(Map strTbl) { + type = StringUtil.dedupString(type, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -1413,10 +1491,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyItemCondition implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1457,19 +1533,20 @@ public List getValues() { * @param values the value to set */ public void setValues(List values) { - if (this.values == null) { - this.values = new ArrayList<>(); - } + this.values = nullSafeList(values); + } - if(this.values == values) { - return; + public boolean addValue(String value) { + if (CollectionUtils.isEmpty(values)) { + values = new ArrayList<>(); } - this.values.clear(); + return values.add(value); + } - if(values != null) { - this.values.addAll(values); - } + public void dedupStrings(Map strTbl) { + type = StringUtil.dedupString(type, strTbl); + values = StringUtil.dedupStringsList(values, strTbl); } @Override @@ -1531,10 +1608,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyItemDataMaskInfo implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1550,6 +1625,12 @@ public RangerPolicyItemDataMaskInfo(String dataMaskType, String conditionExpr, S setValueExpr(valueExpr); } + public RangerPolicyItemDataMaskInfo(RangerPolicyItemDataMaskInfo that) { + this.dataMaskType = that.dataMaskType; + this.conditionExpr = that.conditionExpr; + this.valueExpr = that.valueExpr; + } + public String getDataMaskType() { return dataMaskType; } @@ -1586,8 +1667,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if(! super.equals(obj)) - return false; if (this == obj) return true; if (obj == null) @@ -1636,10 +1715,8 @@ public StringBuilder toString(StringBuilder sb) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyItemRowFilterInfo implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1651,6 +1728,10 @@ public RangerPolicyItemRowFilterInfo(String filterExpr) { setFilterExpr(filterExpr); } + public RangerPolicyItemRowFilterInfo(RangerPolicyItemRowFilterInfo that) { + this.filterExpr = that.filterExpr; + } + public String getFilterExpr() { return filterExpr; } @@ -1669,8 +1750,6 @@ public int hashCode() { @Override public boolean equals(Object obj) { - if(! super.equals(obj)) - return false; if (this == obj) return true; if (obj == null) @@ -1696,7 +1775,7 @@ public String toString( ) { } public StringBuilder toString(StringBuilder sb) { - sb.append("RangerPolicyItemDataMaskInfo={"); + sb.append("RangerPolicyItemRowFilterInfo={"); sb.append("filterExpr={").append(filterExpr).append("} "); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java index 1d2b1432bd..2d0e054bbe 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyDelta.java @@ -19,21 +19,17 @@ package org.apache.ranger.plugin.model; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnore; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import java.util.Map; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerPolicyDelta implements java.io.Serializable { public static final int CHANGE_TYPE_POLICY_CREATE = 0; @@ -45,28 +41,30 @@ public class RangerPolicyDelta implements java.io.Serializable { public static final int CHANGE_TYPE_LOG_ERROR = 6; public static final int CHANGE_TYPE_INVALIDATE_POLICY_DELTAS = 7; public static final int CHANGE_TYPE_ROLE_UPDATE = 8; + public static final int CHANGE_TYPE_GDS_UPDATE = 9; - private static String[] changeTypeNames = { "POLICY_CREATE", "POLICY_UPDATE", "POLICY_DELETE", "SERVICE_CHANGE", "SERVICE_DEF_CHANGE", "RANGER_ADMIN_START", "LOG_ERROR", "INVALIDATE_POLICY_DELTAS", "ROLE_UPDATE" }; + private static String[] changeTypeNames = { "POLICY_CREATE", "POLICY_UPDATE", "POLICY_DELETE", "SERVICE_CHANGE", "SERVICE_DEF_CHANGE", "RANGER_ADMIN_START", "LOG_ERROR", "INVALIDATE_POLICY_DELTAS", "ROLE_UPDATE", "GDS_UPDATE" }; private Long id; private Integer changeType; + private Long policiesVersion; private RangerPolicy policy; public RangerPolicyDelta() { - this(null, null, null); + this(null, null, null, null); } - public RangerPolicyDelta(final Long id, final Integer changeType, final RangerPolicy policy) { + public RangerPolicyDelta(final Long id, final Integer changeType, final Long policiesVersion, final RangerPolicy policy) { setId(id); setChangeType(changeType); + setPoliciesVersion(policiesVersion); setPolicy(policy); } public Long getId() { return id; } public Integer getChangeType() { return changeType; } - @JsonIgnore - public Long getPolicyVersion() { return policy != null ? policy.getVersion() : null; } + public Long getPoliciesVersion() { return policiesVersion; } @JsonIgnore public String getServiceType() { return policy != null ? policy.getServiceType() : null; } @@ -84,15 +82,23 @@ public RangerPolicyDelta(final Long id, final Integer changeType, final RangerPo public void setId(Long id) { this.id = id;} - private void setChangeType(Integer changeType) { this.changeType = changeType; } + public void setChangeType(Integer changeType) { this.changeType = changeType; } + + private void setPoliciesVersion(Long policiesVersion) { this.policiesVersion = policiesVersion; } public void setPolicy(RangerPolicy policy) { this.policy = policy; } + public void dedupStrings(Map strTbl) { + if (policy != null) { + policy.dedupStrings(strTbl); + } + } + @Override public String toString() { return "id:" + id + ", changeType:" + changeTypeNames[changeType] - + ", policyVersion:" + getPolicyVersion() + + ", policiesVersion:" + getPoliciesVersion() + ", serviceType:" + getServiceType() + ", policyType:" + getPolicyType() + ", policyId:[" + getPolicyId() + "]" diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyHeader.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyHeader.java new file mode 100644 index 0000000000..b8fdf102d9 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyHeader.java @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; + +import java.util.HashMap; +import java.util.Map; + + +@JsonAutoDetect(fieldVisibility=Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerPolicyHeader extends RangerBaseModelObject implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + private String service; + private String name; + private Integer policyType; + private String zoneName; + private Map resources; + + public RangerPolicyHeader(RangerPolicy rangerPolicy) { + super(); + + setId(rangerPolicy.getId()); + setGuid(rangerPolicy.getGuid()); + setName(rangerPolicy.getName()); + setResources(rangerPolicy.getResources()); + setIsEnabled(rangerPolicy.getIsEnabled()); + setService(rangerPolicy.getService()); + setVersion(rangerPolicy.getVersion()); + setPolicyType(rangerPolicy.getPolicyType()); + setZoneName(rangerPolicy.getZoneName()); + } + + public String getService() { + return service; + } + + public void setService(String service) { + this.service = service; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Integer getPolicyType() { + return policyType; + } + + public void setPolicyType(Integer policyType) { + this.policyType = policyType; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public Map getResources() { + return resources; + } + + public void setResources(Map resources) { + if (this.resources == null) { + this.resources = new HashMap<>(); + } + + if (this.resources == resources) { + return; + } + + this.resources.clear(); + + if (resources != null) { + for (Map.Entry e : resources.entrySet()) { + this.resources.put(e.getKey(), e.getValue()); + } + } + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("id={").append(getId()).append("} "); + sb.append("guid={").append(getGuid()).append("} "); + sb.append("name={").append(name).append("} "); + sb.append("resources={"); + if (resources != null) { + for (Map.Entry e : resources.entrySet()) { + sb.append(e.getKey()).append("={"); + e.getValue().toString(sb); + sb.append("} "); + } + } + sb.append("} "); + sb.append("isEnabled={").append(getIsEnabled()).append("} "); + sb.append("service={").append(service).append("} "); + sb.append("version={").append(name).append("} "); + sb.append("policyType={").append(policyType).append("} "); + sb.append("zoneName={").append(zoneName).append("} "); + + return sb; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java old mode 100644 new mode 100755 index 2bb65891a6..75dc6870be --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPolicyResourceSignature.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -29,16 +30,18 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig; +import org.apache.ranger.plugin.model.RangerGds.RangerSharedResource; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; -import org.apache.solr.common.StringUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerPolicyResourceSignature { static final int _SignatureVersion = 1; - private static final Log LOG = LogFactory.getLog(RangerPolicyResourceSignature.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPolicyResourceSignature.class); static final RangerPolicyResourceSignature _EmptyResourceSignature = new RangerPolicyResourceSignature((RangerPolicy)null); private final String _string; @@ -49,9 +52,27 @@ public RangerPolicyResourceSignature(RangerPolicy policy) { _policy = policy; PolicySerializer serializer = new PolicySerializer(_policy); _string = serializer.toString(); - _hash = DigestUtils.sha256Hex(_string); + if (RangerAdminConfig.getInstance().isFipsEnabled()) { + _hash = DigestUtils.sha512Hex(_string); + } else { + _hash = DigestUtils.sha256Hex(_string); + } + } + + public RangerPolicyResourceSignature(RangerSharedResource resource) { + this(toSignatureString(resource)); + } + + public RangerPolicyResourceSignature(Map resources) { + this(toSignatureString(resources)); } + // alternate to constructor that takes Map> + public static RangerPolicyResourceSignature from(Map> resources) { + return new RangerPolicyResourceSignature(toPolicyResources(resources)); + } + + /** * Only added for testability. Do not make public * @param string @@ -63,7 +84,11 @@ public RangerPolicyResourceSignature(RangerPolicy policy) { } else { _string = string; } - _hash = DigestUtils.sha256Hex(_string); + if (RangerAdminConfig.getInstance().isFipsEnabled()) { + _hash = DigestUtils.sha384Hex(_string); + } else { + _hash = DigestUtils.sha256Hex(_string); + } } String asString() { @@ -112,6 +137,8 @@ boolean isPolicyValidForResourceSignatureComputation() { LOG.debug("isPolicyValidForResourceSignatureComputation: resources collection on policy was null!"); } else if (_policy.getResources().containsKey(null)) { LOG.debug("isPolicyValidForResourceSignatureComputation: resources collection has resource with null name!"); + } else if (!_policy.getIsEnabled() && StringUtils.isEmpty(_policy.getGuid())) { + LOG.debug("isPolicyValidForResourceSignatureComputation: policy GUID is empty for a disabled policy!"); } else { valid = true; } @@ -121,7 +148,7 @@ boolean isPolicyValidForResourceSignatureComputation() { } return valid; } - + @Override public String toString() { // invalid/empty policy gets a deterministic signature as if it had an @@ -133,13 +160,9 @@ public String toString() { if (_policy.getPolicyType() != null) { type = _policy.getPolicyType(); } - Map resources = new TreeMap<>(); - for (Map.Entry entry : _policy.getResources().entrySet()) { - String resourceName = entry.getKey(); - ResourceSerializer resourceView = new ResourceSerializer(entry.getValue()); - resources.put(resourceName, resourceView); - } - String resource = resources.toString(); + + String resource = toSignatureString(_policy.getResources(), _policy.getAdditionalResources()); + if (CollectionUtils.isNotEmpty(_policy.getValiditySchedules())) { resource += _policy.getValiditySchedules().toString(); } @@ -154,6 +177,9 @@ public String toString() { CustomConditionSerialiser customConditionSerialiser = new CustomConditionSerialiser(_policy.getConditions()); resource += customConditionSerialiser.toString(); } + if (!_policy.getIsEnabled()) { + resource += _policy.getGuid(); + } String result = String.format("{version=%d,type=%d,resource=%s}", _SignatureVersion, type, resource); return result; @@ -161,10 +187,73 @@ public String toString() { } - static class ResourceSerializer { + public static String toSignatureString(Map resource) { + Map resources = new TreeMap<>(); + + for (Map.Entry entry : resource.entrySet()) { + String resourceName = entry.getKey(); + ResourceSerializer resourceView = new ResourceSerializer(entry.getValue()); + + resources.put(resourceName, resourceView); + } + + return resources.toString(); + } + + public static String toSignatureString(Map resource, List> additionalResources) { + String ret = toSignatureString(resource); + + if (additionalResources != null && !additionalResources.isEmpty()) { + List signatures = new ArrayList<>(additionalResources.size() + 1); + + signatures.add(ret); + + for (Map additionalResource : additionalResources) { + signatures.add(toSignatureString(additionalResource)); + } + + Collections.sort(signatures); + + ret = signatures.toString(); + } + + return ret; + } + + public static String toSignatureString(RangerSharedResource resource) { + final Map policyResource; + + if (StringUtils.isNotBlank(resource.getSubResourceType()) && resource.getSubResource() != null && CollectionUtils.isNotEmpty(resource.getSubResource().getValues())) { + policyResource = new HashMap<>(resource.getResource()); + + policyResource.put(resource.getSubResourceType(), resource.getSubResource()); + } else { + policyResource = resource.getResource(); + } + + String signature = toSignatureString(policyResource); + + if (StringUtils.isNotEmpty(resource.getConditionExpr())) { + signature += resource.getConditionExpr(); + } + + return String.format("{version=%d,ret=%s}", _SignatureVersion, signature); + } + + private static Map toPolicyResources(Map> resources) { + Map ret = new TreeMap<>(); + + for (Map.Entry> entry : resources.entrySet()) { + ret.put(entry.getKey(), new RangerPolicyResource(entry.getValue(), false, false)); + } + + return ret; + } + + static public class ResourceSerializer { final RangerPolicyResource _policyResource; - ResourceSerializer(RangerPolicyResource policyResource) { + public ResourceSerializer(RangerPolicyResource policyResource) { _policyResource = policyResource; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPrincipal.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPrincipal.java new file mode 100644 index 0000000000..bf96922595 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPrincipal.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Objects; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class RangerPrincipal implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + public enum PrincipalType { USER, GROUP, ROLE } + + private PrincipalType type; + private String name; + + public RangerPrincipal() { + this(null, null); + } + + public RangerPrincipal(PrincipalType type, String name) { + setType(type); + setName(name); + } + + public PrincipalType getType() { + return type; + } + + public void setType(PrincipalType type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(type, name); + } + + @Override + public boolean equals(Object obj) { + final boolean ret; + + if (this == obj) { + ret = true; + } else if (obj == null) { + ret = false; + } else if (getClass() != obj.getClass()) { + ret = false; + } else { + RangerPrincipal other = (RangerPrincipal) obj; + + ret = Objects.equals(type, other.type) && Objects.equals(name, other.name); + } + + return ret; + } + + @Override + public String toString() { + return "{type=" + type + ", name=" + name + "}"; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerRole.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerRole.java index 54b73f9f21..1fdf9e26c3 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerRole.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerRole.java @@ -20,13 +20,10 @@ package org.apache.ranger.plugin.model; import org.apache.commons.collections.MapUtils; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -34,10 +31,8 @@ import java.util.Objects; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerRole extends RangerBaseModelObject implements java.io.Serializable { public static final String KEY_USER = "user"; public static final String KEY_GROUP = "group"; @@ -137,10 +132,8 @@ public String toString() { } @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RoleMember implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -195,4 +188,60 @@ private String getPrintableOptions(Map options) { return ret.toString(); } -} \ No newline at end of file + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((options == null) ? 0 : options.hashCode()); + result = prime * result + ((users == null) ? 0 : users.hashCode()); + result = prime * result + ((groups == null) ? 0 : groups.hashCode()); + result = prime * result + ((roles == null) ? 0 : roles.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RangerRole other = (RangerRole) obj; + if (name == null) { + if (other.name != null) { + return false; + } + } else if (other.name == null || !name.equals(other.name)) { + return false; + } + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; + if (options == null) { + if (other.options != null) + return false; + } else if (!options.equals(other.options)) + return false; + if (users == null) { + if (other.users != null) + return false; + } else if (!users.equals(other.users)) + return false; + if (groups == null) { + if (other.groups != null) + return false; + } else if (!groups.equals(other.groups)) + return false; + if (roles == null) { + if (other.roles != null) + return false; + } else if (!roles.equals(other.roles)) + return false; + return true; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java index 49887770b3..e488c078e5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java @@ -19,23 +19,22 @@ package org.apache.ranger.plugin.model; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.apache.ranger.plugin.model.RangerPrincipal.PrincipalType; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResourceBase; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerSecurityZone extends RangerBaseModelObject implements java.io.Serializable { public static final long RANGER_UNZONED_SECURITY_ZONE_ID = 1L; private static final long serialVersionUID = 1L; @@ -44,21 +43,29 @@ public class RangerSecurityZone extends RangerBaseModelObject implements java.io private List tagServices; private List adminUsers; private List adminUserGroups; + private List adminRoles; private List auditUsers; private List auditUserGroups; + private List auditRoles; private String description; public RangerSecurityZone() { - this(null, null, null, null, null, null, null,null); + this(null, null, null, null, null, null, null,null, null, null); } public RangerSecurityZone(String name, Map services,List tagServices, List adminUsers, List adminUserGroups, List auditUsers, List auditUserGroups, String description) { + this(name, services, tagServices, adminUsers, adminUserGroups, null, auditUsers, auditUserGroups, null, description); + } + + public RangerSecurityZone(String name, Map services,List tagServices, List adminUsers, List adminUserGroups, List adminRoles, List auditUsers, List auditUserGroups, List auditRoles, String description) { setName(name); setServices(services); setAdminUsers(adminUsers); setAdminUserGroups(adminUserGroups); + setAdminRoles(adminRoles); setAuditUsers(auditUsers); setAuditUserGroups(auditUserGroups); + setAuditRoles(auditRoles); setDescription(description); setTagServices(tagServices); } @@ -92,6 +99,12 @@ public void setAdminUserGroups(List adminUserGroups) { this.adminUserGroups = adminUserGroups == null ? new ArrayList<>() : adminUserGroups; } + public List getAdminRoles() { return adminRoles; } + + public void setAdminRoles(List adminRoles) { + this.adminRoles = adminRoles == null ? new ArrayList<>() : adminRoles; + } + public List getAuditUsers() { return auditUsers; } public void setAuditUsers(List auditUsers) { @@ -104,6 +117,12 @@ public void setAuditUserGroups(List auditUserGroups) { this.auditUserGroups = auditUserGroups == null ? new ArrayList<>() : auditUserGroups; } + public List getAuditRoles() { return auditRoles; } + + public void setAuditRoles(List auditRoles) { + this.auditRoles = auditRoles == null ? new ArrayList<>() : auditRoles; + } + public List getTagServices() { return tagServices; } @@ -119,27 +138,33 @@ public String toString() { + ", tagServices=" + tagServices + ", adminUsers=" + adminUsers + ", adminUserGroups=" + adminUserGroups + + ", adminRoles=" + adminRoles + ", auditUsers=" + auditUsers + ", auditUserGroups=" + auditUserGroups + + ", auditRoles=" + auditRoles + ", description="+ description +"}"; } @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerSecurityZoneService implements java.io.Serializable { private static final long serialVersionUID = 1L; - private List>> resources; + private List>> resources; + private List resourcesBaseInfo; public RangerSecurityZoneService() { - this(null); + this(null, null); } public RangerSecurityZoneService(List>> resources) { + this(resources, null); + } + + public RangerSecurityZoneService(List>> resources, List resourcesBaseInfo) { setResources(resources); + setResourcesBaseInfo(resourcesBaseInfo); } public List>> getResources() { return resources; } @@ -148,22 +173,161 @@ public void setResources(List>> resources) { this.resources = resources == null ? new ArrayList<>() : resources; } + public List getResourcesBaseInfo() { return resourcesBaseInfo; } + + public void setResourcesBaseInfo(List resourcesBaseInfo) { + this.resourcesBaseInfo = resourcesBaseInfo == null ? new ArrayList<>() : resourcesBaseInfo; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("{resources={"); - for (Map> resource : resources) { - sb.append("[ "); - for (Map.Entry> entry : resource.entrySet()) { - sb.append("{resource-def-name=").append(entry.getKey()).append(", values=").append(entry.getValue()).append("},"); + sb.append("{resources=["); + if (resources != null) { + for (int i = 0; i < resources.size(); i++) { + HashMap> resource = resources.get(i); + RangerSecurityZoneResourceBase baseInfo = (resourcesBaseInfo != null && resourcesBaseInfo.size() > i) ? resourcesBaseInfo.get(i) : null; + + sb.append("{resource="); + if (resource != null) { + for (Map.Entry> entry : resource.entrySet()) { + sb.append("{resource-def-name=").append(entry.getKey()).append(", values=").append(entry.getValue()).append("} "); + } + } + sb.append("} "); + + sb.append("{baseInfo="); + if (baseInfo != null) { + baseInfo.toString(sb); + } + sb.append("} "); } - sb.append(" ],"); } - sb.append("}}"); + sb.append("]}"); return sb.toString(); } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class SecurityZoneSummary extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private Long totalResourceCount; + private Map adminCount; + private Map auditorCount; + private List tagServices; + private List services; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Long getTotalResourceCount() { + return totalResourceCount; + } + + public void setTotalResourceCount(Long totalResourceCount) { + this.totalResourceCount = totalResourceCount; + } + + public Map getAdminCount() { + return adminCount; + } + + public void setAdminCount(Map adminCount) { + this.adminCount = adminCount; + } + public Map getAuditorCount() { + return auditorCount; + } + + public void setAuditorCount(Map auditorCount) { + this.auditorCount = auditorCount; + } + + public List getTagServices() { + return tagServices; + } + + public void setTagServices(List tagServices) { + this.tagServices = tagServices; + } + + public List getServices() { + return services; + } + + public void setServices(List services) { + this.services = services; + } + } + + public static class ZoneServiceSummary implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + private String type; + private String displayName; + private Long resourceCount; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Long getResourceCount() { + return resourceCount; + } + + public void setResourceCount(Long resourceCount) { + this.resourceCount = resourceCount; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneHeaderInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneHeaderInfo.java new file mode 100644 index 0000000000..a6b06b98a1 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneHeaderInfo.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RangerSecurityZoneHeaderInfo extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + private String name; + + public RangerSecurityZoneHeaderInfo() { + super(); + setId(-1L); + setName(""); + } + + public RangerSecurityZoneHeaderInfo(Long id, String name) { + super(); + setId(id); + setName(name); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("id={").append(getId()).append("} "); + sb.append("name={").append(name).append("} "); + sb.append("isEnabled={").append(getIsEnabled()).append("} "); + return sb; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneV2.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneV2.java new file mode 100644 index 0000000000..a85ad92d67 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneV2.java @@ -0,0 +1,507 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerSecurityZoneV2 extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private Map services; + private List tagServices; + private List admins; + private List auditors; + + public RangerSecurityZoneV2() { + this(null, null, null, null, null, null); + } + + public RangerSecurityZoneV2(String name, String description, Map services, List tagServices, List admins, List auditors) { + setName(name); + setDescription(description); + setServices(services); + setTagServices(tagServices); + setAdmins(admins); + setAuditors(auditors); + } + + public RangerSecurityZoneV2(RangerSecurityZone other) { + setId(other.getId()); + setGuid(other.getGuid()); + setIsEnabled(other.getIsEnabled()); + setCreatedBy(other.getCreatedBy()); + setUpdatedBy(other.getUpdatedBy()); + setCreateTime(other.getCreateTime()); + setUpdateTime(other.getUpdateTime()); + setVersion(other.getVersion()); + setName(other.getName()); + setDescription(other.getDescription()); + setTagServices((other.getTagServices() != null) ? new ArrayList<>(other.getTagServices()) : new ArrayList<>()); + setAdmins(toPrincipals(other.getAdminUsers(), other.getAdminUserGroups(), other.getAdminRoles())); + setAuditors(toPrincipals(other.getAuditUsers(), other.getAuditUserGroups(), other.getAuditRoles())); + + services = new HashMap<>(); + + if (other.getServices() != null) { + for (Map.Entry entry : other.getServices().entrySet()) { + services.put(entry.getKey(), new RangerSecurityZoneServiceV2(entry.getValue())); + } + } + } + + public String getName() { return name; } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { return description; } + + public void setDescription(String description) { + this.description = description; + } + + public Map getServices() { return services; } + + public void setServices(Map services) { + this.services = services == null ? new HashMap<>() : services; + } + + public List getTagServices() { + return tagServices; + } + + public void setTagServices(List tagServices) { + this.tagServices = (tagServices != null) ? tagServices : new ArrayList<>(); + } + + public List getAdmins() { return admins; } + + public void setAdmins(List admins) { + this.admins = admins == null ? new ArrayList<>() : admins; + } + + public List getAuditors() { return auditors; } + + public void setAuditors(List auditors) { + this.auditors = auditors == null ? new ArrayList<>() : auditors; + } + + public RangerSecurityZone toV1() { + RangerSecurityZone ret = new RangerSecurityZone(); + + ret.setId(getId()); + ret.setGuid(getGuid()); + ret.setIsEnabled(getIsEnabled()); + ret.setCreatedBy(getCreatedBy()); + ret.setUpdatedBy(getUpdatedBy()); + ret.setCreateTime(getCreateTime()); + ret.setUpdateTime(getUpdateTime()); + ret.setVersion(getVersion()); + ret.setName(name); + ret.setDescription(description); + + if (services != null) { + for (Map.Entry entry : services.entrySet()) { + ret.getServices().put(entry.getKey(), entry.getValue().toV1()); + } + } + + if (tagServices != null) { + ret.getTagServices().addAll(tagServices); + } + + fromPrincipals(admins, ret.getAdminUsers(), ret.getAdminUserGroups(), ret.getAdminRoles()); + fromPrincipals(auditors, ret.getAuditUsers(), ret.getAuditUserGroups(), ret.getAuditRoles()); + + return ret; + } + + @Override + public String toString() { + return "{name=" + name + + ", description="+ description + + ", services=" + services + + ", tagServices=" + tagServices + + ", admins=" + admins + + ", auditors=" + auditors + +"}"; + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneServiceV2 implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private List resources; + + public RangerSecurityZoneServiceV2() { + this((List) null); + } + + public RangerSecurityZoneServiceV2(List resources) { + setResources(resources); + } + + public RangerSecurityZoneServiceV2(RangerSecurityZoneService other) { + resources = new ArrayList<>(); + + if (other != null && other.getResources() != null) { + for (int i = 0; i < other.getResources().size(); i++) { + RangerSecurityZoneResource resource = getResourceAt(other, i); + + if (resource != null) { + resources.add(resource); + } + } + } + } + + public List getResources() { return resources; } + + public void setResources(List resources) { + this.resources = resources == null ? new ArrayList<>() : resources; + } + + public RangerSecurityZoneService toV1() { + RangerSecurityZoneService ret = new RangerSecurityZoneService(); + + if (resources != null) { + for (RangerSecurityZoneResource resource : resources) { + ret.getResources().add((HashMap> ) resource.getResource()); + ret.getResourcesBaseInfo().add(new RangerSecurityZoneResourceBase(resource)); + } + } + return ret; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{resources=["); + if (resources != null) { + for (RangerSecurityZoneResource resource : resources) { + if (resource != null) { + resource.toString(sb).append(" "); + } + } + } + sb.append("]}"); + + return sb.toString(); + } + + private RangerSecurityZoneResource getResourceAt(RangerSecurityZoneService zoneService, int idx) { + Map> resource = zoneService.getResources() != null && zoneService.getResources().size() > idx ? zoneService.getResources().get(idx) : null; + RangerSecurityZoneResourceBase baseInfo = zoneService.getResourcesBaseInfo() != null && zoneService.getResourcesBaseInfo().size() > idx ? zoneService.getResourcesBaseInfo().get(idx) : null; + + return resource != null ? new RangerSecurityZoneResource(resource, baseInfo) : null; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneResourceBase implements java.io.Serializable { + private Long id; + private String createdBy; + private String updatedBy; + private Date createTime; + private Date updateTime; + + public RangerSecurityZoneResourceBase() { } + + public RangerSecurityZoneResourceBase(RangerSecurityZoneResourceBase other) { + if (other != null) { + setId(other.getId()); + setCreatedBy(other.getCreatedBy()); + setCreateTime(other.getCreateTime()); + setUpdatedBy(other.getUpdatedBy()); + setUpdateTime(other.getUpdateTime()); + } + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public String getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append("{id=").append(id) + .append(", createdBy=").append(createdBy) + .append(", createTime=").append(createTime) + .append(", updatedBy=").append(updatedBy) + .append(", updateTime=").append(updateTime) + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneResource extends RangerSecurityZoneResourceBase implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Map> resource; + + public RangerSecurityZoneResource() { this(null, null); } + + public RangerSecurityZoneResource(Map> resource) { + this(resource, null); + } + + public RangerSecurityZoneResource(Map> resource, RangerSecurityZoneResourceBase baseObj) { + super(baseObj); + + setResource(resource); + } + + public Map> getResource() { return resource; } + + public void setResource(Map> resource) { this.resource = resource == null ? new HashMap<>() : resource; } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + @Override + public StringBuilder toString(StringBuilder sb) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append("{resource="); + super.toString(sb); + if (resource != null) { + for (Map.Entry> entry : resource.entrySet()) { + sb.append("{resource-def-name=").append(entry.getKey()) + .append(", values=").append(entry.getValue()).append("} "); + } + } + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneChangeRequest implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private Map resourcesToUpdate; + private Map resourcesToRemove; + private List tagServicesToAdd; + private List tagServicesToRemove; + private List adminsToAdd; + private List adminsToRemove; + private List auditorsToAdd; + private List auditorsToRemove; + + public RangerSecurityZoneChangeRequest() { } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Map getResourcesToUpdate() { + return resourcesToUpdate; + } + + public void setResourcesToUpdate(Map resourcesToUpdate) { + this.resourcesToUpdate = resourcesToUpdate; + } + + public Map getResourcesToRemove() { + return resourcesToRemove; + } + + public void setResourcesToRemove(Map resourcesToRemove) { + this.resourcesToRemove = resourcesToRemove; + } + + public List getTagServicesToAdd() { + return tagServicesToAdd; + } + + public void setTagServicesToAdd(List tagServicesToAdd) { + this.tagServicesToAdd = tagServicesToAdd; + } + + public List getTagServicesToRemove() { + return tagServicesToRemove; + } + + public void setTagServicesToRemove(List tagServicesToRemove) { + this.tagServicesToRemove = tagServicesToRemove; + } + + public List getAdminsToAdd() { + return adminsToAdd; + } + + public void setAdminsToAdd(List adminsToAdd) { + this.adminsToAdd = adminsToAdd; + } + + public List getAdminsToRemove() { + return adminsToRemove; + } + + public void setAdminsToRemove(List adminsToRemove) { + this.adminsToRemove = adminsToRemove; + } + + public List getAuditorsToAdd() { + return auditorsToAdd; + } + + public void setAuditorsToAdd(List auditorsToAdd) { + this.auditorsToAdd = auditorsToAdd; + } + + public List getAuditorsToRemove() { + return auditorsToRemove; + } + + public void setAuditorsToRemove(List auditorsToRemove) { + this.auditorsToRemove = auditorsToRemove; + } + + + } + + private void fromPrincipals(List principals, List users, List groups, List roles) { + if (principals != null) { + for (RangerPrincipal principal : principals) { + if (principal.getType() == RangerPrincipal.PrincipalType.USER) { + users.add(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.GROUP) { + groups.add(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.ROLE) { + roles.add(principal.getName()); + } + } + } + } + + private List toPrincipals(List users, List groups, List roles) { + List ret = new ArrayList<>(); + + if (users != null) { + for (String name : users) { + ret.add(new RangerPrincipal(RangerPrincipal.PrincipalType.USER, name)); + } + } + + if (groups != null) { + for (String name : groups) { + ret.add(new RangerPrincipal(RangerPrincipal.PrincipalType.GROUP, name)); + } + } + + if (roles != null) { + for (String name : roles) { + ret.add(new RangerPrincipal(RangerPrincipal.PrincipalType.ROLE, name)); + } + } + + return ret; + } +} + diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServerHealth.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServerHealth.java new file mode 100644 index 0000000000..e7127833ac --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServerHealth.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonIgnoreProperties(ignoreUnknown=true) +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +@JsonInclude(Include.NON_EMPTY) +public class RangerServerHealth { + public enum RangerServerStatus { + UNKNOWN, INITIALIZING, INITIALIZATION_FAILURE, UP, DOWN + } + + private final RangerServerStatus status; + private final Map details; + + private RangerServerHealth(Builder builder) { + this.status = builder.status; + this.details = Collections.unmodifiableMap(builder.details); + } + + public RangerServerStatus getStatus() { + return this.status; + } + + public Map getDetails() { + return this.details; + } + + public static Builder unknown() { + return status(RangerServerStatus.UNKNOWN); + } + + public static Builder up() { + return status(RangerServerStatus.UP); + } + + public static Builder down() { + return status(RangerServerStatus.DOWN); + } + + public static Builder status(RangerServerStatus status) { + return new Builder(status); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } else if (!(o instanceof RangerServerHealth)) { + return false; + } + + RangerServerHealth health = (RangerServerHealth) o; + + return Objects.equals(status, health.status) && Objects.equals(details, health.details); + } + + @Override + public int hashCode() { + return Objects.hash(status, details); + } + + @Override + public String toString() { + return getStatus() + " " + getDetails(); + } + + public static class Builder { + private RangerServerStatus status; + private final Map details; + + public Builder() { + this.status = RangerServerStatus.UNKNOWN; + this.details = new LinkedHashMap<>(); + } + + public Builder(RangerServerStatus status) { + this.status = status; + this.details = new LinkedHashMap<>(); + } + + public Builder(RangerServerStatus status, Map details) { + this.status = status; + this.details = new LinkedHashMap<>(details); + } + + public Builder withDetail(String key, Object value) { + this.details.put(key, value); + return this; + } + + public RangerServerHealth build() { + return new RangerServerHealth(this); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerService.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerService.java index 8bd4586f6f..1699980cb6 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerService.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerService.java @@ -23,24 +23,22 @@ import java.util.HashMap; import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.apache.commons.lang.StringUtils; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerService extends RangerBaseModelObject implements java.io.Serializable { private static final long serialVersionUID = 1L; + public static final String CONFIG_PASSWORD = "password"; + public static final String MASKED_PASSWORD_VALUE = "*******"; + private String type; private String name; private String displayName; @@ -87,8 +85,12 @@ public void updateFrom(RangerService other) { setName(other.getName()); setDisplayName(other.getDisplayName()); setDescription(other.getDescription()); + setTagService(other.getTagService()); setConfigs(other.getConfigs()); - setTagService(other.tagService); + setPolicyVersion(other.getPolicyVersion()); + setPolicyUpdateTime(other.getPolicyUpdateTime()); + setTagVersion(other.getTagVersion()); + setTagUpdateTime(other.getTagUpdateTime()); } /** @@ -262,7 +264,10 @@ public StringBuilder toString(StringBuilder sb) { sb.append("configs={"); if(configs != null) { for(Map.Entry e : configs.entrySet()) { - sb.append(e.getKey()).append("={").append(e.getValue()).append("} "); + String key = e.getKey(); + boolean maskValue = StringUtils.containsIgnoreCase(key, CONFIG_PASSWORD); + + sb.append(key).append("={").append(maskValue ? MASKED_PASSWORD_VALUE : e.getValue()).append("} "); } } sb.append("} "); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java index 1ac45f1752..08afeb4381 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceDef.java @@ -27,25 +27,22 @@ import java.util.Map; import java.util.Set; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.apache.ranger.authorization.utils.StringUtil; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerServiceDef extends RangerBaseModelObject implements java.io.Serializable { private static final long serialVersionUID = 1L; public static final String OPTION_ENABLE_DENY_AND_EXCEPTIONS_IN_POLICIES = "enableDenyAndExceptionsInPolicies"; + public static final String OPTION_ENABLE_IMPLICIT_CONDITION_EXPRESSION = "enableImplicitConditionExpression"; + public static final String OPTION_ENABLE_TAG_BASED_POLICIES = "enableTagBasedPolicies"; private String name; private String displayName; @@ -63,6 +60,7 @@ public class RangerServiceDef extends RangerBaseModelObject implements java.io.S private List enums; private RangerDataMaskDef dataMaskDef; private RangerRowFilterDef rowFilterDef; + private List markerAccessTypes; // read-only public RangerServiceDef() { this(null, null, null, null, null, null, null, null, null, null, null, null, null); @@ -102,6 +100,7 @@ public RangerServiceDef(String name, String implClass, String label, String desc setEnums(enums); setDataMaskDef(dataMaskDef); setRowFilterDef(rowFilterDef); + setMarkerAccessTypes(null); } public RangerServiceDef(String name, String displayName, String implClass, String label, String description, @@ -124,14 +123,18 @@ public void updateFrom(RangerServiceDef other) { setImplClass(other.getImplClass()); setLabel(other.getLabel()); setDescription(other.getDescription()); - setConfigs(other.getConfigs()); + setRbKeyLabel(other.getRbKeyLabel()); + setRbKeyDescription(other.getRbKeyDescription()); setOptions(other.getOptions()); + setConfigs(other.getConfigs()); setResources(other.getResources()); setAccessTypes(other.getAccessTypes()); setPolicyConditions(other.getPolicyConditions()); + setContextEnrichers(other.getContextEnrichers()); setEnums(other.getEnums()); setDataMaskDef(other.getDataMaskDef()); setRowFilterDef(other.getRowFilterDef()); + setMarkerAccessTypes(other.getMarkerAccessTypes()); } /** @@ -416,6 +419,26 @@ public void setRowFilterDef(RangerRowFilterDef rowFilterDef) { this.rowFilterDef = rowFilterDef == null ? new RangerRowFilterDef() : rowFilterDef; } + public List getMarkerAccessTypes() { + return markerAccessTypes; + } + + public void setMarkerAccessTypes(List markerAccessTypes) { + if (this.markerAccessTypes == null) { + this.markerAccessTypes = new ArrayList<>(); + } + + if (this.markerAccessTypes == markerAccessTypes) { + return; + } + + this.markerAccessTypes.clear(); + + if(markerAccessTypes != null) { + this.markerAccessTypes.addAll(markerAccessTypes); + } + } + public String getDisplayName() { return displayName; } @@ -424,6 +447,66 @@ public void setDisplayName(String displayName) { this.displayName = displayName; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + displayName = StringUtil.dedupString(displayName, strTbl); + implClass = StringUtil.dedupString(implClass, strTbl); + label = StringUtil.dedupString(label, strTbl); + description = StringUtil.dedupString(description, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + options = StringUtil.dedupStringsMap(options, strTbl); + + if (configs != null) { + for (RangerServiceConfigDef config : configs) { + config.dedupStrings(strTbl); + } + } + + if (resources != null) { + for (RangerResourceDef resource : resources) { + resource.dedupStrings(strTbl); + } + } + + if (accessTypes != null) { + for (RangerAccessTypeDef accessType : accessTypes) { + accessType.dedupStrings(strTbl); + } + } + + if (policyConditions != null) { + for (RangerPolicyConditionDef policyCondition : policyConditions) { + policyCondition.dedupStrings(strTbl); + } + } + + if (contextEnrichers != null) { + for (RangerContextEnricherDef contextEnricher : contextEnrichers) { + contextEnricher.dedupStrings(strTbl); + } + } + + if (enums != null) { + for (RangerEnumDef enu : enums) { + enu.dedupStrings(strTbl); + } + } + + if (dataMaskDef != null) { + dataMaskDef.dedupStrings(strTbl); + } + + if (rowFilterDef != null) { + rowFilterDef.dedupStrings(strTbl); + } + + if (markerAccessTypes != null) { + for (RangerAccessTypeDef accessType : markerAccessTypes) { + accessType.dedupStrings(strTbl); + } + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -526,6 +609,16 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); + sb.append("markerAccessTypes={"); + if(markerAccessTypes != null) { + for(RangerAccessTypeDef accessType : markerAccessTypes) { + if(accessType != null) { + accessType.toString(sb); + } + } + } + sb.append("} "); + sb.append("}"); return sb; @@ -533,10 +626,8 @@ public StringBuilder toString(StringBuilder sb) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerEnumDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -624,6 +715,14 @@ public void setDefaultIndex(Integer defaultIndex) { this.defaultIndex = (defaultIndex != null && this.elements.size() > defaultIndex) ? defaultIndex : 0; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + + for (RangerEnumElementDef element : elements) { + element.dedupStrings(strTbl); + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -701,10 +800,8 @@ public boolean equals(Object obj) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerEnumElementDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -780,6 +877,12 @@ public void setRbKeyLabel(String rbKeyLabel) { this.rbKeyLabel = rbKeyLabel; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + label = StringUtil.dedupString(label, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -850,10 +953,8 @@ public boolean equals(Object obj) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerServiceConfigDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1089,6 +1190,21 @@ public void setRbKeyValidationMessage(String rbKeyValidationMessage) { this.rbKeyValidationMessage = rbKeyValidationMessage; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + type = StringUtil.dedupString(type, strTbl); + subType = StringUtil.dedupString(subType, strTbl); + defaultValue = StringUtil.dedupString(defaultValue, strTbl); + validationRegEx = StringUtil.dedupString(validationRegEx, strTbl); + validationMessage = StringUtil.dedupString(validationMessage, strTbl); + uiHint = StringUtil.dedupString(uiHint, strTbl); + label = StringUtil.dedupString(label, strTbl); + description = StringUtil.dedupString(description, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + rbKeyDescription = StringUtil.dedupString(rbKeyDescription, strTbl); + rbKeyValidationMessage = StringUtil.dedupString(rbKeyValidationMessage, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -1238,10 +1354,8 @@ public boolean equals(Object obj) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerResourceDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -1599,6 +1713,23 @@ public void setIsValidLeaf(Boolean isValidLeaf) { this.isValidLeaf = isValidLeaf; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + type = StringUtil.dedupString(type, strTbl); + parent = StringUtil.dedupString(parent, strTbl); + matcher = StringUtil.dedupString(matcher, strTbl); + matcherOptions = StringUtil.dedupStringsMap(matcherOptions, strTbl); + validationRegEx = StringUtil.dedupString(validationRegEx, strTbl); + validationMessage = StringUtil.dedupString(validationMessage, strTbl); + uiHint = StringUtil.dedupString(uiHint, strTbl); + label = StringUtil.dedupString(label, strTbl); + description = StringUtil.dedupString(description, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + rbKeyDescription = StringUtil.dedupString(rbKeyDescription, strTbl); + rbKeyValidationMessage = StringUtil.dedupString(rbKeyValidationMessage, strTbl); + accessTypeRestrictions = StringUtil.dedupStringsSet(accessTypeRestrictions, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -1813,29 +1944,39 @@ public boolean equals(Object obj) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerAccessTypeDef implements java.io.Serializable { private static final long serialVersionUID = 1L; + public enum AccessTypeCategory { CREATE, READ, UPDATE, DELETE, MANAGE } + private Long itemId; private String name; private String label; private String rbKeyLabel; private Collection impliedGrants; + private AccessTypeCategory category; public RangerAccessTypeDef() { - this(null, null, null, null, null); + this(null, null, null, null, null, null); + } + + public RangerAccessTypeDef(String name) { + this(null, name, name, null, null, null); } public RangerAccessTypeDef(Long itemId, String name, String label, String rbKeyLabel, Collection impliedGrants) { + this(itemId, name, label, rbKeyLabel, impliedGrants, null); + } + + public RangerAccessTypeDef(Long itemId, String name, String label, String rbKeyLabel, Collection impliedGrants, AccessTypeCategory category) { setItemId(itemId); setName(name); setLabel(label); setRbKeyLabel(rbKeyLabel); setImpliedGrants(impliedGrants); + setCategory(category); } public RangerAccessTypeDef(RangerAccessTypeDef other) { @@ -1844,6 +1985,7 @@ public RangerAccessTypeDef(RangerAccessTypeDef other) { setLabel(other.getLabel()); setRbKeyLabel(other.getRbKeyLabel()); setImpliedGrants(other.getImpliedGrants()); + setCategory((other.getCategory())); } /** @@ -1928,6 +2070,21 @@ public void setImpliedGrants(Collection impliedGrants) { } } + public AccessTypeCategory getCategory() { + return category; + } + + public void setCategory(AccessTypeCategory category) { + this.category = category; + } + + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + label = StringUtil.dedupString(label, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + impliedGrants = StringUtil.dedupStringsCollection(impliedGrants, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -1953,6 +2110,7 @@ public StringBuilder toString(StringBuilder sb) { } } sb.append("} "); + sb.append("category={").append(category).append("} "); sb.append("}"); @@ -2015,10 +2173,8 @@ public boolean equals(Object obj) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerPolicyConditionDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -2226,6 +2382,20 @@ public void setRbKeyValidationMessage(String rbKeyValidationMessage) { this.rbKeyValidationMessage = rbKeyValidationMessage; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + evaluator = StringUtil.dedupString(evaluator, strTbl); + evaluatorOptions = StringUtil.dedupStringsMap(evaluatorOptions, strTbl); + validationRegEx = StringUtil.dedupString(validationRegEx, strTbl); + validationMessage = StringUtil.dedupString(validationMessage, strTbl); + uiHint = StringUtil.dedupString(uiHint, strTbl); + label = StringUtil.dedupString(label, strTbl); + description = StringUtil.dedupString(description, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + rbKeyDescription = StringUtil.dedupString(rbKeyDescription, strTbl); + rbKeyValidationMessage = StringUtil.dedupString(rbKeyValidationMessage, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -2370,10 +2540,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerContextEnricherDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -2449,6 +2617,12 @@ public void setEnricherOptions(Map enricherOptions) { this.enricherOptions = enricherOptions == null ? new HashMap() : enricherOptions; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + enricher = StringUtil.dedupString(enricher, strTbl); + enricherOptions = StringUtil.dedupStringsMap(enricherOptions, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -2521,10 +2695,8 @@ public boolean equals(Object obj) { @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerDataMaskDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -2611,6 +2783,26 @@ public void setResources(List resources) { } } + public void dedupStrings(Map strTbl) { + if (maskTypes != null) { + for (RangerDataMaskTypeDef maskType : maskTypes) { + maskType.dedupStrings(strTbl); + } + } + + if (accessTypes != null) { + for (RangerAccessTypeDef accessType : accessTypes) { + accessType.dedupStrings(strTbl); + } + } + + if (resources != null) { + for (RangerResourceDef resource : resources) { + resource.dedupStrings(strTbl); + } + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -2698,10 +2890,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerDataMaskTypeDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -2852,6 +3042,16 @@ public void setRbKeyDescription(String rbKeyDescription) { this.rbKeyDescription = rbKeyDescription; } + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + label = StringUtil.dedupString(label, strTbl); + description = StringUtil.dedupString(description, strTbl); + transformer = StringUtil.dedupString(transformer, strTbl); + dataMaskOptions = StringUtil.dedupStringsMap(dataMaskOptions, strTbl); + rbKeyLabel = StringUtil.dedupString(rbKeyLabel, strTbl); + rbKeyDescription = StringUtil.dedupString(rbKeyDescription, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -2949,10 +3149,8 @@ public boolean equals(Object obj) { } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RangerRowFilterDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -3015,6 +3213,20 @@ public void setResources(List resources) { } } + public void dedupStrings(Map strTbl) { + if (accessTypes != null) { + for (RangerAccessTypeDef accessType : accessTypes) { + accessType.dedupStrings(strTbl); + } + } + + if (resources != null) { + for (RangerResourceDef resource : resources) { + resource.dedupStrings(strTbl); + } + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceHeaderInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceHeaderInfo.java new file mode 100644 index 0000000000..589a279d1e --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceHeaderInfo.java @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; + +import static org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME; + +@JsonAutoDetect(getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE, fieldVisibility = Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RangerServiceHeaderInfo extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String displayName; + private String type; + private Boolean isTagService; + private Boolean isGdsService; + + public RangerServiceHeaderInfo() { + this(-1L, "", false, false); + } + + public RangerServiceHeaderInfo(Long id, String name, boolean isTagService) { + this(id, name, isTagService, false); + } + + public RangerServiceHeaderInfo(Long id, String name, boolean isTagService, boolean isGdsService) { + super(); + setId(id); + setName(name); + setIsTagService(isTagService); + setIsGdsService(isGdsService); + } + + public RangerServiceHeaderInfo(Long id, String name, String displayName, String type) { + super(); + setId(id); + setName(name); + setDisplayName(displayName); + setType(type); + setIsTagService(EMBEDDED_SERVICEDEF_TAG_NAME.equals(type)); + } + + public RangerServiceHeaderInfo(Long id, String name, String displayName, String type, Boolean isEnabled) { + super(); + setId(id); + setName(name); + setDisplayName(displayName); + setType(type); + setIsTagService(EMBEDDED_SERVICEDEF_TAG_NAME.equals(type)); + setIsEnabled(isEnabled); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Boolean getIsTagService() { + return isTagService; + } + + public void setIsTagService(Boolean isTagService) { + this.isTagService = isTagService; + } + + public Boolean getIsGdsService() { + return isGdsService; + } + + public void setIsGdsService(Boolean isGdsService) { + this.isGdsService = isGdsService; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResource.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResource.java index 67230c6deb..cfb496ee5a 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResource.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResource.java @@ -19,44 +19,51 @@ package org.apache.ranger.plugin.model; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; -import java.util.HashMap; +import org.apache.ranger.authorization.utils.StringUtil; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.util.Map; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerServiceResource extends RangerBaseModelObject { private static final long serialVersionUID = 1L; private String serviceName; private Map resourceElements; + private String ownerUser; private String resourceSignature; + private Map additionalInfo; - public RangerServiceResource(String guid, String serviceName, Map resourceElements, String resourceSignature) { + public RangerServiceResource(String guid, String serviceName, Map resourceElements, String resourceSignature, String ownerUser, Map additionalInfo) { super(); setGuid(guid); setServiceName(serviceName); setResourceElements(resourceElements); setResourceSignature(resourceSignature); + setOwnerUser(ownerUser); + setAdditionalInfo(additionalInfo); + } + public RangerServiceResource(String guid, String serviceName, Map resourceElements, String resourceSignature, String ownerUser) { + this(guid, serviceName, resourceElements, resourceSignature,ownerUser, null); + } + public RangerServiceResource(String guid, String serviceName, Map resourceElements, String resourceSignature) { + this(guid, serviceName, resourceElements, resourceSignature, null); + } + public RangerServiceResource(String guid, String serviceName, Map resourceElements) { - this(guid, serviceName, resourceElements, null); + this(guid, serviceName, resourceElements, null, null); } public RangerServiceResource(String serviceName, Map resourceElements) { - this(null, serviceName, resourceElements, null); + this(null, serviceName, resourceElements, null, null); } public RangerServiceResource() { - this(null, null, null, null); + this(null, null, null, null, null); } public String getServiceName() { return serviceName; } @@ -67,18 +74,43 @@ public String getResourceSignature() { return resourceSignature; } + public String getOwnerUser() { + return ownerUser; + } + + public Map getAdditionalInfo() { + return additionalInfo; + } + public void setServiceName(String serviceName) { this.serviceName = serviceName; } public void setResourceElements(Map resource) { - this.resourceElements = resource == null ? new HashMap() : resource; + this.resourceElements = resource; } public void setResourceSignature(String resourceSignature) { this.resourceSignature = resourceSignature; } + public void setOwnerUser(String ownerUser) { + this.ownerUser = ownerUser; + } + + public void setAdditionalInfo(Map additionalInfo) { + this.additionalInfo = additionalInfo; + } + + public void dedupStrings(Map strTbl) { + super.dedupStrings(strTbl); + + serviceName = StringUtil.dedupString(serviceName, strTbl); + resourceElements = StringUtil.dedupStringsMapOfPolicyResource(resourceElements, strTbl); + ownerUser = StringUtil.dedupString(ownerUser, strTbl); + additionalInfo = StringUtil.dedupStringsMap(additionalInfo, strTbl); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -107,6 +139,16 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); + sb.append("ownerUser={").append(ownerUser).append("} "); + + sb.append("additionalInfo={"); + if(additionalInfo != null) { + for(Map.Entry e : additionalInfo.entrySet()) { + sb.append(e.getKey()).append("={").append(e.getValue()).append("} "); + } + } + sb.append("} "); + sb.append("resourceSignature={").append(resourceSignature).append("} "); sb.append(" }"); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResourceWithTags.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResourceWithTags.java new file mode 100755 index 0000000000..ac3d24f755 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceResourceWithTags.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerServiceResourceWithTags extends RangerServiceResource implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private List associatedTags; + + public List getAssociatedTags() { + return associatedTags; + } + + public void setAssociatedTags(List associatedTags) { + this.associatedTags = associatedTags; + } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerServiceResourceWithTags={ "); + + super.toString(sb); + + sb.append("associatedTags=["); + if (associatedTags != null) { + String prefix = ""; + + for (RangerTag associatedTag : associatedTags) { + sb.append(prefix); + + associatedTag.toString(sb); + + prefix = ", "; + } + } + sb.append("] "); + + sb.append(" }"); + + return sb; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceTags.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceTags.java new file mode 100644 index 0000000000..3a26339741 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceTags.java @@ -0,0 +1,210 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + + +import org.apache.ranger.plugin.util.ServiceTags; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JsonAutoDetect(fieldVisibility=Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerServiceTags implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + public static final String OP_SET = "set"; // set tags for the given serviceResources + public static final String OP_DELETE = "delete"; // delete tags associated with the given serviceResources + public static final String OP_REPLACE = "replace"; // replace all resources and tags in the given serviceName with the given serviceResources and tags + + private String op = OP_SET; + private String serviceName; + private Map tagDefinitions; + private Map tags; + private List serviceResources; + private Map> resourceToTagIds; + private Long tagVersion; // read-only field + private Date tagUpdateTime; // read-only field + + public RangerServiceTags() { + this(OP_SET, null, null, null, null, null, null, null); + } + + public RangerServiceTags(String op, String serviceName, Map tagDefinitions, + Map tags, List serviceResources, + Map> resourceToTagIds, Long tagVersion, Date tagUpdateTime) { + setOp(op); + setServiceName(serviceName); + setTagDefinitions(tagDefinitions); + setTags(tags); + setServiceResources(serviceResources); + setResourceToTagIds(resourceToTagIds); + setTagVersion(tagVersion); + setTagUpdateTime(tagUpdateTime); + } + + /** + * @return the op + */ + public String getOp() { + return op; + } + + /** + * @return the serviceName + */ + public String getServiceName() { + return serviceName; + } + + /** + * @param op the op to set + */ + public void setOp(String op) { + this.op = op; + } + + /** + * @param serviceName the serviceName to set + */ + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public Map getTagDefinitions() { + return tagDefinitions; + } + + public void setTagDefinitions(Map tagDefinitions) { + this.tagDefinitions = tagDefinitions == null ? new HashMap<>() : tagDefinitions; + } + + public Map getTags() { + return tags; + } + + public void setTags(Map tags) { + this.tags = tags == null ? new HashMap<>() : tags; + } + + public List getServiceResources() { + return serviceResources; + } + + public void setServiceResources(List serviceResources) { + this.serviceResources = serviceResources == null ? new ArrayList<>() : serviceResources; + } + + public Map> getResourceToTagIds() { + return resourceToTagIds; + } + + public void setResourceToTagIds(Map> resourceToTagIds) { + this.resourceToTagIds = resourceToTagIds == null ? new HashMap<>() : resourceToTagIds; + } + + public Long getTagVersion() { + return tagVersion; + } + + public void setTagVersion(Long tagVersion) { + this.tagVersion = tagVersion; + } + + public Date getTagUpdateTime() { + return tagUpdateTime; + } + + public void setTagUpdateTime(Date tagUpdateTime) { + this.tagUpdateTime = tagUpdateTime; + } + + + @Override + public String toString( ) { + StringBuilder sb = new StringBuilder(); + + toString(sb); + + return sb.toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerServiceTags={") + .append("op=").append(op).append(", ") + .append("serviceName=").append(serviceName).append(", ") + .append("}"); + + return sb; + } + + public static ServiceTags toServiceTags(RangerServiceTags tags) { + ServiceTags ret = null; + + if (tags != null) { + ret = new ServiceTags(toServiceTagsOp(tags.getOp()), tags.getServiceName(), + tags.tagVersion, tags.getTagUpdateTime(), tags.getTagDefinitions(), tags.getTags(), + tags.getServiceResources(), tags.getResourceToTagIds(), false, + ServiceTags.TagsChangeExtent.ALL, false); + } + + return ret; + } + + public static RangerServiceTags toRangerServiceTags(ServiceTags tags) { + RangerServiceTags ret = null; + + if (tags != null) { + ret = new RangerServiceTags(toRangerServiceTagsOp(tags.getOp()), tags.getServiceName(), + tags.getTagDefinitions(), tags.getTags(), tags.getServiceResources(), + tags.getResourceToTagIds(), tags.getTagVersion(), tags.getTagUpdateTime()); + } + + return ret; + } + + private static String toServiceTagsOp(String rangerServiceTagsOp) { + String ret = rangerServiceTagsOp; + + if (RangerServiceTags.OP_SET.equals(rangerServiceTagsOp)) { + ret = ServiceTags.OP_ADD_OR_UPDATE; + } + + return ret; + } + + private static String toRangerServiceTagsOp(String serviceTagsOp) { + String ret = serviceTagsOp; + + if (ServiceTags.OP_ADD_OR_UPDATE.equals(serviceTagsOp)) { + ret = RangerServiceTags.OP_SET; + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java index 9e620c32c8..8001b09147 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTag.java @@ -19,23 +19,17 @@ package org.apache.ranger.plugin.model; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; -import java.util.ArrayList; -import java.util.HashMap; +import org.apache.ranger.authorization.utils.StringUtil; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.util.List; import java.util.Map; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerTag extends RangerBaseModelObject { private static final long serialVersionUID = 1L; @@ -45,7 +39,7 @@ public class RangerTag extends RangerBaseModelObject { public static final String OPTION_TAG_VALIDITY_PERIODS = "TAG_VALIDITY_PERIODS"; private String type; - private Short owner = OWNER_SERVICERESOURCE; + private Short owner; private Map attributes; private Map options; private List validityPeriods; @@ -87,7 +81,7 @@ public Map getAttributes() { } public void setAttributes(Map attributes) { - this.attributes = attributes == null ? new HashMap() : attributes; + this.attributes = attributes; } public Short getOwner() { @@ -101,36 +95,21 @@ public void setOwner(Short owner) { public Map getOptions() { return options; } public void setOptions(Map options) { - if (this.options == null) { - this.options = new HashMap<>(); - } - if (this.options == options) { - return; - } - this.options.clear(); - - if(options != null) { - for(Map.Entry e : options.entrySet()) { - this.options.put(e.getKey(), e.getValue()); - } - } + this.options = options; } public List getValidityPeriods() { return validityPeriods; } public void setValidityPeriods(List validityPeriods) { - if (this.validityPeriods == null) { - this.validityPeriods = new ArrayList<>(); - } - if (this.validityPeriods == validityPeriods) { - return; - } - this.validityPeriods.clear(); + this.validityPeriods = validityPeriods; + } - if(validityPeriods != null) { - this.validityPeriods.addAll(validityPeriods); - } + public void dedupStrings(Map strTbl) { + type = StringUtil.dedupString(type, strTbl); + attributes = StringUtil.dedupStringsMap(attributes, strTbl); + options = StringUtil.dedupStringsMapOfObject(options, strTbl); } + @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -158,6 +137,16 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); + sb.append("options={"); + if (options != null) { + for (Map.Entry e : options.entrySet()) { + sb.append(e.getKey()).append("={"); + sb.append(e.getValue()); + sb.append("} "); + } + } + sb.append("} "); + if (validityPeriods != null) { sb.append("validityPeriods={").append(validityPeriods).append("} "); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagDef.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagDef.java index c787beca59..088d3de180 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagDef.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagDef.java @@ -19,15 +19,14 @@ package org.apache.ranger.plugin.model; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; -import java.util.ArrayList; +import org.apache.ranger.authorization.utils.StringUtil; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.util.List; +import java.util.Map; +import java.util.Objects; /** @@ -36,10 +35,8 @@ * */ @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerTagDef extends RangerBaseModelObject { private static final long serialVersionUID = 1L; @@ -85,7 +82,42 @@ public List getAttributeDefs() { } public void setAttributeDefs(List attributeDefs) { - this.attributeDefs = attributeDefs == null ? new ArrayList() : attributeDefs; + this.attributeDefs = attributeDefs; + } + + public void dedupStrings(Map strTbl) { + super.dedupStrings(strTbl); + + name = StringUtil.dedupString(name, strTbl); + source = StringUtil.dedupString(source, strTbl); + + if (attributeDefs != null) { + for (RangerTagAttributeDef attributeDef : attributeDefs) { + attributeDef.dedupStrings(strTbl); + } + } + } + + @Override + public int hashCode() { + return Objects.hash(name, source, attributeDefs); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } + + RangerTagDef other = (RangerTagDef) obj; + + return Objects.equals(name, other.name) && + Objects.equals(source, other.source) && + Objects.equals(attributeDefs, other.attributeDefs); } /** @@ -96,11 +128,8 @@ public void setAttributeDefs(List attributeDefs) { */ @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) - public static class RangerTagAttributeDef implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -130,5 +159,31 @@ public void setName(String name) { public void setType(String type) { this.type = type == null ? "" : type; } + + public void dedupStrings(Map strTbl) { + name = StringUtil.dedupString(name, strTbl); + type = StringUtil.dedupString(type, strTbl); + } + + @Override + public int hashCode() { + return Objects.hash(name, type); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } + + RangerTagAttributeDef other = (RangerTagAttributeDef) obj; + + return Objects.equals(name, other.name) && + Objects.equals(type, other.type); + } } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagResourceMap.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagResourceMap.java index a41bf18997..eed216c24b 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagResourceMap.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerTagResourceMap.java @@ -19,19 +19,13 @@ package org.apache.ranger.plugin.model; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerTagResourceMap extends RangerBaseModelObject { private static final long serialVersionUID = 1L; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java index 23b4a8c6f8..0f7cb7a22e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValidityRecurrence.java @@ -19,28 +19,20 @@ package org.apache.ranger.plugin.model; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) - public class RangerValidityRecurrence implements Serializable { @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class ValidityInterval { private final int days; private final int hours; @@ -81,10 +73,8 @@ public String toString() { } @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class RecurrenceSchedule { static final String PERMITTED_SPECIAL_CHARACTERS = "*,-"; static final String PERMITTED_SPECIAL_CHARACTERS_FOR_MINUTES = ","; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java index d2271aec5e..066692d5f4 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerValiditySchedule.java @@ -20,14 +20,10 @@ package org.apache.ranger.plugin.model; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; import java.io.Serializable; import java.util.ArrayList; @@ -35,11 +31,8 @@ import java.util.List; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) - public class RangerValiditySchedule implements Serializable { public static final String VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION = "yyyy/MM/dd HH:mm:ss"; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/ServiceDeleteResponse.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/ServiceDeleteResponse.java new file mode 100644 index 0000000000..42802e4dab --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/ServiceDeleteResponse.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; + +@JsonAutoDetect(fieldVisibility=Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class ServiceDeleteResponse implements java.io.Serializable { + /** + * + */ + private static final long serialVersionUID = 1L; + + private String serviceName; + private Long serviceId; + private Boolean isDeleted; + private String errorMsg; + + public ServiceDeleteResponse(Long serviceId) { + this.serviceId = serviceId; + } + + public String getServiceName() { + return serviceName; + } + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + public Long getServiceId() { + return serviceId; + } + public void setServiceId(Long serviceId) { + this.serviceId = serviceId; + } + public Boolean getIsDeleted() { + return isDeleted; + } + public void setIsDeleted(Boolean isDeleted) { + this.isDeleted = isDeleted; + } + public String getErrorMsg() { + return errorMsg; + } + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java index 3e933e8fa8..50bcf301a0 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/UserInfo.java @@ -20,20 +20,15 @@ package org.apache.ranger.plugin.model; import org.apache.ranger.plugin.util.RangerUserStoreUtil; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.util.*; @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class UserInfo extends RangerBaseModelObject implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -92,4 +87,4 @@ public String toString() { + ", groups=" + groups + "}"; } -} \ No newline at end of file +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java index fb0afbafbd..d73d91b361 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerPolicyValidator.java @@ -23,8 +23,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.errors.ValidationErrorCode; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem; @@ -40,10 +38,12 @@ import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerValiditySchedule; import org.apache.ranger.plugin.store.ServiceStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerPolicyValidator extends RangerValidator { - private static final Log LOG = LogFactory.getLog(RangerPolicyValidator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPolicyValidator.class); public RangerPolicyValidator(ServiceStore store) { super(store); @@ -168,6 +168,7 @@ boolean isValid(RangerPolicy policy, Action action, boolean isAdmin, List tagSvcList = zone.getTagServices(); - Set svcNameSet = zone.getServices().keySet(); - if(!svcNameSet.contains(serviceName) && !tagSvcList.contains(serviceName)){ - ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_SERVICE_NOT_ASSOCIATED_TO_ZONE; - failures.add(new ValidationFailureDetailsBuilder() - .field("zoneName") - .isSemanticallyIncorrect() - .becauseOf(error.getMessage(serviceName, zoneName)) - .errorCode(error.getErrorCode()) - .build()); - valid = false; + } else { + List tagSvcList = zone.getTagServices(); + Set svcNameSet = zone.getServices().keySet(); + if (!svcNameSet.contains(serviceName) && !tagSvcList.contains(serviceName)) { + ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_SERVICE_NOT_ASSOCIATED_TO_ZONE; + failures.add(new ValidationFailureDetailsBuilder().field("zoneName").isSemanticallyIncorrect().becauseOf(error.getMessage(serviceName, zoneName)).errorCode(error.getErrorCode()).build()); + valid = false; + } } } @@ -398,6 +411,18 @@ boolean isValidAccessTypeDef(RangerPolicy policy, final List> additionalResources = policy.getAdditionalResources(); + + if (additionalResources != null) { + for (Map additionalResource : additionalResources) { + valid = isValidResourceValues(additionalResource, failures, serviceDef) && valid; + valid = isValidResourceFlags(additionalResource, failures, serviceDef.getResources(), serviceDef.getName(), policy.getName(), isAdmin) && valid; + } + } } } @@ -522,25 +568,22 @@ boolean isPolicyResourceUnique(RangerPolicy policy, final List policies = getPoliciesForResourceSignature(policy.getService(), signature); - if (CollectionUtils.isNotEmpty(policies)) { - ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_DUPLICATE_POLICY_RESOURCE; - RangerPolicy matchedPolicy = policies.iterator().next(); - // there shouldn't be a matching policy for create. During update only match should be to itself - if (action == Action.CREATE || (action == Action.UPDATE && (policies.size() > 1 || !matchedPolicy.getId().equals(policy.getId())))) { - failures.add(new ValidationFailureDetailsBuilder() - .field("resources") - .isSemanticallyIncorrect() - .becauseOf(error.getMessage(matchedPolicy.getName(), policy.getService())) - .errorCode(error.getErrorCode()) - .build()); - valid = false; - } + + RangerPolicyResourceSignature policySignature = _factory.createPolicyResourceSignature(policy); + String signature = policySignature.getSignature(); + List policies = getPoliciesForResourceSignature(policy.getService(), signature); + if (CollectionUtils.isNotEmpty(policies)) { + ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_DUPLICATE_POLICY_RESOURCE; + RangerPolicy matchedPolicy = policies.iterator().next(); + // there shouldn't be a matching policy for create. During update only match should be to itself + if (action == Action.CREATE || (action == Action.UPDATE && !matchedPolicy.getId().equals(policy.getId()))) { + failures.add(new ValidationFailureDetailsBuilder() + .field("resources") + .isSemanticallyIncorrect() + .becauseOf(error.getMessage(matchedPolicy.getName(), policy.getService())) + .errorCode(error.getErrorCode()) + .build()); + valid = false; } } @@ -551,17 +594,43 @@ boolean isPolicyResourceUnique(RangerPolicy policy, final List failures, final RangerServiceDef serviceDef) { - - if(LOG.isDebugEnabled()) { + + if (LOG.isDebugEnabled()) { LOG.debug(String.format("==> RangerPolicyValidator.isValidResourceNames(%s, %s, %s)", policy, failures, serviceDef)); } boolean valid = true; - convertPolicyResourceNamesToLower(policy); - Set policyResources = policy.getResources().keySet(); + + Map resources = policy.getResources(); + + if (resources != null) { + valid = isValidResourceNames(resources, failures, serviceDef, policy.getPolicyType()) && valid; + + List> additionalResources = policy.getAdditionalResources(); + + if (additionalResources != null) { + for (Map additionalResource : additionalResources) { + valid = isValidResourceNames(additionalResource, failures, serviceDef, policy.getPolicyType()) && valid; + } + } + + } + + if(LOG.isDebugEnabled()) { + LOG.debug(String.format("<== RangerPolicyValidator.isValidResourceNames(%s, %s, %s): %s", policy, failures, serviceDef, valid)); + } + + return valid; + } + + boolean isValidResourceNames(Map resources, List failures, RangerServiceDef serviceDef, Integer policyType) { + boolean valid = true; + + convertPolicyResourceNamesToLower(resources); + Set policyResources = resources.keySet(); RangerServiceDefHelper defHelper = new RangerServiceDefHelper(serviceDef); - Set> hierarchies = defHelper.getResourceHierarchies(policy.getPolicyType()); // this can be empty but not null! + Set> hierarchies = defHelper.getResourceHierarchies(policyType); // this can be empty but not null! if (hierarchies.isEmpty()) { if (LOG.isDebugEnabled()) { LOG.debug("RangerPolicyValidator.isValidResourceNames: serviceDef does not have any resource hierarchies, possibly due to invalid service def!!"); @@ -636,7 +705,7 @@ boolean isValidResourceNames(final RangerPolicy policy, final List resourceMap, Lis String name = entry.getKey(); RangerPolicyResource policyResource = entry.getValue(); if(policyResource != null) { - if(CollectionUtils.isNotEmpty(policyResource.getValues())) { - Set resources = new HashSet<>(policyResource.getValues()); - for (String aValue : resources) { - if (StringUtils.isBlank(aValue)) { - policyResource.getValues().remove(aValue); - } - } - } - + policyResource.getValues().removeIf(StringUtils::isBlank); if(CollectionUtils.isEmpty(policyResource.getValues())){ ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_MISSING_RESOURCE_LIST; if(LOG.isDebugEnabled()) { @@ -833,23 +894,40 @@ boolean isValidResourceValues(Map resourceMap, Lis .build()); valid=false; } - - if (validationRegExMap.containsKey(name) && CollectionUtils.isNotEmpty(policyResource.getValues())) { - String regEx = validationRegExMap.get(name); - for (String aValue : policyResource.getValues()) { - if (!aValue.matches(regEx)) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("Resource failed regex check: value[%s], resource-name[%s], regEx[%s], service-def-name[%s]", aValue, name, regEx, serviceDef.getName())); - } - ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_VALUE_REGEX; - failures.add(new ValidationFailureDetailsBuilder() + else{ + String duplicateValue = getDuplicate(policyResource.getValues()); + if (!StringUtils.isBlank(duplicateValue)){ + ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_DUPLICATE_VALUES_FOR_RESOURCE; + if (LOG.isDebugEnabled()){ + LOG.debug(String.format("Duplicate values found for the resource name[%s] value[%s] service-def-name[%s]",name, duplicateValue,serviceDef.getName())); + } + failures.add(new ValidationFailureDetailsBuilder() .field("resource-values") .subField(name) .isSemanticallyIncorrect() - .becauseOf(error.getMessage(aValue, name)) + .becauseOf(error.getMessage(name, duplicateValue)) .errorCode(error.getErrorCode()) - .build()); - valid = false; + .build() + ); + valid = false; + } + if (validationRegExMap.containsKey(name)) { + String regEx = validationRegExMap.get(name); + for (String aValue : policyResource.getValues()) { + if (!aValue.matches(regEx)) { + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Resource failed regex check: value[%s], resource-name[%s], regEx[%s], service-def-name[%s]", aValue, name, regEx, serviceDef.getName())); + } + ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_INVALID_RESOURCE_VALUE_REGEX; + failures.add(new ValidationFailureDetailsBuilder() + .field("resource-values") + .subField(name) + .isSemanticallyIncorrect() + .becauseOf(error.getMessage(aValue, name)) + .errorCode(error.getErrorCode()) + .build()); + valid = false; + } } } } @@ -862,6 +940,28 @@ boolean isValidResourceValues(Map resourceMap, Lis return valid; } + private String getDuplicate(List values) { + String duplicate = ""; + if (values!=null) { + HashSet set = new HashSet<>(); + for (String val:values){ + if (set.contains(val)){ + duplicate = val; + break; + } + set.add(val); + } + } + return duplicate; + } + private static void removeDuplicates(List values){ + if (values==null || values.isEmpty()){ + return; + } + HashSet uniqueElements = new HashSet<>(); + values.removeIf(e -> !uniqueElements.add(e)); + } + boolean isValidPolicyItems(List policyItems, List failures, RangerServiceDef serviceDef) { if(LOG.isDebugEnabled()) { LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", policyItems, failures, serviceDef)); @@ -898,7 +998,9 @@ boolean isValidPolicyItem(RangerPolicyItem policyItem, List RangerPolicyValidator.isValid(%s, %s, %s)", policyItem, failures, serviceDef)); } - + + List invalidItems = new ArrayList(Arrays.asList("null", "NULL", "Null", null)); + boolean valid = true; if (policyItem == null) { LOG.debug("policy item was null!"); @@ -924,12 +1026,46 @@ boolean isValidPolicyItem(RangerPolicyItem policyItem, List accesses, List accessTypes = getAccessTypes(serviceDef); - for (RangerPolicyItemAccess access : accesses) { + Set uniqueAccesses = new HashSet<>(); + Iterator accessTypeIterator = accesses.iterator(); + while (accessTypeIterator.hasNext()) { + RangerPolicyItemAccess access = accessTypeIterator.next(); if (access == null) { ValidationErrorCode error = ValidationErrorCode.POLICY_VALIDATION_ERR_NULL_POLICY_ITEM_ACCESS; failures.add(new ValidationFailureDetailsBuilder() @@ -961,7 +1100,12 @@ boolean isValidItemAccesses(List accesses, List RangerPolicyValidator.validate(%s, %s)", securityZone, action)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.validate(%s, %s)", securityZone, action)); } List failures = new ArrayList<>(); + boolean valid = isValid(securityZone, action, failures); - boolean valid = isValid(securityZone, action, failures); - - String message; try { if (!valid) { - message = serializeFailures(failures); + String message = serializeFailures(failures); + throw new Exception(message); } - } finally { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.validate(%s, %s)", securityZone, action)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.validate(%s, %s)", securityZone, action)); } } } @@ -82,7 +88,7 @@ public void validate(RangerSecurityZone securityZone, Action action) throws Exce @Override boolean isValid(String name, Action action, List failures) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", name, action, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.isValid(%s, %s, %s)", name, action, failures)); } boolean ret = true; @@ -92,24 +98,20 @@ boolean isValid(String name, Action action, List failu failures.add(new ValidationFailureDetailsBuilder().isAnInternalError().becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build()); ret = false; - } else { - if (StringUtils.isEmpty(name)) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_FIELD; + } else if (StringUtils.isEmpty(name)) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_FIELD; - failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone name was null/missing").field("name").isMissing().errorCode(error.getErrorCode()).becauseOf(error.getMessage("name")).build()); - ret = false; - } else { - if (getSecurityZone(name) == null) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_ZONE_ID; + failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone name was null/missing").field("name").isMissing().errorCode(error.getErrorCode()).becauseOf(error.getMessage("name")).build()); + ret = false; + } else if (getSecurityZone(name) == null) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_ZONE_ID; - failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone does not exist").field("name").errorCode(error.getErrorCode()).becauseOf(error.getMessage(name)).build()); - ret = false; - } - } + failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone does not exist").field("name").errorCode(error.getErrorCode()).becauseOf(error.getMessage(name)).build()); + ret = false; } if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s) : %s", name, action, failures, ret)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.isValid(%s, %s, %s) : %s", name, action, failures, ret)); } return ret; @@ -118,7 +120,7 @@ boolean isValid(String name, Action action, List failu @Override boolean isValid(Long id, Action action, List failures) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", id, action, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.isValid(%s, %s, %s)", id, action, failures)); } boolean ret = true; @@ -134,32 +136,31 @@ boolean isValid(Long id, Action action, List failures) failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone id was null/missing").field("id").isMissing().errorCode(error.getErrorCode()).becauseOf(error.getMessage("id")).build()); ret = false; } else if (getSecurityZone(id) == null) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_ZONE_ID; + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_ZONE_ID; - failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone id does not exist").field("id").errorCode(error.getErrorCode()).becauseOf(error.getMessage(id)).build()); - ret = false; + failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone id does not exist").field("id").errorCode(error.getErrorCode()).becauseOf(error.getMessage(id)).build()); + ret = false; } if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.isValid(%s, %s, %s) : %s", id, action, failures, ret)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.isValid(%s, %s, %s) : %s", id, action, failures, ret)); } return ret; } - boolean isValid(RangerSecurityZone securityZone, Action action, List failures) { + private boolean isValid(RangerSecurityZone securityZone, Action action, List failures) { if(LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.isValid(%s, %s, %s)", securityZone, action, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.isValid(%s, %s, %s)", securityZone, action, failures)); } if (!(action == Action.CREATE || action == Action.UPDATE)) { - throw new IllegalArgumentException("isValid(RangerPolicy, ...) is only supported for create/update"); + throw new IllegalArgumentException("isValid(RangerSecurityZone, ...) is only supported for create/update"); } - boolean ret = true; - - RangerSecurityZone existingZone; + boolean ret = true; final String zoneName = securityZone.getName(); + if (StringUtils.isEmpty(StringUtils.trim(zoneName))) { ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_FIELD; @@ -167,9 +168,13 @@ boolean isValid(RangerSecurityZone securityZone, Action action, List failures) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.validateWithinSecurityZone(%s, %s, %s)", securityZone, action, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.validateWithinSecurityZone(%s, %s, %s)", securityZone, action, failures)); } boolean ret = true; - // Validate each service for existence, not being tag-service and each resource-spec for validity - if (MapUtils.isNotEmpty(securityZone.getServices())) { - for (Map.Entry serviceSpecification : securityZone.getServices().entrySet()) { - String serviceName = serviceSpecification.getKey(); - RangerSecurityZone.RangerSecurityZoneService securityZoneService = serviceSpecification.getValue(); - - ret = ret && validateSecurityZoneService(serviceName, securityZoneService, failures); - } - } else { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_SERVICES; + // admin users, user-groups and roles collections can't be empty + if (CollectionUtils.isEmpty(securityZone.getAdminUsers()) && CollectionUtils.isEmpty(securityZone.getAdminUserGroups()) && CollectionUtils.isEmpty(securityZone.getAdminRoles())) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS_AND_ROLES; - failures.add(new ValidationFailureDetailsBuilder().becauseOf("security zone services").isMissing().field("services").errorCode(error.getErrorCode()).becauseOf(error.getMessage(securityZone.getName())).build()); + failures.add(new ValidationFailureDetailsBuilder().field("security zone admin users/user-groups/roles").isMissing().becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build()); ret = false; } - // both admin users and user-groups collections can't be empty - if (CollectionUtils.isEmpty(securityZone.getAdminUsers()) && CollectionUtils.isEmpty(securityZone.getAdminUserGroups())) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS; + // audit users, user-groups and roles collections can't be empty + if (CollectionUtils.isEmpty(securityZone.getAuditUsers()) && CollectionUtils.isEmpty(securityZone.getAuditUserGroups()) && CollectionUtils.isEmpty(securityZone.getAuditRoles())) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS_AND_ROLES; - failures.add(new ValidationFailureDetailsBuilder().field("security zone admin users/user-groups").isMissing().becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build()); + failures.add(new ValidationFailureDetailsBuilder().field("security zone audit users/user-groups/roles").isMissing().becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build()); ret = false; } - // both audit users and user-groups collections can't be empty - if (CollectionUtils.isEmpty(securityZone.getAuditUsers()) && CollectionUtils.isEmpty(securityZone.getAuditUserGroups())) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS; - failures.add(new ValidationFailureDetailsBuilder().field("security zone audit users/user-groups").isMissing().becauseOf(error.getMessage()).errorCode(error.getErrorCode()).build()); - ret = false; + // Validate each service for existence, not being tag-service and each resource-spec for validity + if (MapUtils.isNotEmpty(securityZone.getServices())) { + for (Map.Entry entry : securityZone.getServices().entrySet()) { + String serviceName = entry.getKey(); + RangerSecurityZoneService securityZoneService = entry.getValue(); + + ret = validateSecurityZoneService(serviceName, securityZoneService, failures) && ret; + } } - if (securityZone.getServices() != null) { - for (Map.Entry serviceResouceMapEntry : securityZone.getServices() - .entrySet()) { - if (serviceResouceMapEntry.getValue().getResources() != null) { - for (Map> resource : serviceResouceMapEntry.getValue().getResources()) { - if (resource != null) { - for (Map.Entry> entry : resource.entrySet()) { - if (CollectionUtils.isEmpty(entry.getValue())) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_RESOURCES; - failures.add(new ValidationFailureDetailsBuilder().field("security zone resources") - .subField("resources").isMissing() - .becauseOf(error.getMessage(serviceResouceMapEntry.getKey())) - .errorCode(error.getErrorCode()).build()); - ret = false; - } - } - } - } - } - } - } if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.validateWithinSecurityZone(%s, %s, %s) : %s", securityZone, action, failures, ret)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.validateWithinSecurityZone(%s, %s, %s) : %s", securityZone, action, failures, ret)); } + return ret; } private boolean validateAgainstAllSecurityZones(RangerSecurityZone securityZone, Action action, List failures) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.validateAgainstAllSecurityZones(%s, %s, %s)", securityZone, action, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.validateAgainstAllSecurityZones(%s, %s, %s)", securityZone, action, failures)); } - boolean ret = true; - + boolean ret = true; final String zoneName; if (securityZone.getId() != -1L) { RangerSecurityZone existingZone = getSecurityZone(securityZone.getId()); + zoneName = existingZone.getName(); } else { zoneName = securityZone.getName(); } - for (Map.Entry entry: securityZone.getServices().entrySet()) { - String serviceName = entry.getKey(); - RangerSecurityZone.RangerSecurityZoneService serviceResources = entry.getValue(); + for (Map.Entry entry: securityZone.getServices().entrySet()) { + String serviceName = entry.getKey(); + RangerSecurityZoneService securityZoneService = entry.getValue(); + + if (CollectionUtils.isEmpty(securityZoneService.getResources())) { + continue; + } - if (CollectionUtils.isNotEmpty(serviceResources.getResources())) { - SearchFilter filter = new SearchFilter(); - List zones = null; + SearchFilter filter = new SearchFilter(); + List zones = null; - filter.setParam(SearchFilter.SERVICE_NAME, serviceName); - filter.setParam(SearchFilter.ZONE_NAME, zoneName); + filter.setParam(SearchFilter.SERVICE_NAME, serviceName); + filter.setParam(SearchFilter.NOT_ZONE_NAME, zoneName); - try { - zones = securityZoneStore.getSecurityZones(filter); - } catch (Exception excp) { - LOG.error("Failed to get Security-Zones", excp); - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INTERNAL_ERROR; + try { + zones = securityZoneStore.getSecurityZones(filter); + } catch (Exception excp) { + LOG.error("Failed to get Security-Zones", excp); + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INTERNAL_ERROR; - failures.add(new ValidationFailureDetailsBuilder().becauseOf(error.getMessage(excp.getMessage())).errorCode(error.getErrorCode()).build()); - ret = false; - } + failures.add(new ValidationFailureDetailsBuilder().becauseOf(error.getMessage(excp.getMessage())).errorCode(error.getErrorCode()).build()); + ret = false; + } - if (CollectionUtils.isNotEmpty(zones)) { - RangerService service = getService(serviceName); - RangerServiceDef serviceDef = service != null ? getServiceDef(service.getType()) : null; + if (CollectionUtils.isEmpty(zones)) { + continue; + } - if (serviceDef == null) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INTERNAL_ERROR; + RangerService service = getService(serviceName); + RangerServiceDef serviceDef = service != null ? getServiceDef(service.getType()) : null; - failures.add(new ValidationFailureDetailsBuilder().becauseOf(error.getMessage(serviceName)).errorCode(error.getErrorCode()).build()); - ret = false; + if (serviceDef == null) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INTERNAL_ERROR; - } else { - zones.add(securityZone); - ret = ret && validateZoneServiceInAllZones(zones, serviceName, serviceDef, failures); - } - } + failures.add(new ValidationFailureDetailsBuilder().becauseOf(error.getMessage(serviceName)).errorCode(error.getErrorCode()).build()); + ret = false; + } else { + zones.add(securityZone); + ret = ret && validateZoneServiceInAllZones(zones, serviceName, serviceDef, failures); } } if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.validateAgainstAllSecurityZones(%s, %s, %s) : %s", securityZone, action, failures, ret)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.validateAgainstAllSecurityZones(%s, %s, %s) : %s", securityZone, action, failures, ret)); } return ret; @@ -337,67 +320,55 @@ private boolean validateAgainstAllSecurityZones(RangerSecurityZone securityZone, private boolean validateZoneServiceInAllZones(List zones, String serviceName, RangerServiceDef serviceDef, List failures) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.validateZoneServiceInAllZones(%s, %s, %s, %s)", zones, serviceName, serviceDef, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.validateZoneServiceInAllZones(%s, %s, %s, %s)", zones, serviceName, serviceDef, failures)); } - boolean ret = true; + boolean ret = true; + RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef); + List matchers = new ArrayList<>(); + Set resourceNames = new HashSet<>(); // For each zone, get list-of-resources corresponding to serviceName. // For each list-of-resources: // get one resource (this is a map of >); convert it into map of . excludes is always false, recursive true only for HDFS // build a subclass of RangerPolicyResourceEvaluator with id of zone, zoneName as a member, and RangerDefaultResourceMatcher as matcher. // add this to list-of-evaluators - - Map> matchersForResourceDef = new HashMap<>(); - for (RangerSecurityZone zone : zones) { - List>> resources = zone.getServices().get(serviceName).getResources(); + Map zoneServices = zone.getServices(); + RangerSecurityZoneService zoneService = zoneServices != null ? zoneServices.get(serviceName) : null; + List>> resources = zoneService != null ? zoneService.getResources() : null; + + if (CollectionUtils.isEmpty(resources)) { + continue; + } for (Map> resource : resources) { - Map policyResources = new HashMap<>(); + Map policyResources = new HashMap<>(); for (Map.Entry> entry : resource.entrySet()) { String resourceDefName = entry.getKey(); List resourceValues = entry.getValue(); - RangerPolicy.RangerPolicyResource policyResource = new RangerPolicy.RangerPolicyResource(); + RangerPolicyResource policyResource = new RangerPolicyResource(resourceValues, false, EmbeddedServiceDefsUtil.isRecursiveEnabled(serviceDef, resourceDefName)); - policyResource.setIsExcludes(false); - policyResource.setIsRecursive(EmbeddedServiceDefsUtil.isRecursiveEnabled(serviceDef, resourceDefName)); - policyResource.setValues(resourceValues); policyResources.put(resourceDefName, policyResource); - - if (matchersForResourceDef.get(resourceDefName) == null) { - matchersForResourceDef.put(resourceDefName, new ArrayList<>()); - } } - RangerZoneResourceMatcher matcher = new RangerZoneResourceMatcher(zone.getName(), policyResources, serviceDef); + RangerZoneResourceMatcher matcher = new RangerZoneResourceMatcher(zone.getName(), policyResources, serviceDefHelper, null); - for (String resourceDefName : resource.keySet()) { - matchersForResourceDef.get(resourceDefName).add(matcher); - } + matchers.add(matcher); + resourceNames.addAll(policyResources.keySet()); } } // Build a map of trie with list-of-evaluators with one entry corresponds to one resource-def if it exists in the list-of-resources Map> trieMap = new HashMap<>(); - List resourceDefs = serviceDef.getResources(); - - for (Map.Entry> entry : matchersForResourceDef.entrySet()) { - String resourceDefName = entry.getKey(); - List matchers = entry.getValue(); - RangerServiceDef.RangerResourceDef resourceDef = null; - for (RangerServiceDef.RangerResourceDef element : resourceDefs) { - if (StringUtils.equals(element.getName(), resourceDefName)) { - resourceDef = element; - break; - } - } + for (String resourceName : resourceNames) { + RangerResourceDef resourceDef = ServiceDefUtil.getResourceDef(serviceDef, resourceName); - trieMap.put(entry.getKey(), new RangerResourceTrie<>(resourceDef, matchers)); + trieMap.put(resourceName, new RangerResourceTrie<>(resourceDef, matchers)); } // For each zone, get list-of-resources corresponding to serviceName @@ -406,67 +377,18 @@ private boolean validateZoneServiceInAllZones(List zones, St // check each evaluator to see if the resource-match actually happens. If yes then add the zone-evaluator to matching evaluators. // flag error if there are more than one matching evaluators with different zone-ids. // + for (RangerSecurityZone zone : zones) { List>> resources = zone.getServices().get(serviceName).getResources(); for (Map> resource : resources) { + Collection smallestList = RangerResourceEvaluatorsRetriever.getEvaluators(trieMap, resource); - List> zoneMatchersList = null; - Set smallestList = null; - - for (Map.Entry> entry : resource.entrySet()) { - String resourceDefName = entry.getKey(); - List resourceValues = entry.getValue(); - - RangerResourceTrie trie = trieMap.get(resourceDefName); - Set matchedZones = trie.getEvaluatorsForResource(resourceValues); - - if (LOG.isDebugEnabled()) { - LOG.debug("ResourceDefName:[" + resourceDefName +"], values:[" + resourceValues +"], matched-zones:[" + matchedZones +"]"); - } - if (CollectionUtils.isEmpty(matchedZones)) { // no policies for this resource, bail out - zoneMatchersList = null; - smallestList = null; - break; - } - - if (smallestList == null) { - smallestList = matchedZones; - } else { - if (zoneMatchersList == null) { - zoneMatchersList = new ArrayList<>(); - zoneMatchersList.add(smallestList); - } - zoneMatchersList.add(matchedZones); - - if (smallestList.size() > matchedZones.size()) { - smallestList = matchedZones; - } - } - } - if (smallestList == null) { - continue; - } - final Set intersection; - - if (zoneMatchersList != null) { - intersection = new HashSet<>(smallestList); - for (Set zoneMatchers : zoneMatchersList) { - if (zoneMatchers != smallestList) { - // remove zones from intersection that are not in zoneMatchers - intersection.retainAll(zoneMatchers); - if (CollectionUtils.isEmpty(intersection)) { // if no zoneMatcher exists, bail out and return empty list - break; - } - } - } - } else { - intersection = smallestList; - } if (LOG.isDebugEnabled()) { - LOG.debug("Resource:[" + resource +"], matched-zones:[" + intersection +"]"); + LOG.debug("Resource:[" + resource +"], matched-zones:[" + smallestList +"]"); } - if (intersection.size() <= 1) { + + if (CollectionUtils.isEmpty(smallestList) || smallestList.size() == 1) { continue; } @@ -480,7 +402,7 @@ private boolean validateZoneServiceInAllZones(List zones, St Set matchedZoneNames = new HashSet<>(); - for (RangerZoneResourceMatcher zoneMatcher : intersection) { + for (RangerZoneResourceMatcher zoneMatcher : smallestList) { if (LOG.isDebugEnabled()) { LOG.debug("Trying to match resource:[" + accessResource +"] using zoneMatcher:[" + zoneMatcher + "]"); } @@ -510,20 +432,18 @@ private boolean validateZoneServiceInAllZones(List zones, St } if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.validateZoneServiceInAllZones(%s, %s, %s, %s) : %s", zones, serviceName, serviceDef, failures, ret)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.validateZoneServiceInAllZones(%s, %s, %s, %s) : %s", zones, serviceName, serviceDef, failures, ret)); } return ret; } - private boolean validateSecurityZoneService(String serviceName, RangerSecurityZone.RangerSecurityZoneService securityZoneService, List failures) { + private boolean validateSecurityZoneService(String serviceName, RangerSecurityZoneService securityZoneService, List failures) { if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.validateSecurityZoneService(%s, %s, %s)", serviceName, securityZoneService, failures)); + LOG.debug(String.format("==> RangerSecurityZoneValidator.validateSecurityZoneService(%s, %s, %s)", serviceName, securityZoneService, failures)); } - boolean ret = true; - - // Verify service with serviceName exists - get the service-type - RangerService service = getService(serviceName); + boolean ret = true; + RangerService service = getService(serviceName); // Verify service with serviceName exists if (service == null) { ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_SERVICE_NAME; @@ -535,68 +455,68 @@ private boolean validateSecurityZoneService(String serviceName, RangerSecurityZo if (serviceDef == null) { ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_SERVICE_TYPE; + failures.add(new ValidationFailureDetailsBuilder().field("security zone resource service-type").becauseOf(error.getMessage(service.getType())).errorCode(error.getErrorCode()).build()); ret = false; } else { - String serviceType = serviceDef.getName(); + if (CollectionUtils.isNotEmpty(securityZoneService.getResources())) { + // For each resource-spec, verify that it forms valid hierarchy for some policy-type + Set resourceSignatures = new HashSet<>(); - if (StringUtils.equals(serviceType, EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME)) { - if (CollectionUtils.isNotEmpty(securityZoneService.getResources())) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_UNEXPECTED_RESOURCES; - failures.add(new ValidationFailureDetailsBuilder().field("security zone resources").becauseOf(error.getMessage(serviceName)).errorCode(error.getErrorCode()).build()); - ret = false; - } - } else { - if (CollectionUtils.isEmpty(securityZoneService.getResources())) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_RESOURCES; - failures.add(new ValidationFailureDetailsBuilder().field("security zone resources").isMissing().becauseOf(error.getMessage(serviceName)).errorCode(error.getErrorCode()).build()); - ret = false; - } else { - // For each resource-spec, verify that it forms valid hierarchy for some policy-type - for (Map> resource : securityZoneService.getResources()) { - Set resourceDefNames = resource.keySet(); - RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef); - boolean isValidHierarchy = false; + for (Map> resource : securityZoneService.getResources()) { + Set resourceDefNames = resource.keySet(); + RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef); + boolean isValidHierarchy = false; - for (int policyType : RangerPolicy.POLICY_TYPES) { - Set> resourceHierarchies = serviceDefHelper.getResourceHierarchies(policyType, resourceDefNames); - - if (LOG.isDebugEnabled()) { - LOG.debug("Size of resourceHierarchies for resourceDefNames:[" + resourceDefNames + ", policyType=" + policyType + "] = " + resourceHierarchies.size()); - } + for (int policyType : POLICY_TYPES) { + Set> resourceHierarchies = serviceDefHelper.getResourceHierarchies(policyType, resourceDefNames); - for (List resourceHierarchy : resourceHierarchies) { + if (LOG.isDebugEnabled()) { + LOG.debug("Size of resourceHierarchies for resourceDefNames:[" + resourceDefNames + ", policyType=" + policyType + "] = " + resourceHierarchies.size()); + } - if (RangerDefaultPolicyResourceMatcher.isHierarchyValidForResources(resourceHierarchy, resource)) { - isValidHierarchy = true; - break; - } else { - LOG.info("gaps found in resource, skipping hierarchy:[" + resourceHierarchies + "]"); - } + for (List resourceHierarchy : resourceHierarchies) { + if (RangerDefaultPolicyResourceMatcher.isHierarchyValidForResources(resourceHierarchy, resource)) { + isValidHierarchy = true; + break; + } else { + LOG.info("gaps found in resource, skipping hierarchy:[" + resourceHierarchies + "]"); } } + } + + if (!isValidHierarchy) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_RESOURCE_HIERARCHY; + + failures.add(new ValidationFailureDetailsBuilder().field("security zone resource hierarchy").becauseOf(error.getMessage(serviceName, resourceDefNames)).errorCode(error.getErrorCode()).build()); + ret = false; + } - if (!isValidHierarchy) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_INVALID_RESOURCE_HIERARCHY; + for (Map.Entry> resourceEntry : resource.entrySet()) { + String resourceName = resourceEntry.getKey(); + List resourceValues = resourceEntry.getValue(); - failures.add(new ValidationFailureDetailsBuilder().field("security zone resource hierarchy").becauseOf(error.getMessage(serviceName, resourceDefNames)).errorCode(error.getErrorCode()).build()); + if (CollectionUtils.isEmpty(resourceValues)) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_RESOURCES; + + failures.add(new ValidationFailureDetailsBuilder().field("security zone resources") + .subField("resources").isMissing() + .becauseOf(error.getMessage(resourceName)) + .errorCode(error.getErrorCode()).build()); ret = false; } + } - /* - * Ignore this check. It should be possible to have all wildcard resource in a zone if zone-admin so desires - * - boolean isValidResourceSpec = isAnyNonWildcardResource(resource, failures); + RangerPolicyResourceSignature resourceSignature = RangerPolicyResourceSignature.from(resource); - if (!isValidResourceSpec) { - ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_ALL_WILDCARD_RESOURCE_VALUES; + if (!resourceSignatures.add(resourceSignature.getSignature())) { + ValidationErrorCode error = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_DUPLICATE_RESOURCE_ENTRY; - failures.add(new ValidationFailureDetailsBuilder().field("security zone resource values").becauseOf(error.getMessage(serviceName)).errorCode(error.getErrorCode()).build()); + failures.add(new ValidationFailureDetailsBuilder().field("security zone resources") + .subField("resources") + .becauseOf(error.getMessage(resource, serviceName)) + .errorCode(error.getErrorCode()).build()); ret = false; - LOG.warn("RangerPolicyValidator.validateSecurityZoneService() : All wildcard resource-values specified for service :[" + serviceName + "]"); - } - */ - } } } @@ -604,41 +524,9 @@ private boolean validateSecurityZoneService(String serviceName, RangerSecurityZo } if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.validateSecurityZoneService(%s, %s, %s) : %s", serviceName, securityZoneService, failures, ret)); + LOG.debug(String.format("<== RangerSecurityZoneValidator.validateSecurityZoneService(%s, %s, %s) : %s", serviceName, securityZoneService, failures, ret)); } return ret; } - - /* - private boolean isAnyNonWildcardResource(Map> resource, List failures) { - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("==> RangerPolicyValidator.isAnyNonWildcardResource(%s, %s)", resource, failures)); - } - - boolean ret = false; - - for (Map.Entry> resourceDefValue : resource.entrySet()) { - boolean wildCardResourceFound = false; - List resourceValues = resourceDefValue.getValue(); - - for (String resourceValue : resourceValues) { - if (StringUtils.equals(resourceValue, RangerDefaultResourceMatcher.WILDCARD_ASTERISK)) { - wildCardResourceFound = true; - break; - } - } - - if (!wildCardResourceFound) { - ret = true; - break; - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug(String.format("<== RangerPolicyValidator.isAnyNonWildcardResource(%s, %s) : %s", resource, failures, ret)); - } - return ret; - } - */ } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java index 0d9a346d44..75328b985a 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefHelper.java @@ -31,21 +31,22 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import com.google.common.collect.Sets; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; +import org.apache.hadoop.thirdparty.com.google.common.collect.Sets; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.common.collect.Lists; import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; import org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher; public class RangerServiceDefHelper { - private static final Log LOG = LogFactory.getLog(RangerServiceDefHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceDefHelper.class); static final Map _Cache = new ConcurrentHashMap<>(); final Delegate _delegate; @@ -171,6 +172,14 @@ public void patchServiceDefWithDefaultValues() { _delegate.patchServiceDefWithDefaultValues(); } + public RangerResourceDef getResourceDef(String resourceName) { + return _delegate.getResourceDef(resourceName, RangerPolicy.POLICY_TYPE_ACCESS); + } + + public RangerResourceDef getResourceDef(String resourceName, Integer policyType) { + return _delegate.getResourceDef(resourceName, policyType); + } + /** * for a resource definition as follows: * @@ -187,6 +196,10 @@ public Set> getResourceHierarchies(Integer policyType) { return _delegate.getResourceHierarchies(policyType); } + public Set> getResourceHierarchyKeys(Integer policyType) { + return _delegate.getResourceHierarchyKeys(policyType); + } + public Set> filterHierarchies_containsOnlyMandatoryResources(Integer policyType) { Set> hierarchies = getResourceHierarchies(policyType); Set> result = new HashSet>(hierarchies.size()); @@ -199,6 +212,32 @@ public Set> filterHierarchies_containsOnlyMandatoryResou return result; } + public boolean isValidHierarchy(Integer policyType, Collection keys, boolean requireExactMatch) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> isValidHierarchy(policyType=" + policyType + ", keys=" + StringUtils.join(keys, ", ") + ", requireExactMatch=" + requireExactMatch + ")"); + } + + boolean ret = false; + + for (Set hierarchyKeys : getResourceHierarchyKeys(policyType)) { + if (requireExactMatch) { + ret = hierarchyKeys.equals(keys); + } else { + ret = hierarchyKeys.containsAll(keys); + } + + if (ret) { + break; + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== isValidHierarchy(policyType=" + policyType + ", keys=" + StringUtils.join(keys, ", ") + ", requireExactMatch=" + requireExactMatch + "): ret=" + ret); + } + + return ret; + } + public Set> getResourceHierarchies(Integer policyType, Collection keys) { if (LOG.isDebugEnabled()) { LOG.debug("==> getResourceHierarchies(policyType=" + policyType + ", keys=" + StringUtils.join(keys, ",") + ")"); @@ -206,6 +245,10 @@ public Set> getResourceHierarchies(Integer policyType, C Set> ret = new HashSet>(); + if (policyType == RangerPolicy.POLICY_TYPE_AUDIT) { + policyType = RangerPolicy.POLICY_TYPE_ACCESS; + } + for (List hierarchy : getResourceHierarchies(policyType)) { if (hierarchyHasAllResources(hierarchy, keys)) { ret.add(hierarchy); @@ -260,7 +303,7 @@ public Set getMandatoryResourceNames(List hierarchy) * @param hierarchy * @return */ - public Set getAllResourceNames(List hierarchy) { + public static Set getAllResourceNames(List hierarchy) { Set result = new HashSet(hierarchy.size()); for (RangerResourceDef resourceDef : hierarchy) { result.add(resourceDef.getName()); @@ -285,16 +328,72 @@ public boolean isResourceGraphValid() { return _delegate.isResourceGraphValid(); } + public List getOrderedResourceNames(Collection resourceNames) { + final List ret; + if (resourceNames != null) { + ret = new ArrayList<>(); + for (String orderedName : _delegate.getAllOrderedResourceNames()) { + for (String resourceName : resourceNames) { + if (StringUtils.equals(orderedName, resourceName) && !ret.contains(resourceName)) { + ret.add(resourceName); + break; + } + } + } + } else { + ret = Collections.EMPTY_LIST; + } + return ret; + } + + public RangerResourceDef getWildcardEnabledResourceDef(String resourceName, Integer policyType) { + return _delegate.getWildcardEnabledResourceDef(resourceName, policyType); + } + + public Map> getImpliedAccessGrants() { + return _delegate.getImpliedAccessGrants(); + } + + public Set expandImpliedAccessGrants(Set accessTypes) { + final Set ret; + + if (CollectionUtils.isNotEmpty(accessTypes)) { + Map> impliedGrants = getImpliedAccessGrants(); + + if (CollectionUtils.containsAny(impliedGrants.keySet(), accessTypes)) { + ret = new HashSet<>(accessTypes); + + for (String accessType : accessTypes) { + Collection impliedAccessTypes = impliedGrants.get(accessType); + + if (CollectionUtils.isNotEmpty(impliedAccessTypes)) { + ret.addAll(impliedAccessTypes); + } + } + } else { + ret = accessTypes; + } + } else { + ret = Collections.emptySet(); + } + + return ret; + } + /** * Not designed for public access. Package level only for testability. */ static class Delegate { final RangerServiceDef _serviceDef; final Map>> _hierarchies = new HashMap<>(); + final Map>> _hierarchyKeys = new HashMap<>(); + final Map> _wildcardEnabledResourceDefs = new HashMap<>(); final Date _serviceDefFreshnessDate; final String _serviceName; final boolean _checkForCycles; final boolean _valid; + final List _orderedResourceNames; + final Map> _impliedGrants; final static Set> EMPTY_RESOURCE_HIERARCHY = Collections.unmodifiableSet(new HashSet>()); @@ -313,16 +412,34 @@ public Delegate(RangerServiceDef serviceDef, boolean checkForCycles) { if(graph != null) { Map resourceDefMap = getResourcesAsMap(resources); if (isValid(graph, resourceDefMap)) { - Set> hierarchies = getHierarchies(graph, resourceDefMap); + Set> hierarchies = getHierarchies(graph, resourceDefMap); + Set> hierachyKeys = new HashSet<>(hierarchies.size()); + + for (List hierarchy : hierarchies) { + hierachyKeys.add(Collections.unmodifiableSet(getAllResourceNames(hierarchy))); + } + _hierarchies.put(policyType, Collections.unmodifiableSet(hierarchies)); + _hierarchyKeys.put(policyType, Collections.unmodifiableSet(hierachyKeys)); } else { isValid = false; _hierarchies.put(policyType, EMPTY_RESOURCE_HIERARCHY); + _hierarchyKeys.put(policyType, Collections.emptySet()); } } else { _hierarchies.put(policyType, EMPTY_RESOURCE_HIERARCHY); + _hierarchyKeys.put(policyType, Collections.emptySet()); } } + + _impliedGrants = computeImpliedGrants(); + + if (isValid) { + _orderedResourceNames = buildSortedResourceNames(); + } else { + _orderedResourceNames = new ArrayList<>(); + } + _valid = isValid; if (LOG.isDebugEnabled()) { String message = String.format("Found [%d] resource hierarchies for service [%s] update-date[%s]: %s", _hierarchies.size(), _serviceName, @@ -345,6 +462,28 @@ public void patchServiceDefWithDefaultValues() { } } + public RangerResourceDef getResourceDef(String resourceName, Integer policyType) { + RangerResourceDef ret = null; + + if (policyType == null) { + policyType = RangerPolicy.POLICY_TYPE_ACCESS; + } + + List resourceDefs = this.getResourceDefs(_serviceDef, policyType); + + if (resourceDefs != null) { + for (RangerResourceDef resourceDef : resourceDefs) { + if (StringUtils.equals(resourceName, resourceDef.getName())) { + ret = resourceDef; + + break; + } + } + } + + return ret; + } + public Set> getResourceHierarchies(Integer policyType) { if(policyType == null) { policyType = RangerPolicy.POLICY_TYPE_ACCESS; @@ -359,6 +498,16 @@ public Set> getResourceHierarchies(Integer policyType) { return ret; } + public Set> getResourceHierarchyKeys(Integer policyType) { + if (policyType == null || policyType == RangerPolicy.POLICY_TYPE_AUDIT) { + policyType = RangerPolicy.POLICY_TYPE_ACCESS; + } + + Set> ret = _hierarchyKeys.get(policyType); + + return ret != null ? ret : Collections.emptySet(); + } + public String getServiceName() { return _serviceName; } @@ -399,6 +548,44 @@ DirectedGraph createGraph(List resourceDefs) { return graph; } + RangerResourceDef getWildcardEnabledResourceDef(String resourceName, Integer policyType) { + if (policyType == null) { + policyType = RangerPolicy.POLICY_TYPE_ACCESS; + } + + Map wResourceDefs = _wildcardEnabledResourceDefs.get(policyType); + + if (wResourceDefs == null) { + wResourceDefs = new HashMap<>(); + + _wildcardEnabledResourceDefs.put(policyType, wResourceDefs); + } + + RangerResourceDef ret = null; + + if (!wResourceDefs.containsKey(resourceName)) { + List resourceDefs = getResourceDefs(_serviceDef, policyType); + + if (resourceDefs != null) { + for (RangerResourceDef resourceDef : resourceDefs) { + if (StringUtils.equals(resourceName, resourceDef.getName())) { + ret = new RangerResourceDef(resourceDef); + + ret.getMatcherOptions().put(RangerAbstractResourceMatcher.OPTION_WILD_CARD, Boolean.TRUE.toString()); + + break; + } + } + } + + wResourceDefs.put(resourceName, ret); + } else { + ret = wResourceDefs.get(resourceName); + } + + return ret; + } + List getResourceDefs(RangerServiceDef serviceDef, Integer policyType) { final List resourceDefs; @@ -533,6 +720,93 @@ Map getResourcesAsMap(List resourc } return map; } + + List getAllOrderedResourceNames() { + return this._orderedResourceNames; + } + + Map> getImpliedAccessGrants() { return _impliedGrants; } + + private Map> computeImpliedGrants() { + Map> ret = new HashMap<>(); + + if (_serviceDef != null && CollectionUtils.isNotEmpty(_serviceDef.getAccessTypes())) { + for (RangerAccessTypeDef accessTypeDef : _serviceDef.getAccessTypes()) { + if (CollectionUtils.isNotEmpty(accessTypeDef.getImpliedGrants())) { + Collection impliedAccessGrants = ret.get(accessTypeDef.getName()); + + if(impliedAccessGrants == null) { + impliedAccessGrants = new HashSet<>(); + + ret.put(accessTypeDef.getName(), impliedAccessGrants); + } + + impliedAccessGrants.addAll(accessTypeDef.getImpliedGrants()); + } + } + + if (_serviceDef.getMarkerAccessTypes() != null) { + for (RangerAccessTypeDef accessTypeDef : _serviceDef.getMarkerAccessTypes()) { + if(CollectionUtils.isNotEmpty(accessTypeDef.getImpliedGrants())) { + Collection impliedAccessGrants = ret.get(accessTypeDef.getName()); + + if(impliedAccessGrants == null) { + impliedAccessGrants = new HashSet<>(); + + ret.put(accessTypeDef.getName(), impliedAccessGrants); + } + + impliedAccessGrants.addAll(accessTypeDef.getImpliedGrants()); + } + } + } + } + + return ret; + } + + private static class ResourceNameLevel implements Comparable { + private String resourceName; + private int level; + + ResourceNameLevel(String resourceName, int level) { + this.resourceName = resourceName; + this.level = level; + } + + @Override + public int compareTo(ResourceNameLevel other) { + return Integer.compare(this.level, other.level); + } + } + + private List buildSortedResourceNames() { + final List ret = new ArrayList<>(); + + boolean isValid = true; + List resourceNameLevels = new ArrayList<>(); + for (RangerResourceDef resourceDef : _serviceDef.getResources()) { + String name = resourceDef.getName(); + Integer level = resourceDef.getLevel(); + if (name != null && level != null) { + ResourceNameLevel resourceNameLevel = new ResourceNameLevel(name, level); + resourceNameLevels.add(resourceNameLevel); + } else { + LOG.error("Incorrect resourceDef:[name=" + name + "]"); + isValid = false; + break; + } + } + + if (isValid) { + Collections.sort(resourceNameLevels); + + for (ResourceNameLevel resourceNameLevel : resourceNameLevels) { + ret.add(resourceNameLevel.resourceName); + } + } + return ret; + } } /** diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java index 8a21f4d571..0e65db0d6e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceDefValidator.java @@ -30,8 +30,8 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableSet; +import org.apache.hadoop.thirdparty.com.google.common.collect.Sets; import org.apache.ranger.plugin.errors.ValidationErrorCode; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; @@ -43,14 +43,14 @@ import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskTypeDef; import org.apache.ranger.plugin.store.ServiceStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.common.collect.ImmutableSet; -import com.google.common.collect.Sets; import org.apache.ranger.plugin.util.ServiceDefUtil; public class RangerServiceDefValidator extends RangerValidator { - private static final Log LOG = LogFactory.getLog(RangerServiceDefValidator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceDefValidator.class); public RangerServiceDefValidator(ServiceStore store) { super(store); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java index 6c4d6c1656..bc53f55a24 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerServiceValidator.java @@ -24,18 +24,18 @@ import java.util.Set; import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.thirdparty.com.google.common.collect.Sets; import org.apache.ranger.plugin.errors.ValidationErrorCode; import org.apache.ranger.plugin.model.RangerService; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.store.ServiceStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.common.collect.Sets; public class RangerServiceValidator extends RangerValidator { - private static final Log LOG = LogFactory.getLog(RangerServiceValidator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceValidator.class); private static final Pattern SERVICE_NAME_VALIDATION_REGEX = Pattern.compile("^[a-zA-Z0-9_-][a-zA-Z0-9_-]{0,254}", Pattern.CASE_INSENSITIVE); private static final Pattern LEGACY_SERVICE_NAME_VALIDATION_REGEX = Pattern.compile("^[a-zA-Z0-9_-][a-zA-Z0-9\\s_-]{0,254}", Pattern.CASE_INSENSITIVE); @@ -273,7 +273,16 @@ boolean isValid(RangerService service, Action action, List getPoliciesForResourceSignature(String serviceName, String po if(LOG.isDebugEnabled()) { LOG.debug(String.format("==> RangerValidator.getPoliciesForResourceSignature(%s, %s)", serviceName, policySignature)); } + final List ret; - List policies = null; + List enabledPolicies = new ArrayList<>(); + List disabledPolicies = new ArrayList<>(); try { - policies = _store.getPoliciesByResourceSignature(serviceName, policySignature, true); // only look for enabled policies + enabledPolicies = _store.getPoliciesByResourceSignature(serviceName, policySignature, true); + disabledPolicies = _store.getPoliciesByResourceSignature(serviceName, policySignature, false); } catch (Exception e) { LOG.debug("Encountred exception while retrieving policies from service store!", e); } + if (CollectionUtils.isEmpty(enabledPolicies)) { + ret = disabledPolicies; + } else if (CollectionUtils.isEmpty(disabledPolicies)) { + ret = enabledPolicies; + } else { + ret = enabledPolicies; + ret.addAll(disabledPolicies); + } if(LOG.isDebugEnabled()) { - int count = policies == null ? 0 : policies.size(); - LOG.debug(String.format("<== RangerValidator.getPoliciesForResourceSignature(%s, %s): count[%d], %s", serviceName, policySignature, count, policies)); + int count = ret == null ? 0 : ret.size(); + LOG.debug(String.format("<== RangerValidator.getPoliciesForResourceSignature(%s, %s): count[%d], %s", serviceName, policySignature, count, ret)); } - return policies; + return ret; } RangerSecurityZone getSecurityZone(Long id) { @@ -423,6 +434,14 @@ Set getAccessTypes(RangerServiceDef serviceDef) { } } } + + if (serviceDef.getMarkerAccessTypes() != null) { + for (RangerAccessTypeDef accessTypeDef : serviceDef.getMarkerAccessTypes()) { + if (accessTypeDef != null) { + accessTypes.add(accessTypeDef.getName()); + } + } + } } if(LOG.isDebugEnabled()) { @@ -528,19 +547,21 @@ Set getAllResourceNames(RangerServiceDef serviceDef) { /** * Converts, in place, the resources defined in the policy to have lower-case resource-def-names - * @param policy + * @param resources * @return */ - void convertPolicyResourceNamesToLower(RangerPolicy policy) { + void convertPolicyResourceNamesToLower(Map resources) { Map lowerCasePolicyResources = new HashMap<>(); - if (policy.getResources() != null) { - for (Map.Entry entry : policy.getResources().entrySet()) { + if (resources != null) { + for (Map.Entry entry : resources.entrySet()) { String lowerCasekey = entry.getKey().toLowerCase(); lowerCasePolicyResources.put(lowerCasekey, entry.getValue()); } + + resources.clear(); + resources.putAll(lowerCasePolicyResources); } - policy.setResources(lowerCasePolicyResources); } Map getValidationRegExes(RangerServiceDef serviceDef) { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java index 3bfdf9396b..9a0b44aea0 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerValidityScheduleValidator.java @@ -21,8 +21,8 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.text.DateFormat; import java.text.ParseException; @@ -44,12 +44,14 @@ import javax.annotation.Nonnull; public class RangerValidityScheduleValidator { - private static final Log LOG = LogFactory.getLog(RangerValidityScheduleValidator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerValidityScheduleValidator.class); private static final ThreadLocal DATE_FORMATTER = new ThreadLocal() { @Override protected DateFormat initialValue() { - return new SimpleDateFormat(RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION); + SimpleDateFormat sd = new SimpleDateFormat(RangerValiditySchedule.VALIDITY_SCHEDULE_DATE_STRING_SPECIFICATION); + sd.setLenient(false); + return sd; } }; @@ -183,7 +185,8 @@ private boolean validateValidityInterval(RangerValidityRecurrence recurrence, Li if (validityInterval.getDays() < 0 || (validityInterval.getHours() < 0 || validityInterval.getHours() > 23) - || (validityInterval.getMinutes() < 0 || validityInterval.getMinutes() > 59)) { + || (validityInterval.getMinutes() < 0 || validityInterval.getMinutes() > 59) + || (validityInterval.getDays() == 0 && validityInterval.getHours() == 0 && validityInterval.getMinutes() == 0 )) { validationFailures.add(new ValidationFailureDetails(0, "interval", "", false, true, false, "invalid interval")); ret = false; } @@ -226,6 +229,26 @@ private boolean validateFieldSpec(RangerValidityRecurrence recurrence, RangerVal int maximum = field == RangerValidityRecurrence.RecurrenceSchedule.ScheduleFieldSpec.month ? field.maximum + 1 : field.maximum; ret = validateRanges(recurrence, field, minimum, maximum, validationFailures); } + + if(ret) { + final int minimum; + final int maximum; + + if (field == RecurrenceSchedule.ScheduleFieldSpec.year) { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy"); + + minimum = Integer.valueOf(formatter.format(startTime)); + maximum = Integer.valueOf(formatter.format(endTime)); + } else if (field == RecurrenceSchedule.ScheduleFieldSpec.month) { + minimum = field.minimum + 1; + maximum = field.maximum + 1; + } else { + minimum = field.minimum; + maximum = field.maximum; + } + + ret = validateRanges(recurrence, field, minimum, maximum, validationFailures); + } return ret; } @@ -340,36 +363,47 @@ public int compare(Range me, Range other) { if (StringUtils.isNotEmpty(spec)) { // Range if (spec.startsWith("-") || spec.endsWith("-")) { - validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range spec")); + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range spec: " + spec)); ret = false; } else { String[] ranges = StringUtils.split(spec, "-"); if (ranges.length > 2) { - validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range spec")); + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range spec: " + spec)); ret = false; } else if (ranges.length == 2) { int val1 = minValidValue, val2 = maxValidValue; if (!StringUtils.equals(ranges[0], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) { val1 = Integer.valueOf(ranges[0]); - if (val1 < minValidValue || val1 > maxValidValue) { - validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect lower range value")); - ret = false; - } } else { value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD; } + if (!StringUtils.equals(ranges[1], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) { val2 = Integer.valueOf(ranges[1]); - if (val1 < minValidValue || val2 > maxValidValue) { - validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect upper range value")); - ret = false; - } } else { value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD; } + + if (field == RecurrenceSchedule.ScheduleFieldSpec.year) { // for year, one bound (lower or upper) can be outside the range + if (val1 < minValidValue && val2 > maxValidValue) { + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range: (" + val1 + ", " + val2 + "). valid range: (" + minValidValue + ", " + maxValidValue + ")")); + ret = false; + } + } else { // for month/dayOfMonth/dayOfWeek/hour/minute both bounds (lower and upper) must be within range + if (val1 < minValidValue || val1 > maxValidValue) { + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect lower range: " + val1 + ". valid range: (" + minValidValue + ", " + maxValidValue + ")")); + ret = false; + } + + if (val2 < minValidValue || val2 > maxValidValue) { + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect upper range: " + val2 + ". valid range: (" + minValidValue + ", " + maxValidValue + ")")); + ret = false; + } + } + if (ret) { if (val1 >= val2) { - validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range")); + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect range: min=" + val1 + ", max=" + val2)); ret = false; } else { value = RangerValidityRecurrence.RecurrenceSchedule.WILDCARD; @@ -389,7 +423,7 @@ public int compare(Range me, Range other) { if (!StringUtils.equals(ranges[0], RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) { int val = Integer.valueOf(ranges[0]); if (val < minValidValue || val > maxValidValue) { - validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect value")); + validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "incorrect value: " + val + ". Valid range: (" + minValidValue + "-" + maxValidValue + ")")); ret = false; } else { if (!StringUtils.equals(value, RangerValidityRecurrence.RecurrenceSchedule.WILDCARD)) { @@ -414,7 +448,7 @@ public int compare(Range me, Range other) { int upper = range.upper; for (int j = i+1; j < rangeOfValues.size(); j++) { Range r = rangeOfValues.get(j); - if (upper < r.upper) { + if (upper > r.lower) { validationFailures.add(new ValidationFailureDetails(0, fieldName, "", false, true, false, "overlapping range value")); ret = false; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerZoneResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerZoneResourceMatcher.java index 2b570f6a19..27ed49b0b7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerZoneResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/RangerZoneResourceMatcher.java @@ -19,37 +19,42 @@ package org.apache.ranger.plugin.model.validation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.List; import java.util.Map; -public class RangerZoneResourceMatcher implements RangerPolicyResourceEvaluator { - private static final Log LOG = LogFactory.getLog(RangerZoneResourceMatcher.class); +public class RangerZoneResourceMatcher implements RangerResourceEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(RangerZoneResourceMatcher.class); private final String securityZoneName; private final Map policyResource; private final RangerPolicyResourceMatcher policyResourceMatcher; private RangerServiceDef.RangerResourceDef leafResourceDef; - public RangerZoneResourceMatcher(final String securityZoneName, final Map policyResource, final RangerServiceDef serviceDef) { - - RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef); - final Collection resourceKeys = policyResource.keySet(); + public RangerZoneResourceMatcher(final String securityZoneName, final Map policyResource, final RangerServiceDef serviceDef, RangerPluginContext pluginContext) { + this(securityZoneName, policyResource, new RangerServiceDefHelper(serviceDef), pluginContext); + } - RangerDefaultPolicyResourceMatcher matcher = new RangerDefaultPolicyResourceMatcher(); + public RangerZoneResourceMatcher(final String securityZoneName, final Map policyResource, final RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + final RangerServiceDef serviceDef = serviceDefHelper.getServiceDef(); + final Collection resourceKeys = policyResource.keySet(); + final RangerDefaultPolicyResourceMatcher matcher = new RangerDefaultPolicyResourceMatcher(); matcher.setServiceDef(serviceDef); matcher.setServiceDefHelper(serviceDefHelper); + matcher.setPluginContext(pluginContext); boolean found = false; @@ -105,6 +110,9 @@ public boolean isAncestorOf(RangerServiceDef.RangerResourceDef resourceDef) { return ServiceDefUtil.isAncestorOf(policyResourceMatcher.getServiceDef(), leafResourceDef, resourceDef); } + @Override + public boolean isLeaf(String resourceName) { return StringUtils.equals(resourceName, leafResourceDef.getName()); } + @Override public String toString() { return "{security-zone-name:[" + securityZoneName + "], policyResource=[" + policyResource +"]}"; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java index d53d52a61f..6f6617a4a2 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/validation/ValidationFailureDetails.java @@ -19,14 +19,15 @@ package org.apache.ranger.plugin.model.validation; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.errors.ValidationErrorCode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Objects; public class ValidationFailureDetails { - private static final Log LOG = LogFactory.getLog(ValidationFailureDetails.class); + private static final Logger LOG = LoggerFactory.getLogger(ValidationFailureDetails.class); final String _fieldName; final String _subFieldName; @@ -36,6 +37,10 @@ public class ValidationFailureDetails { final String _reason; final int _errorCode; + public ValidationFailureDetails(ValidationErrorCode errorCode, String fieldName, Object... errorCodeArgs) { + this(errorCode.getErrorCode(), fieldName, null, false, false, false, errorCode.getMessage(errorCodeArgs)); + } + public ValidationFailureDetails(int errorCode, String fieldName, String subFieldName, boolean missing, boolean semanticError, boolean internalError, String reason) { _errorCode = errorCode; _missing = missing; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/CacheMap.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/CacheMap.java index 51f3f6b88d..4555ca4c35 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/CacheMap.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/CacheMap.java @@ -18,15 +18,15 @@ */ package org.apache.ranger.plugin.policyengine; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.LinkedHashMap; import java.util.Map; public class CacheMap extends LinkedHashMap { private static final long serialVersionUID = 1L; - private static final Log LOG = LogFactory.getLog(CacheMap.class); + private static final Logger LOG = LoggerFactory.getLogger(CacheMap.class); private static final float RANGER_CACHE_DEFAULT_LOAD_FACTOR = 0.75f; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java index 3250719de2..2de3cfa0d5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEngine.java @@ -21,47 +21,60 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.collections.ListUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicyDelta; import org.apache.ranger.plugin.model.RangerServiceDef; -import org.apache.ranger.plugin.model.validation.RangerZoneResourceMatcher; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; import org.apache.ranger.plugin.service.RangerAuthContext; -import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.util.RangerPerfTracer; import org.apache.ranger.plugin.util.RangerPolicyDeltaUtil; +import org.apache.ranger.plugin.util.RangerReadWriteLock; import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.ServiceDefUtil; import org.apache.ranger.plugin.util.ServicePolicies; +import org.apache.ranger.plugin.util.StringTokenReplacer; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class PolicyEngine { - private static final Log LOG = LogFactory.getLog(PolicyEngine.class); + private static final Logger LOG = LoggerFactory.getLogger(PolicyEngine.class); - private static final Log PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init"); - private static final Log PERF_POLICYENGINE_REBALANCE_LOG = RangerPerfTracer.getPerfLogger("policyengine.rebalance"); + private static final Logger PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init"); + private static final Logger PERF_POLICYENGINE_REBALANCE_LOG = RangerPerfTracer.getPerfLogger("policyengine.rebalance"); + private final RangerServiceDefHelper serviceDefHelper; private final RangerPolicyRepository policyRepository; private final RangerPolicyRepository tagPolicyRepository; private final List allContextEnrichers; private final RangerPluginContext pluginContext; private final Map zonePolicyRepositories = new HashMap<>(); - private final Map resourceZoneTrie = new HashMap<>(); - private final Map zoneTagServiceMap = new HashMap<>(); + private final RangerSecurityZoneMatcher zoneMatcher; private boolean useForwardedIPAddress; private String[] trustedProxyAddresses; + private final Map tokenReplacers = new HashMap<>(); + private final RangerReadWriteLock lock; + + + public RangerReadWriteLock.RangerLock getReadLock() { + return lock.getReadLock(); + } + + public RangerReadWriteLock.RangerLock getWriteLock() { + return lock.getWriteLock(); + } public boolean getUseForwardedIPAddress() { return useForwardedIPAddress; @@ -95,6 +108,8 @@ public long getPolicyVersion() { return policyRepository.getPolicyVersion(); } + public RangerServiceDefHelper getServiceDefHelper() { return serviceDefHelper; } + public RangerPolicyRepository getPolicyRepository() { return policyRepository; } @@ -109,6 +124,10 @@ public RangerPolicyRepository getTagPolicyRepository() { public RangerPluginContext getPluginContext() { return pluginContext; } + public StringTokenReplacer getStringTokenReplacer(String resourceName) { + return tokenReplacers.get(resourceName); + } + @Override public String toString() { return toString(new StringBuilder()).toString(); @@ -144,6 +163,8 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); + sb.append(lock.toString()); + sb.append("}"); return sb; @@ -152,14 +173,14 @@ public StringBuilder toString(StringBuilder sb) { public List getResourcePolicies(String zoneName) { RangerPolicyRepository zoneResourceRepository = zonePolicyRepositories.get(zoneName); - return zoneResourceRepository == null ? ListUtils.EMPTY_LIST : zoneResourceRepository.getPolicies(); + return zoneResourceRepository == null ? Collections.emptyList() : zoneResourceRepository.getPolicies(); } - Map getResourceZoneTrie() { - return resourceZoneTrie; + RangerSecurityZoneMatcher getZoneMatcher() { + return zoneMatcher; } - public PolicyEngine(ServicePolicies servicePolicies, RangerPluginContext pluginContext, RangerRoles roles) { + public PolicyEngine(ServicePolicies servicePolicies, RangerPluginContext pluginContext, RangerRoles roles, boolean isUseReadWriteLock) { if (LOG.isDebugEnabled()) { LOG.debug("==> PolicyEngine(" + ", " + servicePolicies + ", " + pluginContext + ")"); } @@ -175,9 +196,28 @@ public PolicyEngine(ServicePolicies servicePolicies, RangerPluginContext pluginC PERF_POLICYENGINE_INIT_LOG.debug("In-Use memory: " + (totalMemory - freeMemory) + ", Free memory:" + freeMemory); } + normalizeServiceDefs(servicePolicies); + pluginContext.cleanResourceMatchers(); + this.pluginContext = pluginContext; + this.lock = new RangerReadWriteLock(isUseReadWriteLock); + this.zoneMatcher = new RangerSecurityZoneMatcher(servicePolicies.getSecurityZones(), servicePolicies.getServiceDef(), pluginContext); - this.pluginContext.setAuthContext(new RangerAuthContext(null, roles)); + Boolean hasPolicyDeltas = RangerPolicyDeltaUtil.hasPolicyDeltas(servicePolicies); + + if (hasPolicyDeltas != null) { + if (hasPolicyDeltas.equals(Boolean.TRUE)) { + LOG.info("Policy engine will" + (isUseReadWriteLock ? " " : " not ") + "perform in place update while processing policy-deltas."); + } else { + LOG.info("Policy engine will" + (isUseReadWriteLock ? " " : " not ") + "perform in place update while processing policies."); + } + } + + RangerAuthContext currAuthContext = pluginContext.getAuthContext(); + RangerUserStore userStore = currAuthContext != null ? currAuthContext.getUserStoreUtil().getUserStore() : null; + RangerAuthContext authContext = new RangerAuthContext(null, zoneMatcher, roles, userStore); + + this.pluginContext.setAuthContext(authContext); RangerPolicyEngineOptions options = pluginContext.getConfig().getPolicyEngineOptions(); @@ -186,14 +226,14 @@ public PolicyEngine(ServicePolicies servicePolicies, RangerPluginContext pluginC } policyRepository = new RangerPolicyRepository(servicePolicies, this.pluginContext); + serviceDefHelper = new RangerServiceDefHelper(policyRepository.getServiceDef(), false); ServicePolicies.TagPolicies tagPolicies = servicePolicies.getTagPolicies(); if (!options.disableTagPolicyEvaluation && tagPolicies != null && !StringUtils.isEmpty(tagPolicies.getServiceName()) - && tagPolicies.getServiceDef() != null - && !CollectionUtils.isEmpty(tagPolicies.getPolicies())) { + && tagPolicies.getServiceDef() != null) { if (LOG.isDebugEnabled()) { LOG.debug("PolicyEngine : Building tag-policy-repository for tag-service " + tagPolicies.getServiceName()); } @@ -224,8 +264,6 @@ public PolicyEngine(ServicePolicies servicePolicies, RangerPluginContext pluginC this.allContextEnrichers = tmpList; if (MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { - buildZoneTrie(servicePolicies); - for (Map.Entry zone : servicePolicies.getSecurityZones().entrySet()) { RangerPolicyRepository policyRepository = new RangerPolicyRepository(servicePolicies, this.pluginContext, zone.getKey()); @@ -233,6 +271,20 @@ public PolicyEngine(ServicePolicies servicePolicies, RangerPluginContext pluginC } } + for (RangerServiceDef.RangerResourceDef resourceDef : getServiceDef().getResources()) { + Map matchOptions = resourceDef.getMatcherOptions(); + + if (RangerAbstractResourceMatcher.getOptionReplaceTokens(matchOptions)) { + String delimiterPrefix = RangerAbstractResourceMatcher.getOptionDelimiterPrefix(matchOptions); + char delimiterStart = RangerAbstractResourceMatcher.getOptionDelimiterStart(matchOptions); + char delimiterEnd = RangerAbstractResourceMatcher.getOptionDelimiterEnd(matchOptions); + char escapeChar = RangerAbstractResourceMatcher.getOptionDelimiterEscape(matchOptions); + + StringTokenReplacer tokenReplacer = new StringTokenReplacer(delimiterStart, delimiterEnd, escapeChar, delimiterPrefix); + tokenReplacers.put(resourceDef.getName(), tokenReplacer); + } + } + RangerPerfTracer.log(perf); if (PERF_POLICYENGINE_INIT_LOG.isDebugEnabled()) { @@ -259,33 +311,46 @@ public PolicyEngine cloneWithDelta(ServicePolicies servicePolicies) { perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_INIT_LOG, "RangerPolicyEngine.cloneWithDelta()"); } - RangerServiceDef serviceDef = this.getServiceDef(); - String serviceType = (serviceDef != null) ? serviceDef.getName() : ""; - boolean isValidDeltas = false; + try (RangerReadWriteLock.RangerLock writeLock = getWriteLock()) { + if (LOG.isDebugEnabled()) { + if (writeLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + writeLock); + } + } - if (CollectionUtils.isNotEmpty(servicePolicies.getPolicyDeltas()) || MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { - isValidDeltas = CollectionUtils.isEmpty(servicePolicies.getPolicyDeltas()) || RangerPolicyDeltaUtil.isValidDeltas(servicePolicies.getPolicyDeltas(), serviceType); + RangerServiceDef serviceDef = this.getServiceDef(); + String serviceType = (serviceDef != null) ? serviceDef.getName() : ""; + boolean isValidDeltas = false; - if (isValidDeltas) { - if (MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { - for (Map.Entry entry : servicePolicies.getSecurityZones().entrySet()) { - if (!RangerPolicyDeltaUtil.isValidDeltas(entry.getValue().getPolicyDeltas(), serviceType)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Invalid policy-deltas for security zone:[" + entry.getKey() + "]"); - } + if (CollectionUtils.isNotEmpty(servicePolicies.getPolicyDeltas()) || MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { + isValidDeltas = CollectionUtils.isEmpty(servicePolicies.getPolicyDeltas()) || RangerPolicyDeltaUtil.isValidDeltas(servicePolicies.getPolicyDeltas(), serviceType); + + if (isValidDeltas) { + if (MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { + for (Map.Entry entry : servicePolicies.getSecurityZones().entrySet()) { + if (!RangerPolicyDeltaUtil.isValidDeltas(entry.getValue().getPolicyDeltas(), serviceType)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Invalid policy-deltas for security zone:[" + entry.getKey() + "]"); + } - isValidDeltas = false; - break; + isValidDeltas = false; + break; + } } } } } - } - if (isValidDeltas) { - ret = new PolicyEngine(this, servicePolicies); - } else { - ret = null; + if (isValidDeltas) { + if (writeLock.isLockingEnabled()) { + updatePolicyEngine(servicePolicies); + ret = this; + } else { + ret = new PolicyEngine(this, servicePolicies); + } + } else { + ret = null; + } } RangerPerfTracer.log(perf); @@ -311,32 +376,34 @@ public RangerPolicyRepository getRepositoryForMatchedZone(RangerPolicy policy) { return ret; } - public Set getMatchedZonesForResourceAndChildren(RangerAccessResource accessResource) { + public Set getMatchedZonesForResourceAndChildren(Map resource) { + Set ret = zoneMatcher.getZonesForResourceAndChildren(resource); + if (LOG.isDebugEnabled()) { - LOG.debug("==> PolicyEngine.getMatchedZonesForResourceAndChildren(" + accessResource + ")"); + LOG.debug("getMatchedZonesForResourceAndChildren(resource={}): ret={}", resource, ret); } - Set ret = null; + return ret; + } - if (MapUtils.isNotEmpty(this.resourceZoneTrie)) { - ret = getMatchedZonesForResourceAndChildren(accessResource.getAsMap(), accessResource); - } + public Set getMatchedZonesForResourceAndChildren(RangerAccessResource resource) { + Set ret = zoneMatcher.getZonesForResourceAndChildren(resource); if (LOG.isDebugEnabled()) { - LOG.debug("<== PolicyEngine.getMatchedZonesForResourceAndChildren(" + accessResource + ") : " + ret); + LOG.debug("getMatchedZonesForResourceAndChildren(resource={}): ret={}", resource, ret); } return ret; } public String getUniquelyMatchedZoneName(Map resourceAsMap) { - String ret = null; - Set matchedZones = getMatchedZonesForResourceAndChildren(resourceAsMap, convertToAccessResource(resourceAsMap)); - if (CollectionUtils.isNotEmpty(matchedZones) && matchedZones.size() == 1) { - String[] matchedZonesArray = new String[1]; - matchedZones.toArray(matchedZonesArray); - ret = matchedZonesArray[0]; + Set matchedZones = zoneMatcher.getZonesForResourceAndChildren(resourceAsMap); + String ret = (matchedZones != null && matchedZones.size() == 1) ? matchedZones.iterator().next() : null; + + if (LOG.isDebugEnabled()) { + LOG.debug("getUniquelyMatchedZoneName(" + resourceAsMap + "): matchedZones=" + matchedZones + ", ret=" + ret); } + return ret; } @@ -371,7 +438,7 @@ public boolean hasResourcePolicies(RangerPolicyRepository policyRepository) { public boolean isResourceZoneAssociatedWithTagService(String resourceZoneName) { final boolean ret; - if (StringUtils.isNotEmpty(resourceZoneName) && tagPolicyRepository != null && zoneTagServiceMap.get(resourceZoneName) != null) { + if (StringUtils.isNotEmpty(resourceZoneName) && tagPolicyRepository != null && zoneMatcher.hasTagService(resourceZoneName)) { if (LOG.isDebugEnabled()) { LOG.debug("Accessed resource is in a zone:[" + resourceZoneName + "] which is associated with the tag-service:[" + tagPolicyRepository.getServiceName() + "]"); } @@ -408,214 +475,65 @@ public void preCleanup(boolean isForced) { } } - private Set getMatchedZonesForResourceAndChildren(Map resource, RangerAccessResource accessResource) { - if (LOG.isDebugEnabled()) { - LOG.debug("==> PolicyEngine.getMatchedZonesForResourceAndChildren(" + resource + ", " + accessResource + ")"); - } - - Set ret = null; - - if (MapUtils.isNotEmpty(this.resourceZoneTrie)) { - List> zoneMatchersList = null; - Set smallestList = null; - - for (Map.Entry entry : resource.entrySet()) { - String resourceDefName = entry.getKey(); - Object resourceValues = entry.getValue(); - RangerResourceTrie trie = resourceZoneTrie.get(resourceDefName); - - if (trie == null) { - continue; - } - - Set matchedZones = trie.getEvaluatorsForResource(resourceValues); - - if (LOG.isDebugEnabled()) { - LOG.debug("ResourceDefName:[" + resourceDefName + "], values:[" + resourceValues + "], matched-zones:[" + matchedZones + "]"); - } - - if (CollectionUtils.isEmpty(matchedZones)) { // no policies for this resource, bail out - zoneMatchersList = null; - smallestList = null; - - break; - } - - if (smallestList == null) { - smallestList = matchedZones; - } else { - if (zoneMatchersList == null) { - zoneMatchersList = new ArrayList<>(); - - zoneMatchersList.add(smallestList); - } - - zoneMatchersList.add(matchedZones); - - if (smallestList.size() > matchedZones.size()) { - smallestList = matchedZones; - } - } - } - if (smallestList != null) { - final Set intersection; - - if (zoneMatchersList != null) { - intersection = new HashSet<>(smallestList); - - for (Set zoneMatchers : zoneMatchersList) { - if (zoneMatchers != smallestList) { - // remove zones from intersection that are not in zoneMatchers - intersection.retainAll(zoneMatchers); - - if (CollectionUtils.isEmpty(intersection)) { // if no zoneMatcher exists, bail out and return empty list - break; - } - } - } - } else { - intersection = smallestList; - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Resource:[" + resource + "], matched-zones:[" + intersection + "]"); - } - - if (intersection.size() > 0) { - ret = new HashSet<>(); + private void normalizeServiceDefs(ServicePolicies servicePolicies) { + RangerServiceDef serviceDef = servicePolicies.getServiceDef(); - for (RangerZoneResourceMatcher zoneMatcher : intersection) { - if (LOG.isDebugEnabled()) { - LOG.debug("Trying to match resource:[" + accessResource + "] using zoneMatcher:[" + zoneMatcher + "]"); - } + if (serviceDef != null) { + ServiceDefUtil.normalize(serviceDef); - // These are potential matches. Try to really match them - if (zoneMatcher.getPolicyResourceMatcher().isMatch(accessResource, RangerPolicyResourceMatcher.MatchScope.ANY, null)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Matched resource:[" + accessResource + "] using zoneMatcher:[" + zoneMatcher + "]"); - } + RangerServiceDef tagServiceDef = servicePolicies.getTagPolicies() != null ? servicePolicies.getTagPolicies().getServiceDef() : null; - // Actual match happened - ret.add(zoneMatcher.getSecurityZoneName()); - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Did not match resource:[" + accessResource + "] using zoneMatcher:[" + zoneMatcher + "]"); - } - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("The following zone-names matched resource:[" + accessResource + "]: " + ret); - } - } + if (tagServiceDef != null) { + ServiceDefUtil.normalizeAccessTypeDefs(ServiceDefUtil.normalize(tagServiceDef), serviceDef.getName()); } } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== PolicyEngine.getMatchedZonesForResourceAndChildren(" + resource + ", " + accessResource + ") : " + ret); - } - - return ret; } - private RangerAccessResource convertToAccessResource(Map resource) { - RangerAccessResourceImpl ret = new RangerAccessResourceImpl(); - - ret.setServiceDef(getServiceDef()); - - for (Map.Entry entry : resource.entrySet()) { - ret.setValue(entry.getKey(), entry.getValue()); - } - - return ret; - } - - private PolicyEngine(final PolicyEngine other, ServicePolicies servicePolicies) { this.useForwardedIPAddress = other.useForwardedIPAddress; this.trustedProxyAddresses = other.trustedProxyAddresses; + this.serviceDefHelper = other.serviceDefHelper; this.pluginContext = other.pluginContext; + this.lock = other.lock; + this.zoneMatcher = new RangerSecurityZoneMatcher(servicePolicies.getSecurityZones(), servicePolicies.getServiceDef(), pluginContext); long policyVersion = servicePolicies.getPolicyVersion() != null ? servicePolicies.getPolicyVersion() : -1L; List defaultZoneDeltas = new ArrayList<>(); List defaultZoneDeltasForTagPolicies = new ArrayList<>(); - if (MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { - buildZoneTrie(servicePolicies); - - Map> zoneDeltasMap = new HashMap<>(); - - for (Map.Entry zone : servicePolicies.getSecurityZones().entrySet()) { - List deltas = zone.getValue().getPolicyDeltas(); - - for (RangerPolicyDelta delta : deltas) { - String zoneName = delta.getZoneName(); + getDeltasSortedByZones(other, servicePolicies, defaultZoneDeltas, defaultZoneDeltasForTagPolicies); - if (StringUtils.isNotEmpty(zoneName)) { - List zoneDeltas = zoneDeltasMap.get(zoneName); - - if (zoneDeltas == null) { - zoneDeltas = new ArrayList<>(); - zoneDeltasMap.put(zoneName, zoneDeltas); - } + if (other.policyRepository != null && CollectionUtils.isNotEmpty(defaultZoneDeltas)) { + this.policyRepository = new RangerPolicyRepository(other.policyRepository, defaultZoneDeltas, policyVersion); + } else { + this.policyRepository = shareWith(other.policyRepository); + } - zoneDeltas.add(delta); - } else { - LOG.warn("policyDelta : [" + delta + "] does not belong to any zone. Should not have come here."); - } - } + if (MapUtils.isEmpty(zonePolicyRepositories) && MapUtils.isNotEmpty(other.zonePolicyRepositories)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Existing engine contains some zonePolicyRepositories and new engine contains no zonePolicyRepositories"); } - - for (Map.Entry> entry : zoneDeltasMap.entrySet()) { - final String zoneName = entry.getKey(); - final List zoneDeltas = entry.getValue(); - final RangerPolicyRepository otherRepository = other.zonePolicyRepositories.get(zoneName); - final RangerPolicyRepository policyRepository; - - if (CollectionUtils.isNotEmpty(zoneDeltas)) { - if (otherRepository == null) { - List policies = new ArrayList<>(); - - for (RangerPolicyDelta delta : zoneDeltas) { - if (delta.getChangeType() == RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE) { - policies.add(delta.getPolicy()); - } else { - LOG.warn("Expected changeType:[" + RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE + "], found policy-change-delta:[" + delta +"]"); - } - } - - servicePolicies.getSecurityZones().get(zoneName).setPolicies(policies); - - policyRepository = new RangerPolicyRepository(servicePolicies, this.pluginContext, zoneName); - } else { - policyRepository = new RangerPolicyRepository(otherRepository, zoneDeltas, policyVersion); - } - } else { - policyRepository = shareWith(otherRepository); + for (Map.Entry entry : other.zonePolicyRepositories.entrySet()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Copying over zoneRepository for zone :[" + entry.getKey() + "]"); } - - zonePolicyRepositories.put(zoneName, policyRepository); - } - } - - List unzonedDeltas = servicePolicies.getPolicyDeltas(); - - for (RangerPolicyDelta delta : unzonedDeltas) { - if (servicePolicies.getServiceDef().getName().equals(delta.getServiceType())) { - defaultZoneDeltas.add(delta); - } else { - defaultZoneDeltasForTagPolicies.add(delta); + RangerPolicyRepository otherZonePolicyRepository = entry.getValue(); + RangerPolicyRepository zonePolicyRepository = shareWith(otherZonePolicyRepository); + this.zonePolicyRepositories.put(entry.getKey(), zonePolicyRepository); } - } - - if (other.policyRepository != null && CollectionUtils.isNotEmpty(defaultZoneDeltas)) { - this.policyRepository = new RangerPolicyRepository(other.policyRepository, defaultZoneDeltas, policyVersion); } else { - this.policyRepository = shareWith(other.policyRepository); + if (LOG.isDebugEnabled()) { + LOG.debug("Existing engine contains no zonePolicyRepositories or new engine contains some zonePolicyRepositories"); + LOG.debug("Not copying zoneRepositories from existing engine, as they are already copied or modified"); + } } if (servicePolicies.getTagPolicies() != null && CollectionUtils.isNotEmpty(defaultZoneDeltasForTagPolicies)) { if (other.tagPolicyRepository == null) { + + if (LOG.isDebugEnabled()) { + LOG.debug("Current policy-engine does not have any tagPolicyRepository"); + } // Only creates are expected List tagPolicies = new ArrayList<>(); @@ -631,15 +549,21 @@ private PolicyEngine(final PolicyEngine other, ServicePolicies servicePolicies) this.tagPolicyRepository = new RangerPolicyRepository(servicePolicies.getTagPolicies(), this.pluginContext, servicePolicies.getServiceDef(), servicePolicies.getServiceName()); } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Current policy-engine has a tagPolicyRepository"); + } this.tagPolicyRepository = new RangerPolicyRepository(other.tagPolicyRepository, defaultZoneDeltasForTagPolicies, policyVersion); } } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Either no associated tag repository or no changes to tag policies"); + } this.tagPolicyRepository = shareWith(other.tagPolicyRepository); } List tmpList; List tagContextEnrichers = tagPolicyRepository == null ? null :tagPolicyRepository.getContextEnrichers(); - List resourceContextEnrichers = policyRepository.getContextEnrichers(); + List resourceContextEnrichers = policyRepository == null ? null : policyRepository.getContextEnrichers(); if (CollectionUtils.isEmpty(tagContextEnrichers)) { tmpList = resourceContextEnrichers; @@ -656,72 +580,6 @@ private PolicyEngine(final PolicyEngine other, ServicePolicies servicePolicies) reorderPolicyEvaluators(); } - private void buildZoneTrie(ServicePolicies servicePolicies) { - if (LOG.isDebugEnabled()) { - LOG.debug("==> PolicyEngine.buildZoneTrie()"); - } - - Map securityZones = servicePolicies.getSecurityZones(); - - if (MapUtils.isNotEmpty(securityZones)) { - RangerServiceDef serviceDef = servicePolicies.getServiceDef(); - List matchers = new ArrayList<>(); - - for (Map.Entry securityZone : securityZones.entrySet()) { - String zoneName = securityZone.getKey(); - ServicePolicies.SecurityZoneInfo zoneDetails = securityZone.getValue(); - - if (LOG.isDebugEnabled()) { - LOG.debug("Building matchers for zone:[" + zoneName +"]"); - } - - for (Map> resource : zoneDetails.getResources()) { - if (LOG.isDebugEnabled()) { - LOG.debug("Building matcher for resource:[" + resource + "] in zone:[" + zoneName +"]"); - } - - Map policyResources = new HashMap<>(); - - for (Map.Entry> entry : resource.entrySet()) { - String resourceDefName = entry.getKey(); - List resourceValues = entry.getValue(); - RangerPolicy.RangerPolicyResource policyResource = new RangerPolicy.RangerPolicyResource(); - policyResource.setIsExcludes(false); - policyResource.setIsRecursive(EmbeddedServiceDefsUtil.isRecursiveEnabled(serviceDef, resourceDefName)); - policyResource.setValues(resourceValues); - policyResources.put(resourceDefName, policyResource); - } - - matchers.add(new RangerZoneResourceMatcher(zoneName, policyResources, serviceDef)); - - if (LOG.isDebugEnabled()) { - LOG.debug("Built matcher for resource:[" + resource +"] in zone:[" + zoneName + "]"); - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Built all matchers for zone:[" + zoneName +"]"); - } - - if (zoneDetails.getContainsAssociatedTagService()) { - zoneTagServiceMap.put(zoneName, zoneName); - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("Built matchers for all Zones"); - } - - for (RangerServiceDef.RangerResourceDef resourceDef : serviceDef.getResources()) { - resourceZoneTrie.put(resourceDef.getName(), new RangerResourceTrie<>(resourceDef, matchers)); - } - } - - if (LOG.isDebugEnabled()) { - LOG.debug("<== PolicyEngine.buildZoneTrie()"); - } - } - private RangerPolicyRepository shareWith(RangerPolicyRepository other) { if (other != null) { other.setShared(); @@ -787,5 +645,125 @@ private void cleanup() { LOG.debug("<== PolicyEngine.cleanup()"); } } + + void updatePolicyEngine(ServicePolicies servicePolicies) { + List defaultZoneDeltas = new ArrayList<>(); + List defaultZoneDeltasForTagPolicies = new ArrayList<>(); + + getDeltasSortedByZones(this, servicePolicies, defaultZoneDeltas, defaultZoneDeltasForTagPolicies); + + if (this.policyRepository != null && CollectionUtils.isNotEmpty(defaultZoneDeltas)) { + this.policyRepository.reinit(defaultZoneDeltas); + } + + if (servicePolicies.getTagPolicies() != null && CollectionUtils.isNotEmpty(defaultZoneDeltasForTagPolicies)) { + if (this.tagPolicyRepository != null) { + this.tagPolicyRepository.reinit(defaultZoneDeltasForTagPolicies); + } else { + LOG.error("No previous tagPolicyRepository to update! Should not have come here!!"); + } + } + + reorderPolicyEvaluators(); + } + + private void getDeltasSortedByZones(PolicyEngine current, ServicePolicies servicePolicies, List defaultZoneDeltas, List defaultZoneDeltasForTagPolicies) { + + if (LOG.isDebugEnabled()) { + LOG.debug("==> getDeltasSortedByZones()"); + } + + long policyVersion = servicePolicies.getPolicyVersion() != null ? servicePolicies.getPolicyVersion() : -1L; + + if (CollectionUtils.isNotEmpty(defaultZoneDeltas)) { + LOG.warn("Emptying out defaultZoneDeltas!"); + defaultZoneDeltas.clear(); + } + if (CollectionUtils.isNotEmpty(defaultZoneDeltasForTagPolicies)) { + LOG.warn("Emptying out defaultZoneDeltasForTagPolicies!"); + defaultZoneDeltasForTagPolicies.clear(); + } + + if (MapUtils.isNotEmpty(servicePolicies.getSecurityZones())) { + Map> zoneDeltasMap = new HashMap<>(); + + for (Map.Entry zone : servicePolicies.getSecurityZones().entrySet()) { + String zoneName = zone.getKey(); + List deltas = zone.getValue().getPolicyDeltas(); + List zoneDeltas = new ArrayList<>(); + + if (StringUtils.isNotEmpty(zoneName)) { + zoneDeltasMap.put(zoneName, zoneDeltas); + + for (RangerPolicyDelta delta : deltas) { + zoneDeltas = zoneDeltasMap.get(zoneName); + zoneDeltas.add(delta); + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Security zones found in the service-policies:[" + zoneDeltasMap.keySet() + "]"); + } + + for (Map.Entry> entry : zoneDeltasMap.entrySet()) { + final String zoneName = entry.getKey(); + final List zoneDeltas = entry.getValue(); + final RangerPolicyRepository otherRepository = current.zonePolicyRepositories.get(zoneName); + final RangerPolicyRepository policyRepository; + + if (LOG.isDebugEnabled()) { + LOG.debug("zoneName:[" + zoneName + "], zoneDeltas:[" + Arrays.toString(zoneDeltas.toArray()) + "], doesOtherRepositoryExist:[" + (otherRepository != null) + "]"); + } + + if (CollectionUtils.isNotEmpty(zoneDeltas)) { + if (otherRepository == null) { + List policies = new ArrayList<>(); + + for (RangerPolicyDelta delta : zoneDeltas) { + if (delta.getChangeType() == RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE) { + policies.add(delta.getPolicy()); + } else { + LOG.warn("Expected changeType:[" + RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE + "], found policy-change-delta:[" + delta +"]"); + } + } + + servicePolicies.getSecurityZones().get(zoneName).setPolicies(policies); + + policyRepository = new RangerPolicyRepository(servicePolicies, current.pluginContext, zoneName); + } else { + policyRepository = new RangerPolicyRepository(otherRepository, zoneDeltas, policyVersion); + } + } else { + policyRepository = shareWith(otherRepository); + } + + zonePolicyRepositories.put(zoneName, policyRepository); + } + } + + List unzonedDeltas = servicePolicies.getPolicyDeltas(); + + if (LOG.isDebugEnabled()) { + LOG.debug("ServicePolicies.policyDeltas:[" + Arrays.toString(servicePolicies.getPolicyDeltas().toArray()) + "]"); + } + + for (RangerPolicyDelta delta : unzonedDeltas) { + if (servicePolicies.getServiceDef().getName().equals(delta.getServiceType())) { + defaultZoneDeltas.add(delta); + } else { + defaultZoneDeltasForTagPolicies.add(delta); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("defaultZoneDeltas:[" + Arrays.toString(defaultZoneDeltas.toArray()) + "]"); + LOG.debug("defaultZoneDeltasForTagPolicies:[" + Arrays.toString(defaultZoneDeltasForTagPolicies.toArray()) + "]"); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== getDeltasSortedByZones()"); + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java index 1d961a197a..f1646c96f1 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/PolicyEvaluatorForTag.java @@ -21,13 +21,16 @@ import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; import java.io.Serializable; import java.util.Comparator; + public class PolicyEvaluatorForTag { public static final Comparator EVAL_ORDER_COMPARATOR = new PolicyEvalOrderComparator(); public static final Comparator NAME_COMPARATOR = new PolicyNameComparator(); + public static final Comparator MATCH_TYPE_COMPARATOR = new MatchTypeComparator(); private final RangerPolicyEvaluator evaluator; private final RangerTagForEval tag; @@ -58,4 +61,17 @@ public int compare(PolicyEvaluatorForTag me, PolicyEvaluatorForTag other) { return RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR.compare(me.getEvaluator(), other.getEvaluator()); } } + + static class MatchTypeComparator implements Comparator, Serializable { + @Override + public int compare(PolicyEvaluatorForTag me, PolicyEvaluatorForTag other) { + int ret = RangerPolicyResourceMatcher.MATCH_TYPE_COMPARATOR.compare(me.getTag().getMatchType(), other.getTag().getMatchType()); + + if (ret == 0) { + ret = RangerPolicyEvaluator.NAME_COMPARATOR.compare(me.getEvaluator(), other.getEvaluator()); + } + + return ret; + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequest.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequest.java index 4a12168bec..01848df764 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequest.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequest.java @@ -19,6 +19,7 @@ package org.apache.ranger.plugin.policyengine; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -65,5 +66,13 @@ public interface RangerAccessRequest { ResourceMatchingScope getResourceMatchingScope(); - enum ResourceMatchingScope {SELF, SELF_OR_DESCENDANTS} + default Map getResourceElementMatchingScopes() { + return Collections.emptyMap(); + } + + enum ResourceMatchingScope { SELF, SELF_OR_DESCENDANTS } + + enum ResourceElementMatchingScope { SELF, SELF_OR_CHILD, SELF_OR_PREFIX } + + enum ResourceElementMatchType { NONE, SELF, CHILD, PREFIX } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java index 74a7a2615c..019a0d8936 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestImpl.java @@ -20,6 +20,7 @@ package org.apache.ranger.plugin.policyengine; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -29,10 +30,12 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAccessRequestImpl implements RangerAccessRequest { - private static final Logger LOG = Logger.getLogger(RangerAccessRequestImpl.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAccessRequestImpl.class); private RangerAccessResource resource; private String accessType; @@ -54,6 +57,7 @@ public class RangerAccessRequestImpl implements RangerAccessRequest { private boolean isAccessTypeAny; private boolean isAccessTypeDelegatedAdmin; private ResourceMatchingScope resourceMatchingScope = ResourceMatchingScope.SELF; + private Map resourceElementMatchingScopes = Collections.emptyMap(); public RangerAccessRequestImpl() { this(null, null, null, null, null); @@ -78,6 +82,27 @@ public RangerAccessRequestImpl(RangerAccessResource resource, String accessType, setClusterName(null); } + public RangerAccessRequestImpl(RangerAccessRequest request) { + setResource(request.getResource()); + setAccessType(request.getAccessType()); + setUser(request.getUser()); + setUserGroups(request.getUserGroups()); + setUserRoles(request.getUserRoles()); + setForwardedAddresses(request.getForwardedAddresses()); + setAccessTime(request.getAccessTime()); + setRemoteIPAddress(request.getRemoteIPAddress()); + setClientType(request.getClientType()); + setAction(request.getAction()); + setRequestData(request.getRequestData()); + setSessionId(request.getSessionId()); + setContext(request.getContext()); + setClusterName(request.getClusterName()); + setResourceMatchingScope(request.getResourceMatchingScope()); + setResourceElementMatchingScopes(request.getResourceElementMatchingScopes()); + setClientIPAddress(request.getClientIPAddress()); + setClusterType(request.getClusterType()); + } + @Override public RangerAccessResource getResource() { return resource; @@ -149,6 +174,9 @@ public ResourceMatchingScope getResourceMatchingScope() { return resourceMatchingScope; } + @Override + public Map getResourceElementMatchingScopes() { return this.resourceElementMatchingScopes; } + @Override public boolean isAccessTypeAny() { return isAccessTypeAny; @@ -161,6 +189,9 @@ public boolean isAccessTypeDelegatedAdmin() { public void setResource(RangerAccessResource resource) { this.resource = resource; + if (context != null) { + RangerAccessRequestUtil.setIsRequestPreprocessed(context, Boolean.FALSE); + } } public void setAccessType(String accessType) { @@ -233,10 +264,29 @@ public void setClusterType(String clusterType) { this.clusterType = clusterType; } - public void setResourceMatchingScope(ResourceMatchingScope scope) { this.resourceMatchingScope = scope; } + public void setResourceMatchingScope(ResourceMatchingScope scope) { + this.resourceMatchingScope = scope; + if (context != null) { + RangerAccessRequestUtil.setIsRequestPreprocessed(context, Boolean.FALSE); + } + } + + public void setResourceElementMatchingScopes(Map resourceElementMatchingScopes) { + this.resourceElementMatchingScopes = resourceElementMatchingScopes == null ? Collections.emptyMap() : resourceElementMatchingScopes; + } public void setContext(Map context) { - this.context = (context == null) ? new HashMap() : context; + if (context == null) { + this.context = new HashMap<>(); + } else { + this.context = context; + } + + RangerAccessRequest current = RangerAccessRequestUtil.getRequestFromContext(this.context); + + if (current == null) { + RangerAccessRequestUtil.setRequestInContext(this); + } } public void extractAndSetClientIPAddress(boolean useForwardedIPAddress, String[]trustedProxyAddresses) { @@ -318,13 +368,18 @@ public StringBuilder toString(StringBuilder sb) { sb.append("requestData={").append(requestData).append("} "); sb.append("sessionId={").append(sessionId).append("} "); sb.append("resourceMatchingScope={").append(resourceMatchingScope).append("} "); + sb.append("resourceElementMatchingScopes={").append(resourceElementMatchingScopes).append("} "); sb.append("clusterName={").append(clusterName).append("} "); sb.append("clusterType={").append(clusterType).append("} "); sb.append("context={"); if(context != null) { for(Map.Entry e : context.entrySet()) { - sb.append(e.getKey()).append("={").append(e.getValue()).append("} "); + Object val = e.getValue(); + + if (!(val instanceof RangerAccessRequest)) { // to avoid recursive calls + sb.append(e.getKey()).append("={").append(val).append("} "); + } } } sb.append("} "); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestReadOnly.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestReadOnly.java index 4887c01127..b732dfab66 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestReadOnly.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestReadOnly.java @@ -98,6 +98,9 @@ public class RangerAccessRequestReadOnly implements RangerAccessRequest { @Override public ResourceMatchingScope getResourceMatchingScope() { return source.getResourceMatchingScope(); } + @Override + public Map getResourceElementMatchingScopes() { return source.getResourceElementMatchingScopes(); } + @Override public String getClusterName() { return source.getClusterName(); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestWrapper.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestWrapper.java new file mode 100644 index 0000000000..96f851e9a1 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessRequestWrapper.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine; + +import org.apache.commons.lang.StringUtils; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class RangerAccessRequestWrapper implements RangerAccessRequest { + + private final RangerAccessRequest request; + private final String accessType; + private final boolean isAccessTypeAny; + private final boolean isAccessTypeDelegatedAdmin; + + + public RangerAccessRequestWrapper(RangerAccessRequest request, String accessType) { + this.request = request; + this.accessType = accessType; + this.isAccessTypeAny = StringUtils.equals(accessType, RangerPolicyEngine.ANY_ACCESS); + this.isAccessTypeDelegatedAdmin = StringUtils.equals(accessType, RangerPolicyEngine.ADMIN_ACCESS); + } + + @Override + public RangerAccessResource getResource() { return request.getResource(); } + + @Override + public String getAccessType() { return accessType; } + + @Override + public boolean isAccessTypeAny() { return isAccessTypeAny; } + + @Override + public boolean isAccessTypeDelegatedAdmin() { return isAccessTypeDelegatedAdmin; } + + @Override + public String getUser() { return request.getUser(); } + + @Override + public Set getUserGroups() { return request.getUserGroups(); } + + @Override + public Set getUserRoles() {return request.getUserRoles(); } + + @Override + public Date getAccessTime() { return request.getAccessTime(); } + + @Override + public String getClientIPAddress() { return request.getClientIPAddress(); } + + @Override + public String getRemoteIPAddress() { return request.getRemoteIPAddress(); } + + @Override + public List getForwardedAddresses() { return request.getForwardedAddresses(); } + + @Override + public String getClientType() { return request.getClientType(); } + + @Override + public String getAction() { return request.getAction(); } + + @Override + public String getRequestData() { return request.getRequestData(); } + + @Override + public String getSessionId() { return request.getSessionId(); } + + @Override + public String getClusterName() { return request.getClusterName(); } + + @Override + public String getClusterType() { return request.getClusterType(); } + + @Override + public Map getContext() { return request.getContext(); } + + @Override + public RangerAccessRequest getReadOnlyCopy() { return request.getReadOnlyCopy(); } + + @Override + public ResourceMatchingScope getResourceMatchingScope() { return request.getResourceMatchingScope(); } + + @Override + public Map getResourceElementMatchingScopes() { return request.getResourceElementMatchingScopes(); } + +} + diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java index c54ef17048..4bf2617aa0 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerAccessResult.java @@ -26,13 +26,17 @@ import java.util.HashMap; import java.util.Map; +import java.util.Set; public class RangerAccessResult { - public final static String KEY_MASK_TYPE = "maskType"; - public final static String KEY_MASK_CONDITION = "maskCondition"; - public final static String KEY_MASKED_VALUE = "maskedValue"; - - private static String KEY_FILTER_EXPR = "filterExpr"; + public final static String KEY_MASK_TYPE = "maskType"; + public final static String KEY_MASK_CONDITION = "maskCondition"; + public final static String KEY_MASKED_VALUE = "maskedValue"; + private final static String KEY_FILTER_EXPR = "filterExpr"; + private final static String KEY_DATASETS = "datasets"; + private final static String KEY_PROJECTS = "projects"; + private final static String KEY_ALLOWED_BY_DATASETS = "allowedByDatasets"; + private final static String KEY_ALLOWED_BY_PROJECTS = "allowedByProjects"; private final String serviceName; private final RangerServiceDef serviceDef; @@ -155,7 +159,7 @@ public void setReason(String reason) { public boolean getIsAuditedDetermined() { return isAuditedDetermined; } - private void setIsAuditedDetermined(boolean value) { isAuditedDetermined = value; } + public void setIsAuditedDetermined(boolean value) { isAuditedDetermined = value; } /** * @return the isAudited @@ -326,6 +330,54 @@ public boolean isRowFilterEnabled() { return StringUtils.isNotEmpty(getFilterExpr()); } + public Set getDatasets() { + return additionalInfo == null ? null : (Set) additionalInfo.get(KEY_DATASETS); + } + + public void setDatasets(Set datasets) { + if (datasets == null) { + removeAdditionalInfo(KEY_DATASETS); + } else { + addAdditionalInfo(KEY_DATASETS, datasets); + } + } + + public Set getProjects() { + return additionalInfo == null ? null : (Set) additionalInfo.get(KEY_PROJECTS); + } + + public void setProjects(Set projects) { + if (projects == null) { + removeAdditionalInfo(KEY_PROJECTS); + } else { + addAdditionalInfo(KEY_PROJECTS, projects); + } + } + + public Set getAllowedByDatasets() { + return additionalInfo == null ? null : (Set) additionalInfo.get(KEY_ALLOWED_BY_DATASETS); + } + + public void setAllowedByDatasets(Set datasets) { + if (datasets == null) { + removeAdditionalInfo(KEY_ALLOWED_BY_DATASETS); + } else { + addAdditionalInfo(KEY_ALLOWED_BY_DATASETS, datasets); + } + } + + public Set getAllowedByProjects() { + return additionalInfo == null ? null : (Set) additionalInfo.get(KEY_ALLOWED_BY_PROJECTS); + } + + public void setAllowedByProjects(Set projects) { + if (projects == null) { + removeAdditionalInfo(KEY_ALLOWED_BY_PROJECTS); + } else { + addAdditionalInfo(KEY_ALLOWED_BY_PROJECTS, projects); + } + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPluginContext.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPluginContext.java index a8e76009e8..8a3e43e488 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPluginContext.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPluginContext.java @@ -19,14 +19,30 @@ package org.apache.ranger.plugin.policyengine; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.admin.client.RangerAdminRESTClient; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; import org.apache.ranger.plugin.service.RangerAuthContext; import org.apache.ranger.plugin.service.RangerAuthContextListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class RangerPluginContext { - private final RangerPluginConfig config; - private RangerAuthContext authContext; - private RangerAuthContextListener authContextListener; + private static final Logger LOG = LoggerFactory.getLogger(RangerPluginContext.class); + + private final RangerPluginConfig config; + private RangerAuthContext authContext; + private RangerAuthContextListener authContextListener; + private RangerAdminClient adminClient; + private final Map> resourceMatchers = new HashMap<>(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); // fair lock public RangerPluginContext(RangerPluginConfig config) { @@ -45,6 +61,65 @@ public String getClusterType() { public RangerAuthContext getAuthContext() { return authContext; } + public RangerResourceMatcher getResourceMatcher(String resourceDefName, RangerPolicy.RangerPolicyResource resource) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> getResourceMatcher(resourceDefName={}, resource={})", resourceDefName, resource); + } + RangerResourceMatcher ret = null; + + try { + lock.readLock().lock(); + + Map matchersForResource = resourceMatchers.get(resourceDefName); + + if (matchersForResource != null) { + ret = matchersForResource.get(resource); + } + } finally { + lock.readLock().unlock(); + } + if (LOG.isDebugEnabled()) { + LOG.debug("<== getResourceMatcher(resourceDefName={}, resource={}) : ret={}", resourceDefName, resource, ret); + } + + return ret; + } + + public void setResourceMatcher(String resourceDefName, RangerPolicy.RangerPolicyResource resource, RangerResourceMatcher matcher) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> setResourceMatcher(resourceDefName={}, resource={}, matcher={})", resourceDefName, resource, matcher); + } + if (config != null && config.getPolicyEngineOptions().enableResourceMatcherReuse) { + try { + lock.writeLock().lock(); + + Map matchersForResource = resourceMatchers.computeIfAbsent(resourceDefName, k -> new HashMap<>()); + matchersForResource.put(resource, matcher); + } finally { + lock.writeLock().unlock(); + } + } + if (LOG.isDebugEnabled()) { + LOG.debug("<== setResourceMatcher(resourceDefName={}, resource={}, matcher={})", resourceDefName, resource, matcher); + } + } + + void cleanResourceMatchers() { + if (LOG.isDebugEnabled()) { + LOG.debug("==> cleanResourceMatchers()"); + } + try { + lock.writeLock().lock(); + + resourceMatchers.clear(); + } finally { + lock.writeLock().unlock(); + } + if (LOG.isDebugEnabled()) { + LOG.debug("<== cleanResourceMatchers()"); + } + } + public void setAuthContext(RangerAuthContext authContext) { this.authContext = authContext; } public void setAuthContextListener(RangerAuthContextListener authContextListener) { this.authContextListener = authContextListener; } @@ -57,4 +132,54 @@ public void notifyAuthContextChanged() { } } + public RangerAdminClient getAdminClient() { + return adminClient; + } + + public void setAdminClient(RangerAdminClient adminClient) { + this.adminClient = adminClient; + } + + public RangerAdminClient createAdminClient(RangerPluginConfig pluginConfig) { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerBasePlugin.createAdminClient(" + pluginConfig.getServiceName() + ", " + pluginConfig.getAppId() + ", " + pluginConfig.getPropertyPrefix() + ")"); + } + + RangerAdminClient ret = null; + String propertyName = pluginConfig.getPropertyPrefix() + ".policy.source.impl"; + String policySourceImpl = pluginConfig.get(propertyName); + + if(StringUtils.isEmpty(policySourceImpl)) { + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Value for property[%s] was null or empty. Unexpected! Will use policy source of type[%s]", propertyName, RangerAdminRESTClient.class.getName())); + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug(String.format("Value for property[%s] was [%s].", propertyName, policySourceImpl)); + } + + try { + @SuppressWarnings("unchecked") + Class adminClass = (Class)Class.forName(policySourceImpl); + + ret = adminClass.newInstance(); + } catch (Exception excp) { + LOG.error("failed to instantiate policy source of type '" + policySourceImpl + "'. Will use policy source of type '" + RangerAdminRESTClient.class.getName() + "'", excp); + } + } + + if(ret == null) { + ret = new RangerAdminRESTClient(); + } + + ret.init(pluginConfig.getServiceName(), pluginConfig.getAppId(), pluginConfig.getPropertyPrefix(), pluginConfig); + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerBasePlugin.createAdminClient(" + pluginConfig.getServiceName() + ", " + pluginConfig.getAppId() + ", " + pluginConfig.getPropertyPrefix() + "): policySourceImpl=" + policySourceImpl + ", client=" + ret); + } + + setAdminClient(ret); + + return ret; + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java index 100d1f1aa2..7bf8c7ca40 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngine.java @@ -30,9 +30,10 @@ import org.apache.ranger.plugin.util.RangerRoles; public interface RangerPolicyEngine { - String GROUP_PUBLIC = "public"; - String ANY_ACCESS = "_any"; - String ADMIN_ACCESS = "_admin"; + String GROUP_PUBLIC = "public"; + String ANY_ACCESS = "_any"; + String ADMIN_ACCESS = "_admin"; + String SUPER_USER_ACCESS = "_super_user"; String AUDIT_ALL = "audit-all"; String AUDIT_NONE = "audit-none"; @@ -43,6 +44,8 @@ public interface RangerPolicyEngine { String PLUGIN_AUDIT_EXCLUDE_ROLES = "ranger.plugin.audit.exclude.roles"; String PLUGIN_SUPER_USERS = "ranger.plugin.super.users"; String PLUGIN_SUPER_GROUPS = "ranger.plugin.super.groups"; + String PLUGIN_AUDIT_FILTER = "ranger.plugin.audit.filters"; + String PLUGIN_SERVICE_ADMINS = "ranger.plugin.service.admins"; String USER_CURRENT = "{" + RangerAccessRequestUtil.KEY_USER + "}"; String RESOURCE_OWNER = "{OWNER}"; @@ -63,10 +66,18 @@ public interface RangerPolicyEngine { Collection evaluatePolicies(Collection requests, int policyType, RangerAccessResultProcessor resultProcessor); + void evaluateAuditPolicies(RangerAccessResult result); + RangerResourceACLs getResourceACLs(RangerAccessRequest request); + RangerResourceACLs getResourceACLs(RangerAccessRequest request, Integer requestedPolicyType); + Set getRolesFromUserAndGroups(String user, Set groups); + RangerRoles getRangerRoles(); + + RangerPluginContext getPluginContext(); + String getUniquelyMatchedZoneName(GrantRevokeRequest grantRevokeRequest); // Helpers diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java index 25c8d2251c..232ef90da3 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineImpl.java @@ -23,22 +23,25 @@ import org.apache.commons.collections.ListUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.hadoop.conf.Configuration; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.contextenricher.RangerTagForEval; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; -import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.PolicyACLSummary; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; import org.apache.ranger.plugin.service.RangerDefaultRequestProcessor; import org.apache.ranger.plugin.util.GrantRevokeRequest; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerCommonConstants; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerReadWriteLock; import org.apache.ranger.plugin.util.RangerRoles; import org.apache.ranger.plugin.util.ServicePolicies; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; @@ -50,14 +53,14 @@ import java.util.Map; import java.util.Set; -import static org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.ACCESS_CONDITIONAL; +import static org.apache.ranger.plugin.policyengine.PolicyEvaluatorForTag.MATCH_TYPE_COMPARATOR; public class RangerPolicyEngineImpl implements RangerPolicyEngine { - private static final Log LOG = LogFactory.getLog(RangerPolicyEngineImpl.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPolicyEngineImpl.class); - private static final Log PERF_POLICYENGINE_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policyengine.request"); - private static final Log PERF_POLICYENGINE_AUDIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.audit"); - private static final Log PERF_POLICYENGINE_GET_ACLS_LOG = RangerPerfTracer.getPerfLogger("policyengine.getResourceACLs"); + private static final Logger PERF_POLICYENGINE_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policyengine.request"); + private static final Logger PERF_POLICYENGINE_AUDIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.audit"); + private static final Logger PERF_POLICYENGINE_GET_ACLS_LOG = RangerPerfTracer.getPerfLogger("policyengine.getResourceACLs"); private final PolicyEngine policyEngine; private final RangerAccessRequestProcessor requestProcessor; @@ -71,7 +74,11 @@ static public RangerPolicyEngine getPolicyEngine(final RangerPolicyEngineImpl ot PolicyEngine policyEngine = other.policyEngine.cloneWithDelta(servicePolicies); if (policyEngine != null) { - ret = new RangerPolicyEngineImpl(policyEngine, other); + if (policyEngine == other.policyEngine) { + ret = other; + } else { + ret = new RangerPolicyEngineImpl(policyEngine, other); + } } } @@ -79,7 +86,18 @@ static public RangerPolicyEngine getPolicyEngine(final RangerPolicyEngineImpl ot } public RangerPolicyEngineImpl(ServicePolicies servicePolicies, RangerPluginContext pluginContext, RangerRoles roles) { - policyEngine = new PolicyEngine(servicePolicies, pluginContext, roles); + final boolean isUseReadWriteLock; + + Configuration config = pluginContext != null ? pluginContext.getConfig() : null; + + if (config != null) { + boolean isDeltasSupported = config.getBoolean(pluginContext.getConfig().getPropertyPrefix() + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_POLICY_DELTA_DEFAULT); + isUseReadWriteLock = isDeltasSupported && config.getBoolean(pluginContext.getConfig().getPropertyPrefix() + RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_IN_PLACE_POLICY_UPDATES, RangerCommonConstants.PLUGIN_CONFIG_SUFFIX_IN_PLACE_POLICY_UPDATES_DEFAULT); + } else { + isUseReadWriteLock = false; + } + + policyEngine = new PolicyEngine(servicePolicies, pluginContext, roles, isUseReadWriteLock); serviceConfig = new ServiceConfig(servicePolicies.getServiceConfig()); requestProcessor = new RangerDefaultRequestProcessor(policyEngine); } @@ -105,22 +123,32 @@ public RangerAccessResult evaluatePolicies(RangerAccessRequest request, int poli LOG.info("RangerPolicyEngineImpl.evaluatePolicies(" + requestHashCode + ", " + request + ")"); } - requestProcessor.preProcess(request); + RangerAccessResult ret; - RangerAccessResult ret = zoneAwareAccessEvaluationWithNoAudit(request, policyType); + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } - if (resultProcessor != null) { - RangerPerfTracer perfAuditTracer = null; + requestProcessor.preProcess(request); - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_AUDIT_LOG)) { - String requestHashCode = Integer.toHexString(System.identityHashCode(request)) + "_" + policyType; + ret = zoneAwareAccessEvaluationWithNoAudit(request, policyType); - perfAuditTracer = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_AUDIT_LOG, "RangerPolicyEngine.processAudit(requestHashCode=" + requestHashCode + ")"); - } + if (resultProcessor != null) { + RangerPerfTracer perfAuditTracer = null; - resultProcessor.processResult(ret); + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_AUDIT_LOG)) { + String requestHashCode = Integer.toHexString(System.identityHashCode(request)) + "_" + policyType; - RangerPerfTracer.log(perfAuditTracer); + perfAuditTracer = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_AUDIT_LOG, "RangerPolicyEngine.processAudit(requestHashCode=" + requestHashCode + ")"); + } + + resultProcessor.processResult(ret); + + RangerPerfTracer.log(perfAuditTracer); + } } RangerPerfTracer.log(perf); @@ -140,18 +168,25 @@ public Collection evaluatePolicies(Collection ret = new ArrayList<>(); - if (requests != null) { - for (RangerAccessRequest request : requests) { - requestProcessor.preProcess(request); + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + if (requests != null) { + for (RangerAccessRequest request : requests) { + requestProcessor.preProcess(request); - RangerAccessResult result = zoneAwareAccessEvaluationWithNoAudit(request, policyType); + RangerAccessResult result = zoneAwareAccessEvaluationWithNoAudit(request, policyType); - ret.add(result); + ret.add(result); + } } - } - if (resultProcessor != null) { - resultProcessor.processResults(ret); + if (resultProcessor != null) { + resultProcessor.processResults(ret); + } } if (LOG.isDebugEnabled()) { @@ -162,140 +197,142 @@ public Collection evaluatePolicies(Collection RangerPolicyEngineImpl.getResourceACLs(request=" + request + ")"); + LOG.debug("==> RangerPolicyEngineImpl.evaluateAuditPolicies(result=" + result + ")"); } - RangerResourceACLs ret = new RangerResourceACLs(); - RangerPerfTracer perf = null; + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_GET_ACLS_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_GET_ACLS_LOG, "RangerPolicyEngine.getResourceACLs(requestHashCode=" + request.getResource().getAsString() + ")"); - } + RangerPolicyRepository tagPolicyRepository = policyEngine.getTagPolicyRepository(); + RangerPolicyRepository policyRepository = policyEngine.getPolicyRepository(); + RangerAccessRequest request = result.getAccessRequest(); + boolean savedIsAuditedDetermined = result.getIsAuditedDetermined(); + boolean savedIsAudited = result.getIsAudited(); - requestProcessor.preProcess(request); + result.setIsAudited(false); + result.setIsAuditedDetermined(false); + + try { + if (tagPolicyRepository != null) { + evaluateTagAuditPolicies(request, result, tagPolicyRepository); + } - String zoneName = policyEngine.getUniquelyMatchedZoneName(request.getResource().getAsMap()); + if (!result.getIsAuditedDetermined() && policyRepository != null) { + evaluateResourceAuditPolicies(request, result, policyRepository); + } + } finally { + if (!result.getIsAuditedDetermined()) { + result.setIsAudited(savedIsAudited); + result.setIsAuditedDetermined(savedIsAuditedDetermined); + } + } + } if (LOG.isDebugEnabled()) { - LOG.debug("zoneName:[" + zoneName + "]"); + LOG.debug("<== RangerPolicyEngineImpl.evaluateAuditPolicies(result=" + result + ")"); } - List allEvaluators = new ArrayList<>(); - Map tagMatchTypeMap = new HashMap<>(); - Set policyIdForTemporalTags = new HashSet<>(); + } - getResourceACLEvaluatorsForZone(request, zoneName, allEvaluators, tagMatchTypeMap, policyIdForTemporalTags); + @Override + public RangerResourceACLs getResourceACLs(RangerAccessRequest request) { + return getResourceACLs(request, null); + } - allEvaluators.sort(RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR); + @Override + public RangerResourceACLs getResourceACLs(RangerAccessRequest request, Integer requestedPolicyType) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerPolicyEngineImpl.getResourceACLs(request=" + request + ", policyType=" + requestedPolicyType + ")"); + } - if (CollectionUtils.isNotEmpty(allEvaluators)) { - Integer policyPriority = null; + RangerResourceACLs ret = new RangerResourceACLs(); + RangerPerfTracer perf = null; - for (RangerPolicyEvaluator evaluator : allEvaluators) { - if (policyPriority == null) { - policyPriority = evaluator.getPolicyPriority(); + if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_GET_ACLS_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_GET_ACLS_LOG, "RangerPolicyEngine.getResourceACLs(requestHashCode=" + request.getResource().getAsString() + ")"); + } + + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); } + } - if (policyPriority != evaluator.getPolicyPriority()) { - ret.finalizeAcls(); + requestProcessor.preProcess(request); - policyPriority = evaluator.getPolicyPriority(); - } + String zoneName = RangerAccessRequestUtil.getResourceZoneNameFromContext(request.getContext()); - RangerPolicyResourceMatcher.MatchType matchType = tagMatchTypeMap.get(evaluator.getId()); + if (LOG.isDebugEnabled()) { + LOG.debug("zoneName:[" + zoneName + "]"); + } - if (matchType == null) { - matchType = evaluator.getPolicyResourceMatcher().getMatchType(request.getResource(), request.getContext()); - } + int[] policyTypes = requestedPolicyType == null ? RangerPolicy.POLICY_TYPES : new int[] { requestedPolicyType }; - final boolean isMatched; - if (request.getResourceMatchingScope() == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) { - isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; - } else { - isMatched = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS; - } + for (int policyType : policyTypes) { + // if resource isn't applicable for the policyType, skip evaluating policies and gathering ACLs + // for example, following resources are not applicable for listed policy-types + // - database: masking/row-filter policies + // - table: masking policies + // - column: row-filter policies + boolean requireExactMatch = (policyType == RangerPolicy.POLICY_TYPE_DATAMASK) || (policyType == RangerPolicy.POLICY_TYPE_ROWFILTER); - if (!isMatched) { + if (!policyEngine.getServiceDefHelper().isValidHierarchy(policyType, request.getResource().getKeys(), requireExactMatch)) { continue; } - PolicyACLSummary aclSummary = evaluator.getPolicyACLSummary(); + List allEvaluators = new ArrayList<>(); + Map tagMatchTypeMap = new HashMap<>(); + Set policyIdForTemporalTags = new HashSet<>(); - if (aclSummary != null) { - boolean isConditional = policyIdForTemporalTags.contains(evaluator.getId()) || evaluator.getValidityScheduleEvaluatorsCount() != 0; - Integer accessResult; + getResourceACLEvaluatorsForZone(request, zoneName, policyType, allEvaluators, tagMatchTypeMap, policyIdForTemporalTags); - for (Map.Entry> userAccessInfo : aclSummary.getUsersAccessInfo().entrySet()) { - final String userName = userAccessInfo.getKey(); + allEvaluators.sort(RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR); - for (Map.Entry accessInfo : userAccessInfo.getValue().entrySet()) { - if (isConditional) { - accessResult = ACCESS_CONDITIONAL; - } else { - accessResult = accessInfo.getValue().getResult(); - - if (accessResult.equals(RangerPolicyEvaluator.ACCESS_UNDETERMINED)) { - accessResult = RangerPolicyEvaluator.ACCESS_DENIED; - } - } + if (CollectionUtils.isEmpty(allEvaluators)) { + continue; + } - RangerPolicy policy = evaluator.getPolicy(); + Integer policyPriority = null; - ret.setUserAccessInfo(userName, accessInfo.getKey(), accessResult, policy); - } + for (RangerPolicyEvaluator evaluator : allEvaluators) { + if (policyPriority == null) { + policyPriority = evaluator.getPolicyPriority(); } - for (Map.Entry> groupAccessInfo : aclSummary.getGroupsAccessInfo().entrySet()) { - final String groupName = groupAccessInfo.getKey(); - - for (Map.Entry accessInfo : groupAccessInfo.getValue().entrySet()) { - if (isConditional) { - accessResult = ACCESS_CONDITIONAL; - } else { - accessResult = accessInfo.getValue().getResult(); - - if (accessResult.equals(RangerPolicyEvaluator.ACCESS_UNDETERMINED)) { - accessResult = RangerPolicyEvaluator.ACCESS_DENIED; - } - } - - RangerPolicy policy = evaluator.getPolicy(); - - ret.setGroupAccessInfo(groupName, accessInfo.getKey(), accessResult, policy); + if (policyPriority != evaluator.getPolicyPriority()) { + if (policyType == RangerPolicy.POLICY_TYPE_ACCESS) { + ret.finalizeAcls(); } - } - for (Map.Entry> roleAccessInfo : aclSummary.getRolesAccessInfo().entrySet()) { - final String roleName = roleAccessInfo.getKey(); - - for (Map.Entry accessInfo : roleAccessInfo.getValue().entrySet()) { - if (isConditional) { - accessResult = ACCESS_CONDITIONAL; - } else { - accessResult = accessInfo.getValue().getResult(); - - if (accessResult.equals(RangerPolicyEvaluator.ACCESS_UNDETERMINED)) { - accessResult = RangerPolicyEvaluator.ACCESS_DENIED; - } - } + policyPriority = evaluator.getPolicyPriority(); + } - RangerPolicy policy = evaluator.getPolicy(); + boolean isTemporalTagPolicy = policyIdForTemporalTags.contains(evaluator.getPolicyId()); + MatchType tagMatchType = tagMatchTypeMap.get(evaluator.getPolicyId()); - ret.setRoleAccessInfo(roleName, accessInfo.getKey(), accessResult, policy); - } + // tag assigned to ANCESTORS must apply to SELF as well, to be consistent with policy evaluation in RangerDefaultPolicyEvaluator.evaluate() + if (tagMatchType == MatchType.ANCESTOR) { + tagMatchType = MatchType.SELF; } + + evaluator.getResourceACLs(request, ret, isTemporalTagPolicy, null, tagMatchType, policyEngine); } - } - ret.finalizeAcls(); + ret.finalizeAcls(); + } } RangerPerfTracer.logAlways(perf); if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerPolicyEngineImpl.getResourceACLs(request=" + request + ") : ret=" + ret); + LOG.debug("<== RangerPolicyEngineImpl.getResourceACLs(request=" + request + ", policyType=" + requestedPolicyType + ") : ret=" + ret); } return ret; @@ -303,22 +340,56 @@ public RangerResourceACLs getResourceACLs(RangerAccessRequest request) { @Override public void setUseForwardedIPAddress(boolean useForwardedIPAddress) { - policyEngine.setUseForwardedIPAddress(useForwardedIPAddress); + try (RangerReadWriteLock.RangerLock writeLock = policyEngine.getWriteLock()) { + if (LOG.isDebugEnabled()) { + if (writeLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + writeLock); + } + } + policyEngine.setUseForwardedIPAddress(useForwardedIPAddress); + } } @Override public void setTrustedProxyAddresses(String[] trustedProxyAddresses) { - policyEngine.setTrustedProxyAddresses(trustedProxyAddresses); + try (RangerReadWriteLock.RangerLock writeLock = policyEngine.getWriteLock()) { + if (LOG.isDebugEnabled()) { + if (writeLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + writeLock); + } + } + policyEngine.setTrustedProxyAddresses(trustedProxyAddresses); + } } @Override public RangerServiceDef getServiceDef() { - return policyEngine.getServiceDef(); + final RangerServiceDef ret; + + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + ret = policyEngine.getServiceDef(); + } + return ret; } @Override public long getPolicyVersion() { - return policyEngine.getPolicyVersion(); + long ret; + + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + ret = policyEngine.getPolicyVersion(); + } + return ret; } @Override @@ -326,12 +397,39 @@ public long getPolicyVersion() { @Override public void setRoles(RangerRoles roles) { - policyEngine.setRoles(roles); + try (RangerReadWriteLock.RangerLock writeLock = policyEngine.getWriteLock()) { + if (LOG.isDebugEnabled()) { + if (writeLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + writeLock); + } + } + policyEngine.setRoles(roles); + } + } @Override public Set getRolesFromUserAndGroups(String user, Set groups) { - return policyEngine.getPluginContext().getAuthContext().getRolesForUserAndGroups(user, groups); + Set ret; + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + ret = policyEngine.getPluginContext().getAuthContext().getRolesForUserAndGroups(user, groups); + } + return ret; + } + + @Override + public RangerRoles getRangerRoles() { + return policyEngine.getPluginContext().getAuthContext().getRangerRolesUtil().getRoles(); + } + + @Override + public RangerPluginContext getPluginContext() { + return policyEngine.getPluginContext(); } @Override @@ -340,7 +438,16 @@ public String getUniquelyMatchedZoneName(GrantRevokeRequest grantRevokeRequest) LOG.debug("==> RangerPolicyEngineImpl.getUniquelyMatchedZoneName(" + grantRevokeRequest + ")"); } - String ret = policyEngine.getUniquelyMatchedZoneName(grantRevokeRequest.getResource()); + String ret; + + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + ret = policyEngine.getUniquelyMatchedZoneName(grantRevokeRequest.getResource()); + } if (LOG.isDebugEnabled()) { LOG.debug("<== RangerPolicyEngineImpl.getUniquelyMatchedZoneName(" + grantRevokeRequest + ") : " + ret); @@ -351,21 +458,53 @@ public String getUniquelyMatchedZoneName(GrantRevokeRequest grantRevokeRequest) @Override public List getResourcePolicies(String zoneName) { - return policyEngine.getResourcePolicies(zoneName); + List ret; + + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + List oldPolicies = policyEngine.getResourcePolicies(zoneName); + ret = CollectionUtils.isNotEmpty(oldPolicies) ? new ArrayList<>(oldPolicies) : oldPolicies; + } + + return ret; } @Override public List getResourcePolicies() { - RangerPolicyRepository policyRepository = policyEngine.getPolicyRepository(); + List ret; - return policyRepository == null ? ListUtils.EMPTY_LIST : policyRepository.getPolicies(); + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + RangerPolicyRepository policyRepository = policyEngine.getPolicyRepository(); + List oldPolicies = policyRepository == null ? ListUtils.EMPTY_LIST : policyRepository.getPolicies(); + ret = CollectionUtils.isNotEmpty(oldPolicies) ? new ArrayList<>(oldPolicies) : oldPolicies; + } + return ret; } @Override public List getTagPolicies() { - RangerPolicyRepository tagPolicyRepository = policyEngine.getTagPolicyRepository(); + List ret; - return tagPolicyRepository == null ? ListUtils.EMPTY_LIST : tagPolicyRepository.getPolicies(); + try (RangerReadWriteLock.RangerLock readLock = policyEngine.getReadLock()) { + if (LOG.isDebugEnabled()) { + if (readLock.isLockingEnabled()) { + LOG.debug("Acquired lock - " + readLock); + } + } + RangerPolicyRepository tagPolicyRepository = policyEngine.getTagPolicyRepository(); + List oldPolicies = tagPolicyRepository == null ? ListUtils.EMPTY_LIST : tagPolicyRepository.getPolicies(); + ret = CollectionUtils.isNotEmpty(oldPolicies) ? new ArrayList<>(oldPolicies) : oldPolicies; + } + return ret; } // This API is used only used by test code @@ -378,7 +517,7 @@ public RangerResourceAccessInfo getResourceAccessInfo(RangerAccessRequest reques requestProcessor.preProcess(request); RangerResourceAccessInfo ret = new RangerResourceAccessInfo(request); - Set zoneNames = policyEngine.getMatchedZonesForResourceAndChildren(request.getResource()); + Set zoneNames = RangerAccessRequestUtil.getResourceZoneNamesFromContext(request.getContext()); if (LOG.isDebugEnabled()) { LOG.debug("zoneNames:[" + zoneNames + "]"); @@ -420,10 +559,27 @@ public void releaseResources(boolean isForced) { } } - PolicyEngine getPolicyEngine() { + public boolean isServiceAdmin(String userName) { + boolean ret = serviceConfig.isServiceAdmin(userName); + + if (!ret) { + + RangerPluginConfig pluginConfig = policyEngine.getPluginContext().getConfig(); + + ret = pluginConfig.isServiceAdmin(userName); + } + + return ret; + } + + public PolicyEngine getPolicyEngine() { return policyEngine; } + public RangerAccessRequestProcessor getRequestProcessor() { + return requestProcessor; + } + private RangerPolicyEngineImpl(final PolicyEngine policyEngine, RangerPolicyEngineImpl other) { this.policyEngine = policyEngine; this.requestProcessor = new RangerDefaultRequestProcessor(policyEngine); @@ -438,7 +594,7 @@ private RangerAccessResult zoneAwareAccessEvaluationWithNoAudit(RangerAccessRequ RangerAccessResult ret = null; RangerPolicyRepository policyRepository = policyEngine.getPolicyRepository(); RangerPolicyRepository tagPolicyRepository = policyEngine.getTagPolicyRepository(); - Set zoneNames = policyEngine.getMatchedZonesForResourceAndChildren(request.getResource()); // Evaluate zone-name from request + Set zoneNames = RangerAccessRequestUtil.getResourceZoneNamesFromContext(request.getContext()); if (LOG.isDebugEnabled()) { LOG.debug("zoneNames:[" + zoneNames + "]"); @@ -498,6 +654,8 @@ private RangerAccessResult zoneAwareAccessEvaluationWithNoAudit(RangerAccessRequ } } + updateFromGdsResult(ret); + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerPolicyEngineImpl.zoneAwareAccessEvaluationWithNoAudit(" + request + ", policyType =" + policyType + "): " + ret); } @@ -510,13 +668,40 @@ private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, LOG.debug("==> RangerPolicyEngineImpl.evaluatePoliciesNoAudit(" + request + ", policyType =" + policyType + ", zoneName=" + zoneName + ")"); } - final Date accessTime = request.getAccessTime() != null ? request.getAccessTime() : new Date(); - final RangerAccessResult ret = createAccessResult(request, policyType); + final RangerAccessResult ret; + + if (request.isAccessTypeAny()) { + List allAccessDefs = getServiceDef().getAccessTypes(); + + Set allRequestedAccesses = new HashSet<>(); + for (RangerServiceDef.RangerAccessTypeDef accessTypeDef : allAccessDefs) { + String requestedAccess = accessTypeDef.getName(); + allRequestedAccesses.add(requestedAccess); + } + RangerAccessRequestUtil.setAllRequestedAccessTypes(request.getContext(), allRequestedAccesses); + RangerAccessRequestUtil.setIsAnyAccessInContext(request.getContext(), Boolean.TRUE); + } + + ret = evaluatePoliciesForOneAccessTypeNoAudit(request, policyType, zoneName, policyRepository, tagPolicyRepository); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerPolicyEngineImpl.evaluatePoliciesNoAudit(" + request + ", policyType =" + policyType + ", zoneName=" + zoneName + "): " + ret); + } + + return ret; + } + + private RangerAccessResult evaluatePoliciesForOneAccessTypeNoAudit(RangerAccessRequest request, int policyType, String zoneName, RangerPolicyRepository policyRepository, RangerPolicyRepository tagPolicyRepository) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerPolicyEngineImpl.evaluatePoliciesForOneAccessTypeNoAudit(" + request + ", policyType =" + policyType + ", zoneName=" + zoneName + ")"); + } + final boolean isSuperUser = isSuperUser(request.getUser(), request.getUserGroups()); + final Date accessTime = request.getAccessTime() != null ? request.getAccessTime() : new Date(); + final RangerAccessResult ret = createAccessResult(request, policyType); - // for superusers, set access as allowed - if (isSuperUser) { - ret.setIsAllowed(true); + if (isSuperUser || StringUtils.equals(request.getAccessType(), RangerPolicyEngine.SUPER_USER_ACCESS)) { + ret.setIsAllowed(isSuperUser); ret.setIsAccessDetermined(true); ret.setPolicyId(-1); ret.setPolicyPriority(Integer.MAX_VALUE); @@ -543,11 +728,9 @@ private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, boolean findAuditByResource = !ret.getIsAuditedDetermined(); boolean foundInCache = findAuditByResource && policyRepository.setAuditEnabledFromCache(request, ret); - if (!isSuperUser) { - ret.setIsAccessDetermined(false); // discard result by tag-policies, to evaluate resource policies for possible override - } + ret.setIsAccessDetermined(false); // discard result by tag-policies, to evaluate resource policies for possible override - List evaluators = policyRepository.getLikelyMatchPolicyEvaluators(request.getResource(), policyType); + List evaluators = policyRepository.getLikelyMatchPolicyEvaluators(request, policyType); for (RangerPolicyEvaluator evaluator : evaluators) { if (!evaluator.isApplicable(accessTime)) { @@ -559,8 +742,16 @@ private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, ret.setIsAccessDetermined(true); } } else if (ret.getIsAllowed()) { - if (ret.getPolicyPriority() > evaluator.getPolicyPriority()) { - ret.setIsAccessDetermined(true); + if (policyType == RangerPolicy.POLICY_TYPE_ACCESS) { + // for access, allow decision made earlier by a policy with higher priority will be final + if (ret.getPolicyPriority() > evaluator.getPolicyPriority()) { + ret.setIsAccessDetermined(true); + } + } else { + // for other types (mask/row-filter), decision made earlier by a policy with same priority or higher will be final + if (ret.getPolicyPriority() >= evaluator.getPolicyPriority()) { + ret.setIsAccessDetermined(true); + } } } @@ -576,7 +767,6 @@ private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, if (ret.getIsAuditedDetermined() && ret.getIsAccessDetermined()) { break; // Break out of policy-evaluation loop } - } if (!ret.getIsAccessDetermined()) { @@ -584,12 +774,21 @@ private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, ret.setIsAllowed(false); } else if (isAllowedByTags) { ret.setIsAllowed(true); + } else { + updateFromGdsResult(ret); + } + if (!ret.getIsAllowed() && + !getIsFallbackSupported()) { + ret.setIsAccessDetermined(true); } } if (ret.getIsAllowed()) { ret.setIsAccessDetermined(true); } + RangerAccessRequestUtil.setAccessTypeResults(request.getContext(), null); + RangerAccessRequestUtil.setAccessTypeACLResults(request.getContext(), null); + RangerAccessRequestUtil.setIsAnyAccessInContext(request.getContext(), null); if (findAuditByResource && !foundInCache) { policyRepository.storeAuditEnabledInCache(request, ret); @@ -597,7 +796,7 @@ private RangerAccessResult evaluatePoliciesNoAudit(RangerAccessRequest request, } if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerPolicyEngineImpl.evaluatePoliciesNoAudit(" + request + ", policyType =" + policyType + ", zoneName=" + zoneName + "): " + ret); + LOG.debug("<== RangerPolicyEngineImpl.evaluatePoliciesForOneAccessTypeNoAudit(" + request + ", policyType =" + policyType + ", zoneName=" + zoneName + "): " + ret); } return ret; @@ -610,7 +809,7 @@ private void evaluateTagPolicies(final RangerAccessRequest request, int policyTy Date accessTime = request.getAccessTime() != null ? request.getAccessTime() : new Date(); Set tags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); - List policyEvaluators = tagPolicyRepository == null ? null : tagPolicyRepository.getLikelyMatchPolicyEvaluators(tags, policyType, accessTime); + List policyEvaluators = tagPolicyRepository == null ? null : tagPolicyRepository.getLikelyMatchPolicyEvaluators(request, tags, policyType, accessTime); if (CollectionUtils.isNotEmpty(policyEvaluators)) { final boolean useTagPoliciesFromDefaultZone = !policyEngine.isResourceZoneAssociatedWithTagService(zoneName); @@ -751,16 +950,17 @@ private boolean isSuperUser(String userName, Set userGroups) { return ret; } - private void getResourceACLEvaluatorsForZone(RangerAccessRequest request, String zoneName, List allEvaluators, Map tagMatchTypeMap, Set policyIdForTemporalTags) { + private void getResourceACLEvaluatorsForZone(RangerAccessRequest request, String zoneName, int policyType, List allEvaluators, Map tagMatchTypeMap, Set policyIdForTemporalTags) { final RangerPolicyRepository matchedRepository = policyEngine.getRepositoryForZone(zoneName); if (matchedRepository == null) { LOG.error("policyRepository for zoneName:[" + zoneName + "], serviceName:[" + policyEngine.getPolicyRepository().getServiceName() + "], policyVersion:[" + getPolicyVersion() + "] is null!! ERROR!"); } else { Set tags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); - List tagPolicyEvaluators = policyEngine.getTagPolicyRepository() == null ? null : policyEngine.getTagPolicyRepository().getLikelyMatchPolicyEvaluators(tags, RangerPolicy.POLICY_TYPE_ACCESS, null); + List tagPolicyEvaluators = policyEngine.getTagPolicyRepository() == null ? null : policyEngine.getTagPolicyRepository().getLikelyMatchPolicyEvaluators(request, tags, policyType, null); if (CollectionUtils.isNotEmpty(tagPolicyEvaluators)) { + tagPolicyEvaluators.sort(MATCH_TYPE_COMPARATOR); final boolean useTagPoliciesFromDefaultZone = !policyEngine.isResourceZoneAssociatedWithTagService(zoneName); @@ -788,16 +988,19 @@ private void getResourceACLEvaluatorsForZone(RangerAccessRequest request, String RangerTagForEval tag = tagEvaluator.getTag(); - allEvaluators.add(evaluator); - tagMatchTypeMap.put(evaluator.getId(), tag.getMatchType()); + // avoid an evaluator making into the list multiple times when the same tag is associated with the resource multiple times + // highest precedence matchType will be recorded in tagMatchTypeMap, since tagPolicyEvaluators is sorted by matchType + if (tagMatchTypeMap.putIfAbsent(evaluator.getPolicyId(), tag.getMatchType()) == null) { + allEvaluators.add(evaluator); + } if (CollectionUtils.isNotEmpty(tag.getValidityPeriods())) { - policyIdForTemporalTags.add(evaluator.getId()); + policyIdForTemporalTags.add(evaluator.getPolicyId()); } } } - List resourcePolicyEvaluators = matchedRepository.getLikelyMatchPolicyEvaluators(request.getResource(), RangerPolicy.POLICY_TYPE_ACCESS); + List resourcePolicyEvaluators = matchedRepository.getLikelyMatchPolicyEvaluators(request, policyType); allEvaluators.addAll(resourcePolicyEvaluators); } @@ -819,7 +1022,7 @@ private void getResourceAccessInfoForZone(RangerAccessRequest request, RangerRes for (RangerTagForEval tag : tags) { RangerAccessRequest tagEvalRequest = new RangerTagAccessRequest(tag, policyEngine.getTagPolicyRepository().getServiceDef(), request); - List evaluators = policyEngine.getTagPolicyRepository().getLikelyMatchPolicyEvaluators(tagEvalRequest.getResource(), RangerPolicy.POLICY_TYPE_ACCESS); + List evaluators = policyEngine.getTagPolicyRepository().getLikelyMatchPolicyEvaluators(tagEvalRequest, RangerPolicy.POLICY_TYPE_ACCESS); for (RangerPolicyEvaluator evaluator : evaluators) { String policyZoneName = evaluator.getPolicy().getZoneName(); @@ -848,7 +1051,7 @@ private void getResourceAccessInfoForZone(RangerAccessRequest request, RangerRes } } - List resPolicyEvaluators = matchedRepository.getLikelyMatchPolicyEvaluators(request.getResource(), RangerPolicy.POLICY_TYPE_ACCESS); + List resPolicyEvaluators = matchedRepository.getLikelyMatchPolicyEvaluators(request, RangerPolicy.POLICY_TYPE_ACCESS); if (CollectionUtils.isNotEmpty(resPolicyEvaluators)) { for (RangerPolicyEvaluator evaluator : resPolicyEvaluators) { @@ -861,12 +1064,127 @@ private void getResourceAccessInfoForZone(RangerAccessRequest request, RangerRes } } + private void evaluateTagAuditPolicies(RangerAccessRequest request, RangerAccessResult result, RangerPolicyRepository tagPolicyRepository) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerPolicyEngineImpl.evaluateTagAuditPolicies(request=" + request + ", result=" + result + ")"); + } + + Set tags = RangerAccessRequestUtil.getRequestTagsFromContext(request.getContext()); + + if (CollectionUtils.isNotEmpty(tags)) { + Date accessTime = request.getAccessTime() != null ? request.getAccessTime() : new Date(); + List evaluators = tagPolicyRepository.getLikelyMatchPolicyEvaluators(request, tags, RangerPolicy.POLICY_TYPE_AUDIT, accessTime); + + if (CollectionUtils.isNotEmpty(evaluators)) { + for (PolicyEvaluatorForTag policyEvaluator : evaluators) { + RangerPolicyEvaluator evaluator = policyEvaluator.getEvaluator(); + RangerTagForEval tag = policyEvaluator.getTag(); + RangerAccessRequest tagEvalRequest = new RangerTagAccessRequest(tag, tagPolicyRepository.getServiceDef(), request); + RangerAccessResult tagEvalResult = createAccessResult(tagEvalRequest, RangerPolicy.POLICY_TYPE_AUDIT); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerPolicyEngineImpl.evaluateTagAuditPolicies: Evaluating Audit policies for tag (" + tag.getType() + ")" + "Tag Evaluator: " + policyEvaluator); + } + + tagEvalResult.setAccessResultFrom(result); + + result.incrementEvaluatedPoliciesCount(); + + evaluator.evaluate(tagEvalRequest, tagEvalResult); + + if (tagEvalResult.getIsAuditedDetermined()) { + result.setIsAudited(tagEvalResult.getIsAudited()); + break; + } + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerPolicyEngineImpl.evaluateTagAuditPolicies(request=" + request + ", result=" + result + ")"); + } + } + + private boolean evaluateResourceAuditPolicies(RangerAccessRequest request, RangerAccessResult result, RangerPolicyRepository policyRepository) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerPolicyEngineImpl.evaluateResourceAuditPolicies(request=" + request + ", result=" + result + ")"); + } + + boolean ret = false; + List evaluators = policyRepository.getLikelyMatchAuditPolicyEvaluators(request); + + if (CollectionUtils.isNotEmpty(evaluators)) { + for (RangerPolicyEvaluator evaluator : evaluators) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerPolicyEngineImpl.evaluateResourceAuditPolicies(): Evaluating RangerPolicyEvaluator...: " + evaluator); + } + + result.incrementEvaluatedPoliciesCount(); + + evaluator.evaluate(request, result); + + if (result.getIsAuditedDetermined()) { + ret = true; + + break; + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerPolicyEngineImpl.evaluateResourceAuditPolicies(request=" + request + ", result=" + result + "): ret=" + ret); + } + + return ret; + } + + private boolean getIsFallbackSupported() { + return policyEngine.getPluginContext().getConfig().getIsFallbackSupported(); + } + + private void updateFromGdsResult(RangerAccessResult result) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> updateFromGdsResult(result={})", result); + } + + RangerAccessRequest request = result.getAccessRequest(); + GdsAccessResult gdsResult = request != null ? RangerAccessRequestUtil.getGdsResultFromContext(request.getContext()) : null; + + if (gdsResult != null) { + if (result.getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS) { + if (!result.getIsAccessDetermined() && gdsResult.getIsAllowed()) { + result.setIsAllowed(true); + result.setIsAccessDetermined(true); + result.setPolicyId(gdsResult.getPolicyId()); + result.setPolicyVersion(gdsResult.getPolicyVersion()); + result.setPolicyPriority(RangerPolicy.POLICY_PRIORITY_NORMAL); + } + } + + if (!result.getIsAuditedDetermined() && gdsResult.getIsAudited()) { + result.setIsAudited(true); + } + + result.setDatasets(gdsResult.getDatasets()); + result.setProjects(gdsResult.getProjects()); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("updateFromGdsResult(): no GdsAccessResult found in request context({})", request); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== updateFromGdsResult(result={})", result); + } + } + private static class ServiceConfig { private final Set auditExcludedUsers; private final Set auditExcludedGroups; private final Set auditExcludedRoles; private final Set superUsers; private final Set superGroups; + private final Set serviceAdmins; public ServiceConfig(Map svcConfig) { if (svcConfig != null) { @@ -875,12 +1193,14 @@ public ServiceConfig(Map svcConfig) { auditExcludedRoles = StringUtil.toSet(svcConfig.get(RangerPolicyEngine.PLUGIN_AUDIT_EXCLUDE_ROLES)); superUsers = StringUtil.toSet(svcConfig.get(RangerPolicyEngine.PLUGIN_SUPER_USERS)); superGroups = StringUtil.toSet(svcConfig.get(RangerPolicyEngine.PLUGIN_SUPER_GROUPS)); + serviceAdmins = StringUtil.toSet(svcConfig.get(RangerPolicyEngine.PLUGIN_SERVICE_ADMINS)); } else { auditExcludedUsers = Collections.emptySet(); auditExcludedGroups = Collections.emptySet(); auditExcludedRoles = Collections.emptySet(); superUsers = Collections.emptySet(); superGroups = Collections.emptySet(); + serviceAdmins = Collections.emptySet(); } } @@ -890,6 +1210,7 @@ public ServiceConfig(ServiceConfig other) { auditExcludedRoles = other == null || CollectionUtils.isEmpty(other.auditExcludedRoles) ? Collections.emptySet() : new HashSet<>(other.auditExcludedRoles); superUsers = other == null || CollectionUtils.isEmpty(other.superUsers) ? Collections.emptySet() : new HashSet<>(other.superUsers); superGroups = other == null || CollectionUtils.isEmpty(other.superGroups) ? Collections.emptySet() : new HashSet<>(other.superGroups); + serviceAdmins = other == null || CollectionUtils.isEmpty(other.serviceAdmins) ? Collections.emptySet() : new HashSet<>(other.serviceAdmins); } public boolean isAuditExcludedUser(String userName) { @@ -911,5 +1232,9 @@ public boolean isSuperUser(String userName) { public boolean hasSuperGroup(Set userGroups) { return userGroups != null && userGroups.size() > 0 && superGroups.size() > 0 && CollectionUtils.containsAny(userGroups, superGroups); } + + public boolean isServiceAdmin(String userName) { + return serviceAdmins.contains(userName); + } } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java index 1f6aed9754..251fb41cf8 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyEngineOptions.java @@ -29,11 +29,23 @@ public class RangerPolicyEngineOptions { public boolean disableCustomConditions = false; public boolean disableTagPolicyEvaluation = false; public boolean disableTrieLookupPrefilter = false; + public boolean disablePolicyRefresher = false; + public boolean disableTagRetriever = false; + public boolean disableUserStoreRetriever = false; + public boolean disableGdsInfoRetriever = false; public boolean cacheAuditResults = true; public boolean evaluateDelegateAdminOnly = false; public boolean enableTagEnricherWithLocalRefresher = false; + public boolean enableUserStoreEnricherWithLocalRefresher = false; + public boolean enableResourceMatcherReuse = true; + @Deprecated public boolean disableAccessEvaluationWithPolicyACLSummary = true; public boolean optimizeTrieForRetrieval = false; + public boolean disableRoleResolution = true; + public boolean optimizeTrieForSpace = false; + public boolean optimizeTagTrieForRetrieval = false; + public boolean optimizeTagTrieForSpace = false; + private RangerServiceDefHelper serviceDefHelper; @@ -44,12 +56,27 @@ public RangerPolicyEngineOptions(final RangerPolicyEngineOptions other) { this.disableCustomConditions = other.disableCustomConditions; this.disableTagPolicyEvaluation = other.disableTagPolicyEvaluation; this.disableTrieLookupPrefilter = other.disableTrieLookupPrefilter; + this.disablePolicyRefresher = other.disablePolicyRefresher; + this.disableTagRetriever = other.disableTagRetriever; + this.disableUserStoreRetriever = other.disableUserStoreRetriever; + this.disableGdsInfoRetriever = other.disableGdsInfoRetriever; this.cacheAuditResults = other.cacheAuditResults; this.evaluateDelegateAdminOnly = other.evaluateDelegateAdminOnly; this.enableTagEnricherWithLocalRefresher = other.enableTagEnricherWithLocalRefresher; - this.disableAccessEvaluationWithPolicyACLSummary = other.disableAccessEvaluationWithPolicyACLSummary; + this.enableUserStoreEnricherWithLocalRefresher = other.enableUserStoreEnricherWithLocalRefresher; + this.enableResourceMatcherReuse = other.enableResourceMatcherReuse; this.optimizeTrieForRetrieval = other.optimizeTrieForRetrieval; + this.disableRoleResolution = other.disableRoleResolution; this.serviceDefHelper = null; + this.optimizeTrieForSpace = other.optimizeTrieForSpace; + this.optimizeTagTrieForRetrieval = other.optimizeTagTrieForRetrieval; + this.optimizeTagTrieForSpace = other.optimizeTagTrieForSpace; + } + + public RangerPolicyEngineOptions(final RangerPolicyEngineOptions other, RangerServiceDefHelper serviceDefHelper) { + this(other); + + this.serviceDefHelper = serviceDefHelper; } public void configureForPlugin(Configuration conf, String propertyPrefix) { @@ -57,16 +84,25 @@ public void configureForPlugin(Configuration conf, String propertyPrefix) { disableCustomConditions = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.custom.conditions", false); disableTagPolicyEvaluation = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tagpolicy.evaluation", false); disableTrieLookupPrefilter = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.trie.lookup.prefilter", false); + disablePolicyRefresher = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.policy.refresher", false); + disableTagRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tag.retriever", false); + disableUserStoreRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.userstore.retriever", false); + disableGdsInfoRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.gdsinfo.retriever", false); cacheAuditResults = conf.getBoolean(propertyPrefix + ".policyengine.option.cache.audit.results", true); + enableResourceMatcherReuse = conf.getBoolean(propertyPrefix + ".policyengine.option.enable.resourcematcher.reuse", true); if (!disableTrieLookupPrefilter) { cacheAuditResults = false; } evaluateDelegateAdminOnly = false; enableTagEnricherWithLocalRefresher = false; - disableAccessEvaluationWithPolicyACLSummary = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.access.evaluation.with.policy.acl.summary", true); + enableUserStoreEnricherWithLocalRefresher = false; optimizeTrieForRetrieval = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.trie.for.retrieval", false); + disableRoleResolution = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.role.resolution", true); + optimizeTrieForSpace = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.trie.for.space", false); + optimizeTagTrieForRetrieval = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.tag.trie.for.retrieval", false); + optimizeTagTrieForSpace = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.tag.trie.for.space", false); } @@ -75,13 +111,18 @@ public void configureDefaultRangerAdmin(Configuration conf, String propertyPrefi disableCustomConditions = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.custom.conditions", true); disableTagPolicyEvaluation = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tagpolicy.evaluation", true); disableTrieLookupPrefilter = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.trie.lookup.prefilter", false); + disablePolicyRefresher = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.policy.refresher", true); + disableTagRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tag.retriever", true); + disableUserStoreRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.userstore.retriever", true); + disableGdsInfoRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.gdsinfo.retriever", true); cacheAuditResults = false; evaluateDelegateAdminOnly = false; enableTagEnricherWithLocalRefresher = false; - disableAccessEvaluationWithPolicyACLSummary = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.access.evaluation.with.policy.acl.summary", true); + enableUserStoreEnricherWithLocalRefresher = false; optimizeTrieForRetrieval = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.trie.for.retrieval", false); - + disableRoleResolution = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.role.resolution", true); + enableResourceMatcherReuse = conf.getBoolean(propertyPrefix + ".policyengine.option.enable.resourcematcher.reuse", true); } public void configureDelegateAdmin(Configuration conf, String propertyPrefix) { @@ -89,13 +130,17 @@ public void configureDelegateAdmin(Configuration conf, String propertyPrefix) { disableCustomConditions = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.custom.conditions", true); disableTagPolicyEvaluation = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tagpolicy.evaluation", true); disableTrieLookupPrefilter = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.trie.lookup.prefilter", false); + disablePolicyRefresher = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.policy.refresher", true); + disableTagRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tag.retriever", true); + disableUserStoreRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.userstore.retriever", true); + disableGdsInfoRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.gdsinfo.retriever", true); optimizeTrieForRetrieval = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.trie.for.retrieval", false); - + enableResourceMatcherReuse = conf.getBoolean(propertyPrefix + ".policyengine.option.enable.resourcematcher.reuse", true); cacheAuditResults = false; evaluateDelegateAdminOnly = true; enableTagEnricherWithLocalRefresher = false; - + enableUserStoreEnricherWithLocalRefresher = false; } public void configureRangerAdminForPolicySearch(Configuration conf, String propertyPrefix) { @@ -103,12 +148,21 @@ public void configureRangerAdminForPolicySearch(Configuration conf, String prope disableCustomConditions = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.custom.conditions", true); disableTagPolicyEvaluation = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tagpolicy.evaluation", false); disableTrieLookupPrefilter = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.trie.lookup.prefilter", false); + disablePolicyRefresher = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.policy.refresher", true); + disableTagRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.tag.retriever", false); + disableUserStoreRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.userstore.retriever", false); + disableGdsInfoRetriever = conf.getBoolean(propertyPrefix + ".policyengine.option.disable.gdsinfo.retriever", false); optimizeTrieForRetrieval = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.trie.for.retrieval", false); - cacheAuditResults = false; evaluateDelegateAdminOnly = false; enableTagEnricherWithLocalRefresher = true; + enableUserStoreEnricherWithLocalRefresher = true; + + optimizeTrieForSpace = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.trie.for.space", false); + optimizeTagTrieForRetrieval = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.tag.trie.for.retrieval", false); + optimizeTagTrieForSpace = conf.getBoolean(propertyPrefix + ".policyengine.option.optimize.tag.trie.for.space", true); + enableResourceMatcherReuse = conf.getBoolean(propertyPrefix + ".policyengine.option.enable.resourcematcher.reuse", true); } public RangerServiceDefHelper getServiceDefHelper() { @@ -133,10 +187,21 @@ public boolean equals(Object other) { && this.disableCustomConditions == that.disableCustomConditions && this.disableTagPolicyEvaluation == that.disableTagPolicyEvaluation && this.disableTrieLookupPrefilter == that.disableTrieLookupPrefilter + && this.disablePolicyRefresher == that.disablePolicyRefresher + && this.disableTagRetriever == that.disableTagRetriever + && this.disableUserStoreRetriever == that.disableUserStoreRetriever + && this.disableGdsInfoRetriever == that.disableGdsInfoRetriever && this.cacheAuditResults == that.cacheAuditResults && this.evaluateDelegateAdminOnly == that.evaluateDelegateAdminOnly && this.enableTagEnricherWithLocalRefresher == that.enableTagEnricherWithLocalRefresher - && this.optimizeTrieForRetrieval == that.optimizeTrieForRetrieval; + && this.enableUserStoreEnricherWithLocalRefresher == that.enableUserStoreEnricherWithLocalRefresher + && this.optimizeTrieForRetrieval == that.optimizeTrieForRetrieval + && this.disableRoleResolution == that.disableRoleResolution + && this.optimizeTrieForSpace == that.optimizeTrieForSpace + && this.optimizeTagTrieForRetrieval == that.optimizeTagTrieForRetrieval + && this.optimizeTagTrieForSpace == that.optimizeTagTrieForSpace + && this.enableResourceMatcherReuse == that.enableResourceMatcherReuse + ; } return ret; } @@ -152,14 +217,34 @@ public int hashCode() { ret *= 2; ret += disableTrieLookupPrefilter ? 1 : 0; ret *= 2; + ret += disablePolicyRefresher ? 1 : 0; + ret *= 2; + ret += disableTagRetriever ? 1 : 0; + ret *= 2; + ret += disableUserStoreRetriever ? 1 : 0; + ret *= 2; + ret += disableGdsInfoRetriever ? 1 : 0; + ret *= 2; ret += cacheAuditResults ? 1 : 0; ret *= 2; ret += evaluateDelegateAdminOnly ? 1 : 0; ret *= 2; ret += enableTagEnricherWithLocalRefresher ? 1 : 0; ret *= 2; + ret += enableUserStoreEnricherWithLocalRefresher ? 1 : 0; + ret *= 2; ret += optimizeTrieForRetrieval ? 1 : 0; ret *= 2; + ret += disableRoleResolution ? 1 : 0; + ret *= 2; + ret += optimizeTrieForSpace ? 1 : 0; + ret *= 2; + ret += optimizeTagTrieForRetrieval ? 1 : 0; + ret *= 2; + ret += optimizeTagTrieForSpace ? 1 : 0; + ret *= 2; + ret += enableResourceMatcherReuse ? 1 : 0; + ret *= 2; return ret; } @@ -171,10 +256,20 @@ public String toString() { ", disableContextEnrichers: " + disableContextEnrichers + ", disableCustomConditions: " + disableContextEnrichers + ", disableTagPolicyEvaluation: " + disableTagPolicyEvaluation + + ", disablePolicyRefresher: " + disablePolicyRefresher + + ", disableTagRetriever: " + disableTagRetriever + + ", disableUserStoreRetriever: " + disableUserStoreRetriever + + ", disableGdsInfoRetriever: " + disableGdsInfoRetriever + ", enableTagEnricherWithLocalRefresher: " + enableTagEnricherWithLocalRefresher + + ", enableUserStoreEnricherWithLocalRefresher: " + enableUserStoreEnricherWithLocalRefresher + ", disableTrieLookupPrefilter: " + disableTrieLookupPrefilter + ", optimizeTrieForRetrieval: " + optimizeTrieForRetrieval + ", cacheAuditResult: " + cacheAuditResults + + ", disableRoleResolution: " + disableRoleResolution + + ", optimizeTrieForSpace: " + optimizeTrieForSpace + + ", optimizeTagTrieForRetrieval: " + optimizeTagTrieForRetrieval + + ", optimizeTagTrieForSpace: " + optimizeTagTrieForSpace + + ", enableResourceMatcherReuse: " + enableResourceMatcherReuse + " }"; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java index 3886eea8b2..e842aa5dcf 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerPolicyRepository.java @@ -20,26 +20,34 @@ package org.apache.ranger.plugin.policyengine; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.contextenricher.RangerAbstractContextEnricher; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; +import org.apache.ranger.plugin.contextenricher.RangerGdsEnricher; import org.apache.ranger.plugin.contextenricher.RangerTagEnricher; import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.contextenricher.RangerUserStoreEnricher; +import org.apache.ranger.plugin.model.AuditFilter; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; import org.apache.ranger.plugin.model.RangerPolicyDelta; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; import org.apache.ranger.plugin.policyevaluator.RangerAbstractPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerAuditPolicyEvaluator; import org.apache.ranger.plugin.policyevaluator.RangerCachedPolicyEvaluator; import org.apache.ranger.plugin.policyevaluator.RangerOptimizedPolicyEvaluator; import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.RangerPolicyResourceEvaluator; import org.apache.ranger.plugin.store.AbstractServiceStore; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerResourceEvaluatorsRetriever; import org.apache.ranger.plugin.util.ServiceDefUtil; import org.apache.ranger.plugin.util.ServicePolicies; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; @@ -52,38 +60,45 @@ import java.util.Map; import java.util.Set; +import static org.apache.ranger.plugin.contextenricher.RangerGdsEnricher.RETRIEVER_CLASSNAME_OPTION; +import static org.apache.ranger.plugin.contextenricher.RangerTagEnricher.TAG_RETRIEVER_CLASSNAME_OPTION; +import static org.apache.ranger.plugin.contextenricher.RangerUserStoreEnricher.USERSTORE_RETRIEVER_CLASSNAME_OPTION; +import static org.apache.ranger.plugin.policyengine.RangerPolicyEngine.PLUGIN_AUDIT_FILTER; + public class RangerPolicyRepository { - private static final Log LOG = LogFactory.getLog(RangerPolicyRepository.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPolicyRepository.class); - private static final Log PERF_CONTEXTENRICHER_INIT_LOG = RangerPerfTracer.getPerfLogger("contextenricher.init"); - private static final Log PERF_TRIE_OP_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.retrieval"); + private static final Logger PERF_CONTEXTENRICHER_INIT_LOG = RangerPerfTracer.getPerfLogger("contextenricher.init"); + private static final Logger PERF_TRIE_OP_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.retrieval"); enum AuditModeEnum { AUDIT_ALL, AUDIT_NONE, AUDIT_DEFAULT } - private final String serviceName; - private final String zoneName; - private final String appId; - private final RangerPolicyEngineOptions options; - private final RangerPluginContext pluginContext; - private final RangerServiceDef serviceDef; - private final List policies; - private final long policyVersion; - private final List contextEnrichers; - private final AuditModeEnum auditModeEnum; - private final Map accessAuditCache; - private final String componentServiceName; - private final RangerServiceDef componentServiceDef; - private final Map policyResourceTrie; - private final Map dataMaskResourceTrie; - private final Map rowFilterResourceTrie; - private List policyEvaluators; - private List dataMaskPolicyEvaluators; - private List rowFilterPolicyEvaluators; - private Map policyEvaluatorsMap; - private boolean isContextEnrichersShared = false; - private boolean isPreCleaned = false; + private final String serviceName; + private final String zoneName; + private final String appId; + private final RangerPolicyEngineOptions options; + private final RangerPluginContext pluginContext; + private final RangerServiceDef serviceDef; + private /*final*/ List policies; + private final long policyVersion; + private /*final*/ List contextEnrichers; + private final AuditModeEnum auditModeEnum; + private final Map accessAuditCache; + private final String componentServiceName; + private final RangerServiceDef componentServiceDef; + private final Map> policyResourceTrie; + private final Map> dataMaskResourceTrie; + private final Map> rowFilterResourceTrie; + private final Map> auditFilterResourceTrie; + private List policyEvaluators; + private List dataMaskPolicyEvaluators; + private List rowFilterPolicyEvaluators; + private final List auditPolicyEvaluators; + private Map policyEvaluatorsMap; + private boolean isContextEnrichersShared = false; + private boolean isPreCleaned = false; RangerPolicyRepository(final RangerPolicyRepository other, final List deltas, long policyVersion) { this.serviceName = other.serviceName; @@ -96,6 +111,7 @@ enum AuditModeEnum { this.policyEvaluators = new ArrayList<>(other.policyEvaluators); this.dataMaskPolicyEvaluators = new ArrayList<>(other.dataMaskPolicyEvaluators); this.rowFilterPolicyEvaluators = new ArrayList<>(other.rowFilterPolicyEvaluators); + this.auditPolicyEvaluators = new ArrayList<>(other.auditPolicyEvaluators); this.auditModeEnum = other.auditModeEnum; this.componentServiceName = other.componentServiceName; this.componentServiceDef = other.componentServiceDef; @@ -104,8 +120,8 @@ enum AuditModeEnum { if (other.policyResourceTrie != null) { this.policyResourceTrie = new HashMap<>(); - for (Map.Entry entry : other.policyResourceTrie.entrySet()) { - policyResourceTrie.put(entry.getKey(), new RangerResourceTrie(entry.getValue())); + for (Map.Entry> entry : other.policyResourceTrie.entrySet()) { + policyResourceTrie.put(entry.getKey(), new RangerResourceTrie<>(entry.getValue())); } } else { this.policyResourceTrie = null; @@ -114,8 +130,8 @@ enum AuditModeEnum { if (other.dataMaskResourceTrie != null) { this.dataMaskResourceTrie = new HashMap<>(); - for (Map.Entry entry : other.dataMaskResourceTrie.entrySet()) { - dataMaskResourceTrie.put(entry.getKey(), new RangerResourceTrie(entry.getValue())); + for (Map.Entry> entry : other.dataMaskResourceTrie.entrySet()) { + dataMaskResourceTrie.put(entry.getKey(), new RangerResourceTrie<>(entry.getValue())); } } else { this.dataMaskResourceTrie = null; @@ -124,120 +140,37 @@ enum AuditModeEnum { if (other.rowFilterResourceTrie != null) { this.rowFilterResourceTrie = new HashMap<>(); - for (Map.Entry entry : other.rowFilterResourceTrie.entrySet()) { - rowFilterResourceTrie.put(entry.getKey(), new RangerResourceTrie(entry.getValue())); + for (Map.Entry> entry : other.rowFilterResourceTrie.entrySet()) { + rowFilterResourceTrie.put(entry.getKey(), new RangerResourceTrie<>(entry.getValue())); } } else { this.rowFilterResourceTrie = null; } - if (other.accessAuditCache != null) { - int auditResultCacheSize = other.accessAuditCache.size(); + if (other.auditFilterResourceTrie != null) { + this.auditFilterResourceTrie = new HashMap<>(); - this.accessAuditCache = Collections.synchronizedMap(new CacheMap(auditResultCacheSize)); + for (Map.Entry> entry : other.auditFilterResourceTrie.entrySet()) { + auditFilterResourceTrie.put(entry.getKey(), new RangerResourceTrie<>(entry.getValue())); + } } else { - this.accessAuditCache = null; + this.auditFilterResourceTrie = null; } - boolean[] flags = new boolean[RangerPolicy.POLICY_TYPES.length]; - - for (RangerPolicyDelta delta : deltas) { - final Integer changeType = delta.getChangeType(); - final String serviceType = delta.getServiceType(); - final Long policyId = delta.getPolicyId(); - final Integer policyType = delta.getPolicyType(); - - if (!serviceType.equals(this.serviceDef.getName())) { - continue; - } - - RangerPolicyEvaluator evaluator = null; - - switch (changeType) { - case RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE: - if (delta.getPolicy() == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Could not find policy for policy-id:[" + policyId + "]"); - } - - continue; - } - break; - - case RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE: - evaluator = getPolicyEvaluator(policyId); - - if (evaluator == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Could not find evaluator for policy-id:[" + policyId + "]"); - } - } - break; - - case RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE: - evaluator = getPolicyEvaluator(policyId); - if (evaluator == null) { - if (LOG.isDebugEnabled()) { - LOG.debug("Could not find evaluator for policy-id:[" + policyId + "]"); - } - } - break; - - default: - LOG.error("Unknown changeType:[" + changeType + "], Ignoring"); - break; - } - - evaluator = update(delta, evaluator); - - if (evaluator != null) { - switch (changeType) { - case RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE: - policyEvaluatorsMap.put(policyId, evaluator); - break; - - case RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE: - policyEvaluatorsMap.put(policyId, evaluator); - break; - - case RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE: - policyEvaluatorsMap.remove(policyId); - break; - - default: - break; - } + if (other.accessAuditCache != null) { + int auditResultCacheSize = other.accessAuditCache.size(); - flags[policyType] = true; - } + this.accessAuditCache = Collections.synchronizedMap(new CacheMap<>(auditResultCacheSize)); + } else { + this.accessAuditCache = null; } - for (int policyType = 0; policyType < flags.length; policyType++) { - if (flags[policyType]) { - Map trie = getTrie(policyType); + final boolean isExistingPolicies = CollectionUtils.isNotEmpty(this.policies); - if (trie != null) { - for (Map.Entry entry : trie.entrySet()) { - entry.getValue().wrapUpUpdate(); - } - } - } - } + updateResourceTrie(deltas); - if (StringUtils.isEmpty(zoneName)) { - if (CollectionUtils.isNotEmpty(other.getPolicies())) { - if (CollectionUtils.isNotEmpty(this.getPolicies())) { - this.contextEnrichers = shareWith(other); - } else { - this.contextEnrichers = null; - } - } else { - if (CollectionUtils.isNotEmpty(this.policies)) { - this.contextEnrichers = Collections.unmodifiableList(buildContextEnrichers(options)); - } else { - this.contextEnrichers = null; - } - } + if (CollectionUtils.isNotEmpty(this.policies)) { + this.contextEnrichers = isExistingPolicies ? shareWith(other) : buildContextEnrichers(options); } else { this.contextEnrichers = null; } @@ -249,7 +182,7 @@ enum AuditModeEnum { this.policyVersion = policyVersion; } - RangerPolicyRepository(ServicePolicies servicePolicies, RangerPluginContext pluginContext) { + public RangerPolicyRepository(ServicePolicies servicePolicies, RangerPluginContext pluginContext) { this(servicePolicies, pluginContext, null); } @@ -264,9 +197,9 @@ enum AuditModeEnum { this.pluginContext = pluginContext; if (StringUtils.isEmpty(zoneName)) { - this.policies = Collections.unmodifiableList(servicePolicies.getPolicies()); + this.policies = servicePolicies.getPolicies(); } else { - this.policies = Collections.unmodifiableList(servicePolicies.getSecurityZones().get(zoneName).getPolicies()); + this.policies = servicePolicies.getSecurityZones().get(zoneName).getPolicies(); } this.policyVersion = servicePolicies.getPolicyVersion() != null ? servicePolicies.getPolicyVersion() : -1; @@ -287,7 +220,7 @@ enum AuditModeEnum { final int RANGER_POLICYENGINE_AUDITRESULT_CACHE_SIZE = 64 * 1024; int auditResultCacheSize = pluginContext.getConfig().getInt(propertyName, RANGER_POLICYENGINE_AUDITRESULT_CACHE_SIZE); - accessAuditCache = Collections.synchronizedMap(new CacheMap(auditResultCacheSize)); + accessAuditCache = Collections.synchronizedMap(new CacheMap<>(auditResultCacheSize)); } else { accessAuditCache = null; } @@ -302,19 +235,23 @@ enum AuditModeEnum { init(options); if (StringUtils.isEmpty(zoneName)) { - this.contextEnrichers = Collections.unmodifiableList(buildContextEnrichers(options)); + this.contextEnrichers = buildContextEnrichers(options); + this.auditPolicyEvaluators = buildAuditPolicyEvaluators(servicePolicies.getServiceConfig()); } else { this.contextEnrichers = null; + this.auditPolicyEvaluators = Collections.emptyList(); } if (options.disableTrieLookupPrefilter) { - policyResourceTrie = null; - dataMaskResourceTrie = null; - rowFilterResourceTrie = null; + policyResourceTrie = null; + dataMaskResourceTrie = null; + rowFilterResourceTrie = null; + auditFilterResourceTrie = null; } else { - policyResourceTrie = createResourceTrieMap(policyEvaluators, options.optimizeTrieForRetrieval); - dataMaskResourceTrie = createResourceTrieMap(dataMaskPolicyEvaluators, options.optimizeTrieForRetrieval); - rowFilterResourceTrie = createResourceTrieMap(rowFilterPolicyEvaluators, options.optimizeTrieForRetrieval); + policyResourceTrie = createResourceTrieMap(policyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + dataMaskResourceTrie = createResourceTrieMap(dataMaskPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + rowFilterResourceTrie = createResourceTrieMap(rowFilterPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + auditFilterResourceTrie = createResourceTrieMap(auditPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); } } @@ -330,7 +267,7 @@ enum AuditModeEnum { this.appId = pluginContext.getConfig().getAppId(); this.options = new RangerPolicyEngineOptions(pluginContext.getConfig().getPolicyEngineOptions()); this.pluginContext = pluginContext; - this.policies = Collections.unmodifiableList(normalizeAndPrunePolicies(tagPolicies.getPolicies(), componentServiceDef.getName())); + this.policies = normalizeAndPrunePolicies(tagPolicies.getPolicies(), componentServiceDef.getName()); this.policyVersion = tagPolicies.getPolicyVersion() != null ? tagPolicies.getPolicyVersion() : -1; String auditMode = tagPolicies.getAuditMode(); @@ -352,20 +289,51 @@ enum AuditModeEnum { init(options); if (StringUtils.isEmpty(zoneName)) { - this.contextEnrichers = Collections.unmodifiableList(buildContextEnrichers(options)); + this.contextEnrichers = buildContextEnrichers(options); + this.auditPolicyEvaluators = buildAuditPolicyEvaluators(tagPolicies.getServiceConfig()); } else { this.contextEnrichers = null; + this.auditPolicyEvaluators = Collections.emptyList(); } if (options.disableTrieLookupPrefilter) { - policyResourceTrie = null; - dataMaskResourceTrie = null; - rowFilterResourceTrie = null; + policyResourceTrie = null; + dataMaskResourceTrie = null; + rowFilterResourceTrie = null; + auditFilterResourceTrie = null; } else { - policyResourceTrie = createResourceTrieMap(policyEvaluators, options.optimizeTrieForRetrieval); - dataMaskResourceTrie = createResourceTrieMap(dataMaskPolicyEvaluators, options.optimizeTrieForRetrieval); - rowFilterResourceTrie = createResourceTrieMap(rowFilterPolicyEvaluators, options.optimizeTrieForRetrieval); + policyResourceTrie = createResourceTrieMap(policyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + dataMaskResourceTrie = createResourceTrieMap(dataMaskPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + rowFilterResourceTrie = createResourceTrieMap(rowFilterPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + auditFilterResourceTrie = createResourceTrieMap(auditPolicyEvaluators, options.optimizeTrieForRetrieval, options.optimizeTrieForSpace); + } + } + + private List buildAuditPolicyEvaluators(Map svcConfigs) { + List ret = Collections.emptyList(); + String jsonStr = svcConfigs != null ? svcConfigs.get(PLUGIN_AUDIT_FILTER) : null; + + if (StringUtils.isNotBlank(jsonStr)) { + List auditFilters = JsonUtils.jsonToAuditFilterList(jsonStr); + int filterCount = auditFilters != null ? auditFilters.size() : 0; + + if (filterCount > 0) { + ret = new ArrayList<>(filterCount); + + // set priority so that policies will be evaluated in the same order as in the list + int policyPriority = filterCount; + + for (AuditFilter auditFilter : auditFilters) { + RangerAuditPolicyEvaluator evaluator = new RangerAuditPolicyEvaluator(auditFilter, policyPriority--); + + evaluator.init(evaluator.getAuditPolicy(), serviceDef, options); + + ret.add(evaluator); + } + } } + + return ret; } @Override @@ -419,6 +387,16 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); + sb.append("auditPolicyEvaluators={"); + if (this.auditPolicyEvaluators != null) { + for (RangerPolicyEvaluator policyEvaluator : auditPolicyEvaluators) { + if (policyEvaluator != null) { + sb.append(policyEvaluator).append(" "); + } + } + } + sb.append("} "); + sb.append("contextEnrichers={"); if (contextEnrichers != null) { for (RangerContextEnricher contextEnricher : contextEnrichers) { @@ -591,6 +569,22 @@ public List getPolicyEvaluators() { return policyEvaluators; } + public int getPolicyEvaluatorCount() { + return policyEvaluators.size(); + } + + public int getDataMaskPolicyEvaluatorCount() { + return dataMaskPolicyEvaluators.size(); + } + + public int getRowFilterPolicyEvaluatorCount() { + return rowFilterPolicyEvaluators.size(); + } + + public int getAuditPolicyEvaluatorCount() { + return auditPolicyEvaluators.size(); + } + List getDataMaskPolicyEvaluators() { return dataMaskPolicyEvaluators; } @@ -599,21 +593,25 @@ List getRowFilterPolicyEvaluators() { return rowFilterPolicyEvaluators; } + List getAuditPolicyEvaluators() { + return auditPolicyEvaluators; + } + String getAppId() { return appId; } RangerPolicyEngineOptions getOptions() { return options; } - List getLikelyMatchPolicyEvaluators(Set tags, int policyType, Date accessTime) { + List getLikelyMatchPolicyEvaluators(RangerAccessRequest request, Set tags, int policyType, Date accessTime) { List ret = Collections.EMPTY_LIST; if (CollectionUtils.isNotEmpty(tags) && getServiceDef() != null) { - ret = new ArrayList(); + ret = new ArrayList<>(); for (RangerTagForEval tag : tags) { if (tag.isApplicable(accessTime)) { - RangerAccessResource resource = new RangerTagResource(tag.getType(), getServiceDef()); - List evaluators = getLikelyMatchPolicyEvaluators(resource, policyType); + RangerAccessRequest tagRequest = new RangerTagAccessRequest(tag, getServiceDef(), request); + List evaluators = getLikelyMatchPolicyEvaluators(tagRequest, policyType); if (CollectionUtils.isNotEmpty(evaluators)) { for (RangerPolicyEvaluator evaluator : evaluators) { @@ -632,10 +630,11 @@ List getLikelyMatchPolicyEvaluators(Set if (CollectionUtils.isNotEmpty(ret)) { switch (policyType) { case RangerPolicy.POLICY_TYPE_ACCESS: + case RangerPolicy.POLICY_TYPE_AUDIT: Collections.sort(ret, PolicyEvaluatorForTag.EVAL_ORDER_COMPARATOR); break; case RangerPolicy.POLICY_TYPE_DATAMASK: - Collections.sort(ret, PolicyEvaluatorForTag.NAME_COMPARATOR); + Collections.sort(ret, PolicyEvaluatorForTag.MATCH_TYPE_COMPARATOR); break; case RangerPolicy.POLICY_TYPE_ROWFILTER: Collections.sort(ret, PolicyEvaluatorForTag.NAME_COMPARATOR); @@ -649,11 +648,11 @@ List getLikelyMatchPolicyEvaluators(Set return ret; } - public List getLikelyMatchPolicyEvaluators(RangerAccessResource resource) { + public List getLikelyMatchPolicyEvaluators(RangerAccessRequest request) { List ret = new ArrayList<>(); for (int policyType : RangerPolicy.POLICY_TYPES) { - List evaluators = getLikelyMatchPolicyEvaluators(resource, policyType); + List evaluators = getLikelyMatchPolicyEvaluators(request, policyType); if (CollectionUtils.isNotEmpty(evaluators)) { ret.addAll(evaluators); } @@ -661,14 +660,16 @@ public List getLikelyMatchPolicyEvaluators(RangerAccessRe return ret; } - public List getLikelyMatchPolicyEvaluators(RangerAccessResource resource, int policyType) { + public List getLikelyMatchPolicyEvaluators(RangerAccessRequest request, int policyType) { switch (policyType) { case RangerPolicy.POLICY_TYPE_ACCESS: - return getLikelyMatchAccessPolicyEvaluators(resource); + return getLikelyMatchAccessPolicyEvaluators(request); case RangerPolicy.POLICY_TYPE_DATAMASK: - return getLikelyMatchDataMaskPolicyEvaluators(resource); + return getLikelyMatchDataMaskPolicyEvaluators(request); case RangerPolicy.POLICY_TYPE_ROWFILTER: - return getLikelyMatchRowFilterPolicyEvaluators(resource); + return getLikelyMatchRowFilterPolicyEvaluators(request); + case RangerPolicy.POLICY_TYPE_AUDIT: + return getLikelyMatchAuditPolicyEvaluators(request); default: return Collections.EMPTY_LIST; } @@ -681,89 +682,71 @@ RangerPolicyEvaluator getPolicyEvaluator(Long id) { return policyEvaluatorsMap.get(id); } - private List getLikelyMatchAccessPolicyEvaluators(RangerAccessResource resource) { - String resourceStr = resource == null ? null : resource.getAsString(); + private List getLikelyMatchAccessPolicyEvaluators(RangerAccessRequest request) { + RangerAccessResource resource = request.getResource(); + String resourceStr = resource == null ? null : resource.getAsString(); + + return policyResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getPolicyEvaluators() : getLikelyMatchPolicyEvaluators(policyResourceTrie, request); + } + + private List getLikelyMatchDataMaskPolicyEvaluators(RangerAccessRequest request) { + RangerAccessResource resource = request.getResource(); + String resourceStr = resource == null ? null : resource.getAsString(); - return policyResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getPolicyEvaluators() : getLikelyMatchPolicyEvaluators(policyResourceTrie, resource); + return dataMaskResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getDataMaskPolicyEvaluators() : getLikelyMatchPolicyEvaluators(dataMaskResourceTrie, request); } - private List getLikelyMatchDataMaskPolicyEvaluators(RangerAccessResource resource) { + private List getLikelyMatchRowFilterPolicyEvaluators(RangerAccessRequest request) { + RangerAccessResource resource = request.getResource(); String resourceStr = resource == null ? null : resource.getAsString(); - return dataMaskResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getDataMaskPolicyEvaluators() : getLikelyMatchPolicyEvaluators(dataMaskResourceTrie, resource); + return rowFilterResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getRowFilterPolicyEvaluators() : getLikelyMatchPolicyEvaluators(rowFilterResourceTrie, request); } - private List getLikelyMatchRowFilterPolicyEvaluators(RangerAccessResource resource) { + List getLikelyMatchAuditPolicyEvaluators(RangerAccessRequest request) { + RangerAccessResource resource = request.getResource(); String resourceStr = resource == null ? null : resource.getAsString(); - return rowFilterResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getRowFilterPolicyEvaluators() : getLikelyMatchPolicyEvaluators(rowFilterResourceTrie, resource); + return auditFilterResourceTrie == null || StringUtils.isEmpty(resourceStr) ? getAuditPolicyEvaluators() : getLikelyMatchPolicyEvaluators(auditFilterResourceTrie, request); } - private List getLikelyMatchPolicyEvaluators(Map resourceTrie, RangerAccessResource resource) { + private List getLikelyMatchPolicyEvaluators(Map> resourceTrie, RangerAccessRequest request) { List ret = Collections.EMPTY_LIST; + RangerAccessResource resource = request.getResource(); + RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_TRIE_OP_LOG)) { perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_OP_LOG, "RangerPolicyRepository.getLikelyMatchEvaluators(resource=" + resource.getAsString() + ")"); } - Set resourceKeys = resource == null ? null : resource.getKeys(); + Collection smallestList = RangerResourceEvaluatorsRetriever.getEvaluators(resourceTrie, resource.getAsMap(), request.getResourceElementMatchingScopes()); - if(CollectionUtils.isNotEmpty(resourceKeys)) { - Set evaluators = null; - List> resourceEvaluatorsSet = null; - Set smallestSet = null; + if (smallestList != null) { + if (smallestList.size() == 0) { + ret = new ArrayList<>(); + } else if (smallestList.size() == 1) { + ret = new ArrayList<>(1); - for (String resourceName : resourceKeys) { - RangerResourceTrie trie = resourceTrie.get(resourceName); + for (RangerPolicyResourceEvaluator resourceEvaluator : smallestList) { + RangerPolicyEvaluator policyEvaluator = resourceEvaluator.getPolicyEvaluator(); - if (trie == null) { // if no trie exists for this resource level, ignore and continue to next level - continue; - } - - Set resourceEvaluators = trie.getEvaluatorsForResource(resource.getValue(resourceName)); - - if (CollectionUtils.isEmpty(resourceEvaluators)) { // no policies for this resource, bail out - resourceEvaluatorsSet = null; - smallestSet = null; - break; + ret.add(policyEvaluator); } + } else { + ret = new ArrayList<>(smallestList.size()); - if (smallestSet == null) { - smallestSet = resourceEvaluators; - } else { - if (resourceEvaluatorsSet == null) { - resourceEvaluatorsSet = new ArrayList<>(); - resourceEvaluatorsSet.add(smallestSet); - } - resourceEvaluatorsSet.add(resourceEvaluators); - - if (smallestSet.size() > resourceEvaluators.size()) { - smallestSet = resourceEvaluators; - } - } - } + Set policyIds = new HashSet<>(); - if (resourceEvaluatorsSet != null) { - evaluators = new HashSet<>(smallestSet); - for (Set resourceEvaluators : resourceEvaluatorsSet) { - if (resourceEvaluators != smallestSet) { - // remove policies from ret that are not in resourceEvaluators - evaluators.retainAll(resourceEvaluators); + for (RangerPolicyResourceEvaluator resourceEvaluator : smallestList) { + RangerPolicyEvaluator policyEvaluator = resourceEvaluator.getPolicyEvaluator(); - if (CollectionUtils.isEmpty(evaluators)) { // if no policy exists, bail out and return empty list - evaluators = null; - break; - } + if (policyIds.add(policyEvaluator.getPolicyId())) { + ret.add(policyEvaluator); } } - } else { - evaluators = smallestSet; - } - if (evaluators != null) { - ret = new ArrayList<>(evaluators); ret.sort(RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR); } } @@ -797,7 +780,7 @@ private List normalizeAndPrunePolicies(List rangerPo } } - return rangerPolicies; + return rangerPolicies == null ? new ArrayList<>() : rangerPolicies; } private boolean isPolicyNeedsPruning(RangerPolicy policy, final String componentType) { @@ -949,18 +932,15 @@ private void init(RangerPolicyEngineOptions options) { } } } - if (LOG.isInfoEnabled()) { - LOG.info("This policy engine contains " + (policyEvaluators.size()+dataMaskPolicyEvaluators.size()+rowFilterPolicyEvaluators.size()) + " policy evaluators"); - } - RangerPolicyEvaluator.PolicyEvalOrderComparator comparator = new RangerPolicyEvaluator.PolicyEvalOrderComparator(); - Collections.sort(policyEvaluators, comparator); - this.policyEvaluators = Collections.unmodifiableList(policyEvaluators); + LOG.info("This policy engine contains " + (policyEvaluators.size()+dataMaskPolicyEvaluators.size()+rowFilterPolicyEvaluators.size()) + " policy evaluators"); + Collections.sort(policyEvaluators, RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR); + this.policyEvaluators = policyEvaluators; - Collections.sort(dataMaskPolicyEvaluators, comparator); - this.dataMaskPolicyEvaluators = Collections.unmodifiableList(dataMaskPolicyEvaluators); + Collections.sort(dataMaskPolicyEvaluators, RangerPolicyEvaluator.NAME_COMPARATOR); + this.dataMaskPolicyEvaluators = dataMaskPolicyEvaluators; - Collections.sort(rowFilterPolicyEvaluators, comparator); - this.rowFilterPolicyEvaluators = Collections.unmodifiableList(rowFilterPolicyEvaluators); + Collections.sort(rowFilterPolicyEvaluators, RangerPolicyEvaluator.NAME_COMPARATOR); + this.rowFilterPolicyEvaluators = rowFilterPolicyEvaluators; this.policyEvaluatorsMap = createPolicyEvaluatorsMap(); @@ -989,6 +969,16 @@ private void init(RangerPolicyEngineOptions options) { LOG.debug("rowFilter policy evaluation order: #" + (++order) + " - policy id=" + policy.getId() + "; name=" + policy.getName() + "; evalOrder=" + policyEvaluator.getEvalOrder()); } + + LOG.debug("audit policy evaluation order: " + ((this.auditPolicyEvaluators != null) ? this.auditPolicyEvaluators.size() : 0 + " policies")); + if (this.auditPolicyEvaluators != null) { + order = 0; + for(RangerPolicyEvaluator policyEvaluator : this.auditPolicyEvaluators) { + RangerPolicy policy = policyEvaluator.getPolicy(); + + LOG.debug("audit policy evaluation order: #" + (++order) + " - policy id=" + policy.getId() + "; name=" + policy.getName() + "; evalOrder=" + policyEvaluator.getEvalOrder()); + } + } } } @@ -1000,15 +990,50 @@ private List buildContextEnrichers(RangerPolicyEngineOpti if (enricherDef == null) { continue; } - if (!options.disableContextEnrichers || options.enableTagEnricherWithLocalRefresher && StringUtils.equals(enricherDef.getEnricher(), RangerTagEnricher.class.getName())) { - // This will be true only if the engine is initialized within ranger-admin - RangerServiceDef.RangerContextEnricherDef contextEnricherDef = enricherDef; - if (options.enableTagEnricherWithLocalRefresher && StringUtils.equals(enricherDef.getEnricher(), RangerTagEnricher.class.getName())) { - contextEnricherDef = new RangerServiceDef.RangerContextEnricherDef(enricherDef.getItemId(), enricherDef.getName(), "org.apache.ranger.common.RangerAdminTagEnricher", null); + if (options.disableTagRetriever && StringUtils.equals(enricherDef.getEnricher(), RangerTagEnricher.class.getName())) { + if (MapUtils.isNotEmpty(enricherDef.getEnricherOptions())) { + Map enricherOptions = new HashMap<>(enricherDef.getEnricherOptions()); + + enricherOptions.remove(TAG_RETRIEVER_CLASSNAME_OPTION); + + enricherDef = new RangerServiceDef.RangerContextEnricherDef(enricherDef.getItemId(), enricherDef.getName(), enricherDef.getEnricher(), enricherOptions); + } + } + + if (options.disableUserStoreRetriever && StringUtils.equals(enricherDef.getEnricher(), RangerUserStoreEnricher.class.getName())) { + if (MapUtils.isNotEmpty(enricherDef.getEnricherOptions())) { + Map enricherOptions = new HashMap<>(enricherDef.getEnricherOptions()); + + enricherOptions.remove(USERSTORE_RETRIEVER_CLASSNAME_OPTION); + + enricherDef = new RangerServiceDef.RangerContextEnricherDef(enricherDef.getItemId(), enricherDef.getName(), enricherDef.getEnricher(), enricherOptions); } + } - RangerContextEnricher contextEnricher = buildContextEnricher(contextEnricherDef); + if (options.disableGdsInfoRetriever && StringUtils.equals(enricherDef.getEnricher(), RangerGdsEnricher.class.getName())) { + if (MapUtils.isNotEmpty(enricherDef.getEnricherOptions())) { + Map enricherOptions = new HashMap<>(enricherDef.getEnricherOptions()); + + enricherOptions.remove(RETRIEVER_CLASSNAME_OPTION); + + enricherDef = new RangerServiceDef.RangerContextEnricherDef(enricherDef.getItemId(), enricherDef.getName(), enricherDef.getEnricher(), enricherOptions); + } + } + + // Following 2 cases will be true only if the engine is initialized within ranger-admin + if (options.enableTagEnricherWithLocalRefresher && StringUtils.equals(enricherDef.getEnricher(), RangerTagEnricher.class.getName())) { + enricherDef = new RangerServiceDef.RangerContextEnricherDef(enricherDef.getItemId(), enricherDef.getName(), "org.apache.ranger.common.RangerAdminTagEnricher", null); + } else if (options.enableUserStoreEnricherWithLocalRefresher && StringUtils.equals(enricherDef.getEnricher(), RangerUserStoreEnricher.class.getName())) { + enricherDef = new RangerServiceDef.RangerContextEnricherDef(enricherDef.getItemId(), enricherDef.getName(), "org.apache.ranger.common.RangerAdminUserStoreEnricher", null); + } else { + if (options.disableContextEnrichers) { + enricherDef = null; + } + } + + if (enricherDef != null) { + RangerContextEnricher contextEnricher = buildContextEnricher(enricherDef, options); if (contextEnricher != null) { contextEnrichers.add(contextEnricher); @@ -1019,7 +1044,7 @@ private List buildContextEnrichers(RangerPolicyEngineOpti return contextEnrichers; } - private RangerContextEnricher buildContextEnricher(RangerServiceDef.RangerContextEnricherDef enricherDef) { + private RangerContextEnricher buildContextEnricher(RangerServiceDef.RangerContextEnricherDef enricherDef, RangerPolicyEngineOptions options) { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerPolicyRepository.buildContextEnricher(" + enricherDef + ")"); } @@ -1059,6 +1084,7 @@ private RangerContextEnricher buildContextEnricher(RangerServiceDef.RangerContex if (ret instanceof RangerAbstractContextEnricher) { RangerAbstractContextEnricher abstractContextEnricher = (RangerAbstractContextEnricher) ret; abstractContextEnricher.setPluginContext(pluginContext); + abstractContextEnricher.setPolicyEngineOptions(options); } ret.init(); } @@ -1162,22 +1188,20 @@ private List getReorderedPolicyEvaluators(List(evaluators); - Collections.sort(ret, new RangerPolicyEvaluator.PolicyEvalOrderComparator()); - - ret = Collections.unmodifiableList(ret); + Collections.sort(ret, RangerPolicyEvaluator.EVAL_ORDER_COMPARATOR); } return ret; } - private Map createResourceTrieMap(List evaluators, boolean optimizeTrieForRetrieval) { - final Map ret; + private Map> createResourceTrieMap(List evaluators, boolean optimizeTrieForRetrieval, boolean optimizeTrieForSpace) { + final Map> ret; if (serviceDef != null && CollectionUtils.isNotEmpty(serviceDef.getResources())) { ret = new HashMap<>(); for (RangerServiceDef.RangerResourceDef resourceDef : serviceDef.getResources()) { - ret.put(resourceDef.getName(), new RangerResourceTrie(resourceDef, evaluators, optimizeTrieForRetrieval, pluginContext)); + ret.put(resourceDef.getName(), new RangerResourceTrie(resourceDef, evaluators, optimizeTrieForRetrieval, optimizeTrieForSpace, pluginContext)); } } else { ret = null; @@ -1186,7 +1210,7 @@ private Map createResourceTrieMap(List trieMap, Integer policyDeltaType, RangerPolicyEvaluator oldEvaluator, RangerPolicyEvaluator newEvaluator) { + private void updateTrie(Map> trieMap, Integer policyDeltaType, RangerPolicyEvaluator oldEvaluator, RangerPolicyEvaluator newEvaluator) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerPolicyRepository.updateTrie(policyDeltaType=" + policyDeltaType + "): "); } @@ -1194,17 +1218,18 @@ private void updateTrie(Map trieMap, Integer policyD String resourceDefName = resourceDef.getName(); - RangerResourceTrie trie = trieMap.get(resourceDefName); + RangerResourceTrie trie = trieMap.get(resourceDefName); if (trie == null) { if (RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE == policyDeltaType || RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE == policyDeltaType) { LOG.warn("policyDeltaType is not for POLICY_CREATE and trie for resourceDef:[" + resourceDefName + "] was null! Should not have happened!!"); } - trie = new RangerResourceTrie<>(resourceDef, new ArrayList<>(), true, pluginContext); + trie = new RangerResourceTrie<>(resourceDef, new ArrayList<>(), options.optimizeTrieForRetrieval, options.optimizeTrieForSpace, pluginContext); trieMap.put(resourceDefName, trie); } if (policyDeltaType == RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE) { + removeEvaluatorFromTrie(oldEvaluator, trie, resourceDefName); addEvaluatorToTrie(newEvaluator, trie, resourceDefName); } else if (policyDeltaType == RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE) { removeEvaluatorFromTrie(oldEvaluator, trie, resourceDefName); @@ -1220,18 +1245,22 @@ private void updateTrie(Map trieMap, Integer policyD } } - private void addEvaluatorToTrie(RangerPolicyEvaluator newEvaluator, RangerResourceTrie trie, String resourceDefName) { + private void addEvaluatorToTrie(RangerPolicyEvaluator newEvaluator, RangerResourceTrie trie, String resourceDefName) { if (newEvaluator != null) { - RangerPolicy.RangerPolicyResource resource = newEvaluator.getPolicyResource().get(resourceDefName); - trie.add(resource, newEvaluator); + for (RangerPolicyResourceEvaluator resourceEvaluator : newEvaluator.getResourceEvaluators()) { + RangerPolicy.RangerPolicyResource resource = resourceEvaluator.getPolicyResource().get(resourceDefName); + + trie.add(resource, resourceEvaluator); + } + } else { + LOG.warn("Unexpected: newPolicyEvaluator is null for resource:[" + resourceDefName + "]"); } } - private void removeEvaluatorFromTrie(RangerPolicyEvaluator oldEvaluator, RangerResourceTrie trie, String resourceDefName) { + private void removeEvaluatorFromTrie(RangerPolicyEvaluator oldEvaluator, RangerResourceTrie trie, String resourceDefName) { if (oldEvaluator != null) { - RangerPolicy.RangerPolicyResource resource = oldEvaluator.getPolicyResource().get(resourceDefName); - if (resource != null) { - trie.delete(resource, oldEvaluator); + for (RangerPolicyResourceEvaluator resourceEvaluator : oldEvaluator.getResourceEvaluators()) { + trie.delete(resourceEvaluator.getPolicyResource().get(resourceDefName), resourceEvaluator); } } } @@ -1249,7 +1278,7 @@ private Map createPolicyEvaluatorsMap() { tmpPolicyEvaluatorMap.put(evaluator.getPolicy().getId(), evaluator); } - return Collections.unmodifiableMap(tmpPolicyEvaluatorMap); + return tmpPolicyEvaluatorMap; } @@ -1277,7 +1306,9 @@ private RangerPolicyEvaluator addPolicy(RangerPolicy policy) { LOG.warn("RangerPolicyEngine: ignoring policy id=" + policy.getId() + " - invalid policyType '" + policy.getPolicyType() + "'"); } - policyEvaluatorsMap.put(policy.getId(), ret); + if (policy.getPolicyType() != RangerPolicy.POLICY_TYPE_AUDIT) { + policyEvaluatorsMap.put(policy.getId(), ret); + } } } } @@ -1296,7 +1327,7 @@ private void removePolicy(Long id) { while (iterator.hasNext()) { if (id.equals(iterator.next().getId())) { iterator.remove(); - break; + //break; } } @@ -1311,7 +1342,10 @@ private void deletePolicyEvaluator(RangerPolicyEvaluator evaluator) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerPolicyRepository.deletePolicyEvaluator(" + evaluator.getPolicy() + ")"); } - int policyType = evaluator.getPolicy().getPolicyType(); + Integer policyType = evaluator.getPolicy().getPolicyType(); + if (policyType == null) { + policyType = RangerPolicy.POLICY_TYPE_ACCESS; + } List evaluators = null; @@ -1348,12 +1382,17 @@ private RangerPolicyEvaluator update(final RangerPolicyDelta delta, final Ranger switch (changeType) { case RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE: + if (currentEvaluator != null) { + removePolicy(policyId); + } if (policy != null) { newEvaluator = addPolicy(policy); } break; case RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE: { - removePolicy(policyId); + if (currentEvaluator != null) { + removePolicy(policyId); + } if (policy != null) { newEvaluator = addPolicy(policy); } @@ -1367,7 +1406,7 @@ private RangerPolicyEvaluator update(final RangerPolicyDelta delta, final Ranger break; } - Map trieMap = getTrie(policyType); + Map> trieMap = getTrie(policyType); if (trieMap != null) { updateTrie(trieMap, changeType, currentEvaluator, newEvaluator); @@ -1388,8 +1427,8 @@ private RangerPolicyEvaluator update(final RangerPolicyDelta delta, final Ranger return ret; } - Map getTrie(final int policyType) { - final Map ret; + Map> getTrie(final int policyType) { + final Map> ret; switch (policyType) { case RangerPolicy.POLICY_TYPE_ACCESS: ret = policyResourceTrie; @@ -1400,6 +1439,9 @@ Map getTrie(final int policyType) { case RangerPolicy.POLICY_TYPE_ROWFILTER: ret = rowFilterResourceTrie; break; + case RangerPolicy.POLICY_TYPE_AUDIT: + ret = auditFilterResourceTrie; + break; default: ret = null; } @@ -1421,4 +1463,110 @@ boolean getIsAudited() { return isAudited; } } + + void reinit(List deltas) { + final boolean isExistingPolicies = CollectionUtils.isNotEmpty(this.policies); + + updateResourceTrie(deltas); + + if (StringUtils.isEmpty(zoneName) && CollectionUtils.isNotEmpty(this.policies)) { + if (!isExistingPolicies) { + this.contextEnrichers = buildContextEnrichers(options); + } + } else { + this.contextEnrichers = null; + } + } + + private void updateResourceTrie(List deltas) { + + boolean[] flags = new boolean[RangerPolicy.POLICY_TYPES.length]; + + for (RangerPolicyDelta delta : deltas) { + final Integer changeType = delta.getChangeType(); + final String serviceType = delta.getServiceType(); + final Long policyId = delta.getPolicyId(); + final Integer policyType = delta.getPolicyType(); + + if (!serviceType.equals(this.serviceDef.getName())) { + continue; + } + + RangerPolicyEvaluator evaluator = null; + + switch (changeType) { + case RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE: + if (delta.getPolicy() == null) { + LOG.warn("Could not find policy for policy-id:[" + policyId + "]"); + continue; + } + evaluator = getPolicyEvaluator(policyId); + if (evaluator != null) { + LOG.warn("Unexpected: Found evaluator for policy-id:[" + policyId + "], changeType=CHANGE_TYPE_POLICY_CREATE"); + } + + break; + + case RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE: + evaluator = getPolicyEvaluator(policyId); + + if (evaluator == null) { + LOG.warn("Unexpected: Did not find evaluator for policy-id:[" + policyId + "], changeType=CHANGE_TYPE_POLICY_UPDATE"); + } + break; + + case RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE: + evaluator = getPolicyEvaluator(policyId); + if (evaluator == null) { + LOG.warn("Unexpected: Did not find evaluator for policy-id:[" + policyId + "], changeType=CHANGE_TYPE_POLICY_DELETE"); + } + break; + + default: + LOG.error("Unknown changeType:[" + changeType + "], Ignoring"); + break; + } + + evaluator = update(delta, evaluator); + + if (evaluator != null) { + switch (changeType) { + case RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE: + policyEvaluatorsMap.put(policyId, evaluator); + break; + + case RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE: + policyEvaluatorsMap.put(policyId, evaluator); + break; + + case RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE: + policyEvaluatorsMap.remove(policyId); + break; + + default: + break; + } + + flags[policyType] = true; + } + } + + for (int policyType = 0; policyType < flags.length; policyType++) { + if (flags[policyType]) { + Map> trie = getTrie(policyType); + + if (trie != null) { + for (Map.Entry> entry : trie.entrySet()) { + entry.getValue().wrapUpUpdate(); + } + } + } + } + + if (auditFilterResourceTrie != null) { + for (Map.Entry> entry : auditFilterResourceTrie.entrySet()) { + entry.getValue().wrapUpUpdate(); + } + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java new file mode 100644 index 0000000000..3270564317 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerRequestScriptEvaluator.java @@ -0,0 +1,1399 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.ranger.authorization.utils.JsonUtils; +import org.apache.ranger.authorization.utils.StringUtil; +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.util.MacroProcessor; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerTimeRangeChecker; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.apache.ranger.plugin.util.JavaScriptEdits; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.apache.ranger.plugin.util.RangerCommonConstants.*; + + +public final class RangerRequestScriptEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(RangerRequestScriptEvaluator.class); + + private static final Logger PERF_POLICY_CONDITION_SCRIPT_TOJSON = RangerPerfTracer.getPerfLogger("policy.condition.script.tojson"); + private static final Logger PERF_POLICY_CONDITION_SCRIPT_EVAL = RangerPerfTracer.getPerfLogger("policy.condition.script.eval"); + private static final String TAG_ATTR_DATE_FORMAT_PROP = "ranger.plugin.tag.attr.additional.date.formats"; + private static final String TAG_ATTR_DATE_FORMAT_SEPARATOR = "||"; + private static final String TAG_ATTR_DATE_FORMAT_SEPARATOR_REGEX = "\\|\\|"; + private static final String DEFAULT_RANGER_TAG_ATTRIBUTE_DATE_FORMAT = "yyyy/MM/dd"; + private static final String DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME = "ATLAS_DATE_FORMAT"; + private static final String DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; + private static final String SCRIPT_SAFE_PREEXEC = "exit=null;quit=null;"; + private static final String SCRIPT_PREEXEC = SCRIPT_VAR__CTX + "=JSON.parse(" + SCRIPT_VAR__CTX_JSON + "); J=JSON.stringify;" + + SCRIPT_VAR_REQ + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_REQUEST + ";" + + SCRIPT_VAR_RES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_RESOURCE + ";" + + SCRIPT_VAR_USER + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ATTRIBUTES + ";" + + SCRIPT_VAR_UGNAMES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUPS + ";" + + SCRIPT_VAR_UG + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_GROUP_ATTRIBUTES + ";" + + SCRIPT_VAR_UGA + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_UGA + ";" + + SCRIPT_VAR_URNAMES + "=" + SCRIPT_VAR_REQ + "." + SCRIPT_FIELD_USER_ROLES + ";" + + SCRIPT_VAR_TAG + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAG + ";" + + SCRIPT_VAR_TAGS + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAGS + ";" + + SCRIPT_VAR_TAGNAMES + "=" + SCRIPT_VAR__CTX + "." + SCRIPT_FIELD_TAG_NAMES + ";"; + private static final Pattern JSON_VAR_NAMES_PATTERN = Pattern.compile(getJsonVarNamesPattern()); + private static final Pattern USER_ATTRIBUTES_PATTERN = Pattern.compile(getUserAttributesPattern()); + private static final Pattern GROUP_ATTRIBUTES_PATTERN = Pattern.compile(getGroupAttributesPattern()); + private static final String STR_QUOTE = "'"; + private static final String STR_COMMA = ","; + + private static final MacroProcessor MACRO_PROCESSOR = new MacroProcessor(getMacrosMap()); + + private static String[] dateFormatStrings = null; + + private final RangerAccessRequest accessRequest; + private final ScriptEngine scriptEngine; + private final Bindings bindings; + private boolean initDone = false; + private Map userAttrs = Collections.emptyMap(); + private Map> groupAttrs = Collections.emptyMap(); + private Map> tags = Collections.emptyMap(); + private Map tag = Collections.emptyMap(); + private Collection userGroups = Collections.emptySet(); + private Collection userRoles = Collections.emptySet(); + private Collection tagNames = Collections.emptySet(); + private Boolean result = false; + + static { + init(null); + } + + private static final ThreadLocal> THREADLOCAL_DATE_FORMATS = + new ThreadLocal>() { + @Override protected List initialValue() { + List ret = new ArrayList<>(); + + for (String dateFormatString : dateFormatStrings) { + try { + if (StringUtils.isNotBlank(dateFormatString)) { + if (StringUtils.equalsIgnoreCase(dateFormatString, DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME)) { + dateFormatString = DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT; + } + SimpleDateFormat df = new SimpleDateFormat(dateFormatString); + df.setLenient(false); + ret.add(df); + } + } catch (Exception exception) { + // Ignore + } + } + + return ret; + } + }; + + + public static boolean needsJsonCtxEnabled(String script) { + boolean ret = false; + + if (script != null) { + Matcher matcher = JSON_VAR_NAMES_PATTERN.matcher(script); + + ret = matcher.find(); + } + + return ret; + } + + public static boolean hasUserAttributeReference(String script) { + boolean ret = false; + + if (script != null) { + Matcher matcher = USER_ATTRIBUTES_PATTERN.matcher(script); + + ret = matcher.find(); + } + + return ret; + } + + public static boolean hasGroupAttributeReference(String script) { + boolean ret = false; + + if (script != null) { + Matcher matcher = GROUP_ATTRIBUTES_PATTERN.matcher(script); + + ret = matcher.find(); + } + + return ret; + } + + public static boolean hasUserGroupAttributeReference(String script) { + return hasUserAttributeReference(script) || hasGroupAttributeReference(script); + } + + public static boolean hasUserGroupAttributeReference(Collection scripts) { + boolean ret = false; + + if (scripts != null) { + for (String script : scripts) { + if (hasUserGroupAttributeReference(script)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public static String expandMacros(String script) { + return MACRO_PROCESSOR.expandMacros(script); + } + + public RangerRequestScriptEvaluator(RangerAccessRequest accessRequest, ScriptEngine scriptEngine) { + this(accessRequest, scriptEngine, true); + } + + public RangerRequestScriptEvaluator(RangerAccessRequest accessRequest, ScriptEngine scriptEngine, boolean enableJsonCtx) { + this.accessRequest = accessRequest.getReadOnlyCopy(); + this.scriptEngine = scriptEngine; + this.bindings = scriptEngine.createBindings(); + + RangerTagForEval currentTag = this.getCurrentTag(); + Map tagAttribs = currentTag != null ? currentTag.getAttributes() : Collections.emptyMap(); + + bindings.put(SCRIPT_VAR_ctx, this); + bindings.put(SCRIPT_VAR_tag, currentTag); + bindings.put(SCRIPT_VAR_tagAttr, tagAttribs); + + String preExecScript = ""; + + if (enableJsonCtx) { + bindings.put(SCRIPT_VAR__CTX_JSON, this.toJson()); + + preExecScript += SCRIPT_PREEXEC; + } + + if (StringUtils.isNotBlank(preExecScript)) { + try { + scriptEngine.eval(preExecScript, bindings); + } catch (ScriptException excp) { + LOG.error("RangerRequestScriptEvaluator(): initialization failed", excp); + } + } + } + + public Object evaluateScript(String script) { + script = expandMacros(script); + + return evaluateScriptImpl(script); + } + + public Object evaluateConditionScript(String script) { + Object ret = evaluateScript(script); + + if (ret == null) { + ret = getResult(); + } + + if (ret instanceof Boolean) { + result = (Boolean) ret; + } + + return ret; + } + + private Object evaluateScriptImpl(String script) { + Object ret = null; + RangerPerfTracer perf = null; + + try { + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_CONDITION_SCRIPT_EVAL)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_CONDITION_SCRIPT_EVAL, "RangerRequestScriptEvaluator.evaluateScript(requestHash=" + accessRequest.hashCode() + ")"); + } + + String preExec = SCRIPT_SAFE_PREEXEC; + + if (script.contains(".includes(")) { + preExec += SCRIPT_POLYFILL_INCLUDES; + } + + if (script.contains(".intersects(")) { + preExec += SCRIPT_POLYFILL_INTERSECTS; + } + + if (JavaScriptEdits.hasDoubleBrackets(script)) { + script = JavaScriptEdits.replaceDoubleBrackets(script); + } + + ret = scriptEngine.eval(preExec + script, bindings); + } catch (NullPointerException nullp) { + LOG.error("RangerRequestScriptEvaluator.evaluateScript(): eval called with NULL argument(s)", nullp); + } catch (ScriptException excp) { + LOG.error("RangerRequestScriptEvaluator.evaluateScript(): failed to evaluate script", excp); + } catch (Throwable t) { + LOG.error("RangerRequestScriptEvaluator.evaluateScript(): failed to evaluate script", t); + } finally { + RangerPerfTracer.log(perf); + } + + return ret; + } + + private String toJson() { + RangerPerfTracer perf = null; + + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_CONDITION_SCRIPT_TOJSON)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_CONDITION_SCRIPT_TOJSON, "RangerRequestScriptEvaluator.toJson(requestHash=" + accessRequest.hashCode() + ")"); + } + + Map ret = new HashMap<>(); + Map request = new HashMap<>(); + Date accessTime = accessRequest.getAccessTime(); + + init(); + + if (accessTime != null) { + request.put(SCRIPT_FIELD_ACCESS_TIME, accessTime.getTime()); + } + + request.put(SCRIPT_FIELD_ACCESS_TYPE, accessRequest.getAccessType()); + request.put(SCRIPT_FIELD_ACTION, accessRequest.getAction()); + request.put(SCRIPT_FIELD_CLIENT_IP_ADDRESS, accessRequest.getClientIPAddress()); + request.put(SCRIPT_FIELD_CLIENT_TYPE, accessRequest.getClientType()); + request.put(SCRIPT_FIELD_CLUSTER_NAME, accessRequest.getClusterName()); + request.put(SCRIPT_FIELD_CLUSTER_TYPE, accessRequest.getClusterType()); + request.put(SCRIPT_FIELD_FORWARDED_ADDRESSES, accessRequest.getForwardedAddresses()); + request.put(SCRIPT_FIELD_REMOTE_IP_ADDRESS, accessRequest.getRemoteIPAddress()); + request.put(SCRIPT_FIELD_REQUEST_DATA, accessRequest.getRequestData()); + + if (accessRequest.getResource() != null) { + Map resource = new HashMap<>(accessRequest.getResource().getAsMap()); + + resource.put(SCRIPT_FIELD__OWNER_USER, accessRequest.getResource().getOwnerUser()); + + request.put(SCRIPT_FIELD_RESOURCE, resource); + } + + request.put(SCRIPT_FIELD_RESOURCE_MATCHING_SCOPE, accessRequest.getResourceMatchingScope()); + + request.put(SCRIPT_FIELD_USER, getUser()); + request.put(SCRIPT_FIELD_USER_GROUPS, userGroups); + request.put(SCRIPT_FIELD_USER_ROLES, userRoles); + + request.put(SCRIPT_FIELD_USER_ATTRIBUTES, userAttrs); + + request.put(SCRIPT_FIELD_USER_GROUP_ATTRIBUTES, groupAttrs); + request.put(SCRIPT_FIELD_UGA, new UserGroupsAttributes(userGroups, groupAttrs).getAttributes()); + + ret.put(SCRIPT_FIELD_REQUEST, request); + + ret.put(SCRIPT_FIELD_TAGS, tags); + ret.put(SCRIPT_FIELD_TAG_NAMES, tagNames); + ret.put(SCRIPT_FIELD_TAG, tag); + + String strRet = JsonUtils.objectToJson(ret); + + RangerPerfTracer.log(perf); + + return strRet; + } + + public static void init(Configuration config) { + StringBuilder sb = new StringBuilder(DEFAULT_RANGER_TAG_ATTRIBUTE_DATE_FORMAT); + + sb.append(TAG_ATTR_DATE_FORMAT_SEPARATOR).append(DEFAULT_ATLAS_TAG_ATTRIBUTE_DATE_FORMAT_NAME); + + String additionalDateFormatsValue = config != null ? config.get(TAG_ATTR_DATE_FORMAT_PROP) : null; + + if (StringUtils.isNotBlank(additionalDateFormatsValue)) { + sb.append(TAG_ATTR_DATE_FORMAT_SEPARATOR).append(additionalDateFormatsValue); + } + + String[] formatStrings = sb.toString().split(TAG_ATTR_DATE_FORMAT_SEPARATOR_REGEX); + + Arrays.sort(formatStrings, new Comparator() { + @Override + public int compare(String first, String second) { + return Integer.compare(second.length(), first.length()); + } + }); + + RangerRequestScriptEvaluator.dateFormatStrings = formatStrings; + } + + public String getResource() { + String ret = null; + RangerAccessResource val = RangerAccessRequestUtil.getCurrentResourceFromContext(getRequestContext()); + + if(val != null) { + ret = val.getAsString(); + } + + return ret; + } + + public String getResourceZone() { + String ret = RangerAccessRequestUtil.getResourceZoneNameFromContext(getRequestContext()); + + return ret != null ? ret : StringUtils.EMPTY; + } + + public Set getResourceZones() { + Set ret = RangerAccessRequestUtil.getResourceZoneNamesFromContext(getRequestContext()); + + return ret != null ? Collections.emptySet() : ret; + } + + public String getRequestContextAttribute(String attributeName) { + String ret = null; + + if (StringUtils.isNotBlank(attributeName)) { + Object val = getRequestContext().get(attributeName); + + if(val != null) { + ret = val.toString(); + } + } + + return ret; + } + + public boolean isAccessTypeAny() { return accessRequest.isAccessTypeAny(); } + + public boolean isAccessTypeDelegatedAdmin() { return accessRequest.isAccessTypeDelegatedAdmin(); } + + public String getUser() { return accessRequest.getUser(); } + + public Set getUserGroups() { return accessRequest.getUserGroups(); } + + public Set getUserRoles() { + return RangerAccessRequestUtil.getUserRoles(accessRequest); + } + + public Date getAccessTime() { return accessRequest.getAccessTime() != null ? accessRequest.getAccessTime() : new Date(); } + + public String getClientIPAddress() { return accessRequest.getClientIPAddress(); } + + public String getClientType() { return accessRequest.getClientType(); } + + public String getAction() { return accessRequest.getAction(); } + + public String getRequestData() { return accessRequest.getRequestData(); } + + public String getSessionId() { return accessRequest.getSessionId(); } + + public RangerTagForEval getCurrentTag() { + RangerTagForEval ret = RangerAccessRequestUtil.getCurrentTagFromContext(getRequestContext()); + + if(ret == null ) { + if (LOG.isDebugEnabled()) { + logDebug("RangerRequestScriptEvaluator.getCurrentTag() - No current TAG object. Script execution must be for resource-based policy."); + } + } + return ret; + } + + public String getCurrentTagType() { + RangerTagForEval tagObject = getCurrentTag(); + return (tagObject != null) ? tagObject.getType() : null; + } + + public Set getAllTagTypes() { + Set allTagTypes = null; + Set tagObjectList = getAllTags(); + + if (CollectionUtils.isNotEmpty(tagObjectList)) { + for (RangerTagForEval tag : tagObjectList) { + String tagType = tag.getType(); + if (allTagTypes == null) { + allTagTypes = new HashSet<>(); + } + allTagTypes.add(tagType); + } + } + + return allTagTypes; + } + + public Map getTagAttributes(final String tagType) { + Map ret = null; + + if (StringUtils.isNotBlank(tagType)) { + Set tagObjectList = getAllTags(); + + // Assumption: There is exactly one tag with given tagType in the list of tags - may not be true ***TODO*** + // This will get attributes of the first tagType that matches + if (CollectionUtils.isNotEmpty(tagObjectList)) { + for (RangerTagForEval tag : tagObjectList) { + if (tag.getType().equals(tagType)) { + ret = tag.getAttributes(); + break; + } + } + } + } + + return ret; + } + + public List> getTagAttributesForAllMatchingTags(final String tagType) { + List> ret = null; + + if (StringUtils.isNotBlank(tagType)) { + Set tagObjectList = getAllTags(); + + // Assumption: There is exactly one tag with given tagType in the list of tags - may not be true ***TODO*** + // This will get attributes of the first tagType that matches + if (CollectionUtils.isNotEmpty(tagObjectList)) { + for (RangerTagForEval tag : tagObjectList) { + if (tag.getType().equals(tagType)) { + Map tagAttributes = tag.getAttributes(); + if (tagAttributes != null) { + if (ret == null) { + ret = new ArrayList<>(); + } + ret.add(tagAttributes); + } + break; + } + } + } + } + + return ret; + } + + public Set getAttributeNames(final String tagType) { + Set ret = null; + Map attributes = getTagAttributes(tagType); + + if (attributes != null) { + ret = attributes.keySet(); + } + + return ret; + } + + public String getAttributeValue(final String tagType, final String attributeName) { + String ret = null; + + if (StringUtils.isNotBlank(tagType) || StringUtils.isNotBlank(attributeName)) { + Map attributes = getTagAttributes(tagType); + + if (attributes != null) { + ret = attributes.get(attributeName); + } + } + return ret; + } + + public List getAttributeValueForAllMatchingTags(final String tagType, final String attributeName) { + List ret = null; + + if (StringUtils.isNotBlank(tagType) || StringUtils.isNotBlank(attributeName)) { + Map attributes = getTagAttributes(tagType); + + if (attributes != null && attributes.get(attributeName) != null) { + if (ret == null) { + ret = new ArrayList<>(); + } + ret.add(attributes.get(attributeName)); + } + } + return ret; + } + + public String getAttributeValue(final String attributeName) { + String ret = null; + + if (StringUtils.isNotBlank(attributeName)) { + RangerTagForEval tag = getCurrentTag(); + Map attributes = null; + if (tag != null) { + attributes = tag.getAttributes(); + } + if (attributes != null) { + ret = attributes.get(attributeName); + } + } + + return ret; + } + + public boolean getResult() { + return result; + + } + + public void setResult(final boolean result) { + this.result = result; + } + + private Date getAsDate(String value, SimpleDateFormat df) { + Date ret = null; + + TimeZone savedTimeZone = df.getTimeZone(); + try { + ret = df.parse(value); + } catch (ParseException exception) { + // Ignore + } finally { + df.setTimeZone(savedTimeZone); + } + + return ret; + } + + public Date getAsDate(String value) { + Date ret = null; + + if (StringUtils.isNotBlank(value)) { + for (SimpleDateFormat simpleDateFormat : THREADLOCAL_DATE_FORMATS.get()) { + ret = getAsDate(value, simpleDateFormat); + if (ret != null) { + if (LOG.isDebugEnabled()) { + logDebug("RangerRequestScriptEvaluator.getAsDate() -The best match found for Format-String:[" + simpleDateFormat.toPattern() + "], date:[" + ret +"]"); + } + break; + } + } + } + + if (ret == null) { + logError("RangerRequestScriptEvaluator.getAsDate() - Could not convert [" + value + "] to Date using any of the Format-Strings: " + Arrays.toString(dateFormatStrings)); + } else { + ret = StringUtil.getUTCDateForLocalDate(ret); + } + + return ret; + } + + public Date getTagAttributeAsDate(String tagType, String attributeName) { + String attrValue = getAttributeValue(tagType, attributeName); + + return getAsDate(attrValue); + } + + public boolean isAccessedAfter(String tagType, String attributeName) { + boolean ret = false; + Date accessDate = getAccessTime(); + Date expiryDate = getTagAttributeAsDate(tagType, attributeName); + + if (expiryDate == null || accessDate.after(expiryDate) || accessDate.equals(expiryDate)) { + ret = true; + } + + return ret; + } + + public boolean isAccessedAfter(String attributeName) { + boolean ret = false; + Date accessDate = getAccessTime(); + Date expiryDate = getAsDate(getAttributeValue(attributeName)); + + if (expiryDate == null || accessDate.after(expiryDate) || accessDate.equals(expiryDate)) { + ret = true; + } + + return ret; + } + + public boolean isAccessedBefore(String tagType, String attributeName) { + boolean ret = true; + Date accessDate = getAccessTime(); + Date expiryDate = getTagAttributeAsDate(tagType, attributeName); + + if (expiryDate == null || accessDate.after(expiryDate)) { + ret = false; + } + + return ret; + } + + public boolean isAccessedBefore(String attributeName) { + boolean ret = true; + Date accessDate = getAccessTime(); + Date expiryDate = getAsDate(getAttributeValue(attributeName)); + + if (expiryDate == null || accessDate.after(expiryDate)) { + ret = false; + } + + return ret; + } + + public String tagNames(Object... args) { + init(); + + return toCsv(tagNames, args); + } + + public String tagNamesQ(Object... args) { + init(); + + return toCsvQ(tagNames, args); + } + + public String tagAttrNames(Object... args) { + init(); + + return toCsv(getTagAttrNames(), args); + } + + public String tagAttrNamesQ(Object... args) { + init(); + + return toCsvQ(getTagAttrNames(), args); + } + + public String tagAttr(String attrName, Object... args) { + init(); + + return toCsv(getTagAttr(attrName), args); + } + + public String tagAttrQ(String attrName, Object... args) { + init(); + + return toCsvQ(getTagAttr(attrName), args); + } + + public String ugNames(Object... args) { + init(); + + return toCsv(userGroups, args); + } + + public String ugNamesQ(Object... args) { + init(); + + return toCsvQ(userGroups, args); + } + + public String ugAttrNames(Object... args) { + init(); + + return toCsv(getUgAttrNames(), args); + } + + public String ugAttrNamesQ(Object... args) { + init(); + + return toCsvQ(getUgAttrNames(), args); + } + + public String ugAttr(String attrName, Object... args) { + init(); + + return toCsv(getUgAttr(attrName), args); + } + + public String ugAttrQ(String attrName, Object... args) { + init(); + + return toCsvQ(getUgAttr(attrName), args); + } + + public String urNames(Object... args) { + init(); + + return toCsv(userRoles, args); + } + + public String urNamesQ(Object... args) { + init(); + + return toCsvQ(userRoles, args); + } + + public String userAttrNames(Object... args) { + init(); + + return toCsv(getUserAttrNames(), args); + } + + public String userAttrNamesQ(Object... args) { + init(); + + return toCsvQ(getUserAttrNames(), args); + } + + public String userAttr(String attrName, Object... args) { + init(); + + String attrVal = userAttrs.get(attrName); + + return toCsv(Collections.singletonList(attrVal), args); + } + + public String userAttrQ(String attrName, Object... args) { + init(); + + String attrVal = userAttrs.get(attrName); + + return toCsvQ(Collections.singletonList(attrVal), args); + } + + public boolean hasTag(String tagName) { + init(); + + return tags.containsKey(tagName); + } + + public boolean hasAnyTag() { + init(); + + return !tags.isEmpty(); + } + + public boolean hasUserAttr(String attrName) { + init(); + + return userAttrs.containsKey(attrName); + } + + public boolean hasUgAttr(String attrName) { + init(); + + boolean ret = false; + + for (Map attrs : groupAttrs.values()) { + if (attrs.containsKey(attrName)) { + ret = true; + + break; + } + } + + return ret; + } + + public boolean hasTagAttr(String attrName) { + init(); + + boolean ret = false; + Set tags = RangerAccessRequestUtil.getRequestTagsFromContext(accessRequest.getContext()); + + if (tags != null) { + for (RangerTagForEval tag : tags) { + if (tag.getAttributes().containsKey(attrName)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public boolean isInGroup(String groupName) { + init(); + + return userGroups.contains(groupName); + } + + public boolean isInRole(String roleName) { + init(); + + return userRoles.contains(roleName); + } + + public boolean isInAnyGroup() { + init(); + + return !userGroups.isEmpty(); + } + + public boolean isInAnyRole() { + init(); + + return !userRoles.isEmpty(); + } + + public boolean isAccessTimeAfter(String strTime) { + return isAccessTimeBetween(strTime, null, null); + } + + public boolean isAccessTimeAfter(String strTime, String timeZone) { + return isAccessTimeBetween(strTime, null, timeZone); + } + + public boolean isAccessTimeBefore(String strTime) { + return isAccessTimeBetween(null, strTime, null); + } + + public boolean isAccessTimeBefore(String strTime, String timeZone) { + return isAccessTimeBetween(null, strTime, timeZone); + } + + public boolean isAccessTimeBetween(String fromTime, String toTime) { + return isAccessTimeBetween(fromTime, toTime, null); + } + + public boolean isAccessTimeBetween(String fromTime, String toTime, String timeZone) { + RangerTimeRangeChecker evaluator = new RangerTimeRangeChecker(fromTime, toTime, timeZone); + + return evaluator.isInRange(getAccessTime().getTime()); + } + + // for backward compatibility + public String ugNamesCsv() { + return ugNames(null, STR_COMMA); + } + + public String ugNamesCsvQ() { + return ugNamesQ(null, STR_COMMA, STR_QUOTE); + } + + public String urNamesCsv() { + return urNames(null, STR_COMMA); + } + + public String urNamesCsvQ() { + return urNamesQ(null, STR_COMMA, STR_QUOTE); + } + + public String tagNamesCsv() { + return tagNames(null, STR_COMMA); + } + + public String tagNamesCsvQ() { + return tagNamesQ(null, STR_COMMA, STR_QUOTE); + } + + public String userAttrNamesCsv() { + return userAttrNames(null, STR_COMMA); + } + + public String userAttrNamesCsvQ() { + return userAttrNamesQ(null, STR_COMMA, STR_QUOTE); + } + + public String ugAttrNamesCsv() { + return ugAttrNames(null, STR_COMMA); + } + + public String ugAttrNamesCsvQ() { + return ugAttrNamesQ(null, STR_COMMA, STR_QUOTE); + } + + public String tagAttrNamesCsv() { + return tagAttrNames(null, STR_COMMA); + } + + public String tagAttrNamesCsvQ() { + return tagAttrNamesQ(null, STR_COMMA, STR_QUOTE); + } + + public String ugAttrCsv(String attrName) { + return ugAttr(attrName, null, STR_COMMA); + } + + public String ugAttrCsvQ(String attrName) { + return ugAttrQ(attrName, null, STR_COMMA, STR_QUOTE); + } + + public String tagAttrCsv(String attrName) { + return tagAttr(attrName, null, STR_COMMA); + } + + public String tagAttrCsvQ(String attrName) { + return tagAttrQ(attrName, null, STR_COMMA, STR_QUOTE); + } + + private void init() { + if (!initDone) { + RangerUserStore userStore = RangerAccessRequestUtil.getRequestUserStoreFromContext(accessRequest.getContext()); + Map> userAttrMapping = userStore != null ? userStore.getUserAttrMapping() : Collections.emptyMap(); + Map> groupAttrMapping = userStore != null ? userStore.getGroupAttrMapping() : Collections.emptyMap(); + + userGroups = getSorted(getUserGroups()); + userRoles = getSorted(getUserRoles()); + userAttrs = copyMap(userAttrMapping.get(accessRequest.getUser())); + groupAttrs = new HashMap<>(); + + userAttrs.put(SCRIPT_FIELD__NAME, getUser()); + + for (String groupName : userGroups) { + Map attrs = groupAttrMapping.get(groupName); + + attrs = attrs != null ? new HashMap<>(attrs) : new HashMap<>(); + + attrs.put(SCRIPT_FIELD__NAME, groupName); + + groupAttrs.put(groupName, attrs); + } + + Set requestTags = RangerAccessRequestUtil.getRequestTagsFromContext(getRequestContext()); + + if (CollectionUtils.isNotEmpty(requestTags)) { + RangerTagForEval currentTag = RangerAccessRequestUtil.getCurrentTagFromContext(getRequestContext()); + + tags = new HashMap(); + tag = (currentTag != null) ? toMap(currentTag) : Collections.emptyMap(); + + for (RangerTagForEval tag : requestTags) { + tags.put(tag.getType(), toMap(tag)); + } + + tagNames = getSorted(tags.keySet()); + } else { + tags = Collections.emptyMap(); + tagNames = Collections.emptySet(); + tag = Collections.emptyMap(); + } + + initDone = true; + } + } + + private Map getRequestContext() { + return accessRequest.getContext(); + } + + private Set getAllTags() { + Set ret = RangerAccessRequestUtil.getRequestTagsFromContext(accessRequest.getContext()); + if(ret == null) { + if (LOG.isDebugEnabled()) { + String resource = accessRequest.getResource().getAsString(); + + logDebug("RangerRequestScriptEvaluator.getAllTags() - No TAGS. No TAGS for the RangerAccessResource=" + resource); + } + } + + return ret; + } + + private static Map toMap(RangerTagForEval tag) { + Map ret = new HashMap<>(); + + if (tag.getAttributes() != null) { + ret.putAll(tag.getAttributes()); + } + + ret.put(SCRIPT_FIELD__TYPE, tag.getType()); + ret.put(SCRIPT_FIELD__MATCH_TYPE, tag.getMatchType()); + + return ret; + } + + private Collection getSorted(Collection values) { + final Collection ret; + + if (values == null) { + ret = Collections.emptyList(); + } else if (values.size() > 1) { + List lst = new ArrayList<>(values); + + Collections.sort(lst); + + ret = lst; + } else { + ret = values; + } + + return ret; + } + + private Map copyMap(Map obj) { return obj == null ? new HashMap<>() : new HashMap<>(obj); } + + private List getUgAttr(String attrName) { + List ret = new ArrayList<>(); + + for (String groupName : userGroups) { + Map attrs = groupAttrs.get(groupName); + Object val = attrs != null ? attrs.get(attrName) : null; + + if (val != null) { + ret.add(val); + } + } + + return ret; + } + + private List getTagAttr(String attrName) { + List ret = new ArrayList<>(); + + for (String tagName : tagNames) { + Map attrs = tags.get(tagName); + Object val = attrs != null ? attrs.get(attrName) : null; + + if (val != null) { + ret.add(val); + } + } + + return ret; + } + + private Collection getUserAttrNames() { + Collection ret = getSorted(userAttrs.keySet()); + + if (ret.contains(SCRIPT_FIELD__NAME)) { + ret.remove(SCRIPT_FIELD__NAME); + } + + return ret; + } + + private Collection getUgAttrNames() { + Set ret = new HashSet<>(); + + for (Map attrs : groupAttrs.values()) { + ret.addAll(attrs.keySet()); + } + + ret.remove(SCRIPT_FIELD__NAME); + + return getSorted(ret); + } + + private Collection getTagAttrNames() { + Set ret = new HashSet<>(); + + for (Map attrs : tags.values()) { + ret.addAll(attrs.keySet()); + } + + ret.remove(SCRIPT_FIELD__TYPE); + ret.remove(SCRIPT_FIELD__MATCH_TYPE); + + return getSorted(ret); + } + + private String toCsv(Collection values, Object[] args) { + StringBuilder sb = new StringBuilder(); + String separator = getSeparator(args); + + for (Object value : values) { + if (value == null) { + continue; + } + + if (sb.length() > 0) { + sb.append(separator); + } + + sb.append(value); + } + + if (sb.length() == 0) { + String defValue = getDefaultValue(args); + + if (defValue != null) { + sb.append(getDefaultValue(args)); + } + } + + return sb.toString(); + } + + private String toCsvQ(Collection values, Object[] args) { + StringBuilder sb = new StringBuilder(); + String openQuote = getOpenQuote(args); + String closeQuote = getCloseQuote(args, openQuote); + String separator = getSeparator(args); + + for (Object value : values) { + if (value == null) { + continue; + } + + if (sb.length() > 0) { + sb.append(separator); + } + + sb.append(openQuote).append(value).append(closeQuote); + } + + if (sb.length() == 0) { + String defValue = getDefaultValue(args); + + if (defValue != null) { + sb.append(openQuote).append(getDefaultValue(args)).append(closeQuote); + } + } + + return sb.toString(); + } + + private String getDefaultValue(Object[] args) { + Object ret = (args != null && args.length > 0) ? args[0] : null; + + return ret != null ? ret.toString() : null; + } + + private String getSeparator(Object[] args) { + Object ret = (args != null && args.length > 1) ? args[1] : STR_COMMA; + + return ret != null ? ret.toString() : ""; + } + + private String getOpenQuote(Object[] args) { + Object ret = (args != null && args.length > 2) ? args[2] : STR_QUOTE; + + return ret != null ? ret.toString() : ""; + } + + private String getCloseQuote(Object[] args, String openQuote) { + Object ret = (args != null && args.length > 3) ? args[3] : null; + + return ret != null ? ret.toString() : openQuote; + } + + private static String getJsonVarNamesPattern() { + List varNames = new ArrayList<>(); + + /* include only variables setup by JSON.parse() + * + varNames.add(SCRIPT_VAR_ctx); + varNames.add(SCRIPT_VAR_tag); + varNames.add(SCRIPT_VAR_tagAttr); + * + */ + varNames.add(SCRIPT_VAR__CTX); + varNames.add(SCRIPT_VAR_REQ); + varNames.add(SCRIPT_VAR_RES); + varNames.add(SCRIPT_VAR_TAG); + varNames.add(SCRIPT_VAR_TAGNAMES); + varNames.add(SCRIPT_VAR_TAGS); + varNames.add(SCRIPT_VAR_UGA); + varNames.add(SCRIPT_VAR_UG); + varNames.add(SCRIPT_VAR_UGNAMES); + varNames.add(SCRIPT_VAR_URNAMES); + varNames.add(SCRIPT_VAR_USER); + + return "\\b(" + StringUtils.join(varNames, '|') + ")\\b"; + } + + private static String getUserAttributesPattern() { + List varNames = new ArrayList<>(); + + varNames.add(SCRIPT_VAR_USER); + + varNames.add(SCRIPT_MACRO_GET_USER_ATTR); + varNames.add(SCRIPT_MACRO_GET_USER_ATTR_Q); + + varNames.add(SCRIPT_MACRO_GET_USER_ATTR_NAMES); + varNames.add(SCRIPT_MACRO_GET_USER_ATTR_NAMES_Q); + varNames.add(SCRIPT_MACRO_USER_ATTR_NAMES_CSV); + varNames.add(SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV); + + varNames.add(SCRIPT_MACRO_HAS_USER_ATTR); + + varNames.add("userAttr"); + varNames.add("userAttrQ"); + varNames.add("userAttrNames"); + varNames.add("userAttrNamesQ"); + varNames.add("userAttrNamesCsv"); + varNames.add("userAttrNamesCsvQ"); + varNames.add("hasUserAttr"); + + return "\\b(" + StringUtils.join(varNames, '|') + ")\\b"; + } + + private static String getGroupAttributesPattern() { + List varNames = new ArrayList<>(); + + varNames.add(SCRIPT_VAR_UG); + varNames.add(SCRIPT_VAR_UGA); + + varNames.add(SCRIPT_MACRO_GET_UG_ATTR); + varNames.add(SCRIPT_MACRO_GET_UG_ATTR_Q); + varNames.add(SCRIPT_MACRO_GET_UG_ATTR_CSV); + varNames.add(SCRIPT_MACRO_GET_UG_ATTR_Q_CSV); + + varNames.add(SCRIPT_MACRO_GET_UG_ATTR_NAMES); + varNames.add(SCRIPT_MACRO_GET_UG_ATTR_NAMES_Q); + varNames.add(SCRIPT_MACRO_UG_ATTR_NAMES_CSV); + varNames.add(SCRIPT_MACRO_UG_ATTR_NAMES_Q_CSV); + + varNames.add(SCRIPT_MACRO_HAS_UG_ATTR); + + varNames.add("ugAttr"); + varNames.add("ugAttrQ"); + varNames.add("ugAttrCsv"); + varNames.add("ugAttrCsvQ"); + + varNames.add("ugAttrNames"); + varNames.add("ugAttrNamesQ"); + varNames.add("ugAttrNamesCsv"); + varNames.add("ugAttrNamesCsvQ"); + + varNames.add("hasUgAttr"); + + return "\\b(" + StringUtils.join(varNames, '|') + ")\\b"; + } + + private static Map getMacrosMap() { + Map ret = new HashMap<>(); + + ret.put(SCRIPT_MACRO_GET_TAG_NAMES, "ctx.tagNames"); + ret.put(SCRIPT_MACRO_GET_TAG_NAMES_Q, "ctx.tagNamesQ"); + ret.put(SCRIPT_MACRO_GET_TAG_ATTR_NAMES, "ctx.tagAttrNames"); + ret.put(SCRIPT_MACRO_GET_TAG_ATTR_NAMES_Q, "ctx.tagAttrNamesQ"); + ret.put(SCRIPT_MACRO_GET_TAG_ATTR, "ctx.tagAttr"); + ret.put(SCRIPT_MACRO_GET_TAG_ATTR_Q, "ctx.tagAttrQ"); + ret.put(SCRIPT_MACRO_GET_UG_NAMES, "ctx.ugNames"); + ret.put(SCRIPT_MACRO_GET_UG_NAMES_Q, "ctx.ugNamesQ"); + ret.put(SCRIPT_MACRO_GET_UG_ATTR_NAMES, "ctx.ugAttrNames"); + ret.put(SCRIPT_MACRO_GET_UG_ATTR_NAMES_Q, "ctx.ugAttrNamesQ"); + ret.put(SCRIPT_MACRO_GET_UG_ATTR, "ctx.ugAttr"); + ret.put(SCRIPT_MACRO_GET_UG_ATTR_Q, "ctx.ugAttrQ"); + ret.put(SCRIPT_MACRO_GET_UR_NAMES, "ctx.urNames"); + ret.put(SCRIPT_MACRO_GET_UR_NAMES_Q, "ctx.urNamesQ"); + ret.put(SCRIPT_MACRO_GET_USER_ATTR_NAMES, "ctx.userAttrNames"); + ret.put(SCRIPT_MACRO_GET_USER_ATTR_NAMES_Q, "ctx.userAttrNamesQ"); + ret.put(SCRIPT_MACRO_GET_USER_ATTR, "ctx.userAttr"); + ret.put(SCRIPT_MACRO_GET_USER_ATTR_Q, "ctx.userAttrQ"); + + ret.put(SCRIPT_MACRO_GET_TAG_ATTR_CSV, "ctx.tagAttrCsv"); + ret.put(SCRIPT_MACRO_GET_TAG_ATTR_Q_CSV, "ctx.tagAttrCsvQ"); + ret.put(SCRIPT_MACRO_GET_UG_ATTR_CSV, "ctx.ugAttrCsv"); + ret.put(SCRIPT_MACRO_GET_UG_ATTR_Q_CSV, "ctx.ugAttrCsvQ"); + ret.put(SCRIPT_MACRO_TAG_ATTR_NAMES_CSV, "ctx.tagAttrNamesCsv()"); + ret.put(SCRIPT_MACRO_TAG_ATTR_NAMES_Q_CSV, "ctx.tagAttrNamesCsvQ()"); + ret.put(SCRIPT_MACRO_TAG_NAMES_CSV, "ctx.tagNamesCsv()"); + ret.put(SCRIPT_MACRO_TAG_NAMES_Q_CSV, "ctx.tagNamesCsvQ()"); + ret.put(SCRIPT_MACRO_UG_ATTR_NAMES_CSV, "ctx.ugAttrNamesCsv()"); + ret.put(SCRIPT_MACRO_UG_ATTR_NAMES_Q_CSV, "ctx.ugAttrNamesCsvQ()"); + ret.put(SCRIPT_MACRO_UG_NAMES_CSV, "ctx.ugNamesCsv()"); + ret.put(SCRIPT_MACRO_UG_NAMES_Q_CSV, "ctx.ugNamesCsvQ()"); + ret.put(SCRIPT_MACRO_UR_NAMES_CSV, "ctx.urNamesCsv()"); + ret.put(SCRIPT_MACRO_UR_NAMES_Q_CSV, "ctx.urNamesCsvQ()"); + ret.put(SCRIPT_MACRO_USER_ATTR_NAMES_CSV, "ctx.userAttrNamesCsv()"); + ret.put(SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV, "ctx.userAttrNamesCsvQ()"); + ret.put(SCRIPT_MACRO_HAS_TAG, "ctx.hasTag"); + ret.put(SCRIPT_MACRO_HAS_ANY_TAG, "ctx.hasAnyTag()"); + ret.put(SCRIPT_MACRO_HAS_NO_TAG, "!ctx.hasAnyTag()"); + ret.put(SCRIPT_MACRO_HAS_USER_ATTR, "ctx.hasUserAttr"); + ret.put(SCRIPT_MACRO_HAS_UG_ATTR, "ctx.hasUgAttr"); + ret.put(SCRIPT_MACRO_HAS_TAG_ATTR, "ctx.hasTagAttr"); + ret.put(SCRIPT_MACRO_IS_IN_GROUP, "ctx.isInGroup"); + ret.put(SCRIPT_MACRO_IS_IN_ROLE, "ctx.isInRole"); + ret.put(SCRIPT_MACRO_IS_IN_ANY_GROUP, "ctx.isInAnyGroup()"); + ret.put(SCRIPT_MACRO_IS_IN_ANY_ROLE, "ctx.isInAnyRole()"); + ret.put(SCRIPT_MACRO_IS_NOT_IN_ANY_GROUP, "!ctx.isInAnyGroup()"); + ret.put(SCRIPT_MACRO_IS_NOT_IN_ANY_ROLE, "!ctx.isInAnyRole()"); + ret.put(SCRIPT_MACRO_IS_ACCESS_TIME_AFTER, "ctx.isAccessTimeAfter"); + ret.put(SCRIPT_MACRO_IS_ACCESS_TIME_BEFORE, "ctx.isAccessTimeBefore"); + ret.put(SCRIPT_MACRO_IS_ACCESS_TIME_BETWEEN, "ctx.isAccessTimeBetween"); + + return ret; + } + + + public void logDebug(Object msg) { + LOG.debug(Objects.toString(msg)); + } + + public void logInfo(Object msg) { + LOG.info(Objects.toString(msg)); + } + + public void logWarn(Object msg) { + LOG.warn(Objects.toString(msg)); + } + + public void logError(Object msg) { + LOG.error(Objects.toString(msg)); + } + + public void logFatal(Object msg) { + LOG.error(Objects.toString(msg)); + } + + public static class UserGroupsAttributes { + private final Collection groupNames; + private final Map> groupAttributes; + + public UserGroupsAttributes(Collection groupNames, Map> groupAttributes) { + this.groupNames = groupNames; + this.groupAttributes = groupAttributes; + } + + /* + { + sVal: { + attr1: val1, + attr2: val2 + }, + mVal: { + attr1: [ val1, val1_2 ], + attr2: [ val2, val2_2 ] + } + } + */ + public Map> getAttributes() { + Map> ret = new HashMap<>(); + Map valueMap = new HashMap<>(); + Map> valuesMap = new HashMap<>(); + + ret.put("sVal", (Map) valueMap); + ret.put("mVal", (Map) valuesMap); + + if (groupNames != null && groupAttributes != null) { + for (String groupName : groupNames) { + Map attributes = groupAttributes.get(groupName); + + if (attributes != null) { + for (Map.Entry entry : attributes.entrySet()) { + String attrName = entry.getKey(); + String attrValue = entry.getValue(); + + if (!valueMap.containsKey(attrName)) { + valueMap.put(attrName, attrValue); + } + + List values = valuesMap.get(attrName); + + if (values == null) { + values = new ArrayList<>(); + + valuesMap.put(attrName, values); + } + + values.add(attrValue); + } + } + } + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceACLs.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceACLs.java index a0def54917..d149532568 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceACLs.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceACLs.java @@ -21,26 +21,35 @@ import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Set; import static org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.ACCESS_ALLOWED; +import static org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.ACCESS_CONDITIONAL; import static org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.ACCESS_DENIED; public class RangerResourceACLs { - final private Map> userACLs = new HashMap<>(); - final private Map> groupACLs = new HashMap<>(); - final private Map> roleACLs = new HashMap<>(); + final private Map> userACLs = new HashMap<>(); + final private Map> groupACLs = new HashMap<>(); + final private Map> roleACLs = new HashMap<>(); + final private List rowFilters = new ArrayList<>(); + final private List dataMasks = new ArrayList<>(); + final private Set datasets = new HashSet<>(); + final private Set projects = new HashSet<>(); + public RangerResourceACLs() { } @@ -54,6 +63,14 @@ public Map> getGroupACLs() { public Map> getRoleACLs() { return roleACLs; } + public List getRowFilters() { return rowFilters; } + + public List getDataMasks() { return dataMasks; } + + public Set getDatasets() { return datasets; } + + public Set getProjects() { return projects; } + public void finalizeAcls() { Map publicGroupAccessInfo = groupACLs.get(RangerPolicyEngine.GROUP_PUBLIC); if (publicGroupAccessInfo != null) { @@ -104,7 +121,7 @@ public void setUserAccessInfo(String userName, String accessType, Integer access accessResult = new AccessResult(access, policy); userAccessInfo.put(accessType, accessResult); - } else { + } else if (access != ACCESS_CONDITIONAL) { accessResult.setResult(access); accessResult.setPolicy(policy); } @@ -125,7 +142,7 @@ public void setGroupAccessInfo(String groupName, String accessType, Integer acce accessResult = new AccessResult(access, policy); groupAccessInfo.put(accessType, accessResult); - } else { + } else if (access != ACCESS_CONDITIONAL) { accessResult.setResult(access); accessResult.setPolicy(policy); } @@ -146,15 +163,39 @@ public void setRoleAccessInfo(String roleName, String accessType, Integer access accessResult = new AccessResult(access, policy); roleAccessInfo.put(accessType, accessResult); - } else { + } else if (access != ACCESS_CONDITIONAL) { accessResult.setResult(access); accessResult.setPolicy(policy); } } + @Override + public int hashCode() { + return Objects.hash(userACLs, groupACLs, roleACLs, rowFilters, dataMasks); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null || !getClass().equals(obj.getClass())) { + return false; + } else { + RangerResourceACLs other = (RangerResourceACLs) obj; + + return Objects.equals(userACLs, other.userACLs) && + Objects.equals(groupACLs, other.groupACLs) && + Objects.equals(roleACLs, other.roleACLs) && + Objects.equals(rowFilters, other.rowFilters) && + Objects.equals(dataMasks, other.dataMasks) && + Objects.equals(datasets, other.datasets) && + Objects.equals(projects, other.projects); + } + } + @Override public String toString() { - StringBuffer sb = new StringBuffer(); + StringBuilder sb = new StringBuilder(); sb.append("{"); @@ -164,7 +205,7 @@ public String toString() { sb.append("permissions={"); for (Map.Entry permission : entry.getValue().entrySet()) { sb.append("{Permission=").append(permission.getKey()).append(", value=").append(permission.getValue()).append("},"); - sb.append("{RangerPolicyID=").append(permission.getValue().getPolicy().getId()).append("},"); + sb.append("{RangerPolicyID=").append(permission.getValue().getPolicy() == null ? null : permission.getValue().getPolicy().getId()).append("},"); } sb.append("},"); } @@ -176,7 +217,7 @@ public String toString() { sb.append("permissions={"); for (Map.Entry permission : entry.getValue().entrySet()) { sb.append("{Permission=").append(permission.getKey()).append(", value=").append(permission.getValue()).append("}, "); - sb.append("{RangerPolicy ID=").append(permission.getValue().getPolicy().getId()).append("},"); + sb.append("{RangerPolicy ID=").append(permission.getValue().getPolicy() == null ? null : permission.getValue().getPolicy().getId()).append("},"); } sb.append("},"); } @@ -188,7 +229,7 @@ public String toString() { sb.append("permissions={"); for (Map.Entry permission : entry.getValue().entrySet()) { sb.append("{Permission=").append(permission.getKey()).append(", value=").append(permission.getValue()).append("}, "); - sb.append("{RangerPolicy ID=").append(permission.getValue().getPolicy().getId()).append("},"); + sb.append("{RangerPolicy ID=").append(permission.getValue().getPolicy() == null ? null : permission.getValue().getPolicy().getId()).append("},"); } sb.append("},"); } @@ -196,6 +237,32 @@ public String toString() { sb.append("}"); + sb.append(", rowFilters=["); + for (RowFilterResult rowFilter : rowFilters) { + rowFilter.toString(sb); + sb.append(" "); + } + sb.append("]"); + + sb.append(", dataMasks=["); + for (DataMaskResult dataMask : dataMasks) { + dataMask.toString(sb); + sb.append(" "); + } + sb.append("]"); + + sb.append(", datasets=["); + for (String dataset : datasets) { + sb.append(dataset).append(" "); + } + sb.append("]"); + + sb.append(", projects=["); + for (String project : projects) { + sb.append(project).append(" "); + } + sb.append("]"); + return sb.toString(); } @@ -217,10 +284,8 @@ private void finalizeAcls(Map> acls) { } @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class AccessResult { private int result; private boolean isFinal; @@ -286,4 +351,234 @@ public String toString() { return "CONDITIONAL_ALLOWED, final=" + isFinal; } } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class DataMaskResult implements Serializable { + private static final long serialVersionUID = 1L; + + private final Set users; + private final Set groups; + private final Set roles; + private final Set accessTypes; + private final RangerPolicyItemDataMaskInfo maskInfo; + private boolean isConditional = false; + + public DataMaskResult(Set users, Set groups, Set roles, Set accessTypes, RangerPolicyItemDataMaskInfo maskInfo) { + this.users = users; + this.groups = groups; + this.roles = roles; + this.accessTypes = accessTypes; + this.maskInfo = maskInfo; + } + + public DataMaskResult(DataMaskResult that) { + this.users = that.users; + this.groups = that.groups; + this.roles = that.roles; + this.accessTypes = that.accessTypes; + this.maskInfo = that.maskInfo; + this.isConditional = that.isConditional; + } + + public Set getUsers() { return users; } + + public Set getGroups() { return groups; } + + public Set getRoles() { return roles; } + + public Set getAccessTypes() { return accessTypes; } + + public RangerPolicyItemDataMaskInfo getMaskInfo() { return maskInfo; } + + public boolean getIsConditional() { return isConditional; } + + public void setIsConditional(boolean isConditional) { this.isConditional = isConditional; } + + @Override + public int hashCode() { return Objects.hash(users, groups, roles, accessTypes, maskInfo, isConditional); } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other == null || getClass() != other.getClass()) { + return false; + } else { + DataMaskResult that = (DataMaskResult) other; + + return Objects.equals(users, that.users) && + Objects.equals(groups, that.groups) && + Objects.equals(roles, that.roles) && + Objects.equals(accessTypes, that.accessTypes) && + Objects.equals(maskInfo, that.maskInfo) && + isConditional == that.isConditional; + } + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("{"); + + if (users != null && !users.isEmpty()) { + sb.append("users:["); + for (String user : users) { + sb.append(user).append(' '); + } + sb.append("] "); + } + + if (groups != null && !groups.isEmpty()) { + sb.append("groups:["); + for (String group : groups) { + sb.append(group).append(' '); + } + sb.append("] "); + } + + if (roles != null && !roles.isEmpty()) { + sb.append("roles:["); + for (String role : roles) { + sb.append(role).append(' '); + } + sb.append("] "); + } + + if (accessTypes != null && !accessTypes.isEmpty()) { + sb.append("accessTypes:["); + for (String accessType : accessTypes) { + sb.append(accessType).append(' '); + } + sb.append("] "); + } + + sb.append("maskInfo="); + maskInfo.toString(sb); + sb.append(" isConditional=").append(isConditional); + + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RowFilterResult implements Serializable { + private static final long serialVersionUID = 1L; + + private final Set users; + private final Set groups; + private final Set roles; + private final Set accessTypes; + private final RangerPolicyItemRowFilterInfo filterInfo; + private boolean isConditional = false; + + public RowFilterResult(Set users, Set groups, Set roles, Set accessTypes, RangerPolicyItemRowFilterInfo filterInfo) { + this.users = users; + this.groups = groups; + this.roles = roles; + this.accessTypes = accessTypes; + this.filterInfo = filterInfo; + } + + public RowFilterResult(RowFilterResult that) { + this.users = that.users; + this.groups = that.groups; + this.roles = that.roles; + this.accessTypes = that.accessTypes; + this.filterInfo = that.filterInfo; + this.isConditional = that.isConditional; + } + + public Set getUsers() { return users; } + + public Set getGroups() { return groups; } + + public Set getRoles() { return roles; } + + public Set getAccessTypes() { return accessTypes; } + + public RangerPolicyItemRowFilterInfo getFilterInfo() { return filterInfo; } + + public boolean getIsConditional() { return isConditional; } + + public void setIsConditional(boolean isConditional) { this.isConditional = isConditional; } + + @Override + public int hashCode() { return Objects.hash(users, groups, roles, accessTypes, filterInfo, isConditional); } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else if (other == null || getClass() != other.getClass()) { + return false; + } else { + RowFilterResult that = (RowFilterResult) other; + + return Objects.equals(users, that.users) && + Objects.equals(groups, that.groups) && + Objects.equals(roles, that.roles) && + Objects.equals(accessTypes, that.accessTypes) && + Objects.equals(filterInfo, that.filterInfo) && + isConditional == that.isConditional; + } + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("{"); + + if (users != null && !users.isEmpty()) { + sb.append("users:["); + for (String user : users) { + sb.append(user).append(' '); + } + sb.append("] "); + } + + if (groups != null && !groups.isEmpty()) { + sb.append("groups:["); + for (String group : groups) { + sb.append(group).append(' '); + } + sb.append("] "); + } + + if (roles != null && !roles.isEmpty()) { + sb.append("roles:["); + for (String role : roles) { + sb.append(role).append(' '); + } + sb.append("] "); + } + + if (accessTypes != null && !accessTypes.isEmpty()) { + sb.append("accessTypes:["); + for (String accessType : accessTypes) { + sb.append(accessType).append(' '); + } + sb.append("] "); + } + + sb.append("filterInfo="); + filterInfo.toString(sb); + sb.append(" isConditional=").append(isConditional); + + sb.append("}"); + + return sb; + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java index 104b38d50a..4f6860486e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerResourceTrie.java @@ -21,44 +21,49 @@ import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.Predicate; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; -import org.apache.ranger.plugin.model.RangerServiceDef; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvaluator; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerRequestExprResolver; +import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; -public class RangerResourceTrie { - private static final Log LOG = LogFactory.getLog(RangerResourceTrie.class); - private static final Log TRACE_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.trace"); - private static final Log PERF_TRIE_INIT_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.init"); - private static final Log PERF_TRIE_OP_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.op"); +import static org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher.DEFAULT_PATH_SEPARATOR_CHAR; +import static org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher.OPTION_PATH_SEPARATOR; - private static final String DEFAULT_WILDCARD_CHARS = "*?"; - private static final String TRIE_BUILDER_THREAD_COUNT = "ranger.policyengine.trie.builder.thread.count"; +public class RangerResourceTrie { + private static final Logger LOG = LoggerFactory.getLogger(RangerResourceTrie.class); + private static final Logger TRACE_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.trace"); + private static final Logger PERF_TRIE_INIT_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.init"); + private static final Logger PERF_TRIE_OP_LOG = RangerPerfTracer.getPerfLogger("resourcetrie.op"); - private final RangerServiceDef.RangerResourceDef resourceDef; - private final boolean optIgnoreCase; - private final boolean optWildcard; - private final String wildcardChars; - private final TrieNode root; - private final boolean isOptimizedForRetrieval; + private static final String DEFAULT_WILDCARD_CHARS = "*?"; + private static final String TRIE_BUILDER_THREAD_COUNT = "ranger.policyengine.trie.builder.thread.count"; - public RangerResourceTrie(RangerServiceDef.RangerResourceDef resourceDef, List evaluators) { + private final RangerResourceDef resourceDef; + private final boolean optIgnoreCase; + private final boolean optWildcard; + private final String wildcardChars; + private final boolean isOptimizedForRetrieval; + private final boolean isOptimizedForSpace; + private final Character separatorChar; + private Set inheritedEvaluators; + private final TrieNode root; + + public RangerResourceTrie(RangerResourceDef resourceDef, List evaluators) { this(resourceDef, evaluators, true, null); } @@ -69,28 +74,43 @@ public RangerResourceTrie(RangerResourceTrie other) { perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_INIT_LOG, "RangerResourceTrie.copyTrie(name=" + other.resourceDef.getName() + ")"); } - this.resourceDef = other.resourceDef; - this.optIgnoreCase = other.optIgnoreCase; - this.optWildcard = other.optWildcard; - this.wildcardChars = other.wildcardChars; - this.isOptimizedForRetrieval = false; - this.root = copyTrieSubtree(other.root, null); + this.resourceDef = other.resourceDef; + this.optIgnoreCase = other.optIgnoreCase; + this.optWildcard = other.optWildcard; + this.wildcardChars = other.wildcardChars; + this.isOptimizedForSpace = other.isOptimizedForSpace; + this.isOptimizedForRetrieval = other.isOptimizedForRetrieval; + this.separatorChar = other.separatorChar; + this.inheritedEvaluators = other.inheritedEvaluators != null ? new HashSet<>(other.inheritedEvaluators) : null; + this.root = copyTrieSubtree(other.root, null); + + wrapUpUpdate(); + + if (!isOptimizedForRetrieval) { + if (LOG.isDebugEnabled()) { + LOG.debug("Trie for " + this.resourceDef.getName() + " is not optimized for retrieval. Resetting isSetup flag by calling undoSetup() on the root"); + } + root.undoSetup(); + } RangerPerfTracer.logAlways(perf); if (PERF_TRIE_INIT_LOG.isDebugEnabled()) { PERF_TRIE_INIT_LOG.debug(toString()); } + if (TRACE_LOG.isTraceEnabled()) { - StringBuilder sb = new StringBuilder(); - root.toString("", sb); - TRACE_LOG.trace("Trie Dump from RangerResourceTrie.copyTrie(name=" + other.resourceDef.getName() + "):\n{" + sb.toString() + "}"); + TRACE_LOG.trace("Trie Dump from RangerResourceTrie.copyTrie(name=" + other.resourceDef.getName() + "):\n[" + dumpTrie() + "]"); } } - RangerResourceTrie(RangerServiceDef.RangerResourceDef resourceDef, List evaluators, boolean isOptimizedForRetrieval, RangerPluginContext pluginContext) { + public RangerResourceTrie(RangerResourceDef resourceDef, List evaluators, boolean isOptimizedForRetrieval, RangerPluginContext pluginContext) { + this(resourceDef, evaluators, isOptimizedForRetrieval, false, pluginContext); + } + + public RangerResourceTrie(RangerResourceDef resourceDef, List evaluators, boolean isOptimizedForRetrieval, boolean isOptimizedForSpace, RangerPluginContext pluginContext) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerResourceTrie(" + resourceDef.getName() + ", evaluatorCount=" + evaluators.size() + ", isOptimizedForRetrieval=" + isOptimizedForRetrieval + ")"); + LOG.debug("==> RangerResourceTrie(" + resourceDef.getName() + ", evaluatorCount=" + evaluators.size() + ", isOptimizedForRetrieval=" + isOptimizedForRetrieval + ", isOptimizedForSpace=" + isOptimizedForSpace + ")"); } RangerPerfTracer perf = null; @@ -110,11 +130,10 @@ public RangerResourceTrie(RangerResourceTrie other) { TRACE_LOG.trace("builderThreadCount is set to [" + builderThreadCount + "]"); } - Map matcherOptions = resourceDef.getMatcherOptions(); - - boolean optReplaceTokens = RangerAbstractResourceMatcher.getOptionReplaceTokens(matcherOptions); - - String tokenReplaceSpecialChars = ""; + Map matcherOptions = resourceDef.getMatcherOptions(); + boolean optReplaceTokens = RangerAbstractResourceMatcher.getOptionReplaceTokens(matcherOptions); + boolean optReplaceReqExpressions = RangerAbstractResourceMatcher.getOptionReplaceReqExpressions(matcherOptions); + String tokenReplaceSpecialChars = ""; if(optReplaceTokens) { char delimiterStart = RangerAbstractResourceMatcher.getOptionDelimiterStart(matcherOptions); @@ -126,15 +145,21 @@ public RangerResourceTrie(RangerResourceTrie other) { tokenReplaceSpecialChars += delimiterEscape; } - this.resourceDef = resourceDef; - this.optIgnoreCase = RangerAbstractResourceMatcher.getOptionIgnoreCase(matcherOptions); - this.optWildcard = RangerAbstractResourceMatcher.getOptionWildCard(matcherOptions); - this.wildcardChars = optWildcard ? DEFAULT_WILDCARD_CHARS + tokenReplaceSpecialChars : "" + tokenReplaceSpecialChars; - this.isOptimizedForRetrieval = isOptimizedForRetrieval; + if (optReplaceReqExpressions) { + tokenReplaceSpecialChars += RangerRequestExprResolver.EXPRESSION_START.charAt(0); + } + + this.resourceDef = resourceDef; + this.optIgnoreCase = RangerAbstractResourceMatcher.getOptionIgnoreCase(matcherOptions); + this.optWildcard = RangerAbstractResourceMatcher.getOptionWildCard(matcherOptions); + this.wildcardChars = optWildcard ? DEFAULT_WILDCARD_CHARS + tokenReplaceSpecialChars : "" + tokenReplaceSpecialChars; + this.isOptimizedForSpace = isOptimizedForSpace; + this.isOptimizedForRetrieval = !isOptimizedForSpace && isOptimizedForRetrieval; // isOptimizedForSpace takes precedence + this.separatorChar = ServiceDefUtil.getCharOption(matcherOptions, OPTION_PATH_SEPARATOR, DEFAULT_PATH_SEPARATOR_CHAR); - TrieNode tmpRoot = buildTrie(resourceDef, evaluators, builderThreadCount); + final TrieNode tmpRoot = buildTrie(resourceDef, evaluators, builderThreadCount); - if (builderThreadCount > 1 && tmpRoot == null) { // if multi-threaded trie-creation failed, build using a single thread + if (builderThreadCount > 1 && tmpRoot == null) { // if multithreaded trie-creation failed, build using a single thread this.root = buildTrie(resourceDef, evaluators, 1); } else { this.root = tmpRoot; @@ -149,35 +174,55 @@ public RangerResourceTrie(RangerResourceTrie other) { } if (TRACE_LOG.isTraceEnabled()) { - StringBuilder sb = new StringBuilder(); - root.toString("", sb); - TRACE_LOG.trace("Trie Dump from RangerResourceTrie.init(name=" + resourceDef.getName() + "):\n{" + sb.toString() + "}"); + TRACE_LOG.trace("Trie Dump from RangerResourceTrie.init(name=" + resourceDef.getName() + "):\n[" + dumpTrie() + "]"); } if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerResourceTrie(" + resourceDef.getName() + ", evaluatorCount=" + evaluators.size() + ", isOptimizedForRetrieval=" + isOptimizedForRetrieval + "): " + toString()); + LOG.debug("<== RangerResourceTrie(" + resourceDef.getName() + ", evaluatorCount=" + evaluators.size() + ", isOptimizedForRetrieval=" + this.isOptimizedForRetrieval + ", isOptimizedForSpace=" + this.isOptimizedForSpace + "): " + this); } } public Set getEvaluatorsForResource(Object resource) { - if (resource instanceof String) { - return getEvaluatorsForResource((String) resource); - } else if (resource instanceof Collection) { - if (CollectionUtils.isEmpty((Collection) resource)) { // treat empty collection same as empty-string - return getEvaluatorsForResource(""); - } else { - @SuppressWarnings("unchecked") - Collection resources = (Collection) resource; + return getEvaluatorsForResource(resource, ResourceElementMatchingScope.SELF); + } - return getEvaluatorsForResources(resources); - } - } + public Set getEvaluatorsForResource(Object resource, ResourceElementMatchingScope scope) { + return getEvaluatorsForResource(resource, scope, (Predicate) null); + } + + public Set getEvaluatorsForResource(Object resource, ResourceElementMatchingScope scope, Predicate predicate) { + EvalCollector ret = new EvalCollector<>(predicate); + + traverse(resource, scope, ret); - return null; + return ret.getResult(); } - public void add(RangerPolicyResource resource, T evaluator) { + public Set getEvaluatorsForResource(Object resource, ResourceElementMatchingScope scope, Set filter) { + return getEvaluatorsForResource(resource, scope, filter, null); + } + + public Set getEvaluatorsForResource(Object resource, ResourceElementMatchingScope scope, Set filter, Predicate predicate) { + EvalSubsetCollector ret = new EvalSubsetCollector<>(filter, predicate); + + traverse(resource, scope, ret); + + return ret.getResult(); + } + + public int getEvaluatorsCountForResource(Object resource, ResourceElementMatchingScope scope) { + return getEvaluatorsCountForResource(resource, scope, null); + } + + public int getEvaluatorsCountForResource(Object resource, ResourceElementMatchingScope scope, Predicate predicate) { + EvalCountCollector ret = new EvalCountCollector<>(predicate); + + traverse(resource, scope, ret); + + return ret.getResult(); + } + public void add(RangerPolicyResource resource, T evaluator) { RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_TRIE_INIT_LOG)) { @@ -186,11 +231,11 @@ public void add(RangerPolicyResource resource, T evaluator) { if (resource == null) { if (evaluator.isAncestorOf(resourceDef)) { - root.addWildcardEvaluator(evaluator); + addInheritedEvaluator(evaluator); } } else { if (resource.getIsExcludes()) { - root.addWildcardEvaluator(evaluator); + addInheritedEvaluator(evaluator); } else { if (CollectionUtils.isNotEmpty(resource.getValues())) { for (String value : resource.getValues()) { @@ -201,26 +246,26 @@ public void add(RangerPolicyResource resource, T evaluator) { } RangerPerfTracer.logAlways(perf); + if (TRACE_LOG.isTraceEnabled()) { - StringBuilder sb = new StringBuilder(); - root.toString("", sb); - TRACE_LOG.trace("Trie Dump from RangerResourceTrie.add(name=" + resource + "):\n{" + sb.toString() + "}"); + TRACE_LOG.trace("Trie Dump from RangerResourceTrie.add(name=" + resource + "):\n[" + dumpTrie() + "]"); } } public void delete(RangerPolicyResource resource, T evaluator) { - RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_TRIE_INIT_LOG)) { perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_INIT_LOG, "RangerResourceTrie.delete(name=" + resource + ")"); } - boolean isRemoved = false; - if (resource.getIsExcludes()) { - isRemoved = root.removeWildcardEvaluator(evaluator); - } - if (!isRemoved) { + if (resource == null) { + if (evaluator.isAncestorOf(resourceDef)) { + removeInheritedEvaluator(evaluator); + } + } else if (resource.getIsExcludes()) { + removeInheritedEvaluator(evaluator); + } else { for (String value : resource.getValues()) { TrieNode node = getNodeForResource(value); if (node != null) { @@ -230,36 +275,65 @@ public void delete(RangerPolicyResource resource, T evaluator) { } RangerPerfTracer.logAlways(perf); + if (TRACE_LOG.isTraceEnabled()) { - StringBuilder sb = new StringBuilder(); - root.toString("", sb); - TRACE_LOG.trace("Trie Dump from RangerResourceTrie.delete(name=" + resource + "):\n{" + sb.toString() + "}"); + TRACE_LOG.trace("Trie Dump from RangerResourceTrie.delete(name=" + resource + "):\n[" + dumpTrie()+ "]"); } } public void wrapUpUpdate() { if (root != null) { root.wrapUpUpdate(); + if (TRACE_LOG.isTraceEnabled()) { + TRACE_LOG.trace("Trie Dump from RangerResourceTrie.wrapUpUpdate(name=" + resourceDef.getName() + "):\n[" + dumpTrie() + "]"); + } + } + } + + public StringBuilder dumpTrie() { + StringBuilder sb = new StringBuilder(); + if (root != null) { + root.toString("", sb); } + return sb; } TrieNode getRoot() { return root; } + private void addInheritedEvaluator(T evaluator) { + if (inheritedEvaluators == null) { + inheritedEvaluators = new HashSet<>(); + } + + inheritedEvaluators.add(evaluator); + } + + private void removeInheritedEvaluator(T evaluator) { + if (CollectionUtils.isNotEmpty(inheritedEvaluators) && inheritedEvaluators.contains(evaluator)) { + inheritedEvaluators.remove(evaluator); + if (CollectionUtils.isEmpty(inheritedEvaluators)) { + inheritedEvaluators = null; + } + } + } + private TrieNode copyTrieSubtree(final TrieNode source, final TrieNode parent) { if (TRACE_LOG.isTraceEnabled()) { StringBuilder sb = new StringBuilder(); source.toString(sb); TRACE_LOG.trace("==> copyTrieSubtree(" + sb + ")"); } + TrieNode dest = new TrieNode<>(source.str); + if (parent != null) { parent.addChild(dest); } synchronized (source.children) { - dest.isSetup = source.isSetup; + dest.isSetup = source.isSetup; dest.isSharingParentWildcardEvaluators = source.isSharingParentWildcardEvaluators; if (source.isSharingParentWildcardEvaluators) { @@ -275,6 +349,7 @@ private TrieNode copyTrieSubtree(final TrieNode source, final TrieNode dest.wildcardEvaluators = null; } } + if (source.evaluators != null) { if (source.evaluators == source.wildcardEvaluators) { dest.evaluators = dest.wildcardEvaluators; @@ -287,6 +362,7 @@ private TrieNode copyTrieSubtree(final TrieNode source, final TrieNode } Map> children = source.getChildren(); + for (Map.Entry> entry : children.entrySet()) { copyTrieSubtree(entry.getValue(), dest); } @@ -298,10 +374,11 @@ private TrieNode copyTrieSubtree(final TrieNode source, final TrieNode TRACE_LOG.trace("<== copyTrieSubtree(" + sourceAsString + ") : " + destAsString); } + return dest; } - private TrieNode buildTrie(RangerServiceDef.RangerResourceDef resourceDef, List evaluators, int builderThreadCount) { + private TrieNode buildTrie(RangerResourceDef resourceDef, List evaluators, int builderThreadCount) { if(LOG.isDebugEnabled()) { LOG.debug("==> buildTrie(" + resourceDef.getName() + ", evaluatorCount=" + evaluators.size() + ", isMultiThreaded=" + (builderThreadCount > 1) + ")"); } @@ -312,74 +389,95 @@ private TrieNode buildTrie(RangerServiceDef.RangerResourceDef resourceDef, Li perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_INIT_LOG, "RangerResourceTrie.init(resourceDef=" + resourceDef.getName() + ")"); } - TrieNode ret = new TrieNode<>(null); + TrieNode ret = new TrieNode<>(null); final boolean isMultiThreaded = builderThreadCount > 1; final List builderThreads; final Map builderThreadMap; - final String resourceName = resourceDef.getName(); + final String resourceName = resourceDef.getName(); int lastUsedThreadIndex = 0; if (isMultiThreaded) { builderThreads = new ArrayList<>(); + for (int i = 0; i < builderThreadCount; i++) { ResourceTrieBuilderThread t = new ResourceTrieBuilderThread(); + t.setDaemon(true); builderThreads.add(t); t.start(); } + builderThreadMap = new HashMap<>(); } else { - builderThreads = null; + builderThreads = null; builderThreadMap = null; } - for (T evaluator : evaluators) { - Map policyResources = evaluator.getPolicyResource(); - RangerPolicyResource policyResource = policyResources != null ? policyResources.get(resourceName) : null; + for (E evaluator : evaluators) { + final List resourceEvaluators; - if (policyResource == null) { - if (evaluator.isAncestorOf(resourceDef)) { - ret.addWildcardEvaluator(evaluator); - } + if (evaluator instanceof RangerPolicyEvaluator) { + resourceEvaluators = (List) ((RangerPolicyEvaluator) evaluator).getResourceEvaluators(); + } else if (evaluator instanceof RangerResourceEvaluator) { + resourceEvaluators = Collections.singletonList((T) evaluator); + } else { + LOG.error("buildTrie(): unexpected evaluator class " + evaluator.getClass().getCanonicalName()); - continue; + resourceEvaluators = Collections.emptyList(); } - if (policyResource.getIsExcludes()) { - ret.addWildcardEvaluator(evaluator); - } else { - RangerResourceMatcher resourceMatcher = evaluator.getResourceMatcher(resourceName); + for (T resourceEvaluator : resourceEvaluators) { + Map policyResources = resourceEvaluator.getPolicyResource(); + RangerPolicyResource policyResource = policyResources != null ? policyResources.get(resourceName) : null; + + if (policyResource == null) { + if (resourceEvaluator.isAncestorOf(resourceDef)) { + addInheritedEvaluator(resourceEvaluator); + } - if (resourceMatcher != null && (resourceMatcher.isMatchAny())) { - ret.addWildcardEvaluator(evaluator); + continue; + } + + if (policyResource.getIsExcludes()) { + addInheritedEvaluator(resourceEvaluator); } else { - if (CollectionUtils.isNotEmpty(policyResource.getValues())) { - for (String resource : policyResource.getValues()) { - if (!isMultiThreaded) { - insert(ret, resource, policyResource.getIsRecursive(), evaluator); - } else { - try { - lastUsedThreadIndex = insert(ret, resource, policyResource.getIsRecursive(), evaluator, builderThreadMap, builderThreads, lastUsedThreadIndex); - } catch (InterruptedException ex) { - LOG.error("Failed to dispatch " + resource + " to " + builderThreads.get(lastUsedThreadIndex)); - LOG.error("Failing and retrying with one thread"); - - ret = null; - - break; + RangerResourceMatcher resourceMatcher = resourceEvaluator.getResourceMatcher(resourceName); + + if (resourceMatcher != null && (resourceMatcher.isMatchAny())) { + ret.addWildcardEvaluator(resourceEvaluator); + } else { + if (CollectionUtils.isNotEmpty(policyResource.getValues())) { + for (String resource : policyResource.getValues()) { + if (!isMultiThreaded) { + insert(ret, resource, policyResource.getIsRecursive(), resourceEvaluator); + } else { + try { + lastUsedThreadIndex = insert(ret, resource, policyResource.getIsRecursive(), resourceEvaluator, builderThreadMap, builderThreads, lastUsedThreadIndex); + } catch (InterruptedException ex) { + LOG.error("Failed to dispatch " + resource + " to " + builderThreads.get(lastUsedThreadIndex)); + LOG.error("Failing and retrying with one thread"); + + ret = null; + + break; + } } } - } - if (ret == null) { - break; + + if (ret == null) { + break; + } } } } } + if (ret == null) { + break; + } } + if (ret != null) { if (isMultiThreaded) { - for (ResourceTrieBuilderThread t : builderThreads) { try { // Send termination signal to each thread @@ -396,6 +494,7 @@ private TrieNode buildTrie(RangerServiceDef.RangerResourceDef resourceDef, Li break; } } + cleanUpThreads(builderThreads); } } @@ -428,6 +527,7 @@ private TrieData getTrieData() { TrieData ret = new TrieData(); root.populateTrieData(ret); + ret.maxDepth = getMaxDepth(); return ret; @@ -467,7 +567,6 @@ private int insert(TrieNode currentRoot, String resource, boolean isRecursive } private void insert(TrieNode currentRoot, String resource, boolean isRecursive, T evaluator) { - TrieNode curr = currentRoot; final String prefix = getNonWildcardPrefix(resource); final boolean isWildcard = prefix.length() != resource.length(); @@ -484,8 +583,7 @@ private void insert(TrieNode currentRoot, String resource, boolean isRecursiv } - private String getNonWildcardPrefix(String str) { - + private int getNonWildcardPrefixLength(String str) { int minIndex = str.length(); for (int i = 0; i < wildcardChars.length(); i++) { @@ -496,31 +594,65 @@ private String getNonWildcardPrefix(String str) { } } - return str.substring(0, minIndex); + return minIndex; } - private Set getEvaluatorsForResource(String resource) { + private String getNonWildcardPrefix(String str) { + int prefixLen = getNonWildcardPrefixLength(str); + + return (prefixLen < str.length()) ? str.substring(0, prefixLen) : str; + + } + + public void traverse(Object resource, ResourceElementMatchingScope scope, TraverseMatchHandler handler) { + if (resource instanceof String) { + traverse((String) resource, scope, handler); + } else if (resource instanceof Collection) { + Collection resources = (Collection) resource; + + if (CollectionUtils.isEmpty(resources)) { // treat empty collection same as empty-string + traverse("", scope, handler); + } else { + traverse(resources, scope, handler); + } + } + } + + public void traverse(Collection resources, ResourceElementMatchingScope scope, TraverseMatchHandler handler) { + for (String resource : resources) { + traverse(resource, scope, handler); + } + } + + public void traverse(String resource, ResourceElementMatchingScope scope, TraverseMatchHandler handler) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerResourceTrie.getEvaluatorsForResource(" + resource + ")"); + LOG.debug("==> RangerResourceTrie.traverse(" + resource + ", " + scope + ")"); } RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_TRIE_OP_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_OP_LOG, "RangerResourceTrie.getEvaluatorsForResource(resource=" + resource + ")"); + perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_OP_LOG, "RangerResourceTrie.traverse(resource=" + resource + ")"); } TrieNode curr = root; TrieNode parent = null; + TrieNode child = null; final int len = resource.length(); int i = 0; + handler.process(inheritedEvaluators); + while (i < len) { - if (!isOptimizedForRetrieval) { + if (!isOptimizedForSpace) { curr.setupIfNeeded(parent); + } else { + if (handler.process(curr.getWildcardEvaluators())) { + break; + } } - final TrieNode child = curr.getChild(getLookupChar(resource, i)); + child = curr.getChild(getLookupChar(resource, i)); if (child == null) { break; @@ -533,23 +665,60 @@ private Set getEvaluatorsForResource(String resource) { } parent = curr; - curr = child; - i += childStr.length(); + curr = child; + i += childStr.length(); } - if (!isOptimizedForRetrieval) { + if (!isOptimizedForSpace) { curr.setupIfNeeded(parent); + } else { + handler.process(curr.getWildcardEvaluators()); + } + + boolean isSelfMatch = (i == len); + + if (!isOptimizedForSpace) { + handler.process(isSelfMatch ? curr.getEvaluators() : curr.getWildcardEvaluators()); + } else { + if (isSelfMatch) { + handler.process(curr.getEvaluators()); + } } - Set ret = i == len ? curr.getEvaluators() : curr.getWildcardEvaluators(); + if (scope == ResourceElementMatchingScope.SELF_OR_CHILD) { + final boolean resourceEndsWithSep = resource.charAt(resource.length() - 1) == separatorChar; + + if (isSelfMatch) { // resource == path(curr) + if (resourceEndsWithSep) { // ex: resource=/tmp/ + curr.getChildren().values().forEach(c -> c.collectChildEvaluators(separatorChar, 0, handler)); + } else { // ex: resource=/tmp + curr = curr.getChild(separatorChar); + + if (curr != null) { + curr.collectChildEvaluators(separatorChar, 1, handler); + } + } + } else if (child != null) { // resource != path(child) ex: (resource=/tmp, path(child)=/tmp/test.txt or path(child)=/tmpdir) + int remainingLen = len - i; + boolean isPrefixMatch = child.getStr().regionMatches(optIgnoreCase, 0, resource, i, remainingLen); + + if (isPrefixMatch) { + if (resourceEndsWithSep) { // ex: resource=/tmp/ + child.collectChildEvaluators(separatorChar, remainingLen, handler); + } else if (child.getStr().charAt(remainingLen) == separatorChar) { // ex: resource=/tmp + child.collectChildEvaluators(separatorChar, remainingLen + 1, handler); + } + } + } + } else if (scope == ResourceElementMatchingScope.SELF_OR_PREFIX) { + curr.collectChildEvaluators(resource, i, handler); + } RangerPerfTracer.logAlways(perf); if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerResourceTrie.getEvaluatorsForResource(" + resource + "): evaluatorCount=" + (ret == null ? 0 : ret.size())); + LOG.debug("<== RangerResourceTrie.traverse(" + resource + ", " + scope + "): evaluators=" + handler); } - - return ret; } private TrieNode getNodeForResource(String resource) { @@ -563,12 +732,11 @@ private TrieNode getNodeForResource(String resource) { perf = RangerPerfTracer.getPerfTracer(PERF_TRIE_OP_LOG, "RangerResourceTrie.getNodeForResource(resource=" + resource + ")"); } - TrieNode curr = root; - final int len = resource.length(); - int i = 0; + TrieNode curr = root; + final int len = getNonWildcardPrefixLength(resource); + int i = 0; while (i < len) { - final TrieNode child = curr.getChild(getLookupChar(resource, i)); if (child == null) { @@ -582,9 +750,11 @@ private TrieNode getNodeForResource(String resource) { } curr = child; - i += childStr.length(); + i += childStr.length(); } + curr = (i == len) ? curr : null; + RangerPerfTracer.logAlways(perf); if(LOG.isDebugEnabled()) { @@ -594,53 +764,6 @@ private TrieNode getNodeForResource(String resource) { return curr; } - private Set getEvaluatorsForResources(Collection resources) { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerResourceTrie.getEvaluatorsForResources(" + resources + ")"); - } - - Set ret = null; - Map evaluatorsMap = null; - - for (String resource : resources) { - Set resourceEvaluators = getEvaluatorsForResource(resource); - - if (CollectionUtils.isEmpty(resourceEvaluators)) { - continue; - } - - if (evaluatorsMap == null) { - if (ret == null) { // first resource: don't create map yet - ret = resourceEvaluators; - } else if (ret != resourceEvaluators) { // if evaluator list is same as earlier resources, retain the list, else create a map - evaluatorsMap = new HashMap<>(); - - for (T evaluator : ret) { - evaluatorsMap.put(evaluator.getId(), evaluator); - } - - ret = null; - } - } - - if (evaluatorsMap != null) { - for (T evaluator : resourceEvaluators) { - evaluatorsMap.put(evaluator.getId(), evaluator); - } - } - } - - if (ret == null && evaluatorsMap != null) { - ret = new HashSet<>(evaluatorsMap.values()); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerResourceTrie.getEvaluatorsForResources(" + resources + "): evaluatorCount=" + (ret == null ? 0 : ret.size())); - } - - return ret; - } - @Override public String toString() { StringBuilder sb = new StringBuilder(); @@ -671,10 +794,11 @@ class WorkItem { final T evaluator; WorkItem(String resourceName, boolean isRecursive, T evaluator) { - this.resourceName = resourceName; - this.isRecursive = isRecursive; - this.evaluator = evaluator; + this.resourceName = resourceName; + this.isRecursive = isRecursive; + this.evaluator = evaluator; } + @Override public String toString() { return @@ -684,8 +808,8 @@ public String toString() { } } - private final TrieNode thisRoot = new TrieNode<>(null); - private final BlockingQueue workQueue = new LinkedBlockingQueue<>(); + private final TrieNode thisRoot = new TrieNode<>(null); + private final BlockingQueue workQueue = new LinkedBlockingQueue<>(); ResourceTrieBuilderThread() { } @@ -719,6 +843,7 @@ public void run() { if (LOG.isDebugEnabled()) { LOG.debug("Received termination signal. " + workItem); } + break; } } @@ -744,8 +869,8 @@ class TrieNode { private String str; private TrieNode parent; private final Map> children = new HashMap<>(); - private Set evaluators; - private Set wildcardEvaluators; + private volatile Set evaluators; + private volatile Set wildcardEvaluators; private boolean isSharingParentWildcardEvaluators; private volatile boolean isSetup = false; @@ -810,7 +935,7 @@ void populateTrieData(RangerResourceTrie.TrieData trieData) { } for (Map.Entry> entry : children.entrySet()) { - TrieNode child = entry.getValue(); + TrieNode child = entry.getValue(); child.populateTrieData(trieData); } @@ -836,35 +961,38 @@ int getMaxDepth() { } TrieNode getOrCreateChild(String str) { - int len = str.length(); - + int len = str.length(); TrieNode child = children.get(getLookupChar(str, 0)); if (child == null) { child = new TrieNode<>(str); + addChild(child); } else { - final String childStr = child.getStr(); - final int childStrLen = childStr.length(); - + final String childStr = child.getStr(); + final int childStrLen = childStr.length(); final boolean isExactMatch = optIgnoreCase ? StringUtils.equalsIgnoreCase(childStr, str) : StringUtils.equals(childStr, str); if (!isExactMatch) { final int numOfCharactersToMatch = Math.min(childStrLen, len); - int index = 1; + int index = 1; + for (; index < numOfCharactersToMatch; index++) { if (getLookupChar(childStr, index) != getLookupChar(str, index)) { break; } } + if (index == numOfCharactersToMatch) { // Matched all if (childStrLen > len) { // Existing node has longer string, need to break up this node TrieNode newChild = new TrieNode<>(str); + this.addChild(newChild); child.setStr(childStr.substring(index)); newChild.addChild(child); + child = newChild; } else { // This is a longer string, build a child with leftover string @@ -872,11 +1000,13 @@ TrieNode getOrCreateChild(String str) { } } else { // Partial match for both; both have leftovers - String matchedPart = str.substring(0, index); - TrieNode newChild = new TrieNode<>(matchedPart); + String matchedPart = str.substring(0, index); + TrieNode newChild = new TrieNode<>(matchedPart); + this.addChild(newChild); child.setStr(childStr.substring(index)); newChild.addChild(child); + child = newChild.getOrCreateChild(str.substring(index)); } } @@ -894,6 +1024,7 @@ void addEvaluator(U evaluator) { if (evaluators == null) { evaluators = new HashSet<>(); } + evaluators.add(evaluator); } @@ -902,41 +1033,35 @@ void addWildcardEvaluator(U evaluator) { wildcardEvaluators = new HashSet<>(); } - if (!wildcardEvaluators.contains(evaluator)) { - wildcardEvaluators.add(evaluator); - undoSetup(); - } + wildcardEvaluators.add(evaluator); } void removeEvaluator(U evaluator) { - if (CollectionUtils.isNotEmpty(evaluators) && evaluators.contains(evaluator)) { + if (CollectionUtils.isNotEmpty(evaluators)) { evaluators.remove(evaluator); + if (CollectionUtils.isEmpty(evaluators)) { evaluators = null; } } } - boolean removeWildcardEvaluator(U evaluator) { - if (CollectionUtils.isNotEmpty(wildcardEvaluators) && wildcardEvaluators.contains(evaluator)) { - undoSetup(); - if (CollectionUtils.isNotEmpty(wildcardEvaluators)) { - wildcardEvaluators.remove(evaluator); - if (CollectionUtils.isEmpty(wildcardEvaluators)) { - wildcardEvaluators = null; - } + void removeWildcardEvaluator(U evaluator) { + if (CollectionUtils.isNotEmpty(wildcardEvaluators)) { + wildcardEvaluators.remove(evaluator); + + if (CollectionUtils.isEmpty(wildcardEvaluators)) { + wildcardEvaluators = null; } - return true; - } else { - return false; } } void undoSetup() { + for (TrieNode child : children.values()) { + child.undoSetup(); + } + if (isSetup) { - for (TrieNode child : children.values()) { - child.undoSetup(); - } if (evaluators != null) { if (evaluators == wildcardEvaluators) { evaluators = null; @@ -947,25 +1072,47 @@ void undoSetup() { if (CollectionUtils.isEmpty(evaluators)) { evaluators = null; } + } + } + } - if (isSharingParentWildcardEvaluators) { - wildcardEvaluators = null; - } else { - Set parentWildcardEvaluators = getParent() == null ? null : getParent().getWildcardEvaluators(); + if (wildcardEvaluators != null) { + if (isSharingParentWildcardEvaluators) { + wildcardEvaluators = null; + } else { + Set parentWildcardEvaluators = getParent() == null ? null : getParent().getWildcardEvaluators(); - if (parentWildcardEvaluators != null) { - wildcardEvaluators.removeAll(parentWildcardEvaluators); + if (parentWildcardEvaluators != null) { + wildcardEvaluators.removeAll(parentWildcardEvaluators); - if (CollectionUtils.isEmpty(wildcardEvaluators)) { - wildcardEvaluators = null; - } - } + if (CollectionUtils.isEmpty(wildcardEvaluators)) { + wildcardEvaluators = null; } } } } + isSharingParentWildcardEvaluators = false; - isSetup = false; + isSetup = false; + } + } + + void removeSelfFromTrie() { + if (LOG.isDebugEnabled()) { + LOG.debug("==> removeSelfFromTrie(" + this + ")"); + } + if (evaluators == null && wildcardEvaluators == null && children.size() == 0) { + TrieNode parent = getParent(); + if (parent != null) { + parent.children.remove(str.charAt(0)); + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("removeSelfFromTrie(" + this + "): node is not removed from Trie : [evaluators:" + evaluators + ", wildcard-evaluators:" + wildcardEvaluators + ", number-of-children-nodes:" + children.size() + "]"); + } + } + if (LOG.isDebugEnabled()) { + LOG.debug("<== removeSelfFromTrie(" + this + ")"); } } @@ -984,7 +1131,6 @@ void wrapUpUpdate() { } void postSetup(Set parentWildcardEvaluators) { - setup(parentWildcardEvaluators); for (Map.Entry> entry : children.entrySet()) { @@ -992,11 +1138,9 @@ void postSetup(Set parentWildcardEvaluators) { child.postSetup(wildcardEvaluators); } - } void setupIfNeeded(TrieNode parent) { - boolean setupNeeded = !isSetup; if (setupNeeded) { @@ -1005,6 +1149,7 @@ void setupIfNeeded(TrieNode parent) { if (setupNeeded) { setup(parent == null ? null : parent.getWildcardEvaluators()); + if (TRACE_LOG.isTraceEnabled()) { StringBuilder sb = new StringBuilder(); this.toString(sb); @@ -1027,6 +1172,7 @@ void setup(Set parentWildcardEvaluators) { } } } + this.isSharingParentWildcardEvaluators = wildcardEvaluators == parentWildcardEvaluators; // finalize evaluators list by including wildcard evaluators @@ -1039,33 +1185,115 @@ void setup(Set parentWildcardEvaluators) { } } } - isSetup = true; } } - private void removeEvaluatorFromSubtree(U evaluator) { - if (removeWildcardEvaluator(evaluator)) { - for (Map.Entry> entry : children.entrySet()) { - entry.getValue().removeEvaluatorFromSubtree(evaluator); + void collectChildEvaluators(Character sep, int startIdx, TraverseMatchHandler handler) { + if (!isOptimizedForSpace) { + setupIfNeeded(getParent()); + } + + final int sepPos = startIdx < str.length() ? str.indexOf(sep, startIdx) : -1; + + if (sepPos == -1) { // ex: startIdx=5, path(str)=/tmp/test, path(a child) could be: /tmp/test.txt, /tmp/test/, /tmp/test/a, /tmp/test/a/b + if (isOptimizedForSpace) { + handler.process(this.wildcardEvaluators); } + + handler.process(this.evaluators); + + children.values().forEach(c -> c.collectChildEvaluators(sep, 0, handler)); + } else if (sepPos == (str.length() - 1)) { // ex: str=/tmp/test/, startIdx=5 + if (isOptimizedForSpace) { + handler.process(this.wildcardEvaluators); + } + + handler.process(this.evaluators); } + } - removeEvaluator(evaluator); + void collectChildEvaluators(String resource, int startIndex, TraverseMatchHandler handler) { + if (startIndex == resource.length()) { + collectChildEvaluators(handler); + } else if (startIndex < resource.length()) { + Character startChar = getLookupChar(resource, startIndex); + TrieNode childNode = children.get(startChar); + if (childNode != null) { + if (!isOptimizedForSpace) { + childNode.setupIfNeeded(childNode.getParent()); + } + + String childStr = childNode.getStr(); + int lenToMatch = Math.min(resource.length() - startIndex, childStr.length()); + + if (resource.regionMatches(optIgnoreCase, startIndex, childStr, 0, lenToMatch)) { + handler.process(childNode.wildcardEvaluators); + handler.process(childNode.evaluators); + + if (resource.length() == (startIndex + lenToMatch)) { + childNode.collectChildEvaluators(handler); + } else { + childNode.children.values().forEach(c -> c.collectChildEvaluators(resource, startIndex + childStr.length(), handler)); + } + } + } + } + } + + private void collectChildEvaluators(TraverseMatchHandler childEvaluators) { + Stack> nodes = new Stack<>(); + + nodes.addAll(children.values()); + + while (!nodes.isEmpty()) { + TrieNode childNode = nodes.pop(); + + if (!isOptimizedForSpace) { + childNode.setupIfNeeded(childNode.getParent()); + } + + childEvaluators.process(childNode.wildcardEvaluators); + childEvaluators.process(childNode.evaluators); + + nodes.addAll(childNode.children.values()); + } + } + + private void removeEvaluatorFromSubtree(U evaluator) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> removeEvaluatorFromSubtree(" + evaluator.getId() + ")"); + } + if (CollectionUtils.isNotEmpty(wildcardEvaluators) && wildcardEvaluators.contains(evaluator)) { + removeWildcardEvaluator(evaluator); + } else { + removeEvaluator(evaluator); + } + removeSelfFromTrie(); + if (LOG.isDebugEnabled()) { + LOG.debug("<== removeEvaluatorFromSubtree(" + evaluator.getId() + ")"); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + toString(sb); + return sb.toString(); } void toString(StringBuilder sb) { String nodeValue = this.str; - sb.append("nodeValue=").append(nodeValue); + sb.append("nodeValue=").append(nodeValue == null ? "ROOT" : nodeValue); sb.append("; isSetup=").append(isSetup); sb.append("; isSharingParentWildcardEvaluators=").append(isSharingParentWildcardEvaluators); sb.append("; childCount=").append(children.size()); - sb.append("; evaluators=[ "); + sb.append("; evaluators=["); if (evaluators != null) { for (U evaluator : evaluators) { - sb.append(evaluator.getId()).append(" "); + sb.append(evaluator.getId()).append("|,|"); } } sb.append("]"); @@ -1073,14 +1301,19 @@ void toString(StringBuilder sb) { sb.append("; wildcardEvaluators=[ "); if (wildcardEvaluators != null) { for (U evaluator : wildcardEvaluators) { - sb.append(evaluator.getId()).append(" "); + sb.append(evaluator.getId()).append("|,|"); } } + sb.append("]"); } void toString(String prefix, StringBuilder sb) { String nodeValue = prefix + (str != null ? str : ""); + if (!nodeValue.equals(prefix)) { + prefix = prefix + "|"; + } + sb.append(prefix); toString(sb); sb.append("]\n"); @@ -1090,7 +1323,135 @@ void toString(String prefix, StringBuilder sb) { child.toString(nodeValue, sb); } + } + } + + public interface TraverseMatchHandler { + // return: true - stop traverse, processing is complete + // false - continue traverse, processing is not complete yet + boolean process(Set evaluators); + } + + public static class EvalCollector implements TraverseMatchHandler { + private final Predicate predicate; + private Set result; + private boolean isOwnedResult = false; + + public EvalCollector(Predicate predicate) { + this.predicate = predicate; + this.result = null; + } + + public Set getResult() { + return result; + } + + @Override + public boolean process(Set evaluators) { + if (evaluators != null && !evaluators.isEmpty()) { + if (result == null) { + if (predicate == null) { + result = evaluators; + } else { + result = new HashSet<>(); + isOwnedResult = true; + + for (T evaluator : evaluators) { + if (predicate.evaluate(evaluator)) { + result.add(evaluator); + } + } + } + } else { + if (!isOwnedResult) { + result = new HashSet<>(result); + + isOwnedResult = true; + } + + if (predicate == null) { + result.addAll(evaluators); + } else { + for (T evaluator : evaluators) { + if (predicate.evaluate(evaluator)) { + result.add(evaluator); + } + } + } + } + } + + return false; // continue traverse + } + } + + public static class EvalSubsetCollector implements TraverseMatchHandler { + private final Predicate predicate; + private final Set filter; + private Set result; + + public EvalSubsetCollector(Set filter, Predicate predicate) { + this.predicate = predicate; + this.filter = filter == null ? Collections.emptySet() : filter; + this.result = null; + } + + public Set getResult() { + return result; + } + + @Override + public boolean process(Set evaluators) { + if (evaluators != null && !evaluators.isEmpty()) { + if (result == null) { + result = new HashSet<>(); + } + + intersect(filter, evaluators, result); + + if (predicate != null) { + result.removeIf(evaluator -> !predicate.evaluate(evaluator)); + } + } + + return result != null && (filter.size() == result.size()); // stop traverse once the result includes all entries in the filter + } + + private static void intersect(Set a, Set b, Set result) { + Set smaller = a.size() < b.size() ? a : b; + Set larger = smaller == a ? b : a; + + smaller.forEach(item -> { if (larger.contains(item)) result.add(item); }); + } + } + + public static class EvalCountCollector implements TraverseMatchHandler { + private final Predicate predicate; + private int result = 0; + + public EvalCountCollector(Predicate predicate) { + this.predicate = predicate; + } + + public int getResult() { + return result; + } + + @Override + public boolean process(Set evaluators) { + if (evaluators != null) { + if (predicate == null) { + result += evaluators.size(); + } else { + for (T evaluator : evaluators) { + if (predicate.evaluate(evaluator)) { + result++; + } + } + } + } + return false; // continue traverse } } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerSecurityZoneMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerSecurityZoneMatcher.java new file mode 100644 index 0000000000..0d44f7109c --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerSecurityZoneMatcher.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.validation.RangerZoneResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; +import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.apache.ranger.plugin.util.RangerResourceEvaluatorsRetriever; +import org.apache.ranger.plugin.util.ServicePolicies.SecurityZoneInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.*; + +public class RangerSecurityZoneMatcher { + private static final Logger LOG = LoggerFactory.getLogger(RangerSecurityZoneMatcher.class); + + private final Map> resourceZoneTrie; + private final Set zonesWithTagService; + private final RangerServiceDef serviceDef; + + public RangerSecurityZoneMatcher(Map securityZones, RangerServiceDef serviceDef, RangerPluginContext pluginContext) { + this.resourceZoneTrie = new HashMap<>(); + this.zonesWithTagService = new HashSet<>(); + this.serviceDef = serviceDef; + + buildZoneTrie(securityZones, serviceDef, pluginContext); + } + + public boolean hasTagService(String zoneName) { + return zonesWithTagService.contains(zoneName); + } + + public Set getZonesForResourceAndChildren(Map resource) { + return getZonesForResourceAndChildren(resource, convertToAccessResource(resource)); + } + + public Set getZonesForResourceAndChildren(RangerAccessResource resource) { + return getZonesForResourceAndChildren(resource.getAsMap(), resource); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null || !getClass().equals(obj.getClass())) { + return false; + } + + RangerSecurityZoneMatcher other = (RangerSecurityZoneMatcher) obj; + + return Objects.equals(resourceZoneTrie, other.resourceZoneTrie) && + Objects.equals(zonesWithTagService, other.zonesWithTagService); + } + + @Override + public int hashCode() { + return Objects.hash(resourceZoneTrie, zonesWithTagService); + } + + private Set getZonesForResourceAndChildren(Map resource, RangerAccessResource accessResource) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerSecurityZoneMatcher.getZonesForResourceAndChildren({})", accessResource); + } + + Set ret = null; + + if (MapUtils.isNotEmpty(this.resourceZoneTrie)) { + Collection matchers = RangerResourceEvaluatorsRetriever.getEvaluators(resourceZoneTrie, resource); + + if (CollectionUtils.isNotEmpty(matchers)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Resource:[{}], matchers:[{}]", resource, matchers); + } + + ret = new HashSet<>(matchers.size()); + + // These are potential matches. Try to really match them + for (RangerZoneResourceMatcher matcher : matchers) { + if (LOG.isDebugEnabled()) { + LOG.debug("Trying to match resource:[{}] using matcher:[{}]", accessResource, matcher); + } + + RangerPolicyResourceMatcher policyResourceMatcher = matcher.getPolicyResourceMatcher(); + MatchType matchType = policyResourceMatcher.getMatchType(accessResource, null); + + if (matchType == MatchType.DESCENDANT) { // add unzoned name + ret.add(""); + } + + if (matchType != MatchType.NONE) { + if (LOG.isDebugEnabled()) { + LOG.debug("Matched resource:[{}] using matcher:[{}]", accessResource, matcher); + } + + // Actual match happened + ret.add(matcher.getSecurityZoneName()); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Did not match resource:[{}] using matcher:[{}]", accessResource, matcher); + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("zone-names matched resource:[{}]: {}", accessResource, ret); + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerSecurityZoneMatcher.getZonesForResourceAndChildren({}): ret={}", accessResource, ret); + } + + return ret; + } + + private void buildZoneTrie(Map securityZones, RangerServiceDef serviceDef, RangerPluginContext pluginContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerSecurityZoneMatcher.buildZoneTrie()"); + } + + Map resourceIsRecursive = new HashMap<>(); + + if (MapUtils.isNotEmpty(securityZones)) { + List matchers = new ArrayList<>(); + + for (Map.Entry securityZone : securityZones.entrySet()) { + String zoneName = securityZone.getKey(); + SecurityZoneInfo zoneDetails = securityZone.getValue(); + + if (LOG.isDebugEnabled()) { + LOG.debug("Building matchers for zone:[{}]", zoneName); + } + + for (Map> resource : zoneDetails.getResources()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Building matcher for resource:[{}] in zone:[{}]", resource, zoneName); + } + + Map policyResources = new HashMap<>(); + + for (Map.Entry> entry : resource.entrySet()) { + String resourceDefName = entry.getKey(); + List resourceValues = entry.getValue(); + Boolean isRecursive = resourceIsRecursive.computeIfAbsent(resourceDefName, f -> EmbeddedServiceDefsUtil.isRecursiveEnabled(serviceDef, resourceDefName)); + + policyResources.put(resourceDefName, new RangerPolicyResource(resourceValues, false, isRecursive)); + } + + matchers.add(new RangerZoneResourceMatcher(zoneName, policyResources, serviceDef, pluginContext)); + + if (LOG.isDebugEnabled()) { + LOG.debug("Built matcher for resource:[{}] in zone:[{}]", resource, zoneName); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Built all matchers for zone:[{}]", zoneName); + } + + if (Boolean.TRUE.equals(zoneDetails.getContainsAssociatedTagService())) { + zonesWithTagService.add(zoneName); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("Built matchers for all Zones"); + } + + RangerPolicyEngineOptions options = pluginContext.getConfig().getPolicyEngineOptions(); + + for (RangerResourceDef resourceDef : serviceDef.getResources()) { + resourceZoneTrie.put(resourceDef.getName(), new RangerResourceTrie<>(resourceDef, matchers, options.optimizeTrieForSpace, options.optimizeTrieForRetrieval, pluginContext)); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerSecurityZoneMatcher.buildZoneTrie()"); + } + } + + private RangerAccessResource convertToAccessResource(Map resource) { + RangerAccessResourceImpl ret = new RangerAccessResourceImpl(); + + ret.setServiceDef(serviceDef); + + for (Map.Entry entry : resource.entrySet()) { + ret.setValue(entry.getKey(), entry.getValue()); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagAccessRequest.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagAccessRequest.java index ebe85e9a25..4b2d706451 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagAccessRequest.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagAccessRequest.java @@ -31,8 +31,11 @@ public class RangerTagAccessRequest extends RangerAccessRequestImpl { private final RangerPolicyResourceMatcher.MatchType matchType; public RangerTagAccessRequest(RangerTagForEval resourceTag, RangerServiceDef tagServiceDef, RangerAccessRequest request) { + String owner = request.getResource() != null ? request.getResource().getOwnerUser() : null; + matchType = resourceTag.getMatchType(); - super.setResource(new RangerTagResource(resourceTag.getType(), tagServiceDef)); + + super.setResource(new RangerTagResource(resourceTag.getType(), tagServiceDef, owner)); super.setUser(request.getUser()); super.setUserGroups(request.getUserGroups()); super.setUserRoles(request.getUserRoles()); @@ -47,8 +50,6 @@ public RangerTagAccessRequest(RangerTagForEval resourceTag, RangerServiceDef tag RangerAccessRequestUtil.setCurrentResourceInContext(request.getContext(), request.getResource()); RangerAccessRequestUtil.setCurrentUserInContext(request.getContext(), request.getUser()); - String owner = request.getResource() != null ? request.getResource().getOwnerUser() : null; - if (StringUtils.isNotEmpty(owner)) { RangerAccessRequestUtil.setOwnerInContext(request.getContext(), owner); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagResource.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagResource.java index 39e190ca43..b6ab66bcdf 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagResource.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/RangerTagResource.java @@ -30,4 +30,10 @@ public RangerTagResource(String tagType, RangerServiceDef tagServiceDef) { super.setValue(KEY_TAG, tagType); super.setServiceDef(tagServiceDef); } + + public RangerTagResource(String tagType, RangerServiceDef tagServiceDef, String ownerUser) { + super.setValue(KEY_TAG, tagType); + super.setServiceDef(tagServiceDef); + super.setOwnerUser(ownerUser); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsAccessResult.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsAccessResult.java new file mode 100644 index 0000000000..74a78e68d4 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsAccessResult.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ranger.plugin.policyengine.gds; + + +import java.util.*; + +public class GdsAccessResult { + private boolean isAllowed; + private boolean isAudited; + private long policyId = -1; + private Long policyVersion; + private Set datasets; + private Set projects; + private Set allowedByDatasets; + private Set allowedByProjects; + + + public GdsAccessResult() { + } + + public boolean getIsAllowed() { + return isAllowed; + } + + public void setIsAllowed(boolean allowed) { + isAllowed = allowed; + } + + public boolean getIsAudited() { + return isAudited; + } + + public void setIsAudited(boolean audited) { + isAudited = audited; + } + + public long getPolicyId() { + return policyId; + } + + public void setPolicyId(long policyId) { + this.policyId = policyId; + } + + public Long getPolicyVersion() { + return policyVersion; + } + + public void setPolicyVersion(Long policyVersion) { + this.policyVersion = policyVersion; + } + + public Set getDatasets() { + return datasets; + } + + public Set getProjects() { + return projects; + } + + public Set getAllowedByDatasets() { + return allowedByDatasets; + } + + public Set getAllowedByProjects() { + return allowedByProjects; + } + + public void addDataset(String name) { + if (datasets == null) { + datasets = new HashSet<>(); + } + + datasets.add(name); + } + + public void addProject(String name) { + if (projects == null) { + projects = new HashSet<>(); + } + + projects.add(name); + } + + public void addAllowedByDataset(String name) { + if (allowedByDatasets == null) { + allowedByDatasets = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + } + + allowedByDatasets.add(name); + } + + public void addAllowedByProject(String name) { + if (allowedByProjects == null) { + allowedByProjects = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + } + + allowedByProjects.add(name); + } + + @Override + public int hashCode() { + return Objects.hash(isAllowed, isAudited, policyId, policyVersion, datasets, projects, allowedByDatasets, allowedByProjects); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if ((obj == null) || !obj.getClass().equals(getClass())) { + return false; + } else { + GdsAccessResult other = (GdsAccessResult) obj; + + return Objects.equals(isAllowed, other.isAllowed) && + Objects.equals(isAudited, other.isAudited) && + Objects.equals(policyId, other.policyId) && + Objects.equals(policyVersion, other.policyVersion) && + Objects.equals(datasets, other.datasets) && + Objects.equals(projects, other.projects) && + Objects.equals(allowedByDatasets, other.allowedByDatasets) && + Objects.equals(allowedByProjects, other.allowedByProjects); + } + } + + @Override + public String toString( ) { + StringBuilder sb = new StringBuilder(); + + toString(sb); + + return sb.toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerGdsAccessResult={"); + sb.append("isAllowed={").append(isAllowed).append("}"); + sb.append(", isAudited={").append(isAudited).append("}"); + sb.append(", policyId={").append(policyId).append("}"); + sb.append(", policyVersion={").append(policyVersion).append("}"); + sb.append(", datasets={").append(datasets).append("}"); + sb.append(", projects={").append(projects).append("}"); + sb.append(", allowedByDatasets={").append(allowedByDatasets).append("}"); + sb.append(", allowedByProjects={").append(allowedByProjects).append("}"); + sb.append("}"); + + return sb; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDataShareEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDataShareEvaluator.java new file mode 100644 index 0000000000..39cd154247 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDataShareEvaluator.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyevaluator.RangerCustomConditionEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.stream.Collectors; + +public class GdsDataShareEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDataShareEvaluator.class); + + public static final GdsDataShareEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsDataShareEvalOrderComparator(); + + private final DataShareInfo dsh; + private final String name; + private final String zoneName; + private final RangerConditionEvaluator conditionEvaluator; + private final Set evaluators = new TreeSet<>(GdsSharedResourceEvaluator.EVAL_ORDER_COMPARATOR); // keep sorted + private final List dshidEvaluators = new ArrayList<>(); + + public GdsDataShareEvaluator(DataShareInfo dsh, RangerServiceDefHelper serviceDefHelper) { + LOG.debug("==> GdsDataShareEvaluator({})", dsh); + + this.dsh = dsh; + this.name = StringUtils.isBlank(dsh.getName()) ? StringUtils.EMPTY : dsh.getName(); + this.zoneName = StringUtils.isBlank(dsh.getZoneName()) ? StringUtils.EMPTY : dsh.getZoneName(); + this.conditionEvaluator = RangerCustomConditionEvaluator.getInstance().getExpressionEvaluator(dsh.getConditionExpr(), serviceDefHelper.getServiceDef()); + + LOG.debug("<== GdsDataShareEvaluator({})", dsh); + } + + public Long getId() { + return dsh.getId(); + } + + public String getName() { + return name; + } + + public String getZoneName() { + return zoneName; + } + + public Set getDefaultAccessTypes() { return dsh.getDefaultAccessTypes(); } + + public Set getResourceEvaluators() { return evaluators; } + + public List getDshidEvaluators() { return dshidEvaluators; } + + public List getResourceEvaluators(RangerAccessRequest request) { + final List ret; + final boolean isAllowed = conditionEvaluator == null || conditionEvaluator.isMatched(request); + + if (isAllowed) { + ret = evaluators.stream().filter(e -> e.isAllowed(request)).collect(Collectors.toList()); + } else { + ret = Collections.emptyList(); + } + + return ret; + } + + public boolean isInDataset(Long datasetId) { + return dshidEvaluators.stream().anyMatch(e -> e.getDatasetId().equals(datasetId) && e.isActive()); + } + + public boolean isInProject(Long projectId) { + return dshidEvaluators.stream().anyMatch(e -> e.getDatasetEvaluator().isInProject(projectId) && e.isActive()); + } + + public void collectDatasets(RangerAccessRequest request, Map> datasetsToEval) { + LOG.debug("==> GdsDataShareEvaluator.collectDatasets({}, {})", request, datasetsToEval); + + boolean isAllowed = conditionEvaluator == null || conditionEvaluator.isMatched(request); + + if (isAllowed) { + dshidEvaluators.stream().filter(dshid -> dshid.isAllowed(request) && dshid.getDatasetEvaluator().isActive()).forEach(dshid -> datasetsToEval.computeIfAbsent(dshid.getDatasetEvaluator(), s -> new TreeSet<>(GdsDataShareEvaluator.EVAL_ORDER_COMPARATOR)).add(this)); + } + + LOG.debug("<== GdsDataShareEvaluator.collectDatasets({}, {})", request, datasetsToEval); + } + + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls) { + LOG.debug("==> GdsDataShareEvaluator.getResourceACLs({}, {})", request, acls); + + List evaluators = getResourceEvaluators(request); + + if (!evaluators.isEmpty()) { + boolean isConditional = conditionEvaluator != null; + + for (GdsSharedResourceEvaluator evaluator : evaluators) { + evaluator.getResourceACLs(request, acls, isConditional, dshidEvaluators); + } + } + + LOG.debug("<== GdsDataShareEvaluator.getResourceACLs({}, {})", request, acls); + } + + void addResourceEvaluator(GdsSharedResourceEvaluator evaluator) { + evaluators.add(evaluator); + } + + void addDshidEvaluator(GdsDshidEvaluator dhidEvaluator) { + dshidEvaluators.add(dhidEvaluator); + } + + public static class GdsDataShareEvalOrderComparator implements Comparator { + @Override + public int compare(GdsDataShareEvaluator me, GdsDataShareEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = me.getName().compareTo(other.dsh.getName()); + + if (ret == 0) { + ret = me.getId().compareTo(other.getId()); + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDatasetEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDatasetEvaluator.java new file mode 100644 index 0000000000..7298825c21 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDatasetEvaluator.java @@ -0,0 +1,234 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyevaluator.RangerOptimizedPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public class GdsDatasetEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDatasetEvaluator.class); + + public static final GdsDatasetEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsDatasetEvalOrderComparator(); + + + private final DatasetInfo dataset; + private final RangerServiceDef gdsServiceDef; + private final String name; + private final RangerValidityScheduleEvaluator scheduleEvaluator; + private final List dipEvaluators = new ArrayList<>(); + private final List policyEvaluators; + + + public GdsDatasetEvaluator(DatasetInfo dataset, RangerServiceDef gdsServiceDef, RangerPolicyEngineOptions options) { + LOG.debug("==> GdsDatasetEvaluator()"); + + this.dataset = dataset; + this.gdsServiceDef = gdsServiceDef; + this.name = StringUtils.isBlank(dataset.getName()) ? StringUtils.EMPTY : dataset.getName(); + + if (dataset.getValiditySchedule() != null) { + scheduleEvaluator = new RangerValidityScheduleEvaluator(dataset.getValiditySchedule()); + } else { + scheduleEvaluator = null; + } + + if (dataset.getPolicies() != null) { + policyEvaluators = new ArrayList<>(dataset.getPolicies().size()); + + for (RangerPolicy policy : dataset.getPolicies()) { + RangerPolicyEvaluator evaluator = new RangerOptimizedPolicyEvaluator(); + + evaluator.init(policy, gdsServiceDef, options); + + policyEvaluators.add(evaluator); + } + } else { + policyEvaluators = Collections.emptyList(); + } + + LOG.debug("<== GdsDatasetEvaluator()"); + } + + public Long getId() { + return dataset.getId(); + } + + public String getName() { + return name; + } + + public boolean isInProject(Long projectId) { + return dipEvaluators.stream().anyMatch(e -> e.getProjectId().equals(projectId) && e.isActive()); + } + + public boolean isActive() { + return scheduleEvaluator == null || scheduleEvaluator.isApplicable(System.currentTimeMillis()); + } + + public void evaluate(RangerAccessRequest request, GdsAccessResult result, Collection projectsToEval) { + LOG.debug("==> GdsDatasetEvaluator.evaluate({}, {}, {})", request, result, projectsToEval); + + if (isActive()) { + result.addDataset(getName()); + + if (!policyEvaluators.isEmpty()) { + GdsDatasetAccessRequest datasetRequest = new GdsDatasetAccessRequest(getId(), gdsServiceDef, request); + RangerAccessResult datasetResult = datasetRequest.createAccessResult(); + + try { + RangerAccessRequestUtil.setAccessTypeResults(datasetRequest.getContext(), null); + RangerAccessRequestUtil.setAccessTypeACLResults(datasetRequest.getContext(), null); + + policyEvaluators.forEach(e -> e.evaluate(datasetRequest, datasetResult)); + } finally { + RangerAccessRequestUtil.setAccessTypeResults(datasetRequest.getContext(), null); + RangerAccessRequestUtil.setAccessTypeACLResults(datasetRequest.getContext(), null); + } + + if (datasetResult.getIsAllowed()) { + result.addAllowedByDataset(getName()); + } + + if (!result.getIsAllowed()) { + if (datasetResult.getIsAllowed()) { + result.setIsAllowed(true); + result.setPolicyId(datasetResult.getPolicyId()); + result.setPolicyVersion(datasetResult.getPolicyVersion()); + } + } + + if (!result.getIsAudited()) { + result.setIsAudited(datasetResult.getIsAudited()); + } + } + + dipEvaluators.stream().filter(e -> e.isAllowed(request) && e.getProjectEvaluator().isActive()).forEach(dip -> projectsToEval.add(dip.getProjectEvaluator())); + } + + LOG.debug("<== GdsDatasetEvaluator.evaluate({}, {}, {})", request, result, projectsToEval); + } + + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set allowedAccessTypes) { + if (isActive()) { + acls.getDatasets().add(getName()); + + if (!policyEvaluators.isEmpty()) { + GdsDatasetAccessRequest datasetRequest = new GdsDatasetAccessRequest(getId(), gdsServiceDef, request); + + for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) { + policyEvaluator.getResourceACLs(datasetRequest, acls, isConditional, allowedAccessTypes, RangerPolicyResourceMatcher.MatchType.SELF, null); + } + } + + for (GdsDipEvaluator dipEvaluator : dipEvaluators) { + dipEvaluator.getResourceACLs(request, acls, isConditional, allowedAccessTypes); + } + } + } + + public boolean hasReference(Set users, Set groups, Set roles) { + boolean ret = false; + + for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) { + ret = policyEvaluator.hasReference(users, groups, roles); + + if (ret) { + break; + } + } + + return ret; + } + + void addDipEvaluator(GdsDipEvaluator dipEvaluator) { + dipEvaluators.add(dipEvaluator); + } + + private static class GdsDatasetAccessRequest extends RangerAccessRequestImpl { + public GdsDatasetAccessRequest(Long datasetId, RangerServiceDef gdsServiceDef, RangerAccessRequest request) { + super.setResource(new RangerDatasetResource(datasetId, gdsServiceDef, request.getResource().getOwnerUser())); + + super.setUser(request.getUser()); + super.setUserGroups(request.getUserGroups()); + super.setUserRoles(request.getUserRoles()); + super.setAction(request.getAction()); + super.setAccessType(request.getAccessType()); + super.setAccessTime(request.getAccessTime()); + super.setRequestData(request.getRequestData()); + super.setContext(request.getContext()); + super.setClientType(request.getClientType()); + super.setClientIPAddress(request.getClientIPAddress()); + super.setRemoteIPAddress(request.getRemoteIPAddress()); + super.setForwardedAddresses(request.getForwardedAddresses()); + super.setSessionId(request.getSessionId()); + super.setResourceMatchingScope(request.getResourceMatchingScope()); + } + + public RangerAccessResult createAccessResult() { + return new RangerAccessResult(RangerPolicy.POLICY_TYPE_ACCESS, GdsPolicyEngine.GDS_SERVICE_NAME, getResource().getServiceDef(), this); + } + } + + public static class RangerDatasetResource extends RangerAccessResourceImpl { + public RangerDatasetResource(Long datasetd, RangerServiceDef gdsServiceDef, String ownerUser) { + super.setValue(GdsPolicyEngine.RESOURCE_NAME_DATASET_ID, datasetd.toString()); + super.setServiceDef(gdsServiceDef); + super.setOwnerUser(ownerUser); + } + } + + public static class GdsDatasetEvalOrderComparator implements Comparator { + @Override + public int compare(GdsDatasetEvaluator me, GdsDatasetEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = me.getName().compareTo(other.getName()); + + if (ret == 0) { + ret = me.getId().compareTo(other.getId()); + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDipEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDipEvaluator.java new file mode 100644 index 0000000000..73610fd1f1 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDipEvaluator.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.ranger.plugin.model.RangerGds; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DatasetInProjectInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +public class GdsDipEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDipEvaluator.class); + + private final DatasetInProjectInfo dip; + private final GdsProjectEvaluator projectEvaluator; + private final RangerValidityScheduleEvaluator scheduleEvaluator; + + public GdsDipEvaluator(DatasetInProjectInfo dip, GdsProjectEvaluator projectEvaluator) { + this.dip = dip; + this.projectEvaluator = projectEvaluator; + + if (dip.getValiditySchedule() != null) { + scheduleEvaluator = new RangerValidityScheduleEvaluator(dip.getValiditySchedule()); + } else { + scheduleEvaluator = null; + } + } + + public Long getDatasetId() { + return dip.getDatasetId(); + } + + public Long getProjectId() { + return dip.getProjectId(); + } + + public GdsProjectEvaluator getProjectEvaluator() { return projectEvaluator; } + + public boolean isActive() { + boolean ret = dip.getStatus() == RangerGds.GdsShareStatus.ACTIVE; + + if (ret && scheduleEvaluator != null) { + ret = scheduleEvaluator.isApplicable(System.currentTimeMillis()); + } + + return ret; + } + + public boolean isAllowed(RangerAccessRequest request) { + boolean ret = isActive(); + + if (ret) { + // TODO: + } + + return ret; + } + + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set allowedAccessTypes) { + LOG.debug("==> GdsDipEvaluator.getResourceACLs({}, {})", request, acls); + + isConditional = isConditional || scheduleEvaluator != null; + + projectEvaluator.getResourceACLs(request, acls, isConditional, allowedAccessTypes); + + LOG.debug("<== GdsDipEvaluator.getResourceACLs({}, {})", request, acls); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDshidEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDshidEvaluator.java new file mode 100644 index 0000000000..9d7ba9c9ec --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsDshidEvaluator.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.ranger.plugin.model.RangerGds; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.apache.ranger.plugin.util.ServiceGdsInfo.DataShareInDatasetInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +public class GdsDshidEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDshidEvaluator.class); + + private final DataShareInDatasetInfo dshid; + private final GdsDatasetEvaluator datasetEvaluator;; + private final RangerValidityScheduleEvaluator scheduleEvaluator; + + public GdsDshidEvaluator(DataShareInDatasetInfo dshid, GdsDatasetEvaluator datasetEvaluator) { + this.dshid = dshid; + this.datasetEvaluator = datasetEvaluator; + + if (dshid.getValiditySchedule() != null) { + scheduleEvaluator = new RangerValidityScheduleEvaluator(dshid.getValiditySchedule()); + } else { + scheduleEvaluator = null; + } + } + + public Long getDataShareId() { + return dshid.getDataShareId(); + } + + public Long getDatasetId() { + return dshid.getDatasetId(); + } + + public GdsDatasetEvaluator getDatasetEvaluator() { + return datasetEvaluator; + } + + public boolean isActive() { + boolean ret = dshid.getStatus() == RangerGds.GdsShareStatus.ACTIVE; + + if (ret && scheduleEvaluator != null) { + ret = scheduleEvaluator.isApplicable(System.currentTimeMillis()); + } + + return ret; + } + + public boolean isAllowed(RangerAccessRequest request) { + boolean ret = isActive(); + + if (ret) { + // TODO: + } + + return ret; + } + + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set allowedAccessTypes) { + LOG.debug("==> GdsDshidEvaluator.getResourceACLs({}, {})", request, acls); + + isConditional = isConditional || scheduleEvaluator != null; + + datasetEvaluator.getResourceACLs(request, acls, isConditional, allowedAccessTypes); + + LOG.debug("<== GdsDshidEvaluator.getResourceACLs({}, {})", request, acls); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsPolicyEngine.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsPolicyEngine.java new file mode 100644 index 0000000000..ddac8da1a9 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsPolicyEngine.java @@ -0,0 +1,519 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerGds; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyengine.RangerResourceTrie; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerResourceEvaluatorsRetriever; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + + +public class GdsPolicyEngine { + private static final Logger LOG = LoggerFactory.getLogger(GdsPolicyEngine.class); + + public static final String GDS_SERVICE_NAME = "_gds"; + public static final String RESOURCE_NAME_DATASET_ID = "dataset-id"; + public static final String RESOURCE_NAME_PROJECT_ID = "project-id"; + + private final ServiceGdsInfo gdsInfo; + private final Set allAccessTypes; + private final Map projects = new HashMap<>(); + private final Map datasets = new HashMap<>(); + private final Map dataShares = new HashMap<>(); + private final Map zoneResources = new HashMap<>(); + + public GdsPolicyEngine(ServiceGdsInfo gdsInfo, RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + LOG.debug("==> RangerGdsPolicyEngine()"); + + this.gdsInfo = gdsInfo; + this.allAccessTypes = Collections.unmodifiableSet(getAllAccessTypes(serviceDefHelper)); + + init(serviceDefHelper, pluginContext); + + LOG.debug("<== RangerGdsPolicyEngine()"); + } + + public ServiceGdsInfo getGdsInfo() { + return gdsInfo; + } + + public GdsAccessResult evaluate(RangerAccessRequest request) { + LOG.debug("==> RangerGdsPolicyEngine.evaluate({})", request); + + final GdsAccessResult ret; + final boolean isAnyAccess = request.isAccessTypeAny(); + + try { + if (isAnyAccess) { + RangerAccessRequestUtil.setAllRequestedAccessTypes(request.getContext(), allAccessTypes); + RangerAccessRequestUtil.setIsAnyAccessInContext(request.getContext(), Boolean.TRUE); + } + + Map> dshResources = getDataShareResources(request); + + if (!dshResources.isEmpty()) { + Map> datasetsToEval = new TreeMap<>(GdsDatasetEvaluator.EVAL_ORDER_COMPARATOR); + + dshResources.keySet().forEach(e -> e.collectDatasets(request, datasetsToEval)); + + if (!datasetsToEval.isEmpty()) { + ret = new GdsAccessResult(); + + Set projectsToEval = new TreeSet<>(GdsProjectEvaluator.EVAL_ORDER_COMPARATOR); + + evaluateDatasetPolicies(datasetsToEval.keySet(), request, ret, projectsToEval); + + evaluateProjectPolicies(projectsToEval, request, ret); + } else { + ret = null; + } + } else { + ret = null; + } + } finally { + if (isAnyAccess) { + RangerAccessRequestUtil.setAllRequestedAccessTypes(request.getContext(), null); + RangerAccessRequestUtil.setIsAnyAccessInContext(request.getContext(), Boolean.FALSE); + } + } + + LOG.debug("<== RangerGdsPolicyEngine.evaluate({}): {}", request, ret); + + return ret; + } + + public RangerResourceACLs getResourceACLs(RangerAccessRequest request) { + RangerResourceACLs ret = new RangerResourceACLs(); + + getDataShareResources(request).keySet().forEach(e -> e.getResourceACLs(request, ret)); + + ret.finalizeAcls(); + + return ret; + } + + public Set getDatasetsSharedWith(Set users, Set groups, Set roles) { + Set ret = new HashSet<>(); + + for (GdsDatasetEvaluator dataset : datasets.values()) { + if (dataset.hasReference(users, groups, roles)) { + ret.add(dataset.getId()); + } + } + + return ret; + } + + public Set getProjectsSharedWith(Set users, Set groups, Set roles) { + Set ret = new HashSet<>(); + + for (GdsProjectEvaluator project : projects.values()) { + if (project.hasReference(users, groups, roles)) { + ret.add(project.getId()); + } + } + + return ret; + } + + public long getDatasetId(String datasetName) { + GdsDatasetEvaluator evaluator = getDatasetEvaluator(datasetName); + + return evaluator == null ? -1 : evaluator.getId(); + } + + public long getProjectId(String projectName) { + GdsProjectEvaluator evaluator = getProjectEvaluator(projectName); + + return evaluator == null ? -1 : evaluator.getId(); + } + + public String getDatasetName(Long id) { + GdsDatasetEvaluator evaluator = datasets.get(id); + + return evaluator == null ? null : evaluator.getName(); + } + + public String getProjectName(Long id) { + GdsProjectEvaluator evaluator = projects.get(id); + + return evaluator == null ? null : evaluator.getName(); + } + + public Iterator getDatasetResources(long datasetId) { + Set dshEvaluators = new TreeSet<>(GdsDataShareEvaluator.EVAL_ORDER_COMPARATOR); + + collectDataSharesForDataset(datasetId, dshEvaluators); + + return new SharedResourceIter(dshEvaluators); + } + + public Iterator getProjectResources(long projectId) { + Set dshEvaluators = new TreeSet<>(GdsDataShareEvaluator.EVAL_ORDER_COMPARATOR); + + collectDataSharesForProject(projectId, dshEvaluators); + + return new SharedResourceIter(dshEvaluators); + } + + public Iterator getDataShareResources(long dataShareId) { + GdsDataShareEvaluator dshEvaluator = dataShares.get(dataShareId); + Set dshEvaluators = dshEvaluator == null ? Collections.emptySet() : Collections.singleton(dshEvaluator); + + return new SharedResourceIter(dshEvaluators); + } + + public Iterator getResources(List projectIds, List datasetIds, List dataShareIds) { + Set dshEvaluators = new TreeSet<>(GdsDataShareEvaluator.EVAL_ORDER_COMPARATOR); + + collectDataShares(projectIds, datasetIds, dataShareIds, dshEvaluators); + + return new SharedResourceIter(dshEvaluators); + } + + + private void init(RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + LOG.debug("==> RangerGdsPolicyEngine.init()"); + + preprocess(serviceDefHelper); + + RangerServiceDef gdsServiceDef = gdsInfo.getGdsServiceDef(); + RangerPolicyEngineOptions options = new RangerPolicyEngineOptions(pluginContext.getConfig().getPolicyEngineOptions(), new RangerServiceDefHelper(gdsServiceDef, false)); + + gdsInfo.getProjects().forEach(project -> projects.put(project.getId(), new GdsProjectEvaluator(project, gdsServiceDef, options))); + + gdsInfo.getDatasets().forEach(dataset -> datasets.put(dataset.getId(), new GdsDatasetEvaluator(dataset, gdsServiceDef, options))); + + gdsInfo.getDataShares().forEach(dataShare -> dataShares.put(dataShare.getId(), new GdsDataShareEvaluator(dataShare, serviceDefHelper))); + + gdsInfo.getDshids().forEach(dshid -> { + if (dshid.getStatus() == RangerGds.GdsShareStatus.ACTIVE) { + GdsDataShareEvaluator dshEvaluator = dataShares.get(dshid.getDataShareId()); + + if (dshEvaluator != null) { + GdsDatasetEvaluator datasetEvaluator = datasets.get(dshid.getDatasetId()); + + if (datasetEvaluator != null) { + GdsDshidEvaluator dshidEvaluator = new GdsDshidEvaluator(dshid, datasetEvaluator); + + dshEvaluator.addDshidEvaluator(dshidEvaluator); + } else { + LOG.error("RangerGdsPolicyEngine(): invalid datasetId in dshid: {}. Ignored", dshid); + } + } else { + LOG.error("RangerGdsPolicyEngine(): invalid dataShareId in dshid: {}. Ignored", dshid); + } + } else { + LOG.error("RangerGdsPolicyEngine(): dshid is not active {}. Ignored", dshid); + } + }); + + gdsInfo.getDips().forEach(dip -> { + if (dip.getStatus() == RangerGds.GdsShareStatus.ACTIVE) { + GdsDatasetEvaluator datasetEvaluator = datasets.get(dip.getDatasetId()); + + if (datasetEvaluator != null) { + GdsProjectEvaluator projectEvaluator = projects.get(dip.getProjectId()); + + if (projectEvaluator != null) { + GdsDipEvaluator dipEvaluator = new GdsDipEvaluator(dip, projectEvaluator); + + datasetEvaluator.addDipEvaluator(dipEvaluator); + } else { + LOG.error("RangerGdsPolicyEngine(): invalid projectId in dip: {}. Ignored", dip); + } + } else { + LOG.error("RangerGdsPolicyEngine(): invalid datasetId in dip: {}. Ignored", dip); + } + } else { + LOG.error("RangerGdsPolicyEngine(): dip is not active {}. Ignored", dip); + } + }); + + // purge dataShares that are not part of any dataset + dataShares.values().removeIf(evaluator -> CollectionUtils.isEmpty(evaluator.getDshidEvaluators())); + + Map> zoneResEvaluators = new HashMap<>(); + + gdsInfo.getResources().forEach(resource -> { + GdsDataShareEvaluator dshEvaluator = dataShares.get(resource.getDataShareId()); + + if (dshEvaluator != null) { + GdsSharedResourceEvaluator evaluator = new GdsSharedResourceEvaluator(resource, dshEvaluator.getDefaultAccessTypes(), serviceDefHelper, pluginContext); + + dshEvaluator.addResourceEvaluator(evaluator); + + zoneResEvaluators.computeIfAbsent(dshEvaluator.getZoneName(), k -> new ArrayList<>()).add(evaluator); + } + }); + + zoneResEvaluators.forEach((zoneName, evaluators) -> zoneResources.put(zoneName, new GdsZoneResources(zoneName, evaluators, serviceDefHelper, pluginContext))); + + LOG.debug("<== RangerGdsPolicyEngine.init()"); + } + + private void preprocess(RangerServiceDefHelper serviceDefHelper) { + if (gdsInfo.getProjects() == null) { + gdsInfo.setProjects(Collections.emptyList()); + } + + if (gdsInfo.getDatasets() == null) { + gdsInfo.setDatasets(Collections.emptyList()); + } + + if (gdsInfo.getDataShares() == null) { + gdsInfo.setDataShares(Collections.emptyList()); + } else { + gdsInfo.getDataShares().stream().filter(dsh -> dsh.getZoneName() == null).forEach(dsh -> dsh.setZoneName(StringUtils.EMPTY)); + } + + if (gdsInfo.getResources() == null) { + gdsInfo.setResources(Collections.emptyList()); + } + + if (gdsInfo.getDshids() == null) { + gdsInfo.setDshids(Collections.emptyList()); + } + + if (gdsInfo.getDips() == null) { + gdsInfo.setDips(Collections.emptyList()); + } + + RangerServiceDef gdsServiceDef = gdsInfo.getGdsServiceDef(); + + // populate accessTypes in GDS servicedef with implied accessTypes from the service + for (RangerAccessTypeDef gdsAccessTypeDef : gdsServiceDef.getAccessTypes()) { + Collection impliedGrants = serviceDefHelper.getImpliedAccessGrants().get(gdsAccessTypeDef.getName()); + + if (impliedGrants != null) { + gdsAccessTypeDef.getImpliedGrants().addAll(impliedGrants); + } + } + + gdsServiceDef.getAccessTypes().addAll(serviceDefHelper.getServiceDef().getAccessTypes()); + } + + private Map> getDataShareResources(RangerAccessRequest request) { + LOG.debug("==> RangerGdsPolicyEngine.getDataShareResources({})", request); + + final Map> ret; + + if (!dataShares.isEmpty()) { + Set zoneNames = RangerAccessRequestUtil.getResourceZoneNamesFromContext(request.getContext()); + + if (zoneNames == null || zoneNames.isEmpty()) { + zoneNames = Collections.singleton(StringUtils.EMPTY); // unzoned + } else if (zoneNames.size() > 1 && !request.isAccessTypeAny()) { + LOG.warn("RangerGdsPolicyEngine.getDataShareResources(): resource matches multiple zones and accessType is not ANY - ignored. resource={}, zones={}", request.getResource(), zoneNames); + + zoneNames = Collections.emptySet(); + } + + ret = new TreeMap<>(GdsDataShareEvaluator.EVAL_ORDER_COMPARATOR); + + zoneNames.stream().map(zoneResources::get).filter(Objects::nonNull).forEach(zr -> zr.collectDataShareResources(request, ret)); + } else { + ret = Collections.emptyMap(); + } + + LOG.debug("<== RangerGdsPolicyEngine.getDataShareResources({}): {}", request, ret); + + return ret; + } + + private void evaluateDatasetPolicies(Set datasets, RangerAccessRequest request, GdsAccessResult result, Set projectsToEval) { + datasets.forEach(e -> e.evaluate(request, result, projectsToEval)); + } + + private void evaluateProjectPolicies(Set projects, RangerAccessRequest request, GdsAccessResult result) { + projects.forEach(e -> e.evaluate(request, result)); + } + + private GdsDatasetEvaluator getDatasetEvaluator(String dsName) { + return datasets.values().stream().filter(e -> StringUtils.equals(e.getName(), dsName)).findFirst().orElse(null); + } + + private GdsProjectEvaluator getProjectEvaluator(String projectName) { + return projects.values().stream().filter(e -> StringUtils.equals(e.getName(), projectName)).findFirst().orElse(null); + } + + private void collectDataSharesForDataset(Long datasetId, Set evaluators) { + dataShares.values().stream().filter(e -> e.isInDataset(datasetId)).forEach(evaluators::add); + } + + private void collectDataSharesForProject(Long projectId, Set evaluators) { + dataShares.values().stream().filter(e -> e.isInProject(projectId)).forEach(evaluators::add); + } + + private void collectDataShares(List projectIds, List datasetIds, List dataShareIds, Set evaluators) { + if (projectIds != null) { + projectIds.forEach(projectId -> collectDataSharesForProject(projectId, evaluators)); + } + + if (datasetIds != null) { + datasetIds.forEach(datasetId -> collectDataSharesForDataset(datasetId, evaluators)); + } + + if (dataShareIds != null) { + dataShareIds.stream().map(dataShares::get).filter(Objects::nonNull).forEach(evaluators::add); + } + } + + private Set getAllAccessTypes(RangerServiceDefHelper serviceDefHelper) { + return serviceDefHelper.getServiceDef().getAccessTypes().stream().map(RangerAccessTypeDef::getName).collect(Collectors.toSet()); + } + + private class GdsZoneResources { + private final String zoneName; + private final Map> resourceTries; + + public GdsZoneResources(String zoneName, List evaluators, RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + this.zoneName = zoneName; + this.resourceTries = createResourceTries(evaluators, serviceDefHelper, pluginContext); + } + + public String getZoneName() { return zoneName; } + + public void collectDataShareResources(RangerAccessRequest request, Map> dshResources) { + Collection resources = RangerResourceEvaluatorsRetriever.getEvaluators(resourceTries, request.getResource().getAsMap(), request.getResourceElementMatchingScopes()); + + if (resources != null) { + for (GdsSharedResourceEvaluator resource : resources) { + if (!resource.isAllowed(request)) { + continue; + } + + GdsDataShareEvaluator dataShare = dataShares.get(resource.getDataShareId()); + + if (dataShare == null) { + continue; + } + + dshResources.computeIfAbsent(dataShare, l -> new TreeSet<>(GdsSharedResourceEvaluator.EVAL_ORDER_COMPARATOR)).add(resource); + } + } + } + + private Map> createResourceTries(List evaluators, RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + Map> ret = new HashMap<>(); + + for (RangerResourceDef resourceDef : serviceDefHelper.getServiceDef().getResources()) { + ret.put(resourceDef.getName(), new RangerResourceTrie<>(resourceDef, evaluators, true, pluginContext)); + } + + return ret; + } + } + + static class SharedResourceIter implements Iterator { + private final Iterator dataShareIter; + private Iterator sharedResourceIter = Collections.emptyIterator(); + private GdsSharedResourceEvaluator nextResource = null; + + SharedResourceIter(Set evaluators) { + if (evaluators == null) { + dataShareIter = Collections.emptyIterator(); + } else { + dataShareIter = evaluators.iterator(); + } + + setNext(); + } + + @Override + public boolean hasNext() { + return nextResource != null; + } + + @Override + public GdsSharedResourceEvaluator next() { + GdsSharedResourceEvaluator ret = nextResource; + + if (ret != null) { + setNext(); + } + + return ret; + } + + private void setNext() { + if (!sharedResourceIter.hasNext()) { + while (dataShareIter.hasNext()) { + GdsDataShareEvaluator dataShareEvaluator = dataShareIter.next(); + + sharedResourceIter = dataShareEvaluator.getResourceEvaluators().iterator(); + + if (sharedResourceIter.hasNext()) { + break; + } + } + } + + nextResource = sharedResourceIter.hasNext() ? sharedResourceIter.next() : null; + } + } +} + +/* + dataShare-1 ----------------------- dataset-1 --- + resource-11 / \ + resource-12 / \ + / \ + dataShare-2 -------------------| | ---- project-1 + resource-21 \ / + resource-22 \ / + -- dataset-2--- + / + dataShare-3 --------------------- + resource-31 + + dataShare-4 ------------------------- dataset-3 --------- project-2 + resource-41 + + dataShare-5 ------------------------- dataset-4 + resource-51 + */ \ No newline at end of file diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsProjectEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsProjectEvaluator.java new file mode 100644 index 0000000000..ea47702395 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsProjectEvaluator.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyevaluator.RangerOptimizedPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo.ProjectInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Set; + +public class GdsProjectEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsDatasetEvaluator.class); + + public static final GdsProjectEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsProjectEvalOrderComparator(); + + private final ProjectInfo project; + private final RangerServiceDef gdsServiceDef; + private final String name; + private final RangerValidityScheduleEvaluator scheduleEvaluator; + private final List policyEvaluators; + + public GdsProjectEvaluator(ProjectInfo project, RangerServiceDef gdsServiceDef, RangerPolicyEngineOptions options) { + LOG.debug("==> GdsProjectEvaluator({})", project); + + this.project = project; + this.gdsServiceDef = gdsServiceDef; + this.name = StringUtils.isBlank(project.getName()) ? StringUtils.EMPTY : project.getName(); + + if (project.getValiditySchedule() != null) { + scheduleEvaluator = new RangerValidityScheduleEvaluator(project.getValiditySchedule()); + } else { + scheduleEvaluator = null; + } + + if (project.getPolicies() != null) { + policyEvaluators = new ArrayList<>(project.getPolicies().size()); + + for (RangerPolicy policy : project.getPolicies()) { + RangerPolicyEvaluator evaluator = new RangerOptimizedPolicyEvaluator(); + + evaluator.init(policy, gdsServiceDef, options); + + policyEvaluators.add(evaluator); + } + } else { + policyEvaluators = Collections.emptyList(); + } + + LOG.debug("<== GdsProjectEvaluator({})", project); + } + + public Long getId() { + return project.getId(); + } + + public String getName() { + return name; + } + + public boolean isActive() { + return scheduleEvaluator == null || scheduleEvaluator.isApplicable(System.currentTimeMillis()); + } + + public void evaluate(RangerAccessRequest request, GdsAccessResult result) { + LOG.debug("==> GdsDatasetEvaluator.evaluate({}, {})", request, result); + + if (isActive()) { + result.addProject(getName()); + + if (!policyEvaluators.isEmpty()) { + GdsProjectAccessRequest projectRequest = new GdsProjectAccessRequest(getId(), gdsServiceDef, request); + RangerAccessResult projectResult = projectRequest.createAccessResult(); + + try { + RangerAccessRequestUtil.setAllRequestedAccessTypes(projectRequest.getContext(), null); + RangerAccessRequestUtil.setAccessTypeACLResults(projectRequest.getContext(), null); + + policyEvaluators.forEach(e -> e.evaluate(projectRequest, projectResult)); + } finally { + RangerAccessRequestUtil.setAccessTypeResults(projectRequest.getContext(), null); + RangerAccessRequestUtil.setAccessTypeACLResults(projectRequest.getContext(), null); + } + + if (projectResult.getIsAllowed()) { + result.addAllowedByProject(getName()); + } + + if (!result.getIsAllowed()) { + if (projectResult.getIsAllowed()) { + result.setIsAllowed(true); + result.setPolicyId(projectResult.getPolicyId()); + result.setPolicyVersion(projectResult.getPolicyVersion()); + } + } + + if (!result.getIsAudited()) { + result.setIsAudited(projectResult.getIsAudited()); + } + } + } + + LOG.debug("<== GdsDatasetEvaluator.evaluate({}, {})", request, result); + } + + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set allowedAccessTypes) { + if (isActive()) { + acls.getProjects().add(getName()); + + if (!policyEvaluators.isEmpty()) { + GdsProjectAccessRequest projectRequest = new GdsProjectAccessRequest(getId(), gdsServiceDef, request); + + for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) { + policyEvaluator.getResourceACLs(projectRequest, acls, isConditional, allowedAccessTypes, RangerPolicyResourceMatcher.MatchType.SELF, null); + } + } + } + } + + public boolean hasReference(Set users, Set groups, Set roles) { + boolean ret = false; + + for (RangerPolicyEvaluator policyEvaluator : policyEvaluators) { + ret = policyEvaluator.hasReference(users, groups, roles); + + if (ret) { + break; + } + } + + return ret; + } + + + private static class GdsProjectAccessRequest extends RangerAccessRequestImpl { + public GdsProjectAccessRequest(Long projectId, RangerServiceDef gdsServiceDef, RangerAccessRequest request) { + super.setResource(new RangerProjectResource(projectId, gdsServiceDef, request.getResource().getOwnerUser())); + + super.setUser(request.getUser()); + super.setUserGroups(request.getUserGroups()); + super.setUserRoles(request.getUserRoles()); + super.setAction(request.getAction()); + super.setAccessType(request.getAccessType()); + super.setAccessTime(request.getAccessTime()); + super.setRequestData(request.getRequestData()); + super.setContext(request.getContext()); + super.setClientType(request.getClientType()); + super.setClientIPAddress(request.getClientIPAddress()); + super.setRemoteIPAddress(request.getRemoteIPAddress()); + super.setForwardedAddresses(request.getForwardedAddresses()); + super.setSessionId(request.getSessionId()); + super.setResourceMatchingScope(request.getResourceMatchingScope()); + } + + public RangerAccessResult createAccessResult() { + return new RangerAccessResult(RangerPolicy.POLICY_TYPE_ACCESS, GdsPolicyEngine.GDS_SERVICE_NAME, getResource().getServiceDef(), this); + } + } + + public static class RangerProjectResource extends RangerAccessResourceImpl { + public RangerProjectResource(Long projectd, RangerServiceDef gdsServiceDef, String ownerUser) { + super.setValue(GdsPolicyEngine.RESOURCE_NAME_PROJECT_ID, projectd.toString()); + super.setServiceDef(gdsServiceDef); + super.setOwnerUser(ownerUser); + } + } + + public static class GdsProjectEvalOrderComparator implements Comparator { + @Override + public int compare(GdsProjectEvaluator me, GdsProjectEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = me.getName().compareTo(other.getName()); + + if (ret == 0) { + ret = me.getId().compareTo(other.getId()); + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsSharedResourceEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsSharedResourceEvaluator.java new file mode 100644 index 0000000000..d03dd00c90 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyengine/gds/GdsSharedResourceEvaluator.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine.gds; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyevaluator.RangerCustomConditionEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; +import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; +import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo.SharedResourceInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class GdsSharedResourceEvaluator implements RangerResourceEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(GdsSharedResourceEvaluator.class); + + public static final GdsSharedResourceEvalOrderComparator EVAL_ORDER_COMPARATOR = new GdsSharedResourceEvalOrderComparator(); + + private final SharedResourceInfo resource; + private final RangerConditionEvaluator conditionEvaluator; + private final Map policyResource; + private final RangerPolicyResourceMatcher policyResourceMatcher; + private final RangerResourceDef leafResourceDef; + private final Set allowedAccessTypes; + + public GdsSharedResourceEvaluator(SharedResourceInfo resource, Set defaultAccessTypes, RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + this.resource = resource; + this.conditionEvaluator = RangerCustomConditionEvaluator.getInstance().getExpressionEvaluator(resource.getConditionExpr(), serviceDefHelper.getServiceDef()); + + if (this.resource.getResource() == null) { + this.resource.setResource(Collections.emptyMap()); + } + + if (StringUtils.isNotBlank(resource.getSubResourceType()) && resource.getSubResource() != null && CollectionUtils.isNotEmpty(resource.getSubResource().getValues())) { + this.policyResource = new HashMap<>(resource.getResource()); + + this.policyResource.put(resource.getSubResourceType(), resource.getSubResource()); + } else { + this.policyResource = resource.getResource(); + } + + this.policyResourceMatcher = initPolicyResourceMatcher(policyResource, serviceDefHelper, pluginContext); + this.leafResourceDef = ServiceDefUtil.getLeafResourceDef(serviceDefHelper.getServiceDef(), policyResource); + this.allowedAccessTypes = serviceDefHelper.expandImpliedAccessGrants(resource.getAccessTypes() != null ? resource.getAccessTypes() : defaultAccessTypes); + + LOG.debug("GdsSharedResourceEvaluator: resource={}, conditionEvaluator={}, policyResource={}, leafResourceDef={}, allowedAccessTypes={}", + resource, conditionEvaluator, policyResource, leafResourceDef, allowedAccessTypes); + } + + @Override + public long getId() { + return resource.getId(); + } + + @Override + public RangerPolicyResourceMatcher getPolicyResourceMatcher() { + return policyResourceMatcher; + } + + @Override + public Map getPolicyResource() { + return policyResource; + } + + @Override + public RangerResourceMatcher getResourceMatcher(String resourceName) { + return policyResourceMatcher.getResourceMatcher(resourceName); + } + + @Override + public boolean isAncestorOf(RangerResourceDef resourceDef) { + return ServiceDefUtil.isAncestorOf(policyResourceMatcher.getServiceDef(), leafResourceDef, resourceDef); + } + + @Override + public boolean isLeaf(String resourceName) { return StringUtils.equals(leafResourceDef.getName(), resourceName); } + + public Long getDataShareId() { return resource.getDataShareId(); } + + public Collection getResourceKeys() { + return resource != null && resource.getResource() != null ? resource.getResource().keySet() : Collections.emptySet(); + } + + public boolean isConditional() { return conditionEvaluator != null; } + + public Set getAllowedAccessTypes() { return allowedAccessTypes; } + + public boolean isAllowed(RangerAccessRequest request) { + LOG.debug("==> GdsSharedResourceEvaluator.evaluate({})", request); + + boolean ret = conditionEvaluator == null || conditionEvaluator.isMatched(request); + + if (ret) { + ret = request.isAccessTypeAny() ? !allowedAccessTypes.isEmpty() : allowedAccessTypes.contains(request.getAccessType()); + + if (ret) { + final RangerAccessRequest.ResourceMatchingScope resourceMatchingScope = request.getResourceMatchingScope() != null ? request.getResourceMatchingScope() : RangerAccessRequest.ResourceMatchingScope.SELF; + final MatchType matchType = policyResourceMatcher.getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()); + + if (request.isAccessTypeAny() || resourceMatchingScope == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) { + ret = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS || matchType == RangerPolicyResourceMatcher.MatchType.DESCENDANT; + } else { + ret = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS; + } + + if (!ret) { + LOG.debug("GdsSharedResourceEvaluator.evaluate({}): not matched for resource {}", request, request.getResource()); + } + } else { + LOG.debug("GdsSharedResourceEvaluator.evaluate({}): not matched for accessType {}", request, request.getAccessType()); + } + } else { + LOG.debug("GdsSharedResourceEvaluator.evaluate({}): not matched for condition {}", request, resource.getConditionExpr()); + } + + LOG.debug("<== GdsSharedResourceEvaluator.evaluate({})", request); + + return ret; + } + + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, List dshidEvaluators) { + LOG.debug("==> GdsSharedResourceEvaluator.getResourceACLs({}, {})", request, acls); + + boolean isResourceMatch = policyResourceMatcher.isMatch(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()); + + if (isResourceMatch) { + isConditional = isConditional || conditionEvaluator != null; + + for (GdsDshidEvaluator dshidEvaluator : dshidEvaluators) { + dshidEvaluator.getResourceACLs(request, acls, isConditional, getAllowedAccessTypes()); + } + } + + LOG.debug("<== GdsSharedResourceEvaluator.getResourceACLs({}, {})", request, acls); + } + + public RangerPolicyItemRowFilterInfo getRowFilter() { + return resource.getRowFilter(); + } + + public RangerPolicyItemDataMaskInfo getDataMask(String subResourceName) { + return resource.getSubResourceMasks() != null ? resource.getSubResourceMasks().get(subResourceName) : null; + } + + private static RangerPolicyResourceMatcher initPolicyResourceMatcher(Map policyResource, RangerServiceDefHelper serviceDefHelper, RangerPluginContext pluginContext) { + RangerDefaultPolicyResourceMatcher matcher = new RangerDefaultPolicyResourceMatcher(); + + matcher.setServiceDefHelper(serviceDefHelper); + matcher.setServiceDef(serviceDefHelper.getServiceDef()); + matcher.setPolicyResources(policyResource, RangerPolicy.POLICY_TYPE_ACCESS); + matcher.setPluginContext(pluginContext); + + matcher.init(); + + return matcher; + } + + public static class GdsSharedResourceEvalOrderComparator implements Comparator { + @Override + public int compare(GdsSharedResourceEvaluator me, GdsSharedResourceEvaluator other) { + int ret = 0; + + if (me != null && other != null) { + ret = compareStrings(me.resource.getName(), other.resource.getName()); + + if (ret == 0) { + ret = Integer.compare(me.resource.getResource().size(), other.resource.getResource().size()); + + if (ret == 0) { + ret = Long.compare(me.getId(), other.getId()); + } + } + } else if (me != null) { + ret = -1; + } else if (other != null) { + ret = 1; + } + + return ret; + } + + static int compareStrings(String str1, String str2) { + if (str1 == null) { + return str2 == null ? 0 : -1; + } else { + return str2 == null ? 1 : str1.compareTo(str2); + } + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java index 99ae598a0d..60399e8bb8 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyEvaluator.java @@ -19,29 +19,68 @@ package org.apache.ranger.plugin.policyevaluator; - - import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.PolicyEngine; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceMatchingScope; import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs.DataMaskResult; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs.RowFilterResult; +import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; +import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; +import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; +import org.apache.ranger.plugin.util.RangerRequestExprResolver; import org.apache.ranger.plugin.util.ServiceDefUtil; - +import org.apache.ranger.plugin.util.StringTokenReplacer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; public abstract class RangerAbstractPolicyEvaluator implements RangerPolicyEvaluator { - private static final Log LOG = LogFactory.getLog(RangerAbstractPolicyEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerAbstractPolicyEvaluator.class); + + private static final AtomicLong NEXT_RESOURCE_EVALUATOR_ID = new AtomicLong(1); + + private final static Map WILDCARD_EVAL_CONTEXT = new HashMap() { + @Override + public boolean containsKey(Object key) { return true; } - private RangerPolicy policy; - private RangerServiceDef serviceDef; - private RangerResourceDef leafResourceDef; - private int evalOrder; - protected RangerPluginContext pluginContext = null; + @Override + public Object get(Object key) { return RangerAbstractResourceMatcher.WILDCARD_ASTERISK; } + }; + + static { + WILDCARD_EVAL_CONTEXT.put(RangerAbstractResourceMatcher.WILDCARD_ASTERISK, RangerAbstractResourceMatcher.WILDCARD_ASTERISK); + } + + private RangerPolicy policy; + private RangerServiceDef serviceDef; + private boolean needsDynamicEval = false; + private int evalOrder; + private List resourceEvaluators = Collections.emptyList(); + protected RangerPluginContext pluginContext = null; public void setPluginContext(RangerPluginContext pluginContext) { this.pluginContext = pluginContext; } @@ -54,23 +93,38 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE LOG.debug("==> RangerAbstractPolicyEvaluator.init(" + policy + ", " + serviceDef + ")"); } - this.policy = policy; - this.serviceDef = serviceDef; - this.leafResourceDef = ServiceDefUtil.getLeafResourceDef(serviceDef, getPolicyResource()); + this.policy = getPrunedPolicy(policy); + this.serviceDef = serviceDef; + this.needsDynamicEval = false; + + List resourceEvaluators = new ArrayList<>(); + RangerDefaultPolicyResourceEvaluator resourceEvaluator = new RangerDefaultPolicyResourceEvaluator(NEXT_RESOURCE_EVALUATOR_ID.getAndIncrement(), policy.getResources(), getPolicyType(), serviceDef, options.getServiceDefHelper()); + + resourceEvaluators.add(resourceEvaluator); + + this.needsDynamicEval = this.needsDynamicEval || resourceEvaluator.getPolicyResourceMatcher().getNeedsDynamicEval(); + + if (CollectionUtils.isNotEmpty(policy.getAdditionalResources())) { + for (Map additionalResource : policy.getAdditionalResources()) { + resourceEvaluator = new RangerDefaultPolicyResourceEvaluator(NEXT_RESOURCE_EVALUATOR_ID.getAndIncrement(), additionalResource, getPolicyType(), serviceDef, options.getServiceDefHelper()); + + resourceEvaluators.add(resourceEvaluator); + + this.needsDynamicEval = this.needsDynamicEval || resourceEvaluator.getPolicyResourceMatcher().getNeedsDynamicEval(); + } + } + + this.resourceEvaluators = resourceEvaluators; if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerAbstractPolicyEvaluator.init(" + policy + ", " + serviceDef + ")"); + LOG.debug("<== RangerAbstractPolicyEvaluator.init(" + this.policy + ", " + serviceDef + ")"); } } - @Override - public long getId() { - return policy != null ? policy.getId() :-1; - } + public int getPolicyType() { + Integer ret = policy != null ? policy.getPolicyType() : null; - @Override - public Map getPolicyResource() { - return policy !=null ? policy.getResources() : null; + return ret != null ? ret.intValue() : RangerPolicy.POLICY_TYPE_ACCESS; } @Override @@ -78,19 +132,68 @@ public RangerPolicy getPolicy() { return policy; } + @Override + public long getPolicyId() { + Long ret = policy != null ? policy.getId() : null; + + return ret != null ? ret.longValue() : -1; + } + @Override public int getPolicyPriority() { return policy != null && policy.getPolicyPriority() != null ? policy.getPolicyPriority() : RangerPolicy.POLICY_PRIORITY_NORMAL; } + @Override + public List getResourceEvaluators() { + return resourceEvaluators; + } + @Override public RangerServiceDef getServiceDef() { return serviceDef; } @Override - public boolean isAncestorOf(RangerResourceDef resourceDef) { - return ServiceDefUtil.isAncestorOf(serviceDef, leafResourceDef, resourceDef); + public void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set targetAccessTypes, MatchType matchType, PolicyEngine policyEngine) { + boolean isMatched = false; + boolean isConditionalMatch = false; + + if (matchType == null) { + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + RangerPolicyResourceMatcher matcher = resourceEvaluator.getPolicyResourceMatcher(); + + matchType = matcher.getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()); + + isMatched = isMatch(matchType, request.getResourceMatchingScope()); + + if (isMatched) { + break; + } else if (matcher.getNeedsDynamicEval() && !isConditionalMatch && policyEngine != null) { + MatchType dynWildCardMatch = resourceEvaluator.getMacrosReplaceWithWildcardMatcher(policyEngine).getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()); + + isConditionalMatch = isMatch(dynWildCardMatch, request.getResourceMatchingScope()); + } + } + } else { + isMatched = isMatch(matchType, request.getResourceMatchingScope()); + } + + if (isMatched || isConditionalMatch) { + if (!isConditionalMatch) { + isConditionalMatch = isConditional || getPolicyConditionsCount() > 0 || getValidityScheduleEvaluatorsCount() != 0; + } + + int policyType = getPolicyType(); + + if (policyType == RangerPolicy.POLICY_TYPE_ACCESS) { + updateFromPolicyACLs(isConditionalMatch, acls, targetAccessTypes); + } else if (policyType == RangerPolicy.POLICY_TYPE_ROWFILTER) { + updateRowFiltersFromPolicy(isConditionalMatch, acls, targetAccessTypes); + } else if (policyType == RangerPolicy.POLICY_TYPE_DATAMASK) { + updateDataMasksFromPolicy(isConditionalMatch, acls, targetAccessTypes); + } + } } public boolean hasAllow() { @@ -105,6 +208,64 @@ public boolean hasDeny() { return policy != null && (policy.getIsDenyAllElse() || CollectionUtils.isNotEmpty(policy.getDenyPolicyItems())); } + protected boolean needsDynamicEval() { return needsDynamicEval; } + + private RangerPolicy getPrunedPolicy(final RangerPolicy policy) { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerAbstractPolicyEvaluator.getPrunedPolicy(" + policy + ")"); + } + + final RangerPolicy ret; + + final boolean isPruningNeeded; + final List prunedAllowItems; + final List prunedDenyItems; + final List prunedAllowExceptions; + final List prunedDenyExceptions; + + final RangerPluginContext pluginContext = getPluginContext(); + + if (pluginContext != null && pluginContext.getConfig().getPolicyEngineOptions().evaluateDelegateAdminOnly) { + prunedAllowItems = policy.getPolicyItems().stream().filter(RangerPolicy.RangerPolicyItem::getDelegateAdmin).collect(Collectors.toList()); + prunedDenyItems = policy.getDenyPolicyItems().stream().filter(RangerPolicy.RangerPolicyItem::getDelegateAdmin).collect(Collectors.toList()); + prunedAllowExceptions = policy.getAllowExceptions().stream().filter(RangerPolicy.RangerPolicyItem::getDelegateAdmin).collect(Collectors.toList()); + prunedDenyExceptions = policy.getDenyExceptions().stream().filter(RangerPolicy.RangerPolicyItem::getDelegateAdmin).collect(Collectors.toList()); + + isPruningNeeded = prunedAllowItems.size() != policy.getPolicyItems().size() + || prunedDenyItems.size() != policy.getDenyPolicyItems().size() + || prunedAllowExceptions.size() != policy.getAllowExceptions().size() + || prunedDenyExceptions.size() != policy.getDenyExceptions().size(); + } else { + prunedAllowItems = null; + prunedDenyItems = null; + prunedAllowExceptions = null; + prunedDenyExceptions = null; + isPruningNeeded = false; + } + + if (!isPruningNeeded) { + ret = policy; + } else { + ret = new RangerPolicy(); + ret.updateFrom(policy); + + ret.setId(policy.getId()); + ret.setGuid(policy.getGuid()); + ret.setVersion(policy.getVersion()); + ret.setServiceType(policy.getServiceType()); + + ret.setPolicyItems(prunedAllowItems); + ret.setDenyPolicyItems(prunedDenyItems); + ret.setAllowExceptions(prunedAllowExceptions); + ret.setDenyExceptions(prunedDenyExceptions); + } + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerAbstractPolicyEvaluator.getPrunedPolicy(isPruningNeeded=" + isPruningNeeded + ") : " + ret); + } + + return ret; + } + @Override public int getEvalOrder() { return evalOrder; @@ -143,4 +304,316 @@ public StringBuilder toString(StringBuilder sb) { return sb; } + + private boolean isMatch(MatchType matchType, ResourceMatchingScope matchingScope) { + final boolean ret; + + matchingScope = matchingScope != null ? matchingScope : ResourceMatchingScope.SELF; + + if (matchingScope == ResourceMatchingScope.SELF_OR_DESCENDANTS) { + ret = matchType == MatchType.SELF || matchType == MatchType.SELF_AND_ALL_DESCENDANTS || matchType == MatchType.DESCENDANT; + } else { + ret = matchType == MatchType.SELF || matchType == MatchType.SELF_AND_ALL_DESCENDANTS; + } + + return ret; + } + + + private void updateFromPolicyACLs(boolean isConditional, RangerResourceACLs resourceACLs, Set targetAccessTypes) { + PolicyACLSummary aclSummary = getPolicyACLSummary(); + + if (aclSummary == null) { + return; + } + + for (Map.Entry> userAccessInfo : aclSummary.getUsersAccessInfo().entrySet()) { + final String userName = userAccessInfo.getKey(); + + for (Map.Entry accessInfo : userAccessInfo.getValue().entrySet()) { + String accessType = accessInfo.getKey(); + + if (targetAccessTypes != null && !targetAccessTypes.contains(accessType)) { + continue; + } + + Integer accessResult; + + if (isConditional) { + accessResult = ACCESS_CONDITIONAL; + } else { + accessResult = accessInfo.getValue().getResult(); + + if (accessResult.equals(RangerPolicyEvaluator.ACCESS_UNDETERMINED)) { + accessResult = RangerPolicyEvaluator.ACCESS_DENIED; + } + } + + RangerPolicy policy = getPolicy(); + + resourceACLs.setUserAccessInfo(userName, accessType, accessResult, policy); + } + } + + for (Map.Entry> groupAccessInfo : aclSummary.getGroupsAccessInfo().entrySet()) { + final String groupName = groupAccessInfo.getKey(); + + for (Map.Entry accessInfo : groupAccessInfo.getValue().entrySet()) { + String accessType = accessInfo.getKey(); + + if (targetAccessTypes != null && !targetAccessTypes.contains(accessType)) { + continue; + } + + Integer accessResult; + + if (isConditional) { + accessResult = ACCESS_CONDITIONAL; + } else { + accessResult = accessInfo.getValue().getResult(); + + if (accessResult.equals(RangerPolicyEvaluator.ACCESS_UNDETERMINED)) { + accessResult = RangerPolicyEvaluator.ACCESS_DENIED; + } + } + + RangerPolicy policy = getPolicy(); + + resourceACLs.setGroupAccessInfo(groupName, accessType, accessResult, policy); + } + } + + for (Map.Entry> roleAccessInfo : aclSummary.getRolesAccessInfo().entrySet()) { + final String roleName = roleAccessInfo.getKey(); + + for (Map.Entry accessInfo : roleAccessInfo.getValue().entrySet()) { + String accessType = accessInfo.getKey(); + + if (targetAccessTypes != null && !targetAccessTypes.contains(accessType)) { + continue; + } + + Integer accessResult; + + if (isConditional) { + accessResult = ACCESS_CONDITIONAL; + } else { + accessResult = accessInfo.getValue().getResult(); + + if (accessResult.equals(RangerPolicyEvaluator.ACCESS_UNDETERMINED)) { + accessResult = RangerPolicyEvaluator.ACCESS_DENIED; + } + } + + RangerPolicy policy = getPolicy(); + + resourceACLs.setRoleAccessInfo(roleName, accessType, accessResult, policy); + } + } + } + + private void updateRowFiltersFromPolicy(boolean isConditional, RangerResourceACLs resourceACLs, Set targetAccessTypes) { + PolicyACLSummary aclSummary = getPolicyACLSummary(); + + if (aclSummary != null) { + for (RowFilterResult rowFilterResult : aclSummary.getRowFilters()) { + if (targetAccessTypes != null && !CollectionUtils.containsAny(targetAccessTypes, rowFilterResult.getAccessTypes())) { + continue; + } + + rowFilterResult = copyRowFilter(rowFilterResult); + + if (isConditional) { + rowFilterResult.setIsConditional(true); + } + + resourceACLs.getRowFilters().add(rowFilterResult); + } + } + } + + private void updateDataMasksFromPolicy(boolean isConditional, RangerResourceACLs resourceACLs, Set targetAccessTypes) { + PolicyACLSummary aclSummary = getPolicyACLSummary(); + + if (aclSummary != null) { + for (DataMaskResult dataMaskResult : aclSummary.getDataMasks()) { + if (targetAccessTypes != null && !CollectionUtils.containsAny(targetAccessTypes, dataMaskResult.getAccessTypes())) { + continue; + } + + dataMaskResult = copyDataMask(dataMaskResult); + + if (isConditional) { + dataMaskResult.setIsConditional(true); + } + + resourceACLs.getDataMasks().add(dataMaskResult); + } + } + } + + private DataMaskResult copyDataMask(DataMaskResult dataMask) { + DataMaskResult ret = new DataMaskResult(copyStrings(dataMask.getUsers()), + copyStrings(dataMask.getGroups()), + copyStrings(dataMask.getRoles()), + copyStrings(dataMask.getAccessTypes()), + new RangerPolicyItemDataMaskInfo(dataMask.getMaskInfo())); + + ret.setIsConditional(dataMask.getIsConditional()); + + return ret; + } + + private RowFilterResult copyRowFilter(RowFilterResult rowFilter) { + RowFilterResult ret = new RowFilterResult(copyStrings(rowFilter.getUsers()), + copyStrings(rowFilter.getGroups()), + copyStrings(rowFilter.getRoles()), + copyStrings(rowFilter.getAccessTypes()), + new RangerPolicyItemRowFilterInfo(rowFilter.getFilterInfo())); + + ret.setIsConditional(rowFilter.getIsConditional()); + + return ret; + } + + private Set copyStrings(Set values) { + return values != null ? new HashSet<>(values) : null; + } + + private Map getPolicyResourcesWithMacrosReplaced(Map resources, PolicyEngine policyEngine) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerAbstractPolicyEvaluator.getPolicyResourcesWithMacrosReplaced(" + resources + ")"); + } + + final Map ret; + final Collection resourceKeys = resources == null ? null : resources.keySet(); + + if (CollectionUtils.isNotEmpty(resourceKeys)) { + ret = new HashMap<>(); + + for (String resourceName : resourceKeys) { + RangerPolicyResource resourceValues = resources.get(resourceName); + List values = resourceValues == null ? null : resourceValues.getValues(); + + if (CollectionUtils.isNotEmpty(values)) { + StringTokenReplacer tokenReplacer = policyEngine.getStringTokenReplacer(resourceName); + + List modifiedValues = new ArrayList<>(); + + for (String value : values) { + RangerRequestExprResolver exprResolver = new RangerRequestExprResolver(value, serviceDef.getName()); + String modifiedValue = exprResolver.resolveExpressions(WILDCARD_EVAL_CONTEXT); + + if (tokenReplacer != null) { + modifiedValue = tokenReplacer.replaceTokens(modifiedValue, WILDCARD_EVAL_CONTEXT); + } + + modifiedValues.add(modifiedValue); + } + + RangerPolicyResource modifiedPolicyResource = new RangerPolicyResource(modifiedValues, resourceValues.getIsExcludes(), resourceValues.getIsRecursive()); + + ret.put(resourceName, modifiedPolicyResource); + } else { + ret.put(resourceName, resourceValues); + } + } + } else { + ret = resources; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerAbstractPolicyEvaluator.getPolicyResourcesWithMacrosReplaced(" + resources + "): " + ret); + } + + return ret; + } + + public class RangerDefaultPolicyResourceEvaluator implements RangerPolicyResourceEvaluator { + private final long id; + private final Map resource; + private final RangerDefaultPolicyResourceMatcher resourceMatcher; + private final RangerResourceDef leafResourceDef; + private volatile RangerDefaultPolicyResourceMatcher macrosReplacedWithWildcardMatcher; + + public RangerDefaultPolicyResourceEvaluator(long id, Map resource, int policyType, RangerServiceDef serviceDef, RangerServiceDefHelper serviceDefHelper) { + this.id = id; + this.resource = resource; + this.leafResourceDef = ServiceDefUtil.getLeafResourceDef(serviceDef, resource); + this.resourceMatcher = new RangerDefaultPolicyResourceMatcher(); + + this.resourceMatcher.setPolicyResources(resource, policyType); + this.resourceMatcher.setServiceDef(serviceDef); + this.resourceMatcher.setServiceDefHelper(serviceDefHelper); + this.resourceMatcher.setPluginContext(pluginContext); + this.resourceMatcher.init(); + } + + @Override + public RangerPolicyEvaluator getPolicyEvaluator() { + return RangerAbstractPolicyEvaluator.this; + } + + @Override + public long getId() { + return id; + } + + @Override + public RangerPolicyResourceMatcher getPolicyResourceMatcher() { + return resourceMatcher; + } + + @Override + public RangerPolicyResourceMatcher getMacrosReplaceWithWildcardMatcher(PolicyEngine policyEngine) { + RangerDefaultPolicyResourceMatcher ret = this.macrosReplacedWithWildcardMatcher; + + if (ret == null) { + synchronized (this) { + ret = this.macrosReplacedWithWildcardMatcher; + + if (ret == null) { + if (resourceMatcher.getNeedsDynamicEval()) { + Map updatedResource = getPolicyResourcesWithMacrosReplaced(resource, policyEngine); + + ret = new RangerDefaultPolicyResourceMatcher(true); + + ret.setPolicyResources(updatedResource, resourceMatcher.getPolicyType()); + ret.setServiceDef(serviceDef); + ret.setServiceDefHelper(resourceMatcher.getServiceDefHelper()); + ret.init(); + } else { + ret = resourceMatcher; + } + + this.macrosReplacedWithWildcardMatcher = ret; + } + } + } + + return ret; + } + + @Override + public Map getPolicyResource() { + return resource; + } + + @Override + public RangerResourceMatcher getResourceMatcher(String resourceName) { + return resourceMatcher.getResourceMatcher(resourceName); + } + + @Override + public boolean isAncestorOf(RangerResourceDef resourceDef) { + if (resourceMatcher.getPolicyType() == RangerPolicy.POLICY_TYPE_AUDIT && (resource == null || resource.isEmpty())) { + return true; + } else { + return ServiceDefUtil.isAncestorOf(serviceDef, leafResourceDef, resourceDef); + } + } + + @Override + public boolean isLeaf(String resourceName) { return StringUtils.equals(resourceName, leafResourceDef.getName()); } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyItemEvaluator.java index 0f09952f7a..5370a27a39 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyItemEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAbstractPolicyItemEvaluator.java @@ -19,8 +19,10 @@ package org.apache.ranger.plugin.policyevaluator; +import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import org.apache.commons.collections.CollectionUtils; import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; @@ -50,6 +52,7 @@ public abstract class RangerAbstractPolicyItemEvaluator implements RangerPolicyI final int evalOrder; List conditionEvaluators = Collections.emptyList(); + RangerPolicyItem withImpliedGrants; RangerAbstractPolicyItemEvaluator(RangerServiceDef serviceDef, RangerPolicy policy, RangerPolicyItem policyItem, int policyItemType, int policyItemIndex, RangerPolicyEngineOptions options) { this.serviceDef = serviceDef; @@ -100,6 +103,61 @@ protected boolean getConditionsDisabledOption() { return options != null && options.disableCustomConditions; } + @Override + public RangerPolicyItem getWithImpliedGrants() { + return withImpliedGrants; + } + + protected RangerPolicyItem computeWithImpliedGrants() { + + final RangerPolicyItem ret; + + if (withImpliedGrants == null) { + if (CollectionUtils.isEmpty(policyItem.getAccesses())) { + ret = policyItem; + } else { + // Compute implied-accesses + Map> impliedAccessGrants = options.getServiceDefHelper().getImpliedAccessGrants(); + + if (impliedAccessGrants != null && !impliedAccessGrants.isEmpty()) { + ret = new RangerPolicyItem(policyItem); + + // Only one round of 'expansion' is done; multi-level impliedGrants (like shown below) are not handled for now + // multi-level impliedGrants: given admin=>write; write=>read: must imply admin=>read,write + for (Map.Entry> e : impliedAccessGrants.entrySet()) { + String implyingAccessType = e.getKey(); + Collection impliedGrants = e.getValue(); + + RangerPolicy.RangerPolicyItemAccess access = RangerDefaultPolicyEvaluator.getAccess(ret, implyingAccessType); + + if (access == null) { + continue; + } + + for (String impliedGrant : impliedGrants) { + RangerPolicy.RangerPolicyItemAccess impliedAccess = RangerDefaultPolicyEvaluator.getAccess(ret, impliedGrant); + + if (impliedAccess == null) { + impliedAccess = new RangerPolicy.RangerPolicyItemAccess(impliedGrant, access.getIsAllowed()); + + ret.addAccess(impliedAccess); + } else { + if (!impliedAccess.getIsAllowed()) { + impliedAccess.setIsAllowed(access.getIsAllowed()); + } + } + } + } + } else { + ret = policyItem; + } + } + } else { + ret = withImpliedGrants; + } + return ret; + } + private int computeEvalOrder() { int evalOrder = RANGER_POLICY_ITEM_EVAL_ORDER_DEFAULT; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAuditPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAuditPolicyEvaluator.java new file mode 100644 index 0000000000..dad135aff8 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerAuditPolicyEvaluator.java @@ -0,0 +1,401 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyevaluator; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.ranger.plugin.model.AuditFilter; +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.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +public class RangerAuditPolicyEvaluator extends RangerDefaultPolicyEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(RangerAuditPolicyEvaluator.class); + + private final RangerAuditPolicy auditPolicy; + private final boolean matchAnyResource; + private final List auditItemEvaluators = new ArrayList<>(); + + public RangerAuditPolicyEvaluator(AuditFilter auditFilter, int priority) { + this.auditPolicy = new RangerAuditPolicy(auditFilter, priority); + this.matchAnyResource = MapUtils.isEmpty(auditFilter.getResources()); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuditPolicyEvaluator(auditFilter=" + auditFilter + ", priority=" + priority + ", matchAnyResource=" + matchAnyResource + ")"); + } + } + + public RangerAuditPolicy getAuditPolicy() { return auditPolicy; } + + @Override + public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerAuditPolicyEvaluator.init(" + auditPolicy.getId() + ")"); + } + + super.init(auditPolicy, serviceDef, options); + + int policyItemIndex = 1; + + for (RangerAuditPolicyItem policyItem : auditPolicy.getAuditPolicyItems()) { + RangerAuditPolicyItemEvaluator itemEvaluator = new RangerAuditPolicyItemEvaluator(serviceDef, auditPolicy, policyItem, policyItemIndex, options); + auditItemEvaluators.add(itemEvaluator); + policyItemIndex = policyItemIndex + 1; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerAuditPolicyEvaluator.init(" + auditPolicy.getId() + ")"); + } + } + + @Override + public void evaluate(RangerAccessRequest request, RangerAccessResult result) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerAuditPolicyEvaluator.evaluate(" + auditPolicy.getId() + ", " + request + ", " + result + ")"); + } + + if (request != null && result != null) { + if (!result.getIsAuditedDetermined()) { + if (matchResource(request)) { + evaluatePolicyItems(request, result); + } + } + } + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerAuditPolicyEvaluator.evaluate(" + auditPolicy.getId() + ", " + request + ", " + result + ")"); + } + } + + @Override + protected void preprocessPolicy(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + super.preprocessPolicy(policy, serviceDef, options); + + Map> impliedAccessGrants = options.getServiceDefHelper().getImpliedAccessGrants(); + + if (impliedAccessGrants == null || impliedAccessGrants.isEmpty()) { + return; + } + + preprocessPolicyItems(auditPolicy.getAuditPolicyItems(), impliedAccessGrants); + } + + private boolean matchResource(RangerAccessRequest request) { + boolean ret = false; + + if (!matchAnyResource) { + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + final RangerPolicyResourceMatcher.MatchType matchType; + + if (request instanceof RangerTagAccessRequest) { + matchType = ((RangerTagAccessRequest) request).getMatchType(); + } else { + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + + if (resourceMatcher != null) { + matchType = resourceMatcher.getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()); + } else { + matchType = RangerPolicyResourceMatcher.MatchType.NONE; + } + } + + final RangerAccessRequest.ResourceMatchingScope resourceMatchingScope = request.getResourceMatchingScope() != null ? request.getResourceMatchingScope() : RangerAccessRequest.ResourceMatchingScope.SELF; + + if (request.isAccessTypeAny() || resourceMatchingScope == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) { + ret = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS || matchType == RangerPolicyResourceMatcher.MatchType.DESCENDANT; + } else { + ret = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS; + } + + if (ret) { + break; + } + } + } else { + ret = true; + } + + return ret; + } + + private void evaluatePolicyItems(RangerAccessRequest request, RangerAccessResult result) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerAuditPolicyEvaluator.evaluatePolicyItems(" + auditPolicy.getId() + ", " + request + ", " + result + ")"); + } + + for (RangerAuditPolicyItemEvaluator itemEvaluator : auditItemEvaluators) { + if (itemEvaluator.isMatch(request, result)) { + Boolean isAudited = itemEvaluator.getIsAudited(); + + if (isAudited != null) { + result.setIsAudited(isAudited); + + break; + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerAuditPolicyEvaluator.evaluatePolicyItems(" + auditPolicy.getId() + ", " + request + ", " + result + ")"); + } + } + + @Override + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerAuditPolicyEvaluator={"); + + super.toString(sb); + + sb.append("auditPolicy={"); + auditPolicy.toString(sb); + sb.append("}"); + + sb.append(" matchAnyResource={").append(matchAnyResource).append("}"); + + sb.append("}"); + + return sb; + } + + + public static class RangerAuditPolicyItemEvaluator extends RangerDefaultPolicyItemEvaluator { + private final RangerAuditPolicyItem auditPolicyItem; + private final boolean matchAnyResult; + private final boolean matchAnyUser; + private final boolean matchAnyAction; + private final boolean hasResourceOwner; + + public RangerAuditPolicyItemEvaluator(RangerServiceDef serviceDef, RangerPolicy policy, RangerAuditPolicyItem policyItem, int policyItemIndex, RangerPolicyEngineOptions options) { + super(serviceDef, policy, policyItem, POLICY_ITEM_TYPE_ALLOW, policyItemIndex, options); + + this.auditPolicyItem = policyItem; + this.matchAnyResult = policyItem.getAccessResult() == null; + + List users = policyItem.getUsers(); + List groups = policyItem.getGroups(); + List roles = policyItem.getRoles(); + this.matchAnyUser = (CollectionUtils.isEmpty(users) && CollectionUtils.isEmpty(groups) && CollectionUtils.isEmpty(roles)) || + (CollectionUtils.isNotEmpty(groups) && groups.contains(RangerPolicyEngine.GROUP_PUBLIC)) || + (CollectionUtils.isNotEmpty(users) && users.contains(RangerPolicyEngine.USER_CURRENT)); + this.matchAnyAction = policyItem.getActions().isEmpty() && policyItem.getAccessTypes().isEmpty(); + this.hasResourceOwner = CollectionUtils.isNotEmpty(users) && users.contains(RangerPolicyEngine.RESOURCE_OWNER); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuditPolicyItemEvaluator(" + auditPolicyItem + ", matchAnyUser=" + matchAnyUser + ", matchAnyAction=" + matchAnyAction + ", hasResourceOwner=" + hasResourceOwner + ")"); + } + } + + public Boolean getIsAudited() { return auditPolicyItem.getIsAudited(); } + + public boolean isMatch(RangerAccessRequest request, RangerAccessResult result) { + boolean ret = matchAccessResult(result) && + matchUserGroupRole(request) && + matchAction(request); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuditPolicyItemEvaluator.isMatch(" + request + ", " + result + "): ret=" + ret); + } + + return ret; + } + + private boolean matchAccessResult(RangerAccessResult result) { + boolean ret = matchAnyResult; + + if (!ret) { + switch (auditPolicyItem.getAccessResult()) { + case DENIED: + ret = result.getIsAccessDetermined() && !result.getIsAllowed(); + break; + + case ALLOWED: + ret = result.getIsAccessDetermined() && result.getIsAllowed(); + break; + + case NOT_DETERMINED: + ret = !result.getIsAccessDetermined(); + break; + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuditPolicyItemEvaluator.matchAccessResult(" + result + "): ret=" + ret); + } + + return ret; + } + + private boolean matchUserGroupRole(RangerAccessRequest request) { + boolean ret = matchAnyUser; + + if (!ret) { + + if (auditPolicyItem.getUsers() != null && request.getUser() != null) { + ret = auditPolicyItem.getUsers().contains(request.getUser()); + + if (!ret && hasResourceOwner) { + String owner = request.getResource() != null ? request.getResource().getOwnerUser() : null; + ret = request.getUser().equals(owner); + } + } + + if (!ret && auditPolicyItem.getGroups() != null && request.getUserGroups() != null) { + ret = CollectionUtils.containsAny(auditPolicyItem.getGroups(), request.getUserGroups()); + } + + if (!ret && auditPolicyItem.getRoles() != null) { + ret = CollectionUtils.containsAny(auditPolicyItem.getRoles(), RangerAccessRequestUtil.getCurrentUserRolesFromContext(request.getContext())); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuditPolicyItemEvaluator.matchUserGroupRole(" + request + "): ret=" + ret); + } + + return ret; + } + + private boolean matchAction(RangerAccessRequest request) { + boolean ret = matchAnyAction; + + if (!ret) { + if (request.getAction() != null) { + ret = auditPolicyItem.getActions().contains(request.getAction()); + } + + if (!ret && request.getAccessType() != null) { + ret = auditPolicyItem.getAccessTypes().contains(request.getAccessType()); + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerAuditPolicyItemEvaluator.matchAction(" + request + "): ret=" + ret); + } + + return ret; + } + } + + public static class RangerAuditPolicy extends RangerPolicy { + private final List auditPolicyItems; + + public RangerAuditPolicy(AuditFilter auditFilter, int priority) { + setId((long)priority); + setResources(auditFilter.getResources()); + setPolicyType(POLICY_TYPE_AUDIT); + setPolicyPriority(priority); + + this.auditPolicyItems = Collections.singletonList(new RangerAuditPolicyItem(auditFilter)); + } + + public List getAuditPolicyItems() { return auditPolicyItems; } + } + + public static class RangerAuditPolicyItem extends RangerPolicyItem { + private final AuditFilter.AccessResult accessResult; + private final Set actions; + private final Set accessTypes; + private final Boolean isAudited; + + public RangerAuditPolicyItem(AuditFilter auditFilter) { + super(getPolicyItemAccesses(auditFilter.getAccessTypes()), auditFilter.getUsers(), auditFilter.getGroups(), auditFilter.getRoles(), null, null); + + this.accessResult = auditFilter.getAccessResult(); + this.actions = auditFilter.getActions() != null ? new HashSet<>(auditFilter.getActions()) : Collections.emptySet(); + this.accessTypes = auditFilter.getAccessTypes() != null ? new HashSet<>(auditFilter.getAccessTypes()) : Collections.emptySet(); + this.isAudited = auditFilter.getIsAudited(); + } + + public Set getActions() { return actions; } + + public Set getAccessTypes() { return accessTypes; } + + public AuditFilter.AccessResult getAccessResult() { return accessResult; } + + public Boolean getIsAudited() { return isAudited; } + + @Override + public StringBuilder toString(StringBuilder sb) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append("RangerAuditPolicyItem={"); + super.toString(sb); + sb.append(" accessResult={").append(accessResult).append("}"); + + sb.append(" actions={"); + if (actions != null) { + for (String action : actions) { + if (action != null) { + sb.append(action).append(" "); + } + } + } + sb.append("}"); + + sb.append(" accessTypes={"); + if (accessTypes != null) { + for (String accessTypes : accessTypes) { + if (accessTypes != null) { + sb.append(accessTypes).append(" "); + } + } + } + sb.append("}"); + + sb.append(" isAudited={").append(isAudited).append("}"); + sb.append("}"); + + return sb; + } + + private static List getPolicyItemAccesses(List accessTypes) { + final List ret; + + if (accessTypes != null) { + ret = new ArrayList<>(accessTypes.size()); + + for (String accessType : accessTypes) { + ret.add(new RangerPolicyItemAccess(accessType)); + } + } else { + ret = Collections.emptyList(); + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java index cc250b5378..511c459a5c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerCustomConditionEvaluator.java @@ -21,160 +21,156 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +// +// this class should have been named RangerConditionEvaluatorFactory +// public class RangerCustomConditionEvaluator { + private static final Logger LOG = LoggerFactory.getLogger(RangerCustomConditionEvaluator.class); + private static final Logger PERF_POLICY_INIT_LOG = RangerPerfTracer.getPerfLogger("policy.init"); + private static final Logger PERF_POLICYITEM_INIT_LOG = RangerPerfTracer.getPerfLogger("policyitem.init"); + private static final Logger PERF_POLICYCONDITION_INIT_LOG = RangerPerfTracer.getPerfLogger("policycondition.init"); - private static final Log LOG = LogFactory.getLog(RangerCustomConditionEvaluator.class); - private static final Log PERF_POLICY_INIT_LOG = RangerPerfTracer.getPerfLogger("policy.init"); - private static final Log PERF_POLICYITEM_INIT_LOG = RangerPerfTracer.getPerfLogger("policyitem.init"); - private static final Log PERF_POLICYCONDITION_INIT_LOG = RangerPerfTracer.getPerfLogger("policycondition.init"); - - public List getRangerPolicyConditionEvaluator(RangerPolicy policy, - RangerServiceDef serviceDef, - RangerPolicyEngineOptions options) { - List conditionEvaluators = new ArrayList<>(); - - if (!getConditionsDisabledOption(options) && CollectionUtils.isNotEmpty(policy.getConditions())) { - - RangerPerfTracer perf = null; - - long policyId = policy.getId(); + private static final RangerPolicyConditionDef EXPRESSION_CONDITION_DEF = ServiceDefUtil.createImplicitExpressionConditionDef(-1L); - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_INIT_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_INIT_LOG, "RangerCustomConditionEvaluator.init(policyId=" + policyId + ")"); - } - - for (RangerPolicy.RangerPolicyItemCondition condition : policy.getConditions()) { - RangerServiceDef.RangerPolicyConditionDef conditionDef = getConditionDef(condition.getType(),serviceDef); - - if (conditionDef == null) { - LOG.error("RangerCustomConditionEvaluator.getRangerPolicyConditionEvaluator(policyId=" + policyId + "): conditionDef '" + condition.getType() + "' not found. Ignoring the condition"); - continue; - } - - RangerConditionEvaluator conditionEvaluator = newConditionEvaluator(conditionDef.getEvaluator()); + public static RangerCustomConditionEvaluator getInstance() { + return RangerCustomConditionEvaluator.SingletonHolder.s_instance; + } - if (conditionEvaluator != null) { - conditionEvaluator.setServiceDef(serviceDef); - conditionEvaluator.setConditionDef(conditionDef); - conditionEvaluator.setPolicyItemCondition(condition); + private RangerCustomConditionEvaluator() { + } - RangerPerfTracer perfConditionInit = null; + public List getPolicyConditionEvaluators(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + RangerPerfTracer perf = null; + String parentId = "policyId=" + policy.getId() ; - if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYCONDITION_INIT_LOG)) { - perfConditionInit = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_INIT_LOG, "RangerConditionEvaluator.init(policyId=" + policyId + "policyConditionType=" + condition.getType() + ")"); - } + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_INIT_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_INIT_LOG, "RangerPolicyEvaluator.getPolicyConditionEvaluators(" + parentId + ")"); + } - conditionEvaluator.init(); + List ret = getConditionEvaluators(parentId, policy.getConditions(), serviceDef, options); - RangerPerfTracer.log(perfConditionInit); + RangerPerfTracer.log(perf); - conditionEvaluators.add(conditionEvaluator); - } else { - LOG.error("RangerCustomConditionEvaluator.getRangerPolicyConditionEvaluator(policyId=" + policyId + "): failed to init Policy ConditionEvaluator '" + condition.getType() + "'; evaluatorClassName='" + conditionDef.getEvaluator() + "'"); - } - } - - RangerPerfTracer.log(perf); - } - return conditionEvaluators; + return ret; } + public List getPolicyItemConditionEvaluators(RangerPolicy policy, RangerPolicyItem policyItem, RangerServiceDef serviceDef, RangerPolicyEngineOptions options, int policyItemIndex) { + RangerPerfTracer perf = null; + String parentId = "policyId=" + policy.getId() + ", policyItemIndex=" + policyItemIndex; - public List getPolicyItemConditionEvaluator(RangerPolicy policy, - RangerPolicyItem policyItem, - RangerServiceDef serviceDef, - RangerPolicyEngineOptions options, - int policyItemIndex) { + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYITEM_INIT_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICYITEM_INIT_LOG, "RangerPolicyItemEvaluator.getPolicyItemConditionEvaluators(" + parentId + ")"); + } - List conditionEvaluators = new ArrayList<>(); + List ret = getConditionEvaluators(parentId, policyItem.getConditions(), serviceDef, options); - if (!getConditionsDisabledOption(options) && CollectionUtils.isNotEmpty(policyItem.getConditions())) { + RangerPerfTracer.log(perf); - RangerPerfTracer perf = null; + return ret; + } - Long policyId = policy.getId(); + public List getConditionEvaluators(String parentId, List conditions, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + final List ret; - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYITEM_INIT_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_POLICYITEM_INIT_LOG, "RangerPolicyItemEvaluator.getRangerPolicyConditionEvaluator(policyId=" + policyId + ",policyItemIndex=" + policyItemIndex + ")"); - } + if (!getConditionsDisabledOption(options) && CollectionUtils.isNotEmpty(conditions)) { + ret = new ArrayList<>(conditions.size()); - for (RangerPolicyItemCondition condition : policyItem.getConditions()) { - RangerServiceDef.RangerPolicyConditionDef conditionDef = getConditionDef(condition.getType(), serviceDef); + for (RangerPolicyItemCondition condition : conditions) { + RangerPolicyConditionDef conditionDef = ServiceDefUtil.getConditionDef(serviceDef, condition.getType()); if (conditionDef == null) { - LOG.error("RangerCustomConditionEvaluator.getPolicyItemConditionEvaluator(policyId=" + policyId + "): conditionDef '" + condition.getType() + "' not found. Ignoring the condition"); + LOG.error("RangerCustomConditionEvaluator.getConditionEvaluators(" + parentId + "): conditionDef '" + condition.getType() + "' not found. Ignoring the condition"); continue; } - RangerConditionEvaluator conditionEvaluator = newConditionEvaluator(conditionDef.getEvaluator()); + RangerConditionEvaluator conditionEvaluator = getConditionEvaluator(parentId, condition, conditionDef, serviceDef, options); if (conditionEvaluator != null) { - conditionEvaluator.setServiceDef(serviceDef); - conditionEvaluator.setConditionDef(conditionDef); - conditionEvaluator.setPolicyItemCondition(condition); + ret.add(conditionEvaluator); + } + } + } else { + ret = Collections.emptyList(); + } - RangerPerfTracer perfConditionInit = null; + return ret; + } + + public RangerConditionEvaluator getConditionEvaluator(String parentId, RangerPolicyItemCondition condition, RangerPolicyConditionDef conditionDef, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + final RangerConditionEvaluator ret; - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYCONDITION_INIT_LOG)) { - perfConditionInit = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_INIT_LOG, "RangerConditionEvaluator.init(policyId=" + policyId + ",policyItemIndex=" + policyItemIndex + ",policyConditionType=" + condition.getType() + ")"); - } + if (condition != null && conditionDef != null && !getConditionsDisabledOption(options)) { + ret = newConditionEvaluator(conditionDef.getEvaluator()); - conditionEvaluator.init(); + if (ret != null) { + ret.setServiceDef(serviceDef); + ret.setConditionDef(conditionDef); + ret.setPolicyItemCondition(condition); - RangerPerfTracer.log(perfConditionInit); + RangerPerfTracer perf = null; - conditionEvaluators.add(conditionEvaluator); - } else { - LOG.error("RangerCustomConditionEvaluator.getPolicyItemConditionEvaluator(policyId=" + policyId + "): failed to init PolicyItem ConditionEvaluator '" + condition.getType() + "'; evaluatorClassName='" + conditionDef.getEvaluator() + "'"); + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYCONDITION_INIT_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_INIT_LOG, "RangerConditionEvaluator.init(" + parentId + ", policyConditionType=" + condition.getType() + ")"); } + + ret.init(); + + RangerPerfTracer.log(perf); + } else { + LOG.error("RangerCustomConditionEvaluator.getConditionEvaluator(" + parentId + "): failed to init ConditionEvaluator '" + condition.getType() + "'; evaluatorClassName='" + conditionDef.getEvaluator() + "'"); } - RangerPerfTracer.log(perf); + } else { + ret = null; } - return conditionEvaluators; + + return ret; } - private RangerServiceDef.RangerPolicyConditionDef getConditionDef(String conditionName, RangerServiceDef serviceDef) { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerCustomConditionEvaluator.getConditionDef(" + conditionName + ")"); - } + public RangerConditionEvaluator getExpressionEvaluator(String expression, RangerServiceDef serviceDef) { + final RangerConditionEvaluator ret; - RangerServiceDef.RangerPolicyConditionDef ret = null; + if (StringUtils.isNotBlank(expression)) { + ret = newConditionEvaluator(EXPRESSION_CONDITION_DEF.getEvaluator()); - if (serviceDef != null && CollectionUtils.isNotEmpty(serviceDef.getPolicyConditions())) { - for(RangerServiceDef.RangerPolicyConditionDef conditionDef : serviceDef.getPolicyConditions()) { - if(StringUtils.equals(conditionName, conditionDef.getName())) { - ret = conditionDef; - break; - } - } - } + if (ret != null) { + ret.setServiceDef(serviceDef); + ret.setConditionDef(EXPRESSION_CONDITION_DEF); + ret.setPolicyItemCondition(new RangerPolicyItemCondition(EXPRESSION_CONDITION_DEF.getName(), Collections.singletonList(expression))); + + RangerPerfTracer perf = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_INIT_LOG, "RangerConditionEvaluator.init(_expression)"); - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerCustomConditionEvaluator.getConditionDef(" + conditionName + "): " + ret); + ret.init(); + + RangerPerfTracer.log(perf); + } + } else { + ret = null; } return ret; } - private RangerConditionEvaluator newConditionEvaluator(String className) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("==> RangerCustomConditionEvaluator.newConditionEvaluator(" + className + ")"); } @@ -182,14 +178,14 @@ private RangerConditionEvaluator newConditionEvaluator(String className) { try { @SuppressWarnings("unchecked") - Class matcherClass = (Class)Class.forName(className); + Class evaluatorClass = (Class)Class.forName(className); - evaluator = matcherClass.newInstance(); - } catch(Throwable t) { + evaluator = evaluatorClass.newInstance(); + } catch (Throwable t) { LOG.error("RangerCustomConditionEvaluator.newConditionEvaluator(" + className + "): error instantiating evaluator", t); } - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerCustomConditionEvaluator.newConditionEvaluator(" + className + "): " + evaluator); } @@ -199,4 +195,8 @@ private RangerConditionEvaluator newConditionEvaluator(String className) { private boolean getConditionsDisabledOption(RangerPolicyEngineOptions options) { return options != null && options.disableCustomConditions; } + + private static class SingletonHolder { + private static final RangerCustomConditionEvaluator s_instance = new RangerCustomConditionEvaluator(); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java index 558212471b..6bf768bf1a 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultDataMaskPolicyItemEvaluator.java @@ -18,6 +18,7 @@ */ package org.apache.ranger.plugin.policyevaluator; +import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem; @@ -25,15 +26,34 @@ import org.apache.ranger.plugin.policyengine.RangerAccessResult; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerRequestExprResolver; public class RangerDefaultDataMaskPolicyItemEvaluator extends RangerDefaultPolicyItemEvaluator implements RangerDataMaskPolicyItemEvaluator { - final private RangerDataMaskPolicyItem dataMaskPolicyItem; + final private RangerDataMaskPolicyItem dataMaskPolicyItem; + final private RangerRequestExprResolver maskedValueExprResolver; + final private RangerRequestExprResolver maskConditionExprResolver; public RangerDefaultDataMaskPolicyItemEvaluator(RangerServiceDef serviceDef, RangerPolicy policy, RangerDataMaskPolicyItem policyItem, int policyItemIndex, RangerPolicyEngineOptions options) { super(serviceDef, policy, policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DATAMASK, policyItemIndex, options); dataMaskPolicyItem = policyItem; + + RangerPolicyItemDataMaskInfo dataMaskInfo = dataMaskPolicyItem != null ? dataMaskPolicyItem.getDataMaskInfo() : null; + String maskedValue = dataMaskInfo != null ? dataMaskInfo.getValueExpr() : null; + String maskCondition = dataMaskInfo != null ? dataMaskInfo.getConditionExpr() : null; + + if (StringUtils.isNotBlank(maskedValue) && RangerRequestExprResolver.hasExpressions(maskedValue)) { + maskedValueExprResolver = new RangerRequestExprResolver(maskedValue, getServiceType()); + } else { + maskedValueExprResolver = null; + } + + if (StringUtils.isNotBlank(maskCondition) && RangerRequestExprResolver.hasExpressions(maskCondition)) { + maskConditionExprResolver = new RangerRequestExprResolver(maskCondition, getServiceType()); + } else { + maskConditionExprResolver = null; + } } @Override @@ -45,10 +65,21 @@ public RangerPolicyItemDataMaskInfo getDataMaskInfo() { public void updateAccessResult(RangerPolicyEvaluator policyEvaluator, RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType) { RangerPolicyItemDataMaskInfo dataMaskInfo = getDataMaskInfo(); - if (result.getMaskType() == null && dataMaskInfo != null) { + if (dataMaskInfo != null) { result.setMaskType(dataMaskInfo.getDataMaskType()); - result.setMaskCondition(dataMaskInfo.getConditionExpr()); - result.setMaskedValue(dataMaskInfo.getValueExpr()); + + if (maskedValueExprResolver != null) { + result.setMaskedValue(maskedValueExprResolver.resolveExpressions(result.getAccessRequest())); + } else { + result.setMaskedValue(dataMaskInfo.getValueExpr()); + } + + if (maskConditionExprResolver != null) { + result.setMaskCondition(maskConditionExprResolver.resolveExpressions(result.getAccessRequest())); + } else { + result.setMaskCondition(dataMaskInfo.getConditionExpr()); + } + policyEvaluator.updateAccessResult(result, matchType, true, getComments()); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java index d75bf46a0a..183d93a4b5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyEvaluator.java @@ -31,8 +31,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.conditionevaluator.RangerAbstractConditionEvaluator; import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; import org.apache.ranger.plugin.model.RangerPolicy; @@ -45,28 +43,30 @@ import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; import org.apache.ranger.plugin.model.RangerValiditySchedule; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestWrapper; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyengine.RangerAccessResult; import org.apache.ranger.plugin.policyengine.RangerPolicyEngine; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; import org.apache.ranger.plugin.policyengine.RangerResourceAccessInfo; import org.apache.ranger.plugin.policyengine.RangerTagAccessRequest; -import org.apache.ranger.plugin.policyresourcematcher.RangerDefaultPolicyResourceMatcher; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; -import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerRolesUtil; import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator { - private static final Log LOG = LogFactory.getLog(RangerDefaultPolicyEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultPolicyEvaluator.class); - private static final Log PERF_POLICY_INIT_LOG = RangerPerfTracer.getPerfLogger("policy.init"); - private static final Log PERF_POLICY_INIT_ACLSUMMARY_LOG = RangerPerfTracer.getPerfLogger("policy.init.ACLSummary"); - private static final Log PERF_POLICY_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policy.request"); - private static final Log PERF_POLICYCONDITION_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policycondition.request"); + private static final Logger PERF_POLICY_INIT_LOG = RangerPerfTracer.getPerfLogger("policy.init"); + private static final Logger PERF_POLICY_INIT_ACLSUMMARY_LOG = RangerPerfTracer.getPerfLogger("policy.init.ACLSummary"); + private static final Logger PERF_POLICY_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policy.request"); + private static final Logger PERF_POLICYCONDITION_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policycondition.request"); - private RangerPolicyResourceMatcher resourceMatcher; private List validityScheduleEvaluators; private List allowEvaluators; private List denyEvaluators; @@ -78,9 +78,19 @@ public class RangerDefaultPolicyEvaluator extends RangerAbstractPolicyEvaluator private List conditionEvaluators; private String perfTag; private PolicyACLSummary aclSummary = null; - private boolean useAclSummaryForEvaluation = false; + private boolean disableRoleResolution = true; - protected boolean needsDynamicEval() { return resourceMatcher != null && resourceMatcher.getNeedsDynamicEval(); } + List getAllowEvaluators() { return allowEvaluators; } + List getAllowExceptionEvaluators() { return allowExceptionEvaluators; } + List getDenyEvaluators() { return denyEvaluators; } + List getDenyExceptionEvaluators() { return denyExceptionEvaluators; } + List getDataMaskEvaluators() { return dataMaskEvaluators; } + List getRowFilterEvaluators() { return rowFilterEvaluators; } + + @Override + public int getPolicyConditionsCount() { + return conditionEvaluators.size(); + } @Override public int getCustomConditionsCount() { @@ -92,14 +102,6 @@ public int getValidityScheduleEvaluatorsCount() { return validityScheduleEvaluators.size(); } - @Override - public RangerPolicyResourceMatcher getPolicyResourceMatcher() { return resourceMatcher; } - - @Override - public RangerResourceMatcher getResourceMatcher(String resourceName) { - return resourceMatcher != null ? resourceMatcher.getResourceMatcher(resourceName) : null; - } - @Override public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { if(LOG.isDebugEnabled()) { @@ -121,39 +123,30 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE super.init(policy, serviceDef, options); - preprocessPolicy(policy, serviceDef); - - resourceMatcher = new RangerDefaultPolicyResourceMatcher(); + policy = getPolicy(); - resourceMatcher.setServiceDef(serviceDef); - resourceMatcher.setPolicy(policy); - resourceMatcher.setServiceDefHelper(options.getServiceDefHelper()); - resourceMatcher.init(); + preprocessPolicy(policy, serviceDef, options); if(policy != null) { validityScheduleEvaluators = createValidityScheduleEvaluators(policy); - if (!options.disableAccessEvaluationWithPolicyACLSummary) { - aclSummary = createPolicyACLSummary(); - } + this.disableRoleResolution = options.disableRoleResolution; - useAclSummaryForEvaluation = aclSummary != null; + allowEvaluators = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW); - if (useAclSummaryForEvaluation) { - allowEvaluators = Collections.emptyList(); - denyEvaluators = Collections.emptyList(); - allowExceptionEvaluators = Collections.emptyList(); - denyExceptionEvaluators = Collections.emptyList(); - } else { - allowEvaluators = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW); + if (ServiceDefUtil.getOption_enableDenyAndExceptionsInPolicies(serviceDef, getPluginContext())) { denyEvaluators = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY); allowExceptionEvaluators = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW_EXCEPTIONS); denyExceptionEvaluators = createPolicyItemEvaluators(policy, serviceDef, options, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY_EXCEPTIONS); + } else { + denyEvaluators = Collections.emptyList(); + allowExceptionEvaluators = Collections.emptyList(); + denyExceptionEvaluators = Collections.emptyList(); } dataMaskEvaluators = createDataMaskPolicyItemEvaluators(policy, serviceDef, options, policy.getDataMaskPolicyItems()); rowFilterEvaluators = createRowFilterPolicyItemEvaluators(policy, serviceDef, options, policy.getRowFilterPolicyItems()); - conditionEvaluators = createRangerPolicyConditionEvaluator(policy, serviceDef, options); + conditionEvaluators = createPolicyConditionEvaluators(policy, serviceDef, options); } else { validityScheduleEvaluators = Collections.emptyList(); allowEvaluators = Collections.emptyList(); @@ -178,10 +171,6 @@ public void init(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyE RangerPerfTracer.log(perf); - if (useAclSummaryForEvaluation && (policy.getPolicyType() == null || policy.getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS)) { - LOG.info("PolicyEvaluator for policy:[" + policy.getId() + "] is set up to use ACL Summary to evaluate access"); - } - if(LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyEvaluator.init()"); } @@ -216,7 +205,7 @@ public boolean isApplicable(Date accessTime) { @Override public void evaluate(RangerAccessRequest request, RangerAccessResult result) { if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.evaluate(policyId=" + getPolicy().getId() + ", " + request + ", " + result + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.evaluate(policyId=" + getPolicyId() + ", " + request + ", " + result + ")"); } RangerPerfTracer perf = null; @@ -227,59 +216,58 @@ public void evaluate(RangerAccessRequest request, RangerAccessResult result) { } if (request != null && result != null) { + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); - if (!result.getIsAccessDetermined() || !result.getIsAuditedDetermined()) { - RangerPolicyResourceMatcher.MatchType matchType; + if (!result.getIsAccessDetermined() || !result.getIsAuditedDetermined()) { + final RangerPolicyResourceMatcher.MatchType matchType; - if (RangerTagAccessRequest.class.isInstance(request)) { - matchType = ((RangerTagAccessRequest) request).getMatchType(); - if (matchType == RangerPolicyResourceMatcher.MatchType.ANCESTOR) { - matchType = RangerPolicyResourceMatcher.MatchType.SELF; + if (request instanceof RangerTagAccessRequest) { + matchType = ((RangerTagAccessRequest) request).getMatchType(); + } else { + matchType = resourceMatcher != null ? resourceMatcher.getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()) : RangerPolicyResourceMatcher.MatchType.NONE; } - } else { - matchType = resourceMatcher != null ? resourceMatcher.getMatchType(request.getResource(), request.getContext()) : RangerPolicyResourceMatcher.MatchType.NONE; - } - final boolean isMatched; + final RangerAccessRequest.ResourceMatchingScope resourceMatchingScope = request.getResourceMatchingScope() != null ? request.getResourceMatchingScope() : RangerAccessRequest.ResourceMatchingScope.SELF; + final boolean isMatched; - if (request.isAccessTypeAny()) { - isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; - } else if (request.getResourceMatchingScope() == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) { - isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; - } else { - isMatched = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS; - } + if (request.isAccessTypeAny() || resourceMatchingScope == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) { + isMatched = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS || matchType == RangerPolicyResourceMatcher.MatchType.DESCENDANT; + } else { + isMatched = matchType == RangerPolicyResourceMatcher.MatchType.SELF || matchType == RangerPolicyResourceMatcher.MatchType.SELF_AND_ALL_DESCENDANTS; + } - if (isMatched) { - //Evaluate Policy Level Custom Conditions, if any and allowed then go ahead for policyItem level evaluation - if(matchPolicyCustomConditions(request)) { - if (!result.getIsAuditedDetermined()) { - if (isAuditEnabled()) { - result.setIsAudited(true); - result.setAuditPolicyId(getPolicy().getId()); + if (isMatched) { + //Evaluate Policy Level Custom Conditions, if any and allowed then go ahead for policyItem level evaluation + if (matchPolicyCustomConditions(request)) { + if (!result.getIsAuditedDetermined()) { + if (isAuditEnabled()) { + result.setIsAudited(true); + result.setAuditPolicyId(getPolicyId()); + } } - } - if (!result.getIsAccessDetermined()) { - if (hasMatchablePolicyItem(request)) { - evaluatePolicyItems(request, matchType, result); + if (!result.getIsAccessDetermined()) { + if (hasMatchablePolicyItem(request)) { + evaluatePolicyItems(request, matchType, result); + } } } } } } - } + } RangerPerfTracer.log(perf); if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.evaluate(policyId=" + getPolicy().getId() + ", " + request + ", " + result + ")"); + LOG.debug("<== RangerDefaultPolicyEvaluator.evaluate(policyId=" + getPolicyId() + ", " + request + ", " + result + ")"); } } @Override public boolean isMatch(RangerAccessResource resource, Map evalContext) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.isMatch(" + resource + ", " + evalContext + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.isMatch(policy-id=" + getPolicyId() + ", " + resource + ", " + evalContext + ")"); } boolean ret = false; @@ -290,14 +278,20 @@ public boolean isMatch(RangerAccessResource resource, Map evalCo perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_REQUEST_LOG, "RangerPolicyEvaluator.isMatch(resource=" + resource.getAsString() + "," + evalContext + "," + perfTag + ")"); } - if(resourceMatcher != null) { - ret = resourceMatcher.isMatch(resource, evalContext); + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + + ret = resourceMatcher != null && resourceMatcher.isMatch(resource, evalContext); + + if (ret) { + break; + } } RangerPerfTracer.log(perf); if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.isMatch(" + resource + ", " + evalContext + "): " + ret); + LOG.debug("<== RangerDefaultPolicyEvaluator.isMatch(policy-id=" + getPolicyId() + ", " + resource + ", " + evalContext + ") : " + ret); } return ret; @@ -309,7 +303,18 @@ public boolean isCompleteMatch(RangerAccessResource resource, Map RangerDefaultPolicyEvaluator.isCompleteMatch(" + resource + ", " + evalContext + ")"); } - boolean ret = resourceMatcher != null && resourceMatcher.isCompleteMatch(resource, evalContext); + final boolean ret; + + List resourceEvaluators = getResourceEvaluators(); + + if (resourceEvaluators.size() == 1) { + RangerPolicyResourceEvaluator resourceEvaluator = resourceEvaluators.get(0); + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + + ret = resourceMatcher != null && resourceMatcher.isCompleteMatch(resource, evalContext); + } else { + ret = false; + } if(LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyEvaluator.isCompleteMatch(" + resource + "): " + ret); @@ -319,12 +324,32 @@ public boolean isCompleteMatch(RangerAccessResource resource, Map resources, Map evalContext) { + public boolean isCompleteMatch(Map resources, List> additionalResources, Map evalContext) { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyEvaluator.isCompleteMatch(" + resources + ", " + evalContext + ")"); } - boolean ret = resourceMatcher != null && resourceMatcher.isCompleteMatch(resources, evalContext); + boolean ret = false; + + List resourceEvaluators = getResourceEvaluators(); + + for (int i = 0; i < resourceEvaluators.size(); i++) { + RangerPolicyResourceEvaluator resourceEvaluator = resourceEvaluators.get(i); + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + Map policyResource = null; + + if (i == 0) { + policyResource = resources; + } else if (additionalResources != null && additionalResources.size() >= i) { + policyResource = additionalResources.get(i - 1); + } + + ret = resourceMatcher != null && policyResource != null && resourceMatcher.isCompleteMatch(policyResource, evalContext); + + if (!ret) { + break; + } + } if(LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyEvaluator.isCompleteMatch(" + resources + ", " + evalContext + "): " + ret); @@ -334,108 +359,153 @@ public boolean isCompleteMatch(Map resources, Map< } @Override - public boolean isAccessAllowed(RangerAccessResource resource, String user, Set userGroups, Set roles, String accessType) { + public Set getAllowedAccesses(RangerAccessResource resource, String user, Set userGroups, Set roles, Set accessTypes) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.isAccessAllowed(" + resource + ", " + user + ", " + userGroups + ", " + roles + ", " + accessType + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.getAllowedAccesses(policy-id=" + getPolicyId() + ", " + resource + ", " + user + ", " + userGroups + ", " + roles + ", " + accessTypes + ")"); + } + + Set ret = null; + + Map evalContext = new HashMap<>(); + RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); + + if (isMatch(resource, evalContext)) { + ret = new HashSet<>(); + for (String accessType : accessTypes) { + if (isAccessAllowed(user, userGroups, roles, resource.getOwnerUser(), accessType)) { + ret.add(accessType); + } + } + } else { + LOG.debug("RangerDefaultPolicyEvaluator.getAllowedAccesses - Not Matched -- (policy-id=" + getPolicyId() + ", " + resource + ", " + user + ", " + userGroups + ", " + roles + ", " + accessTypes + ")"); + } - boolean ret = isAccessAllowed(user, userGroups, roles, resource.getOwnerUser(), accessType) && isMatch(resource, null); - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.isAccessAllowed(" + resource + ", " + user + ", " + userGroups + ", " + roles + ", " + accessType + "): " + ret); + LOG.debug("<== RangerDefaultPolicyEvaluator.getAllowedAccesses(policy-id=" + getPolicyId() + ", " + resource + ", " + user + ", " + userGroups + ", " + roles + ", " + accessTypes + "): " + ret); } return ret; } - /* - * This is used only by test code - */ - @Override - public boolean isAccessAllowed(Map resources, String user, Set userGroups, String accessType) { + public Set getAllowedAccesses(Map resources, String user, Set userGroups, Set roles, Set accessTypes, Map evalContext) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.isAccessAllowed(" + resources + ", " + user + ", " + userGroups + ", " + accessType + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.getAllowedAccesses(" + getPolicyId() + ", " + user + ", " + userGroups + ", " + roles + ", " + accessTypes + ", " + evalContext + ")"); } - boolean ret = isAccessAllowed(user, userGroups, null, null, accessType) && isMatch(resources, null); + Set ret = null; + + if (isMatch(resources, evalContext)) { + if (CollectionUtils.isNotEmpty(accessTypes)) { + ret = new HashSet<>(); + for (String accessType : accessTypes) { + if (isAccessAllowed(user, userGroups, roles, null, accessType)) { + ret.add(accessType); + } + } + } else { + if (isAccessAllowed(user, userGroups, roles, null, null)) { + ret = new HashSet<>(); + } + } + } if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.isAccessAllowed(" + resources + ", " + user + ", " + userGroups + ", " + accessType + "): " + ret); + LOG.debug("<== RangerDefaultPolicyEvaluator.getAllowedAccesses(" + getPolicyId() + ", " + user + ", " + userGroups + ", " + roles + ", " + accessTypes + ", " + evalContext + "): " + ret); } return ret; } + /* + * This is used only by test code + */ @Override - public boolean isAccessAllowed(RangerPolicy policy, String user, Set userGroups, Set roles, String accessType) { + public boolean isAccessAllowed(Map resources, List> additionalResources, String user, Set userGroups, String accessType) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.isAccessAllowed(" + policy.getId() + ", " + user + ", " + userGroups + ", " + roles + ", " + accessType + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.isAccessAllowed(" + resources + ", " + user + ", " + userGroups + ", " + accessType + ")"); + } + + boolean ret = isAccessAllowed(user, userGroups, null, null, accessType) && isMatch(resources, null); + + if (ret && additionalResources != null) { + for (Map additionalResource : additionalResources) { + ret = isMatch(additionalResource, null); + + if (!ret) { + break; + } + } } - boolean ret = isAccessAllowed(user, userGroups, roles, null, accessType) && isMatch(policy, null); - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.isAccessAllowed(" + policy.getId() + ", " + user + ", " + userGroups + ", " + roles + ", " + accessType + "): " + ret); + LOG.debug("<== RangerDefaultPolicyEvaluator.isAccessAllowed(" + resources + ", " + user + ", " + userGroups + ", " + accessType + "): " + ret); } return ret; } + @Override public void getResourceAccessInfo(RangerAccessRequest request, RangerResourceAccessInfo result) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyEvaluator.getResourceAccessInfo(" + request + ", " + result + ")"); } - RangerPolicyResourceMatcher.MatchType matchType; - if (RangerTagAccessRequest.class.isInstance(request)) { - matchType = ((RangerTagAccessRequest) request).getMatchType(); - } else { - matchType = resourceMatcher != null ? resourceMatcher.getMatchType(request.getResource(), request.getContext()) : RangerPolicyResourceMatcher.MatchType.NONE; - } - final boolean isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + RangerPolicyResourceMatcher.MatchType matchType; - if (isMatched) { + if (RangerTagAccessRequest.class.isInstance(request)) { + matchType = ((RangerTagAccessRequest) request).getMatchType(); + } else { + matchType = resourceMatcher != null ? resourceMatcher.getMatchType(request.getResource(), request.getResourceElementMatchingScopes(), request.getContext()) : RangerPolicyResourceMatcher.MatchType.NONE; + } + + final boolean isMatched = matchType != RangerPolicyResourceMatcher.MatchType.NONE; + + if (isMatched) { - if (CollectionUtils.isNotEmpty(allowEvaluators)) { - Set users = new HashSet<>(); - Set groups = new HashSet<>(); + if (CollectionUtils.isNotEmpty(allowEvaluators)) { + Set users = new HashSet<>(); + Set groups = new HashSet<>(); - getResourceAccessInfo(request, allowEvaluators, users, groups); + getResourceAccessInfo(request, allowEvaluators, users, groups); - if (CollectionUtils.isNotEmpty(allowExceptionEvaluators)) { - Set exceptionUsers = new HashSet<>(); - Set exceptionGroups = new HashSet<>(); + if (CollectionUtils.isNotEmpty(allowExceptionEvaluators)) { + Set exceptionUsers = new HashSet<>(); + Set exceptionGroups = new HashSet<>(); - getResourceAccessInfo(request, allowExceptionEvaluators, exceptionUsers, exceptionGroups); + getResourceAccessInfo(request, allowExceptionEvaluators, exceptionUsers, exceptionGroups); - users.removeAll(exceptionUsers); - groups.removeAll(exceptionGroups); + users.removeAll(exceptionUsers); + groups.removeAll(exceptionGroups); + } + + result.getAllowedUsers().addAll(users); + result.getAllowedGroups().addAll(groups); } + if (matchType != RangerPolicyResourceMatcher.MatchType.DESCENDANT) { + if (CollectionUtils.isNotEmpty(denyEvaluators)) { + Set users = new HashSet(); + Set groups = new HashSet(); - result.getAllowedUsers().addAll(users); - result.getAllowedGroups().addAll(groups); - } - if (matchType != RangerPolicyResourceMatcher.MatchType.DESCENDANT) { - if (CollectionUtils.isNotEmpty(denyEvaluators)) { - Set users = new HashSet(); - Set groups = new HashSet(); + getResourceAccessInfo(request, denyEvaluators, users, groups); - getResourceAccessInfo(request, denyEvaluators, users, groups); + if (CollectionUtils.isNotEmpty(denyExceptionEvaluators)) { + Set exceptionUsers = new HashSet(); + Set exceptionGroups = new HashSet(); - if (CollectionUtils.isNotEmpty(denyExceptionEvaluators)) { - Set exceptionUsers = new HashSet(); - Set exceptionGroups = new HashSet(); + getResourceAccessInfo(request, denyExceptionEvaluators, exceptionUsers, exceptionGroups); - getResourceAccessInfo(request, denyExceptionEvaluators, exceptionUsers, exceptionGroups); + users.removeAll(exceptionUsers); + groups.removeAll(exceptionGroups); + } - users.removeAll(exceptionUsers); - groups.removeAll(exceptionGroups); + result.getDeniedUsers().addAll(users); + result.getDeniedGroups().addAll(groups); } - - result.getDeniedUsers().addAll(users); - result.getDeniedGroups().addAll(groups); } } } @@ -454,39 +524,35 @@ public void getResourceAccessInfo(RangerAccessRequest request, RangerResourceAcc @Override public PolicyACLSummary getPolicyACLSummary() { if (aclSummary == null) { - boolean forceCreation = true; - aclSummary = createPolicyACLSummary(forceCreation); + aclSummary = createPolicyACLSummary(ServiceDefUtil.getExpandedImpliedGrants(getServiceDef()), true); } - return aclSummary; } @Override public void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, boolean isAllowed, String reason) { if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.updateAccessResult(" + result + ", " + matchType +", " + isAllowed + ", " + reason + ", " + getId() + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.updateAccessResult(" + result + ", " + matchType +", " + isAllowed + ", " + reason + ", " + getPolicyId() + ")"); } if (!isAllowed) { - if (matchType != RangerPolicyResourceMatcher.MatchType.DESCENDANT || !result.getAccessRequest().isAccessTypeAny()) { + if (matchType != RangerPolicyResourceMatcher.MatchType.DESCENDANT) { result.setIsAllowed(false); result.setPolicyPriority(getPolicyPriority()); - result.setPolicyId(getId()); + result.setPolicyId(getPolicyId()); result.setReason(reason); result.setPolicyVersion(getPolicy().getVersion()); } } else { if (!result.getIsAllowed()) { // if access is not yet allowed by another policy - if (matchType != RangerPolicyResourceMatcher.MatchType.ANCESTOR) { - result.setIsAllowed(true); - result.setPolicyPriority(getPolicyPriority()); - result.setPolicyId(getId()); - result.setReason(reason); - result.setPolicyVersion(getPolicy().getVersion()); - } + result.setIsAllowed(true); + result.setPolicyPriority(getPolicyPriority()); + result.setPolicyId(getPolicyId()); + result.setReason(reason); + result.setPolicyVersion(getPolicy().getVersion()); } } if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.updateAccessResult(" + result + ", " + matchType +", " + isAllowed + ", " + reason + ", " + getId() + ")"); + LOG.debug("<== RangerDefaultPolicyEvaluator.updateAccessResult(" + result + ", " + matchType +", " + isAllowed + ", " + reason + ", " + getPolicyId() + ")"); } } @@ -496,12 +562,8 @@ PolicyACLSummary for access evaluation (that is, if disableAccessEvaluationWithP is set to false). It may return null object if all accesses for all user/groups cannot be determined statically. */ - private PolicyACLSummary createPolicyACLSummary() { - boolean forceCreation = false; - return createPolicyACLSummary(forceCreation); - } - private PolicyACLSummary createPolicyACLSummary(boolean isCreationForced) { + private PolicyACLSummary createPolicyACLSummary(Map> impliedAccessGrants, boolean isCreationForced) { PolicyACLSummary ret = null; RangerPerfTracer perf = null; @@ -509,42 +571,58 @@ private PolicyACLSummary createPolicyACLSummary(boolean isCreationForced) { perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_INIT_ACLSUMMARY_LOG, "RangerPolicyEvaluator.init.ACLSummary(" + perfTag + ")"); } - final boolean hasNonPublicGroupOrConditionsInAllowExceptions = hasNonPublicGroupOrConditions(getPolicy().getAllowExceptions()); - final boolean hasNonPublicGroupOrConditionsInDenyExceptions = hasNonPublicGroupOrConditions(getPolicy().getDenyExceptions()); - final boolean hasPublicGroupInAllowAndUsersInAllowExceptions = hasPublicGroupAndUserInException(getPolicy().getPolicyItems(), getPolicy().getAllowExceptions()); - final boolean hasPublicGroupInDenyAndUsersInDenyExceptions = hasPublicGroupAndUserInException(getPolicy().getDenyPolicyItems(), getPolicy().getDenyExceptions()); + RangerPolicy policy; + if (!disableRoleResolution && hasRoles(getPolicy())) { + policy = getPolicyWithRolesResolved(getPolicy()); + } else { + policy = getPolicy(); + } + + final boolean hasNonPublicGroupOrConditionsInAllowExceptions = hasNonPublicGroupOrConditions(policy.getAllowExceptions()); + final boolean hasNonPublicGroupOrConditionsInDenyExceptions = hasNonPublicGroupOrConditions(policy.getDenyExceptions()); + final boolean hasPublicGroupInAllowAndUsersInAllowExceptions = hasPublicGroupAndUserInException(policy.getPolicyItems(), policy.getAllowExceptions()); + final boolean hasPublicGroupInDenyAndUsersInDenyExceptions = hasPublicGroupAndUserInException(policy.getDenyPolicyItems(), policy.getDenyExceptions()); final boolean hasContextSensitiveSpecification = hasContextSensitiveSpecification(); - final boolean hasRoles = hasRoles(); + final boolean hasRoles = hasRoles(policy); final boolean isUsableForEvaluation = !hasNonPublicGroupOrConditionsInAllowExceptions && !hasNonPublicGroupOrConditionsInDenyExceptions && !hasPublicGroupInAllowAndUsersInAllowExceptions && !hasPublicGroupInDenyAndUsersInDenyExceptions - && !hasContextSensitiveSpecification - && !hasRoles; + && !hasContextSensitiveSpecification + && !hasRoles; if (isUsableForEvaluation || isCreationForced) { ret = new PolicyACLSummary(); - for (RangerPolicyItem policyItem : getPolicy().getDenyPolicyItems()) { - ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY, hasNonPublicGroupOrConditionsInDenyExceptions || hasPublicGroupInDenyAndUsersInDenyExceptions); + for (RangerPolicyItem policyItem : policy.getDenyPolicyItems()) { + ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY, hasNonPublicGroupOrConditionsInDenyExceptions || hasPublicGroupInDenyAndUsersInDenyExceptions, impliedAccessGrants); } if (!hasNonPublicGroupOrConditionsInDenyExceptions && !hasPublicGroupInDenyAndUsersInDenyExceptions) { - for (RangerPolicyItem policyItem : getPolicy().getDenyExceptions()) { - ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY_EXCEPTIONS, false); + for (RangerPolicyItem policyItem : policy.getDenyExceptions()) { + ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY_EXCEPTIONS, false, impliedAccessGrants); } } - for (RangerPolicyItem policyItem : getPolicy().getPolicyItems()) { - ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW, hasNonPublicGroupOrConditionsInAllowExceptions || hasPublicGroupInAllowAndUsersInAllowExceptions); + for (RangerPolicyItem policyItem : policy.getPolicyItems()) { + ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW, hasNonPublicGroupOrConditionsInAllowExceptions || hasPublicGroupInAllowAndUsersInAllowExceptions, impliedAccessGrants); } if (!hasNonPublicGroupOrConditionsInAllowExceptions && !hasPublicGroupInAllowAndUsersInAllowExceptions) { - for (RangerPolicyItem policyItem : getPolicy().getAllowExceptions()) { - ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW_EXCEPTIONS, false); + for (RangerPolicyItem policyItem : policy.getAllowExceptions()) { + ret.processPolicyItem(policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW_EXCEPTIONS, false, impliedAccessGrants); } } - final boolean isDenyAllElse = Boolean.TRUE.equals(getPolicy().getIsDenyAllElse()); + + for (RangerRowFilterPolicyItem policyItem : policy.getRowFilterPolicyItems()) { + ret.processRowFilterPolicyItem(policyItem); + } + + for (RangerDataMaskPolicyItem policyItem : policy.getDataMaskPolicyItems()) { + ret.processDataMaskPolicyItem(policyItem); + } + + final boolean isDenyAllElse = Boolean.TRUE.equals(policy.getIsDenyAllElse()); final Set allAccessTypeNames; @@ -568,6 +646,126 @@ private PolicyACLSummary createPolicyACLSummary(boolean isCreationForced) { return ret; } + private RangerPolicy getPolicyWithRolesResolved(final RangerPolicy policy) { + // Create new policy with no roles in it + // For each policyItem, expand roles into users and groups; and replace all policyItems with expanded roles - TBD + + RangerPolicy ret = new RangerPolicy(); + ret.updateFrom(policy); + ret.setId(policy.getId()); + ret.setGuid(policy.getGuid()); + ret.setVersion(policy.getVersion()); + + if (CollectionUtils.isNotEmpty(policy.getPolicyItems())) { + List policyItems = new ArrayList<>(); + + for (RangerPolicyItem policyItem : policy.getPolicyItems()) { + RangerPolicyItem newPolicyItem = new RangerPolicyItem(policyItem.getAccesses(), policyItem.getUsers(), policyItem.getGroups(), policyItem.getRoles(), policyItem.getConditions(), policyItem.getDelegateAdmin()); + getPolicyItemWithRolesResolved(newPolicyItem, policyItem); + + policyItems.add(newPolicyItem); + } + + ret.setPolicyItems(policyItems); + } + + if (CollectionUtils.isNotEmpty(policy.getDenyPolicyItems())) { + List denyPolicyItems = new ArrayList<>(); + + for (RangerPolicyItem policyItem : policy.getDenyPolicyItems()) { + RangerPolicyItem newPolicyItem = new RangerPolicyItem(policyItem.getAccesses(), policyItem.getUsers(), policyItem.getGroups(), policyItem.getRoles(), policyItem.getConditions(), policyItem.getDelegateAdmin()); + getPolicyItemWithRolesResolved(newPolicyItem, policyItem); + + denyPolicyItems.add(newPolicyItem); + } + + ret.setDenyPolicyItems(denyPolicyItems); + } + + if (CollectionUtils.isNotEmpty(policy.getAllowExceptions())) { + List allowExceptions = new ArrayList<>(); + + for (RangerPolicyItem policyItem : policy.getAllowExceptions()) { + RangerPolicyItem newPolicyItem = new RangerPolicyItem(policyItem.getAccesses(), policyItem.getUsers(), policyItem.getGroups(), policyItem.getRoles(), policyItem.getConditions(), policyItem.getDelegateAdmin()); + getPolicyItemWithRolesResolved(newPolicyItem, policyItem); + + allowExceptions.add(newPolicyItem); + } + + ret.setAllowExceptions(allowExceptions); + } + + if (CollectionUtils.isNotEmpty(policy.getDenyExceptions())) { + List denyExceptions = new ArrayList<>(); + + for (RangerPolicyItem policyItem : policy.getDenyExceptions()) { + RangerPolicyItem newPolicyItem = new RangerPolicyItem(policyItem.getAccesses(), policyItem.getUsers(), policyItem.getGroups(), policyItem.getRoles(), policyItem.getConditions(), policyItem.getDelegateAdmin()); + getPolicyItemWithRolesResolved(newPolicyItem, policyItem); + + denyExceptions.add(newPolicyItem); + } + + ret.setDenyExceptions(denyExceptions); + } + + if (CollectionUtils.isNotEmpty(policy.getDataMaskPolicyItems())) { + List dataMaskPolicyItems = new ArrayList<>(); + + for (RangerDataMaskPolicyItem policyItem : policy.getDataMaskPolicyItems()) { + RangerDataMaskPolicyItem newPolicyItem = new RangerDataMaskPolicyItem(policyItem.getAccesses(), policyItem.getDataMaskInfo(), policyItem.getUsers(), policyItem.getGroups(), policyItem.getRoles(), policyItem.getConditions(), policyItem.getDelegateAdmin()); + getPolicyItemWithRolesResolved(newPolicyItem, policyItem); + + dataMaskPolicyItems.add(newPolicyItem); + } + + ret.setDataMaskPolicyItems(dataMaskPolicyItems); + } + + if (CollectionUtils.isNotEmpty(policy.getRowFilterPolicyItems())) { + List rowFilterPolicyItems = new ArrayList<>(); + + for (RangerRowFilterPolicyItem policyItem : policy.getRowFilterPolicyItems()) { + RangerRowFilterPolicyItem newPolicyItem = new RangerRowFilterPolicyItem(policyItem.getRowFilterInfo(), policyItem.getAccesses(), policyItem.getUsers(), policyItem.getGroups(), policyItem.getRoles(), policyItem.getConditions(), policyItem.getDelegateAdmin()); + getPolicyItemWithRolesResolved(newPolicyItem, policyItem); + + rowFilterPolicyItems.add(newPolicyItem); + } + + ret.setRowFilterPolicyItems(rowFilterPolicyItems); + } + + return ret; + } + + private void getPolicyItemWithRolesResolved(RangerPolicyItem newPolicyItem, final RangerPolicyItem policyItem) { + RangerRolesUtil rolesUtil = getPluginContext().getAuthContext().getRangerRolesUtil();; + Set usersFromRoles = new HashSet<>(); + Set groupsFromRoles = new HashSet<>(); + + for (String role : policyItem.getRoles()) { + Set users = rolesUtil.getRoleToUserMapping().get(role); + Set groups = rolesUtil.getRoleToGroupMapping().get(role); + + if (CollectionUtils.isNotEmpty(users)) { + usersFromRoles.addAll(users); + } + + if (CollectionUtils.isNotEmpty(groups)) { + groupsFromRoles.addAll(groups); + } + } + + if (CollectionUtils.isNotEmpty(usersFromRoles) || CollectionUtils.isNotEmpty(groupsFromRoles)) { + usersFromRoles.addAll(policyItem.getUsers()); + groupsFromRoles.addAll(policyItem.getGroups()); + + newPolicyItem.setUsers(new ArrayList<>(usersFromRoles)); + newPolicyItem.setGroups(new ArrayList<>(groupsFromRoles)); + } + + newPolicyItem.setRoles(null); + } + private boolean hasPublicGroupAndUserInException(List grants, List exceptionItems) { boolean ret = false; @@ -604,123 +802,183 @@ protected void evaluatePolicyItems(RangerAccessRequest request, RangerPolicyReso if(LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyEvaluator.evaluatePolicyItems(" + request + ", " + result + ", " + matchType + ")"); } - if (useAclSummaryForEvaluation && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS)) { - if (LOG.isDebugEnabled()) { - LOG.debug("Using ACL Summary for access evaluation. PolicyId=[" + getId() + "]"); - } - Integer accessResult = lookupPolicyACLSummary(request.getUser(), request.getUserGroups(), request.getUserRoles(), request.getAccessType()); - if (accessResult != null) { - updateAccessResult(result, matchType, accessResult.equals(RangerPolicyEvaluator.ACCESS_ALLOWED), null); - } - } else { - if (LOG.isDebugEnabled()) { - LOG.debug("Using policyItemEvaluators for access evaluation. PolicyId=[" + getId() + "]"); - } - RangerPolicyItemEvaluator matchedPolicyItem = getMatchingPolicyItem(request, result); + Set allRequestedAccesses = RangerAccessRequestUtil.getAllRequestedAccessTypes(request); - if (matchedPolicyItem != null) { - matchedPolicyItem.updateAccessResult(this, result, matchType); - } else if (getPolicy().getIsDenyAllElse() && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS) && !request.isAccessTypeAny()) { - updateAccessResult(result, RangerPolicyResourceMatcher.MatchType.NONE, false, "matched deny-all-else policy"); - } - } + if (CollectionUtils.isNotEmpty(allRequestedAccesses)) { + Map accessTypeResults = RangerAccessRequestUtil.getAccessTypeResults(request); - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyEvaluator.evaluatePolicyItems(" + request + ", " + result + ", " + matchType + ")"); - } - } + for (String accessType : allRequestedAccesses) { - private Integer lookupPolicyACLSummary(String user, Set userGroups, Set userRoles, String accessType) { - Integer accessResult = null; + if (LOG.isDebugEnabled()) { + LOG.debug("Checking for accessType:[" + accessType + "]"); + } + RangerAccessResult denyResult = null; + RangerAccessResult allowResult = null; + boolean noResult = false; - Map accesses = aclSummary.getUsersAccessInfo().get(user); + RangerAccessRequestWrapper oneRequest = new RangerAccessRequestWrapper(request, accessType); + RangerAccessResult oneResult = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), oneRequest); - accessResult = lookupAccess(user, accessType, accesses); + oneResult.setAuditResultFrom(result); - if (accessResult == null) { + RangerPolicyItemEvaluator matchedPolicyItem = getMatchingPolicyItem(oneRequest, oneResult); - Set groups = new HashSet<>(); - groups.add(RangerPolicyEngine.GROUP_PUBLIC); - groups.addAll(userGroups); + if (matchedPolicyItem != null) { + matchedPolicyItem.updateAccessResult(this, oneResult, matchType); + } else if (getPolicy().getIsDenyAllElse() && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS)) { + updateAccessResult(oneResult, matchType, false, "matched deny-all-else policy"); + } - for (String userGroup : groups) { - accesses = aclSummary.getGroupsAccessInfo().get(userGroup); - accessResult = lookupAccess(userGroup, accessType, accesses); - if (accessResult != null) { - break; + if (oneResult.getIsAllowed()) { + allowResult = oneResult; + } else if (oneResult.getIsAccessDetermined()) { + denyResult = oneResult; + } else { + noResult = true; } - } - if (userRoles !=null) { - for (String userRole : userRoles) { - accesses = aclSummary.getRolesAccessInfo().get(userRole); - accessResult = lookupAccess(userRole, accessType, accesses); - if (accessResult != null) { - break; + if (!noResult) { + RangerAccessResult oldResult = accessTypeResults.get(accessType); + if (oldResult == null) { + accessTypeResults.put(accessType, allowResult != null ? allowResult : denyResult); + } else { + int oldPriority = oldResult.getPolicyPriority(); + if (oldResult.getIsAllowed()) { + if (denyResult != null) { + if (getPolicyPriority() >= oldPriority) { + accessTypeResults.put(accessType, denyResult); + } + } else { + if (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS) { + if (getPolicyPriority() > oldPriority) { + accessTypeResults.put(accessType, allowResult); + } + } else { + if (getPolicyPriority() >= oldPriority) { + accessTypeResults.put(accessType, allowResult); + } + } + } + } else { // Earlier evaluator denied this access + if (getPolicyPriority() >= oldPriority && allowResult != null && (oneRequest.isAccessTypeAny() || RangerAccessRequestUtil.getIsAnyAccessInContext(oneRequest.getContext()))) { + accessTypeResults.put(accessType, allowResult); + } else { + if (getPolicyPriority() > oldPriority && denyResult != null) { + accessTypeResults.put(accessType, denyResult); + } + } + } + } + /* At least one access is allowed - this evaluator need not be checked for other accesses as the test below + * implies that there is only one access group in the request + */ + if (oneRequest.isAccessTypeAny() || RangerAccessRequestUtil.getIsAnyAccessInContext(oneRequest.getContext())) { + if (allowResult != null) { + break; + } } } } + + RangerAccessResult compositeAccessResult = getCompositeAccessResult(request, result); + if (compositeAccessResult != null) { + result.setAccessResultFrom(compositeAccessResult); + } + } else { + RangerPolicyItemEvaluator matchedPolicyItem = getMatchingPolicyItem(request, result); + if (matchedPolicyItem != null) { + matchedPolicyItem.updateAccessResult(this, result, matchType); + } else if (getPolicy().getIsDenyAllElse() && (getPolicy().getPolicyType() == null || getPolicy().getPolicyType() == RangerPolicy.POLICY_TYPE_ACCESS)) { + updateAccessResult(result, matchType, false, "matched deny-all-else policy"); + } } - return accessResult; + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerDefaultPolicyEvaluator.evaluatePolicyItems(" + request + ", " + result + ", " + matchType + ")"); + } } - private Integer lookupAccess(String userOrGroup, String accessType, Map accesses) { - Integer ret = null; - if (accesses != null) { - if (accessType.equals(RangerPolicyEngine.ANY_ACCESS)) { - ret = getAccessResultForAnyAccess(accesses); - } else { - PolicyACLSummary.AccessResult accessResult = accesses.get(accessType); - if (accessResult != null) { - if (accessResult.getResult() == RangerPolicyEvaluator.ACCESS_CONDITIONAL) { - LOG.error("Access should not be conditional at this point! user=[" + userOrGroup + "], " + "accessType=[" + accessType + "]"); - } else { - ret = accessResult.getResult(); + private RangerAccessResult deriveAccessResultFromGroup(RangerAccessRequest request, Set accessesInGroup) { + RangerAccessResult ret = null; + Map accessTypeResults = RangerAccessRequestUtil.getAccessTypeResults(request); + + boolean isAccessDetermined = true; + boolean isAccessDenied = false; + RangerAccessResult deniedAccessResult = null; + + for (String accessType : accessesInGroup) { + RangerAccessResult accessResult = accessTypeResults.get(accessType); + if (accessResult != null) { + if (accessResult.getIsAllowed()) { + // Allow + isAccessDenied = false; + ret = accessResult; + break; + } else { + isAccessDenied = true; + if (deniedAccessResult == null) { + deniedAccessResult = accessResult; } } + } else { + isAccessDetermined = false; } } + if (isAccessDetermined && isAccessDenied) { + ret = deniedAccessResult; + } return ret; } - private Integer getAccessResultForAnyAccess(Map accessses) { - Integer ret = null; - - int allowedAccessCount = 0; - int deniedAccessCount = 0; - int undeterminedAccessCount = 0; - int accessesSize = 0; - - for (Map.Entry entry : accessses.entrySet()) { - if (StringUtils.equals(entry.getKey(), RangerPolicyEngine.ADMIN_ACCESS)) { - // Dont count admin access if present - continue; + private RangerAccessResult getCompositeAccessResult(RangerAccessRequest request, RangerAccessResult result) { + RangerAccessResult ret = null; + Set> allAccessTypeGroups = RangerAccessRequestUtil.getAllRequestedAccessTypeGroups(request); + Set allAccessTypes = RangerAccessRequestUtil.getAllRequestedAccessTypes(request); + Set ignoreIfNotDeniedAccessTypes = RangerAccessRequestUtil.getIgnoreIfNotDeniedAccessTypes(request); + + if (CollectionUtils.isEmpty(allAccessTypeGroups)) { + ret = deriveAccessResultFromGroup(request, allAccessTypes); + if (ret == null && CollectionUtils.isNotEmpty(ignoreIfNotDeniedAccessTypes) && ignoreIfNotDeniedAccessTypes.containsAll(allAccessTypes)) { + // group does not allow/deny access and this group's all access-types are also in the ignore-if-not-denied access-type list + ret = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), request); + ret.setAuditResultFrom(result); + ret.setIsAllowed(true); } - PolicyACLSummary.AccessResult accessResult = entry.getValue(); - if (accessResult.getResult() == RangerPolicyEvaluator.ACCESS_ALLOWED) { - allowedAccessCount++; - } else if (accessResult.getResult() == RangerPolicyEvaluator.ACCESS_DENIED) { - deniedAccessCount++; - } else if (accessResult.getResult() == RangerPolicyEvaluator.ACCESS_UNDETERMINED && !accessResult.getHasSeenDeny()) { - undeterminedAccessCount++; + } else { + boolean isAccessDetermined = true; + boolean isAccessAllowed = false; + RangerAccessResult allowResult = null; + + for (Set accessesInGroup : allAccessTypeGroups) { + RangerAccessResult groupResult = deriveAccessResultFromGroup(request, accessesInGroup); + if (groupResult != null) { + if (!groupResult.getIsAllowed()) { + // Deny + isAccessAllowed = false; + ret = groupResult; + break; + } else { + isAccessAllowed = true; + if (allowResult == null) { + allowResult = groupResult; + } + } + } else { + // Some group is not completely authorized yet + if (!(CollectionUtils.isNotEmpty(ignoreIfNotDeniedAccessTypes) && ignoreIfNotDeniedAccessTypes.containsAll(accessesInGroup))) { + // group does not allow/deny access and this group's all access-types are also in the ignore-if-not-denied access-type list + isAccessDetermined = false; + } + } } - accessesSize++; - } - int accessTypeCount = getServiceDef().getAccessTypes().size(); - - if (accessTypeCount == accessesSize) { - // All permissions are represented - if (deniedAccessCount > 0 || undeterminedAccessCount == accessTypeCount) { - // at least one is denied or all are undetermined - ret = RangerPolicyEvaluator.ACCESS_DENIED; - } - } - if (ret == null) { - if (allowedAccessCount > 0 || undeterminedAccessCount > 0) { - ret = RangerPolicyEvaluator.ACCESS_ALLOWED; + if (isAccessDetermined && isAccessAllowed) { + ret = allowResult; + } else if (isAccessDetermined && ret == null) { // If none of the groups allowed/denied access and every group's all access-type results are to be ignored unless denied + ret = new RangerAccessResult(result.getPolicyType(), result.getServiceName(), result.getServiceDef(), request); + ret.setAuditResultFrom(result); + ret.setIsAllowed(true); } } return ret; @@ -793,7 +1051,17 @@ protected boolean isMatch(Map resources, Map RangerDefaultPolicyEvaluator.isMatch(" + resources + ", " + evalContext + ")"); } - boolean ret = resourceMatcher != null && resourceMatcher.isMatch(resources, evalContext); + boolean ret = false; + + for (RangerPolicyResourceEvaluator resourceEvaluator : getResourceEvaluators()) { + RangerPolicyResourceMatcher resourceMatcher = resourceEvaluator.getPolicyResourceMatcher(); + + ret = resourceMatcher != null && resourceMatcher.isMatch(resources, evalContext); + + if (ret) { + break; + } + } if(LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyEvaluator.isMatch(" + resources + ", " + evalContext + "): " + ret); @@ -804,7 +1072,7 @@ protected boolean isMatch(Map resources, Map userGroups, Set roles, String owner, String accessType) { if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyEvaluator.isAccessAllowed(" + user + ", " + userGroups + ", " + roles + ", " + owner + ", " + accessType + ")"); + LOG.debug("==> RangerDefaultPolicyEvaluator.isAccessAllowed(policy-id=" + getPolicyId() + ", " + user + ", " + userGroups + ", " + roles + ", " + owner + ", " + accessType + ")"); } boolean ret = false; @@ -815,31 +1083,20 @@ protected boolean isAccessAllowed(String user, Set userGroups, Set> impliedAccessGrants = getImpliedAccessGrants(serviceDef); + Map> impliedAccessGrants = options.getServiceDefHelper().getImpliedAccessGrants(); if(impliedAccessGrants == null || impliedAccessGrants.isEmpty()) { return; @@ -878,9 +1140,11 @@ private void preprocessPolicy(RangerPolicy policy, RangerServiceDef serviceDef) preprocessPolicyItems(policy.getDenyExceptions(), impliedAccessGrants); preprocessPolicyItems(policy.getDataMaskPolicyItems(), impliedAccessGrants); preprocessPolicyItems(policy.getRowFilterPolicyItems(), impliedAccessGrants); + + */ } - private void preprocessPolicyItems(List policyItems, Map> impliedAccessGrants) { + protected void preprocessPolicyItems(List policyItems, Map> impliedAccessGrants) { for(RangerPolicyItem policyItem : policyItems) { if(CollectionUtils.isEmpty(policyItem.getAccesses())) { continue; @@ -904,7 +1168,7 @@ private void preprocessPolicyItems(List policyItems, if(impliedAccess == null) { impliedAccess = new RangerPolicyItemAccess(impliedGrant, access.getIsAllowed()); - policyItem.getAccesses().add(impliedAccess); + policyItem.addAccess(impliedAccess); } else { if(! impliedAccess.getIsAllowed()) { impliedAccess.setIsAllowed(access.getIsAllowed()); @@ -915,38 +1179,12 @@ private void preprocessPolicyItems(List policyItems, } } - private Map> getImpliedAccessGrants(RangerServiceDef serviceDef) { - Map> ret = null; - - if(serviceDef != null && !CollectionUtils.isEmpty(serviceDef.getAccessTypes())) { - for(RangerAccessTypeDef accessTypeDef : serviceDef.getAccessTypes()) { - if(!CollectionUtils.isEmpty(accessTypeDef.getImpliedGrants())) { - if(ret == null) { - ret = new HashMap<>(); - } - - Collection impliedAccessGrants = ret.get(accessTypeDef.getName()); - - if(impliedAccessGrants == null) { - impliedAccessGrants = new HashSet<>(); - - ret.put(accessTypeDef.getName(), impliedAccessGrants); - } - - impliedAccessGrants.addAll(accessTypeDef.getImpliedGrants()); - } - } - } - - return ret; - } - - private RangerPolicyItemAccess getAccess(RangerPolicyItem policyItem, String accessType) { + static RangerPolicyItemAccess getAccess(RangerPolicyItem policyItem, String accessType) { RangerPolicyItemAccess ret = null; if(policyItem != null && CollectionUtils.isNotEmpty(policyItem.getAccesses())) { for(RangerPolicyItemAccess itemAccess : policyItem.getAccesses()) { - if(StringUtils.equalsIgnoreCase(itemAccess.getType(), accessType)) { + if (itemAccess != null && StringUtils.equalsIgnoreCase(itemAccess.getType(), accessType)) { ret = itemAccess; break; @@ -1102,11 +1340,7 @@ protected RangerPolicyItemEvaluator getMatchingPolicyItem(RangerAccessRequest re switch (policyType) { case RangerPolicy.POLICY_TYPE_ACCESS: { - ret = getMatchingPolicyItem(request, denyEvaluators, denyExceptionEvaluators); - - if(ret == null && !result.getIsAccessDetermined()) { // a deny policy could have set isAllowed=true, but in such case it wouldn't set isAccessDetermined=true - ret = getMatchingPolicyItem(request, allowEvaluators, allowExceptionEvaluators); - } + ret = getMatchingPolicyItemForAccessPolicyForSpecificAccess(request, result); break; } case RangerPolicy.POLICY_TYPE_DATAMASK: { @@ -1124,6 +1358,16 @@ protected RangerPolicyItemEvaluator getMatchingPolicyItem(RangerAccessRequest re return ret; } + protected RangerPolicyItemEvaluator getMatchingPolicyItemForAccessPolicyForSpecificAccess(RangerAccessRequest request, RangerAccessResult result) { + RangerPolicyItemEvaluator ret = getMatchingPolicyItem(request, denyEvaluators, denyExceptionEvaluators); + + if(ret == null && !result.getIsAccessDetermined()) { // a deny policy could have set isAllowed=true, but in such case it wouldn't set isAccessDetermined=true + ret = getMatchingPolicyItem(request, allowEvaluators, allowExceptionEvaluators); + } + + return ret; + } + protected T getMatchingPolicyItem(RangerAccessRequest request, List evaluators) { T ret = getMatchingPolicyItem(request, evaluators, null); @@ -1230,7 +1474,7 @@ private boolean matchPolicyCustomConditions(RangerAccessRequest request) { conditionType = ((RangerAbstractConditionEvaluator)conditionEvaluator).getPolicyItemCondition().getType(); } - perf = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_REQUEST_LOG, "RangerConditionEvaluator.matchPolicyCustomConditions(policyId=" + getId() + ",policyConditionType=" + conditionType + ")"); + perf = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_REQUEST_LOG, "RangerConditionEvaluator.matchPolicyCustomConditions(policyId=" + getPolicyId() + ",policyConditionType=" + conditionType + ")"); } boolean conditionEvalResult = conditionEvaluator.isMatched(request); @@ -1254,20 +1498,12 @@ private boolean matchPolicyCustomConditions(RangerAccessRequest request) { return ret; } - private List createRangerPolicyConditionEvaluator(RangerPolicy policy, - RangerServiceDef serviceDef, - RangerPolicyEngineOptions options) { - List rangerConditionEvaluators = null; + private List createPolicyConditionEvaluators(RangerPolicy policy, RangerServiceDef serviceDef, RangerPolicyEngineOptions options) { + List ret = RangerCustomConditionEvaluator.getInstance().getPolicyConditionEvaluators(policy, serviceDef, options); - RangerCustomConditionEvaluator rangerConditionEvaluator = new RangerCustomConditionEvaluator(); + customConditionsCount += ret.size(); - rangerConditionEvaluators = rangerConditionEvaluator.getRangerPolicyConditionEvaluator(policy,serviceDef,options); - - if (rangerConditionEvaluators != null) { - customConditionsCount += rangerConditionEvaluators.size(); - } - - return rangerConditionEvaluators; + return ret; } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java index 90d96d93d2..9ed0249efe 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultPolicyItemEvaluator.java @@ -19,21 +19,17 @@ package org.apache.ranger.plugin.policyevaluator; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.conditionevaluator.RangerAbstractConditionEvaluator; import org.apache.ranger.plugin.conditionevaluator.RangerConditionEvaluator; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess; -import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyengine.RangerAccessResult; @@ -42,63 +38,42 @@ import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerDefaultPolicyItemEvaluator extends RangerAbstractPolicyItemEvaluator { - private static final Log LOG = LogFactory.getLog(RangerDefaultPolicyItemEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultPolicyItemEvaluator.class); - private static final Log PERF_POLICYITEM_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policyitem.request"); - private static final Log PERF_POLICYCONDITION_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policycondition.request"); + private static final Logger PERF_POLICYITEM_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policyitem.request"); + private static final Logger PERF_POLICYCONDITION_REQUEST_LOG = RangerPerfTracer.getPerfLogger("policycondition.request"); private boolean hasCurrentUser; private boolean hasResourceOwner; - private boolean hasAllPerms; public RangerDefaultPolicyItemEvaluator(RangerServiceDef serviceDef, RangerPolicy policy, RangerPolicyItem policyItem, int policyItemType, int policyItemIndex, RangerPolicyEngineOptions options) { super(serviceDef, policy, policyItem, policyItemType, policyItemIndex, options); } public void init() { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyItemEvaluator(policyId=" + policyId + ", policyItem=" + policyItem + ", serviceType=" + getServiceType() + ", conditionsDisabled=" + getConditionsDisabledOption() + ")"); } - Set accessPerms = new HashSet(); - - List policyItemAccesses = policyItem.getAccesses(); - for(RangerPolicy.RangerPolicyItemAccess policyItemAccess : policyItemAccesses) { - - if (policyItemAccess.getIsAllowed()) { - accessPerms.add(policyItemAccess.getType()); - } - } - - hasAllPerms = true; - List serviceAccessTypes = serviceDef.getAccessTypes(); - for (RangerServiceDef.RangerAccessTypeDef serviceAccessType : serviceAccessTypes) { - String serviceAccessTypeName = serviceAccessType.getName(); - if (!accessPerms.contains(serviceAccessTypeName)) { - hasAllPerms = false; - break; - } - } - - RangerCustomConditionEvaluator rangerCustomConditionEvaluator = new RangerCustomConditionEvaluator(); - - conditionEvaluators = rangerCustomConditionEvaluator.getPolicyItemConditionEvaluator(policy,policyItem,serviceDef,options,policyItemIndex); + conditionEvaluators = RangerCustomConditionEvaluator.getInstance().getPolicyItemConditionEvaluators(policy, policyItem, serviceDef, options, policyItemIndex); List users = policyItem.getUsers(); this.hasCurrentUser = CollectionUtils.isNotEmpty(users) && users.contains(RangerPolicyEngine.USER_CURRENT); this.hasResourceOwner = CollectionUtils.isNotEmpty(users) && users.contains(RangerPolicyEngine.RESOURCE_OWNER); - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyItemEvaluator(policyId=" + policyId + ", conditionsCount=" + getConditionEvaluators().size() + ")"); } } @Override public boolean isMatch(RangerAccessRequest request) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyItemEvaluator.isMatch(" + request + ")"); } @@ -106,44 +81,35 @@ public boolean isMatch(RangerAccessRequest request) { RangerPerfTracer perf = null; - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYITEM_REQUEST_LOG)) { - perf = RangerPerfTracer.getPerfTracer(PERF_POLICYITEM_REQUEST_LOG, "RangerPolicyItemEvaluator.isMatch(resource=" + request.getResource().getAsString() + ")"); + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYITEM_REQUEST_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICYITEM_REQUEST_LOG, "RangerPolicyItemEvaluator.isMatch(resource=" + request.getResource().getAsString() + ")"); } - if(policyItem != null) { - if(matchUserGroupAndOwner(request)) { + if (policyItem != null) { + if (matchUserGroupAndOwner(request)) { if (request.isAccessTypeDelegatedAdmin()) { // used only in grant/revoke scenario if (policyItem.getDelegateAdmin()) { ret = true; } - } else if (CollectionUtils.isNotEmpty(policyItem.getAccesses())) { - boolean isAccessTypeMatched = false; + } else { + if (withImpliedGrants == null) { + withImpliedGrants = computeWithImpliedGrants(); + } - if (request.isAccessTypeAny()) { - if (getPolicyItemType() == POLICY_ITEM_TYPE_DENY || getPolicyItemType() == POLICY_ITEM_TYPE_DENY_EXCEPTIONS) { - if (hasAllPerms) { - isAccessTypeMatched = true; - } - } else { - for (RangerPolicy.RangerPolicyItemAccess access : policyItem.getAccesses()) { - if (access.getIsAllowed()) { - isAccessTypeMatched = true; - break; - } - } - } - } else { - for (RangerPolicy.RangerPolicyItemAccess access : policyItem.getAccesses()) { + if (withImpliedGrants != null && CollectionUtils.isNotEmpty(withImpliedGrants.getAccesses())) { + boolean isAccessTypeMatched = false; + + for (RangerPolicy.RangerPolicyItemAccess access : withImpliedGrants.getAccesses()) { if (access.getIsAllowed() && StringUtils.equalsIgnoreCase(access.getType(), request.getAccessType())) { isAccessTypeMatched = true; break; } } - } - if(isAccessTypeMatched) { - if(matchCustomConditions(request)) { - ret = true; + if (isAccessTypeMatched) { + if (matchCustomConditions(request)) { + ret = true; + } } } } @@ -152,7 +118,7 @@ public boolean isMatch(RangerAccessRequest request) { RangerPerfTracer.log(perf); - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyItemEvaluator.isMatch(" + request + "): " + ret); } @@ -161,17 +127,17 @@ public boolean isMatch(RangerAccessRequest request) { @Override public boolean matchUserGroupAndOwner(String user, Set userGroups, Set roles, String owner) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyItemEvaluator.matchUserGroup(" + policyItem + ", " + user + ", " + userGroups + ", " + roles + ", " + owner + ")"); } boolean ret = false; - if(policyItem != null) { - if(!ret && user != null && policyItem.getUsers() != null) { + if (policyItem != null) { + if (!ret && user != null && policyItem.getUsers() != null) { ret = hasCurrentUser || policyItem.getUsers().contains(user); } - if(!ret && userGroups != null && policyItem.getGroups() != null) { + if (!ret && userGroups != null && policyItem.getGroups() != null) { ret = policyItem.getGroups().contains(RangerPolicyEngine.GROUP_PUBLIC) || !Collections.disjoint(policyItem.getGroups(), userGroups); } @@ -183,7 +149,7 @@ public boolean matchUserGroupAndOwner(String user, Set userGroups, Set userGroups, Set RangerDefaultPolicyItemEvaluator.matchUserGroupAndOwner(" + request + ")"); } @@ -206,54 +172,61 @@ private boolean matchUserGroupAndOwner(RangerAccessRequest request) { if (!ret) { Set roles = null; if (CollectionUtils.isNotEmpty(policyItem.getRoles())) { - roles = RangerAccessRequestUtil.getCurrentUserRolesFromContext(request.getContext()); + roles = RangerAccessRequestUtil.getUserRoles(request); } ret = matchUserGroupAndOwner(user, userGroups, roles, resourceOwner); } - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyItemEvaluator.matchUserGroupAndOwner(" + request + "): " + ret); } return ret; } + @Override public boolean matchAccessType(String accessType) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyItemEvaluator.matchAccessType(" + accessType + ")"); } boolean ret = false; - if(policyItem != null) { + if (policyItem != null) { boolean isAdminAccess = StringUtils.equals(accessType, RangerPolicyEngine.ADMIN_ACCESS); - if(isAdminAccess) { + if (isAdminAccess) { ret = policyItem.getDelegateAdmin(); } else { - if(CollectionUtils.isNotEmpty(policyItem.getAccesses())) { + if (withImpliedGrants == null) { + withImpliedGrants = computeWithImpliedGrants(); + } + + if (CollectionUtils.isNotEmpty(withImpliedGrants.getAccesses())) { boolean isAnyAccess = StringUtils.equals(accessType, RangerPolicyEngine.ANY_ACCESS); - for(RangerPolicyItemAccess itemAccess : policyItem.getAccesses()) { - if(! itemAccess.getIsAllowed()) { + for (RangerPolicyItemAccess itemAccess : withImpliedGrants.getAccesses()) { + if (!itemAccess.getIsAllowed()) { continue; } - if(isAnyAccess) { + if (isAnyAccess) { ret = true; break; - } else if(StringUtils.equalsIgnoreCase(itemAccess.getType(), accessType)) { + } else if (StringUtils.equalsIgnoreCase(itemAccess.getType(), accessType)) { ret = true; break; } } + } else if (StringUtils.isEmpty(accessType)) { + ret = true; } } } - - if(LOG.isDebugEnabled()) { + + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyItemEvaluator.matchAccessType(" + accessType + "): " + ret); } @@ -262,27 +235,27 @@ public boolean matchAccessType(String accessType) { @Override public boolean matchCustomConditions(RangerAccessRequest request) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyItemEvaluator.matchCustomConditions(" + request + ")"); } boolean ret = true; if (CollectionUtils.isNotEmpty(conditionEvaluators)) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("RangerDefaultPolicyItemEvaluator.matchCustomConditions(): conditionCount=" + conditionEvaluators.size()); } - for(RangerConditionEvaluator conditionEvaluator : conditionEvaluators) { - if(LOG.isDebugEnabled()) { + for (RangerConditionEvaluator conditionEvaluator : conditionEvaluators) { + if (LOG.isDebugEnabled()) { LOG.debug("evaluating condition: " + conditionEvaluator); } RangerPerfTracer perf = null; - if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYCONDITION_REQUEST_LOG)) { + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYCONDITION_REQUEST_LOG)) { String conditionType = null; if (conditionEvaluator instanceof RangerAbstractConditionEvaluator) { - conditionType = ((RangerAbstractConditionEvaluator)conditionEvaluator).getPolicyItemCondition().getType(); + conditionType = ((RangerAbstractConditionEvaluator) conditionEvaluator).getPolicyItemCondition().getType(); } perf = RangerPerfTracer.getPerfTracer(PERF_POLICYCONDITION_REQUEST_LOG, "RangerConditionEvaluator.matchCondition(policyId=" + policyId + ",policyItemIndex=" + getPolicyItemIndex() + ",policyConditionType=" + conditionType + ")"); @@ -293,7 +266,7 @@ public boolean matchCustomConditions(RangerAccessRequest request) { RangerPerfTracer.log(perf); if (!conditionEvalResult) { - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug(conditionEvaluator + " returned false"); } @@ -304,7 +277,7 @@ public boolean matchCustomConditions(RangerAccessRequest request) { } } - if(LOG.isDebugEnabled()) { + if (LOG.isDebugEnabled()) { LOG.debug("<== RangerDefaultPolicyItemEvaluator.matchCustomConditions(" + request + "): " + ret); } @@ -315,51 +288,4 @@ public boolean matchCustomConditions(RangerAccessRequest request) { public void updateAccessResult(RangerPolicyEvaluator policyEvaluator, RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType) { policyEvaluator.updateAccessResult(result, matchType, getPolicyItemType() != RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY, getComments()); } - - RangerPolicyConditionDef getConditionDef(String conditionName) { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyItemEvaluator.getConditionDef(" + conditionName + ")"); - } - - RangerPolicyConditionDef ret = null; - - if (serviceDef != null && CollectionUtils.isNotEmpty(serviceDef.getPolicyConditions())) { - for(RangerPolicyConditionDef conditionDef : serviceDef.getPolicyConditions()) { - if(StringUtils.equals(conditionName, conditionDef.getName())) { - ret = conditionDef; - - break; - } - } - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyItemEvaluator.getConditionDef(" + conditionName + "): " + ret); - } - - return ret; - } - - RangerConditionEvaluator newConditionEvaluator(String className) { - if(LOG.isDebugEnabled()) { - LOG.debug("==> RangerDefaultPolicyItemEvaluator.newConditionEvaluator(" + className + ")"); - } - - RangerConditionEvaluator evaluator = null; - - try { - @SuppressWarnings("unchecked") - Class matcherClass = (Class)Class.forName(className); - - evaluator = matcherClass.newInstance(); - } catch(Throwable t) { - LOG.error("RangerDefaultPolicyItemEvaluator.newConditionEvaluator(" + className + "): error instantiating evaluator", t); - } - - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultPolicyItemEvaluator.newConditionEvaluator(" + className + "): " + evaluator); - } - - return evaluator; - } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java index 0831dde08a..759c0ff596 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerDefaultRowFilterPolicyItemEvaluator.java @@ -25,15 +25,39 @@ import org.apache.ranger.plugin.policyengine.RangerAccessResult; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.JavaScriptEdits; +import org.apache.ranger.plugin.util.RangerRequestExprResolver; public class RangerDefaultRowFilterPolicyItemEvaluator extends RangerDefaultPolicyItemEvaluator implements RangerRowFilterPolicyItemEvaluator { final private RangerRowFilterPolicyItem rowFilterPolicyItem; + final private String rowFilterExpr; + final private RangerRequestExprResolver exprResolver; public RangerDefaultRowFilterPolicyItemEvaluator(RangerServiceDef serviceDef, RangerPolicy policy, RangerRowFilterPolicyItem policyItem, int policyItemIndex, RangerPolicyEngineOptions options) { - super(serviceDef, policy, policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DATAMASK, policyItemIndex, options); + super(serviceDef, policy, policyItem, RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ROWFILTER, policyItemIndex, options); rowFilterPolicyItem = policyItem; + + RangerPolicyItemRowFilterInfo rowFilterInfo = getRowFilterInfo(); + + if (rowFilterInfo != null && rowFilterInfo.getFilterExpr() != null) { + String rowFilterExpr = rowFilterInfo.getFilterExpr(); + + if (JavaScriptEdits.hasDoubleBrackets(rowFilterExpr)) { + rowFilterExpr = JavaScriptEdits.replaceDoubleBrackets(rowFilterExpr); + } + + this.rowFilterExpr = rowFilterExpr; + } else { + rowFilterExpr = null; + } + + if (rowFilterExpr != null && RangerRequestExprResolver.hasExpressions(rowFilterExpr)) { + exprResolver = new RangerRequestExprResolver(rowFilterExpr, getServiceType()); + } else { + exprResolver = null; + } } @Override @@ -43,10 +67,13 @@ public RangerPolicyItemRowFilterInfo getRowFilterInfo() { @Override public void updateAccessResult(RangerPolicyEvaluator policyEvaluator, RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType) { - RangerPolicyItemRowFilterInfo rowFilterInfo = getRowFilterInfo(); + if (exprResolver != null) { + result.setFilterExpr(exprResolver.resolveExpressions(result.getAccessRequest())); + } else if (rowFilterExpr != null) { + result.setFilterExpr(rowFilterExpr); + } - if (result.getFilterExpr() == null && rowFilterInfo != null) { - result.setFilterExpr(rowFilterInfo.getFilterExpr()); + if (result.getFilterExpr() != null) { policyEvaluator.updateAccessResult(result, matchType, true, getComments()); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java index bac076c296..665ee3cbec 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerOptimizedPolicyEvaluator.java @@ -21,8 +21,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.model.RangerBaseModelObject; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; @@ -30,21 +29,24 @@ import org.apache.ranger.plugin.policyengine.RangerPolicyEngine; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.*; public class RangerOptimizedPolicyEvaluator extends RangerDefaultPolicyEvaluator { - private static final Log LOG = LogFactory.getLog(RangerOptimizedPolicyEvaluator.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerOptimizedPolicyEvaluator.class); - private Set roles = new HashSet<>(); - private Set groups = new HashSet<>(); - private Set users = new HashSet<>(); - private Set accessPerms = new HashSet<>(); + private Set roles = RangerBaseModelObject.nullSafeSet(null); + private Set groups = RangerBaseModelObject.nullSafeSet(null); + private Set users = RangerBaseModelObject.nullSafeSet(null); + private Set accessPerms = RangerBaseModelObject.nullSafeSet(null); private boolean delegateAdmin; private boolean hasAllPerms; private boolean hasPublicGroup; private boolean hasCurrentUser; private boolean hasResourceOwner; + private boolean hasAllEvaluatorsInitialized; // For computation of priority private static final String RANGER_POLICY_EVAL_MATCH_ANY_PATTERN_STRING = "*"; @@ -252,14 +254,27 @@ protected boolean isAccessAllowed(String user, Set userGroups, Set allRequestedAccesses = RangerAccessRequestUtil.getAllRequestedAccessTypes(request); + ret = CollectionUtils.containsAny(accessPerms, allRequestedAccesses); + } + + if (!ret) { + if (request.isAccessTypeDelegatedAdmin()) { + ret = delegateAdmin; + } + } + } } + } else { + ret = true; } return ret; @@ -284,28 +299,55 @@ private boolean isOwnerMatch(RangerAccessRequest request) { private boolean hasMatchablePolicyItem(String user, Set userGroups, Set rolesFromContext, String owner, String accessType) { boolean ret = false; - boolean hasRole = false; - if (CollectionUtils.isNotEmpty(roles)) { - if (CollectionUtils.isNotEmpty(rolesFromContext)) { - hasRole = CollectionUtils.containsAny(roles, rolesFromContext); + if (checkIfAllEvaluatorsInitialized()) { + boolean hasRole = false; + if (CollectionUtils.isNotEmpty(roles)) { + if (CollectionUtils.isNotEmpty(rolesFromContext)) { + hasRole = CollectionUtils.containsAny(roles, rolesFromContext); + } + } + + if (hasPublicGroup || hasCurrentUser || users.contains(user) || CollectionUtils.containsAny(groups, userGroups) || hasRole || (hasResourceOwner && StringUtils.equals(user, owner))) { + if (hasAllPerms) { + ret = true; + } else { + boolean isAccessTypeAny = StringUtils.isEmpty(accessType) || StringUtils.equals(accessType, RangerPolicyEngine.ANY_ACCESS); + ret = isAccessTypeAny || accessPerms.contains(accessType); + + if (!ret) { + if (StringUtils.equals(accessType, RangerPolicyEngine.ADMIN_ACCESS)) { + ret = delegateAdmin; + } + } + } } + } else { + ret = true; } - if (hasPublicGroup || hasCurrentUser || users.contains(user) || CollectionUtils.containsAny(groups, userGroups) || hasRole || (hasResourceOwner && StringUtils.equals(user, owner))) { - boolean isAdminAccess = StringUtils.equals(accessType, RangerPolicyEngine.ADMIN_ACCESS); + return ret; + } - if(isAdminAccess) { - ret = delegateAdmin; - } else if(hasAllPerms) { - ret = true; + private static Set addAll(Set coll, Collection toAdd) { + if (CollectionUtils.isNotEmpty(toAdd)) { + if (CollectionUtils.isEmpty(coll)) { + coll = new HashSet<>(toAdd); } else { - boolean isAccessTypeAny = StringUtils.isEmpty(accessType) || StringUtils.equals(accessType, RangerPolicyEngine.ANY_ACCESS); - - ret = isAccessTypeAny || accessPerms.contains(accessType); + coll.addAll(toAdd); } } - return ret; + return coll; + } + + private static Set add(Set coll, String value) { + if (CollectionUtils.isEmpty(coll)) { + coll = new HashSet<>(); + } + + coll.add(value); + + return coll; } private void preprocessPolicyItems(List policyItems) { @@ -317,15 +359,13 @@ private void preprocessPolicyItems(List for(RangerPolicy.RangerPolicyItemAccess policyItemAccess : policyItemAccesses) { if (policyItemAccess.getIsAllowed()) { - String accessType = policyItemAccess.getType(); - accessPerms.add(accessType); + add(accessPerms, policyItemAccess.getType()); } } - roles.addAll(item.getRoles()); - groups.addAll(item.getGroups()); - users.addAll(item.getUsers()); - + roles = addAll(roles, item.getRoles()); + groups = addAll(groups, item.getGroups()); + users = addAll(users, item.getUsers()); } } } @@ -355,4 +395,37 @@ private boolean checkIfHasAllPerms() { return result; } + private boolean checkIfAllEvaluatorsInitialized() { + if (!hasAllEvaluatorsInitialized) { + hasAllEvaluatorsInitialized = checkIfWithImpliedGrantsInitialized (getAllowEvaluators()) && + checkIfWithImpliedGrantsInitialized(getAllowExceptionEvaluators()) && + checkIfWithImpliedGrantsInitialized(getDenyEvaluators()) && + checkIfWithImpliedGrantsInitialized(getDenyExceptionEvaluators()) && + checkIfWithImpliedGrantsInitialized(getDataMaskEvaluators()) && + checkIfWithImpliedGrantsInitialized(getRowFilterEvaluators()); + } + return hasAllEvaluatorsInitialized; + } + + private boolean checkIfWithImpliedGrantsInitialized(List evaluators) { + boolean ret = true; + for (RangerPolicyItemEvaluator evaluator: evaluators) { + if (evaluator.getWithImpliedGrants() == null) { + ret = false; + break; + } else { + for (RangerPolicy.RangerPolicyItemAccess access : evaluator.getWithImpliedGrants().getAccesses()) { + if (access.getIsAllowed()) { + if (CollectionUtils.isEmpty(accessPerms)) { + accessPerms = new HashSet<>(); + } + + accessPerms.add(access.getType()); + } + } + } + } + return ret; + } + } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java index 14b626df62..e0a9aa9873 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyEvaluator.java @@ -22,9 +22,11 @@ import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -32,25 +34,34 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem; 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.RangerPolicy.RangerRowFilterPolicyItem; +import org.apache.ranger.plugin.model.RangerPrincipal; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.PolicyEngine; +import org.apache.ranger.plugin.policyengine.RangerResourceAccessInfo; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyengine.RangerAccessResult; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyengine.RangerPolicyEngine; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineOptions; -import org.apache.ranger.plugin.policyengine.RangerResourceAccessInfo; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvaluator; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs.DataMaskResult; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs.RowFilterResult; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; + import static org.apache.ranger.plugin.policyevaluator.RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW; import static org.apache.ranger.plugin.policyevaluator.RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_ALLOW_EXCEPTIONS; import static org.apache.ranger.plugin.policyevaluator.RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY; import static org.apache.ranger.plugin.policyevaluator.RangerPolicyItemEvaluator.POLICY_ITEM_TYPE_DENY_EXCEPTIONS; -public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator { +public interface RangerPolicyEvaluator { Comparator EVAL_ORDER_COMPARATOR = new RangerPolicyEvaluator.PolicyEvalOrderComparator(); Comparator NAME_COMPARATOR = new RangerPolicyEvaluator.PolicyNameComparator(); @@ -74,12 +85,18 @@ public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator { boolean hasDeny(); + long getPolicyId(); + int getPolicyPriority(); + List getResourceEvaluators(); + boolean isApplicable(Date accessTime); int getEvalOrder(); + int getPolicyConditionsCount(); + int getCustomConditionsCount(); int getValidityScheduleEvaluatorsCount(); @@ -88,22 +105,24 @@ public interface RangerPolicyEvaluator extends RangerPolicyResourceEvaluator { void evaluate(RangerAccessRequest request, RangerAccessResult result); + void getResourceACLs(RangerAccessRequest request, RangerResourceACLs acls, boolean isConditional, Set targetAccessTypes, MatchType matchType, PolicyEngine policyEngine); + boolean isMatch(RangerAccessResource resource, Map evalContext); boolean isCompleteMatch(RangerAccessResource resource, Map evalContext); - boolean isCompleteMatch(Map resources, Map evalContext); - - boolean isAccessAllowed(RangerAccessResource resource, String user, Set userGroups, Set roles, String accessType); - - boolean isAccessAllowed(Map resources, String user, Set userGroups, String accessType); + boolean isCompleteMatch(Map resources, List> additionalResources, Map evalContext); - boolean isAccessAllowed(RangerPolicy policy, String user, Set userGroups, Set roles, String accessType); + boolean isAccessAllowed(Map resources, List> additionalResources, String user, Set userGroups, String accessType); void updateAccessResult(RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType, boolean isAllowed, String reason); void getResourceAccessInfo(RangerAccessRequest request, RangerResourceAccessInfo result); + Set getAllowedAccesses(RangerAccessResource resource, String user, Set userGroups, Set roles, Set accessTypes); + + Set getAllowedAccesses(Map resources, String user, Set userGroups, Set roles, Set accessTypes, Map evalContext); + PolicyACLSummary getPolicyACLSummary(); default boolean hasContextSensitiveSpecification() { @@ -132,9 +151,37 @@ default boolean hasContextSensitiveSpecification() { return false; } - default boolean hasRoles() { + default boolean hasReference(Set users, Set groups, Set roles) { RangerPolicy policy = getPolicy(); + for (RangerPolicyItem policyItem : policy.getPolicyItems()) { + if (hasReference(policyItem, users, groups, roles)) { + return true; + } + } + + for (RangerPolicyItem policyItem : policy.getDenyPolicyItems()) { + if (hasReference(policyItem, users, groups, roles)) { + return true; + } + } + + for (RangerPolicyItem policyItem : policy.getAllowExceptions()) { + if (hasReference(policyItem, users, groups, roles)) { + return true; + } + } + + for (RangerPolicyItem policyItem : policy.getDenyExceptions()) { + if (hasReference(policyItem, users, groups, roles)) { + return true; + } + } + return false; + + } + + default boolean hasRoles(final RangerPolicy policy) { for (RangerPolicyItem policyItem : policy.getPolicyItems()) { if (hasRoles(policyItem)) { return true; @@ -155,12 +202,12 @@ default boolean hasRoles() { return true; } } - for (RangerPolicy.RangerDataMaskPolicyItem policyItem : policy.getDataMaskPolicyItems()) { + for (RangerDataMaskPolicyItem policyItem : policy.getDataMaskPolicyItems()) { if (hasRoles(policyItem)) { return true; } } - for (RangerPolicy.RangerRowFilterPolicyItem policyItem : policy.getRowFilterPolicyItems()) { + for (RangerRowFilterPolicyItem policyItem : policy.getRowFilterPolicyItems()) { if (hasRoles(policyItem)) { return true; } @@ -172,10 +219,28 @@ static boolean hasContextSensitiveSpecification(RangerPolicyItem policyItem) { return CollectionUtils.isNotEmpty(policyItem.getConditions()) || policyItem.getUsers().contains(RangerPolicyEngine.RESOURCE_OWNER); /* || policyItem.getGroups().contains(RangerPolicyEngine.RESOURCE_GROUP_OWNER) */ } + static boolean hasReference(RangerPolicyItem policyItem, Set users, Set groups, Set roles) { + return containsAny(policyItem.getUsers(), users) || + containsAny(policyItem.getGroups(), groups) || + containsAny(policyItem.getRoles(), roles); + } + static boolean hasRoles(RangerPolicyItem policyItem) { return CollectionUtils.isNotEmpty(policyItem.getRoles()); } + static int compareStrings(String str1, String str2) { + if (str1 == null) { + return str2 == null ? 0 : -1; + } else { + return str2 == null ? 1 : str1.compareTo(str2); + } + } + + static boolean containsAny(Collection coll1, Collection coll2) { + return coll1 != null && coll2 != null && CollectionUtils.containsAny(coll1, coll2); + } + class PolicyEvalOrderComparator implements Comparator, Serializable { @Override public int compare(RangerPolicyEvaluator me, RangerPolicyEvaluator other) { @@ -185,7 +250,7 @@ public int compare(RangerPolicyEvaluator me, RangerPolicyEvaluator other) { } private int compareNormal(RangerPolicyEvaluator me, RangerPolicyEvaluator other) { - final int result; + int result; if (me.hasDeny() && !other.hasDeny()) { result = -1; @@ -193,6 +258,10 @@ private int compareNormal(RangerPolicyEvaluator me, RangerPolicyEvaluator other) result = 1; } else { result = Integer.compare(me.getEvalOrder(), other.getEvalOrder()); + + if (result == 0) { + result = compareStrings(me.getPolicy().getName(), other.getPolicy().getName()); + } } return result; @@ -215,7 +284,7 @@ private int compareNormal(RangerPolicyEvaluator me, RangerPolicyEvaluator other) } else if (!me.hasDeny() && other.hasDeny()) { result = 1; } else { - result = me.getPolicy().getName().compareTo(other.getPolicy().getName()); + result = compareStrings(me.getPolicy().getName(), other.getPolicy().getName()); } return result; @@ -223,14 +292,16 @@ private int compareNormal(RangerPolicyEvaluator me, RangerPolicyEvaluator other) } class PolicyACLSummary { - private final Map> usersAccessInfo = new HashMap<>(); + private final Map> usersAccessInfo = new HashMap<>(); private final Map> groupsAccessInfo = new HashMap<>(); private final Map> rolesAccessInfo = new HashMap<>(); + private final List rowFilters = new ArrayList<>(); + private final List dataMasks = new ArrayList<>(); private enum AccessorType { USER, GROUP, ROLE } public static class AccessResult { - private int result; + private int result; private final boolean hasSeenDeny; public AccessResult(int result) { @@ -238,8 +309,8 @@ public AccessResult(int result) { } public AccessResult(int result, boolean hasSeenDeny) { + this.result = result; this.hasSeenDeny = hasSeenDeny; - setResult(result); } public int getResult() { @@ -253,6 +324,7 @@ public void setResult(int result) { public boolean getHasSeenDeny() { return hasSeenDeny; } + @Override public String toString() { if (result == RangerPolicyEvaluator.ACCESS_ALLOWED) @@ -280,9 +352,13 @@ public Map> getRolesAccessInfo() { return rolesAccessInfo; } - void processPolicyItem(RangerPolicyItem policyItem, int policyItemType, boolean isConditional) { + public List getRowFilters() { return rowFilters; } + + public List getDataMasks() { return dataMasks; } + + void processPolicyItem(RangerPolicyItem policyItem, int policyItemType, boolean isConditional, Map> impliedAccessGrants) { final Integer result; - final boolean hasContextSensitiveSpecification = RangerPolicyEvaluator.hasContextSensitiveSpecification(policyItem); + final boolean hasContextSensitiveSpecification = CollectionUtils.isNotEmpty(policyItem.getConditions()); switch (policyItemType) { case POLICY_ITEM_TYPE_ALLOW: @@ -307,15 +383,43 @@ void processPolicyItem(RangerPolicyItem policyItem, int policyItemType, boolean } if (result != null) { - final List accesses; + List accesses = new ArrayList<>(); + accesses.addAll(policyItem.getAccesses()); if (policyItem.getDelegateAdmin()) { - accesses = new ArrayList<>(); - accesses.add(new RangerPolicyItemAccess(RangerPolicyEngine.ADMIN_ACCESS, policyItem.getDelegateAdmin())); - accesses.addAll(policyItem.getAccesses()); - } else { - accesses = policyItem.getAccesses(); + } + + if(impliedAccessGrants != null && !impliedAccessGrants.isEmpty()) { + if (CollectionUtils.isNotEmpty(policyItem.getAccesses())) { + + // Only one round of 'expansion' is done; multi-level impliedGrants (like shown below) are not handled for now + // multi-level impliedGrants: given admin=>write; write=>read: must imply admin=>read,write + for (Map.Entry> e : impliedAccessGrants.entrySet()) { + String implyingAccessType = e.getKey(); + Collection impliedGrants = e.getValue(); + + RangerPolicyItemAccess access = RangerDefaultPolicyEvaluator.getAccess(policyItem, implyingAccessType); + + if (access == null) { + continue; + } + + for (String impliedGrant : impliedGrants) { + RangerPolicyItemAccess impliedAccess = RangerDefaultPolicyEvaluator.getAccess(policyItem, impliedGrant); + + if (impliedAccess == null) { + impliedAccess = new RangerPolicyItemAccess(impliedGrant, access.getIsAllowed()); + + accesses.add(impliedAccess); + } else { + if (!impliedAccess.getIsAllowed()) { + impliedAccess.setIsAllowed(access.getIsAllowed()); + } + } + } + } + } } final List groups = policyItem.getGroups(); @@ -355,6 +459,52 @@ void processPolicyItem(RangerPolicyItem policyItem, int policyItemType, boolean } } + void processRowFilterPolicyItem(RangerRowFilterPolicyItem policyItem) { + Set users = new HashSet<>(policyItem.getUsers()); + Set groups = new HashSet<>(policyItem.getGroups()); + Set roles = new HashSet<>(policyItem.getRoles()); + Set accessTypes = new HashSet<>(); + + policyItem.getAccesses().forEach(accessType -> accessTypes.add(accessType.getType())); + + if (users.contains(RangerPolicyEngine.USER_CURRENT)) { // replace with public group + users.remove(RangerPolicyEngine.USER_CURRENT); + + groups.add(RangerPolicyEngine.GROUP_PUBLIC); + } + + RowFilterResult filterResult = new RowFilterResult(users, groups, roles, accessTypes, policyItem.getRowFilterInfo()); + + if (RangerPolicyEvaluator.hasContextSensitiveSpecification(policyItem)) { + filterResult.setIsConditional(true); + } + + rowFilters.add(filterResult); + } + + void processDataMaskPolicyItem(RangerDataMaskPolicyItem policyItem) { + Set users = new HashSet<>(policyItem.getUsers()); + Set groups = new HashSet<>(policyItem.getGroups()); + Set roles = new HashSet<>(policyItem.getRoles()); + Set accessTypes = new HashSet<>(); + + policyItem.getAccesses().forEach(accessType -> accessTypes.add(accessType.getType())); + + if (users.contains(RangerPolicyEngine.USER_CURRENT)) { // replace with public group + users.remove(RangerPolicyEngine.USER_CURRENT); + + groups.add(RangerPolicyEngine.GROUP_PUBLIC); + } + + DataMaskResult dataMaskResult = new DataMaskResult(users, groups, roles, accessTypes, policyItem.getDataMaskInfo()); + + if (RangerPolicyEvaluator.hasContextSensitiveSpecification(policyItem)) { + dataMaskResult.setIsConditional(true); + } + + dataMasks.add(dataMaskResult); + } + void finalizeAcls(final boolean isDenyAllElse, final Set allAccessTypeNames) { Map publicGroupAccessInfo = groupsAccessInfo.get(RangerPolicyEngine.GROUP_PUBLIC); @@ -559,4 +709,16 @@ private void addAccess(String accessorName, AccessorType accessorType, String ac } } } + + interface RangerPolicyResourceEvaluator extends RangerResourceEvaluator { + default long getPolicyId() { + RangerPolicyEvaluator evaluator = getPolicyEvaluator(); + + return evaluator != null ? evaluator.getPolicyId() : -1; + } + + RangerPolicyEvaluator getPolicyEvaluator(); + + RangerPolicyResourceMatcher getMacrosReplaceWithWildcardMatcher(PolicyEngine policyEngine); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java index 1a2ea4c9ec..38d2129dfa 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerPolicyItemEvaluator.java @@ -66,4 +66,5 @@ public int compare(RangerPolicyItemEvaluator me, RangerPolicyItemEvaluator other } } void updateAccessResult(RangerPolicyEvaluator policyEvaluator, RangerAccessResult result, RangerPolicyResourceMatcher.MatchType matchType); + RangerPolicyItem getWithImpliedGrants(); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java index 6715e2b18e..31501c709b 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyevaluator/RangerValidityScheduleEvaluator.java @@ -21,8 +21,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.model.RangerValidityRecurrence; import org.apache.ranger.plugin.model.RangerValiditySchedule; import org.apache.ranger.plugin.resourcematcher.ScheduledTimeAlwaysMatcher; @@ -30,6 +28,8 @@ import org.apache.ranger.plugin.resourcematcher.ScheduledTimeMatcher; import org.apache.ranger.plugin.resourcematcher.ScheduledTimeRangeMatcher; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import java.text.DateFormat; @@ -44,8 +44,8 @@ import java.util.TimeZone; public class RangerValidityScheduleEvaluator { - private static final Log LOG = LogFactory.getLog(RangerValidityScheduleEvaluator.class); - private static final Log PERF_LOG = LogFactory.getLog("test.perf.RangerValidityScheduleEvaluator"); + private static final Logger LOG = LoggerFactory.getLogger(RangerValidityScheduleEvaluator.class); + private static final Logger PERF_LOG = LoggerFactory.getLogger("test.perf.RangerValidityScheduleEvaluator"); private final static TimeZone defaultTZ = TimeZone.getDefault(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java index 9794881818..0c377b3578 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerDefaultPolicyResourceMatcher.java @@ -20,6 +20,7 @@ package org.apache.ranger.plugin.policyresourcematcher; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; @@ -28,24 +29,28 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher; import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher.OPTION_WILD_CARD; public class RangerDefaultPolicyResourceMatcher implements RangerPolicyResourceMatcher { - private static final Log LOG = LogFactory.getLog(RangerDefaultPolicyResourceMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultPolicyResourceMatcher.class); - private static final Log PERF_POLICY_RESOURCE_MATCHER_INIT_LOG = RangerPerfTracer.getPerfLogger("policyresourcematcher.init"); - private static final Log PERF_POLICY_RESOURCE_MATCHER_MATCH_LOG = RangerPerfTracer.getPerfLogger("policyresourcematcher.match"); + private static final Logger PERF_POLICY_RESOURCE_MATCHER_INIT_LOG = RangerPerfTracer.getPerfLogger("policyresourcematcher.init"); + private static final Logger PERF_POLICY_RESOURCE_MATCHER_MATCH_LOG = RangerPerfTracer.getPerfLogger("policyresourcematcher.match"); protected RangerServiceDef serviceDef; protected int policyType; @@ -56,6 +61,17 @@ public class RangerDefaultPolicyResourceMatcher implements RangerPolicyResourceM private List validResourceHierarchy; private boolean isInitialized = false; private RangerServiceDefHelper serviceDefHelper; + private RangerPluginContext pluginContext = null; + + private final boolean forceEnableWildcardMatch; + + public RangerDefaultPolicyResourceMatcher() { + this.forceEnableWildcardMatch = false; + } + + public RangerDefaultPolicyResourceMatcher(boolean forceEnableWildcardMatch) { + this.forceEnableWildcardMatch = forceEnableWildcardMatch; + } @Override public void setServiceDef(RangerServiceDef serviceDef) { @@ -99,6 +115,15 @@ public void setServiceDefHelper(RangerServiceDefHelper serviceDefHelper) { this.serviceDefHelper = serviceDefHelper; } + @Override + public void setPluginContext(RangerPluginContext pluginContext) { this.pluginContext = pluginContext; } + + public int getPolicyType() { return policyType; } + + public RangerServiceDefHelper getServiceDefHelper() { + return serviceDefHelper; + } + @Override public RangerServiceDef getServiceDef() { return serviceDef; @@ -202,7 +227,7 @@ public void init() { errorText = "policyResources is null or empty, or serviceDef is null."; } - if (allMatchers == null) { + if (allMatchers == null && policyType != RangerPolicy.POLICY_TYPE_AUDIT) { serviceDefHelper = null; validResourceHierarchy = null; @@ -360,6 +385,11 @@ public boolean isCompleteMatch(Map resources, Map< @Override public boolean isMatch(RangerPolicy policy, MatchScope scope, Map evalContext) { + return isMatch(policy, Collections.emptyMap(), scope, evalContext); + } + + @Override + public boolean isMatch(RangerPolicy policy, Map scopes, MatchScope scope, Map evalContext) { boolean ret = false; RangerPerfTracer perf = null; @@ -410,7 +440,7 @@ public boolean isMatch(RangerPolicy policy, MatchScope scope, Map evalContext) { + return isMatch(resource, Collections.emptyMap(), evalContext); + } + + @Override + public boolean isMatch(RangerAccessResource resource, Map scopes, Map evalContext) { RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_RESOURCE_MATCHER_MATCH_LOG)) { @@ -469,7 +504,7 @@ public boolean isMatch(RangerAccessResource resource, Map evalCo break; } } - final boolean ret = MapUtils.isNotEmpty(policyResources) && isMatch(policyResources, evalContext); + final boolean ret = MapUtils.isNotEmpty(policyResources) && isMatch(policyResources, scopes, evalContext); RangerPerfTracer.log(perf); @@ -478,6 +513,11 @@ public boolean isMatch(RangerAccessResource resource, Map evalCo @Override public boolean isMatch(Map resources, Map evalContext) { + return isMatch(resources, Collections.emptyMap(), evalContext); + } + + @Override + public boolean isMatch(Map resources, Map scopes, Map evalContext) { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.isMatch(" + resources + ", " + evalContext + ")"); } @@ -506,7 +546,8 @@ public boolean isMatch(Map resources, Map resources, Map evalContext) { - MatchType matchType = getMatchType(resource, evalContext); + return isMatch(resource, Collections.emptyMap(), scope, evalContext); + } + + @Override + public boolean isMatch(RangerAccessResource resource, Map scopes, MatchScope scope, Map evalContext) { + MatchType matchType = getMatchType(resource, scopes, evalContext); return isMatch(scope, matchType); } @Override public MatchType getMatchType(RangerAccessResource resource, Map evalContext) { + return getMatchType(resource, Collections.emptyMap(), evalContext); + } + + @Override + public MatchType getMatchType(RangerAccessResource resource, Map scopes, Map evalContext) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.getMatchType(" + resource + evalContext + ")"); } @@ -564,6 +615,7 @@ public MatchType getMatchType(RangerAccessResource resource, Map ret = MatchType.SELF; } else { List hierarchy = getMatchingHierarchy(resource); + if (CollectionUtils.isNotEmpty(hierarchy)) { int lastNonAnyMatcherIndex = -1; @@ -592,7 +644,7 @@ public MatchType getMatchType(RangerAccessResource resource, Map if (matcher != null) { if (resourceValue != null || matcher.isMatchAny()) { - if (matcher.isMatch(resourceValue, evalContext)) { + if (matcher.isMatch(resourceValue, scopes.get(resourceDef.getName()), evalContext)) { ret = MatchType.SELF; } else { ret = MatchType.NONE; @@ -754,7 +806,7 @@ private boolean isMatch(final MatchScope scope, final MatchType matchType) { return ret; } - private static RangerResourceMatcher createResourceMatcher(RangerResourceDef resourceDef, RangerPolicyResource resource) { + private RangerResourceMatcher createResourceMatcher(RangerResourceDef resourceDef, RangerPolicyResource resource) { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultPolicyResourceMatcher.createResourceMatcher(" + resourceDef + ", " + resource + ")"); } @@ -765,25 +817,41 @@ private static RangerResourceMatcher createResourceMatcher(RangerResourceDef res String resName = resourceDef.getName(); String clsName = resourceDef.getMatcher(); - if (!StringUtils.isEmpty(clsName)) { - try { - @SuppressWarnings("unchecked") - Class matcherClass = (Class) Class.forName(clsName); + if (pluginContext != null) { + ret = pluginContext.getResourceMatcher(resName, resource); + } + + if (ret == null) { + if (!StringUtils.isEmpty(clsName)) { + try { + @SuppressWarnings("unchecked") Class matcherClass = (Class) Class.forName(clsName); - ret = matcherClass.newInstance(); - } catch (Exception excp) { - LOG.error("failed to instantiate resource matcher '" + clsName + "' for '" + resName + "'. Default resource matcher will be used", excp); + ret = matcherClass.newInstance(); + } catch (Exception excp) { + LOG.error("failed to instantiate resource matcher '" + clsName + "' for '" + resName + "'. Default resource matcher will be used", excp); + } } - } - if (ret == null) { - ret = new RangerDefaultResourceMatcher(); - } + if (ret == null) { + ret = new RangerDefaultResourceMatcher(); + } + + if (forceEnableWildcardMatch && !Boolean.parseBoolean(resourceDef.getMatcherOptions().get(OPTION_WILD_CARD))) { + resourceDef = serviceDefHelper.getWildcardEnabledResourceDef(resourceDef.getName(), policyType); + } - ret.setResourceDef(resourceDef); - ret.setPolicyResource(resource); - ret.init(); + ret.setResourceDef(resourceDef); + ret.setPolicyResource(resource); + ret.init(); + if (pluginContext != null) { + pluginContext.setResourceMatcher(resName, resource, ret); + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Did not create a fresh matcher - used matcher from pluginContext"); + } + } } else { LOG.error("RangerDefaultPolicyResourceMatcher: RangerResourceDef is null"); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceMatcher.java index 7978e7fdf7..ad6869ad0f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceMatcher.java @@ -19,19 +19,26 @@ package org.apache.ranger.plugin.policyresourcematcher; +import java.util.Comparator; import java.util.Map; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; public interface RangerPolicyResourceMatcher { enum MatchScope { SELF, SELF_OR_DESCENDANT, SELF_OR_ANCESTOR, DESCENDANT, ANCESTOR, ANY, SELF_AND_ALL_DESCENDANTS} enum MatchType { NONE, SELF, DESCENDANT, ANCESTOR, SELF_AND_ALL_DESCENDANTS} + Comparator MATCH_TYPE_COMPARATOR = new MatchTypeComparator(); + + void init(); + void setServiceDef(RangerServiceDef serviceDef); void setPolicy(RangerPolicy policy); @@ -42,7 +49,7 @@ enum MatchType { NONE, SELF, DESCENDANT, ANCESTOR, SELF_AND_ALL_DESCENDANTS} void setServiceDefHelper(RangerServiceDefHelper serviceDefHelper); - void init(); + void setPluginContext(RangerPluginContext pluginContext); RangerServiceDef getServiceDef(); @@ -50,14 +57,24 @@ enum MatchType { NONE, SELF, DESCENDANT, ANCESTOR, SELF_AND_ALL_DESCENDANTS} boolean isMatch(RangerAccessResource resource, Map evalContext); + boolean isMatch(RangerAccessResource resource, Map scopes, Map evalContext); + boolean isMatch(Map resources, Map evalContext); + boolean isMatch(Map resources, Map scopes, Map evalContext); + boolean isMatch(RangerAccessResource resource, MatchScope scope, Map evalContext); + boolean isMatch(RangerAccessResource resource, Map scopes, MatchScope scope, Map evalContext); + boolean isMatch(RangerPolicy policy, MatchScope scope, Map evalContext); + boolean isMatch(RangerPolicy policy, Map scopes, MatchScope scope, Map evalContext); + MatchType getMatchType(RangerAccessResource resource, Map evalContext); + MatchType getMatchType(RangerAccessResource resource, Map scopes, Map evalContext); + boolean isCompleteMatch(RangerAccessResource resource, Map evalContext); boolean isCompleteMatch(Map resources, Map evalContext); @@ -65,4 +82,45 @@ enum MatchType { NONE, SELF, DESCENDANT, ANCESTOR, SELF_AND_ALL_DESCENDANTS} boolean getNeedsDynamicEval(); StringBuilder toString(StringBuilder sb); + + // order: SELF, SELF_AND_ALL_DESCENDANTS, ANCESTOR, DESCENDANT, NONE + class MatchTypeComparator implements Comparator { + @Override + public int compare(MatchType o1, MatchType o2) { + final int ret; + + if (o1 == o2) { + ret = 0; + } else if (o1 == null) { + return 1; + } else if (o2 == null) { + return -1; + } else { + switch (o1) { + case SELF: + ret = -1; + break; + + case SELF_AND_ALL_DESCENDANTS: + ret = o2 == MatchType.SELF ? 1 : -1; + break; + + case ANCESTOR: + ret = (o2 == MatchType.SELF || o2 == MatchType.SELF_AND_ALL_DESCENDANTS) ? 1 : -1; + break; + + case DESCENDANT: + ret = o2 == MatchType.NONE ? -1 : 1; + break; + + case NONE: + default: + ret = 1; + break; + } + } + + return ret; + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceEvaluator.java b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerResourceEvaluator.java similarity index 94% rename from agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceEvaluator.java rename to agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerResourceEvaluator.java index 9da9fac661..7544f1bdd8 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerPolicyResourceEvaluator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/policyresourcematcher/RangerResourceEvaluator.java @@ -26,7 +26,7 @@ import java.util.Map; -public interface RangerPolicyResourceEvaluator { +public interface RangerResourceEvaluator { long getId(); RangerPolicyResourceMatcher getPolicyResourceMatcher(); @@ -36,4 +36,6 @@ public interface RangerPolicyResourceEvaluator { RangerResourceMatcher getResourceMatcher(String resourceName); boolean isAncestorOf(RangerServiceDef.RangerResourceDef resourceDef); + + boolean isLeaf(String resourceName); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java index 8f6facda5b..5eee8d11ad 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcher.java @@ -28,30 +28,38 @@ import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOCase; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType; import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import static org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType.*; public abstract class RangerAbstractResourceMatcher implements RangerResourceMatcher { - private static final Log LOG = LogFactory.getLog(RangerAbstractResourceMatcher.class); - - public final static String WILDCARD_ASTERISK = "*"; - - public final static String OPTION_IGNORE_CASE = "ignoreCase"; - public final static String OPTION_WILD_CARD = "wildCard"; - public final static String OPTION_REPLACE_TOKENS = "replaceTokens"; - public final static String OPTION_TOKEN_DELIMITER_START = "tokenDelimiterStart"; - public final static String OPTION_TOKEN_DELIMITER_END = "tokenDelimiterEnd"; - public final static String OPTION_TOKEN_DELIMITER_ESCAPE = "tokenDelimiterEscape"; - public final static String OPTION_TOKEN_DELIMITER_PREFIX = "tokenDelimiterPrefix"; + private static final Logger LOG = LoggerFactory.getLogger(RangerAbstractResourceMatcher.class); + + public final static String WILDCARD_ASTERISK = "*"; + public final static String WILDCARD_QUESTION_MARK = "?"; + + public final static String OPTION_IGNORE_CASE = "ignoreCase"; + public final static String OPTION_QUOTED_CASE_SENSITIVE = "quotedCaseSensitive"; + public final static String OPTION_QUOTE_CHARS = "quoteChars"; + public final static String OPTION_WILD_CARD = "wildCard"; + public final static String OPTION_REPLACE_TOKENS = "replaceTokens"; + public final static String OPTION_TOKEN_DELIMITER_START = "tokenDelimiterStart"; + public final static String OPTION_TOKEN_DELIMITER_END = "tokenDelimiterEnd"; + public final static String OPTION_TOKEN_DELIMITER_ESCAPE = "tokenDelimiterEscape"; + public final static String OPTION_TOKEN_DELIMITER_PREFIX = "tokenDelimiterPrefix"; + public final static String OPTION_REPLACE_REQ_EXPRESSIONS = "replaceReqExpressions"; protected RangerResourceDef resourceDef; protected RangerPolicyResource policyResource; protected boolean optIgnoreCase; + protected boolean optQuotedCaseSensitive; + protected String optQuoteChars = "\""; protected boolean optWildCard; protected List policyValues; @@ -83,8 +91,10 @@ public void init() { Map options = resourceDef != null ? resourceDef.getMatcherOptions() : null; - optIgnoreCase = getOptionIgnoreCase(options); - optWildCard = getOptionWildCard(options); + optIgnoreCase = getOptionIgnoreCase(options); + optQuotedCaseSensitive = getOptionQuotedCaseSensitive(options); + optQuoteChars = getOptionQuoteChars(options); + optWildCard = getOptionWildCard(options); policyValues = new ArrayList<>(); policyIsExcludes = policyResource != null && policyResource.getIsExcludes(); @@ -143,6 +153,14 @@ public static boolean getOptionIgnoreCase(Map options) { return ServiceDefUtil.getBooleanOption(options, OPTION_IGNORE_CASE, true); } + public static boolean getOptionQuotedCaseSensitive(Map options) { + return ServiceDefUtil.getBooleanOption(options, OPTION_QUOTED_CASE_SENSITIVE, false); + } + + public static String getOptionQuoteChars(Map options) { + return ServiceDefUtil.getOption(options, OPTION_QUOTE_CHARS, "\""); + } + public static boolean getOptionWildCard(Map options) { return ServiceDefUtil.getBooleanOption(options, OPTION_WILD_CARD, true); } @@ -166,6 +184,13 @@ public static char getOptionDelimiterEscape(Map options) { public static String getOptionDelimiterPrefix(Map options) { return ServiceDefUtil.getOption(options, OPTION_TOKEN_DELIMITER_PREFIX, ""); } + + public static boolean getOptionReplaceReqExpressions(Map options) { + return ServiceDefUtil.getBooleanOption(options, OPTION_REPLACE_REQ_EXPRESSIONS, true); + } + + protected Map getOptions() { return resourceDef != null ? resourceDef.getMatcherOptions() : null; } + protected ResourceMatcherWrapper buildResourceMatchers() { List resourceMatchers = new ArrayList<>(); boolean needsDynamicEval = false; @@ -207,7 +232,7 @@ public boolean isCompleteMatch(String resource, Map evalContext) if(isMatchAny) { ret = StringUtils.isEmpty(resource) || StringUtils.containsOnly(resource, WILDCARD_ASTERISK); } else { - ret = optIgnoreCase ? StringUtils.equalsIgnoreCase(resource, policyValue) : StringUtils.equals(resource, policyValue); + ret = optIgnoreCase && !(optQuotedCaseSensitive && ResourceMatcher.startsWithAnyChar(resource, optQuoteChars)) ? StringUtils.equalsIgnoreCase(resource, policyValue) : StringUtils.equals(resource, policyValue); } if(policyIsExcludes) { @@ -245,6 +270,8 @@ public StringBuilder toString(StringBuilder sb) { } sb.append("} "); sb.append("optIgnoreCase={").append(optIgnoreCase).append("} "); + sb.append("optQuotedCaseSensitive={").append(optQuotedCaseSensitive).append("} "); + sb.append("optQuoteChars={").append(optQuoteChars).append("} "); sb.append("optWildCard={").append(optWildCard).append("} "); sb.append("policyValues={"); @@ -300,6 +327,12 @@ public boolean applyExcludes(boolean allValuesRequested, boolean resultWithoutEx return !resultWithoutExcludes; // all other cases flip it } + public ResourceElementMatchType applyExcludes(boolean allValuesRequested, ResourceElementMatchType resultWithoutExcludes) { + if (!policyIsExcludes) return resultWithoutExcludes; // not an excludes policy! + if (allValuesRequested && !isMatchAny) return resultWithoutExcludes; // one case where excludes has no effect + return resultWithoutExcludes == NONE ? SELF : NONE; // all other cases flip it + } + ResourceMatcher getMatcher(String policyValue) { final int len = policyValue != null ? policyValue.length() : 0; @@ -345,17 +378,17 @@ ResourceMatcher getMatcher(String policyValue) { } if (needWildcardMatch) { // test?, test*a*, test*a*b, *test*a - ret = optIgnoreCase ? new CaseInsensitiveWildcardMatcher(policyValue) : new CaseSensitiveWildcardMatcher(policyValue); + ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, getOptions(), optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue, getOptions())) : new CaseSensitiveWildcardMatcher(policyValue, getOptions()); } else if (wildcardStartIdx == -1) { // test, testa, testab - ret = optIgnoreCase ? new CaseInsensitiveStringMatcher(policyValue) : new CaseSensitiveStringMatcher(policyValue); + ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStringMatcher(policyValue, getOptions(), optQuoteChars) : new CaseInsensitiveStringMatcher(policyValue, getOptions())) : new CaseSensitiveStringMatcher(policyValue, getOptions()); } else if (wildcardStartIdx == 0) { // *test, **test, *testa, *testab String matchStr = policyValue.substring(wildcardEndIdx + 1); - ret = optIgnoreCase ? new CaseInsensitiveEndsWithMatcher(matchStr) : new CaseSensitiveEndsWithMatcher(matchStr); + ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveEndsWithMatcher(matchStr, getOptions(), optQuoteChars) : new CaseInsensitiveEndsWithMatcher(matchStr, getOptions())) : new CaseSensitiveEndsWithMatcher(matchStr, getOptions()); } else if (wildcardEndIdx != (len - 1)) { // test*a, test*ab - ret = optIgnoreCase ? new CaseInsensitiveWildcardMatcher(policyValue) : new CaseSensitiveWildcardMatcher(policyValue); + ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveWildcardMatcher(policyValue, getOptions(), optQuoteChars) : new CaseInsensitiveWildcardMatcher(policyValue, getOptions())) : new CaseSensitiveWildcardMatcher(policyValue, getOptions()); } else { // test*, test**, testa*, testab* String matchStr = policyValue.substring(0, wildcardStartIdx); - ret = optIgnoreCase ? new CaseInsensitiveStartsWithMatcher(matchStr) : new CaseSensitiveStartsWithMatcher(matchStr); + ret = optIgnoreCase ? (optQuotedCaseSensitive ? new QuotedCaseSensitiveStartsWithMatcher(matchStr, getOptions(), optQuoteChars) : new CaseInsensitiveStartsWithMatcher(matchStr, getOptions())) : new CaseSensitiveStartsWithMatcher(matchStr, getOptions()); } if(optReplaceTokens) { @@ -366,96 +399,267 @@ ResourceMatcher getMatcher(String policyValue) { } } -final class CaseSensitiveStringMatcher extends ResourceMatcher { - CaseSensitiveStringMatcher(String value) { - super(value); +abstract class AbstractStringResourceMatcher extends ResourceMatcher { + protected AbstractStringResourceMatcher(String value, Map options) { + super(value, options); + } + + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + return false; // child-match is applicable only for path resource + } +} + +final class CaseSensitiveStringMatcher extends AbstractStringResourceMatcher { + CaseSensitiveStringMatcher(String value, Map options) { + super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return StringUtils.equals(resourceValue, getExpandedValue(evalContext)); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue); + } + int getPriority() { return 1 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} } -final class CaseInsensitiveStringMatcher extends ResourceMatcher { - CaseInsensitiveStringMatcher(String value) { super(value); } +final class CaseInsensitiveStringMatcher extends AbstractStringResourceMatcher { + CaseInsensitiveStringMatcher(String value, Map options) { super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return StringUtils.equalsIgnoreCase(resourceValue, getExpandedValue(evalContext)); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + } + + int getPriority() {return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } +} + +final class QuotedCaseSensitiveStringMatcher extends AbstractStringResourceMatcher { + private final String quoteChars; + + QuotedCaseSensitiveStringMatcher(String value, Map options, String quoteChars) { + super(value, options); + + this.quoteChars = quoteChars; + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (startsWithAnyChar(resourceValue, quoteChars)) { + return StringUtils.equals(resourceValue, getExpandedValue(evalContext)); + } else { + return StringUtils.equalsIgnoreCase(resourceValue, getExpandedValue(evalContext)); + } + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (startsWithAnyChar(resourceValue, quoteChars)) { + return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue); + } else { + return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + } + } + int getPriority() {return 2 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } } -final class CaseSensitiveStartsWithMatcher extends ResourceMatcher { - CaseSensitiveStartsWithMatcher(String value) { - super(value); +final class CaseSensitiveStartsWithMatcher extends AbstractStringResourceMatcher { + CaseSensitiveStartsWithMatcher(String value, Map options) { + super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return StringUtils.startsWith(resourceValue, getExpandedValue(evalContext)); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue); + } + int getPriority() { return 3 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} } -final class CaseInsensitiveStartsWithMatcher extends ResourceMatcher { - CaseInsensitiveStartsWithMatcher(String value) { super(value); } +final class CaseInsensitiveStartsWithMatcher extends AbstractStringResourceMatcher { + CaseInsensitiveStartsWithMatcher(String value, Map options) { super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext)); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + } + int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } } -final class CaseSensitiveEndsWithMatcher extends ResourceMatcher { - CaseSensitiveEndsWithMatcher(String value) { - super(value); +final class QuotedCaseSensitiveStartsWithMatcher extends AbstractStringResourceMatcher { + private final String quoteChars; + + QuotedCaseSensitiveStartsWithMatcher(String value, Map options, String quoteChars) { + super(value, options); + + this.quoteChars = quoteChars; + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (startsWithAnyChar(resourceValue, quoteChars)) { + return StringUtils.startsWith(resourceValue, getExpandedValue(evalContext)); + } else { + return StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext)); + } + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (startsWithAnyChar(resourceValue, quoteChars)) { + return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue); + } else { + return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + } + } + + int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } +} + +final class CaseSensitiveEndsWithMatcher extends AbstractStringResourceMatcher { + CaseSensitiveEndsWithMatcher(String value, Map options) { + super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return StringUtils.endsWith(resourceValue, getExpandedValue(evalContext)); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return true; // isPrefixMatch() is always true for endsWith + } + int getPriority() { return 3 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } } -final class CaseInsensitiveEndsWithMatcher extends ResourceMatcher { - CaseInsensitiveEndsWithMatcher(String value) { - super(value); +final class CaseInsensitiveEndsWithMatcher extends AbstractStringResourceMatcher { + CaseInsensitiveEndsWithMatcher(String value, Map options) { + super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext)); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return true; // isPrefixMatch() is always true for endsWith + } + int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } } -final class CaseSensitiveWildcardMatcher extends ResourceMatcher { - CaseSensitiveWildcardMatcher(String value) { - super(value); +final class QuotedCaseSensitiveEndsWithMatcher extends AbstractStringResourceMatcher { + private final String quoteChars; + + QuotedCaseSensitiveEndsWithMatcher(String value, Map options, String quoteChars) { + super(value, options); + + this.quoteChars = quoteChars; + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (startsWithAnyChar(resourceValue, quoteChars)) { + return StringUtils.endsWith(resourceValue, getExpandedValue(evalContext)); + } else { + return StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext)); + } + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return true; // isPrefixMatch() is always true for endsWith + } + + int getPriority() { return 4 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } +} + +final class CaseSensitiveWildcardMatcher extends AbstractStringResourceMatcher { + CaseSensitiveWildcardMatcher(String value, Map options) { + super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE); + } + int getPriority() { return 5 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } } -final class CaseInsensitiveWildcardMatcher extends ResourceMatcher { - CaseInsensitiveWildcardMatcher(String value) { - super(value); +final class CaseInsensitiveWildcardMatcher extends AbstractStringResourceMatcher { + CaseInsensitiveWildcardMatcher(String value, Map options) { + super(value, options); } @Override boolean isMatch(String resourceValue, Map evalContext) { return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE); + } + + int getPriority() {return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } +} + +final class QuotedCaseSensitiveWildcardMatcher extends AbstractStringResourceMatcher { + private final String quoteChars; + + QuotedCaseSensitiveWildcardMatcher(String value, Map options, String quoteChars) { + super(value, options); + + this.quoteChars = quoteChars; + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + IOCase caseSensitivity = startsWithAnyChar(resourceValue, quoteChars) ? IOCase.SENSITIVE : IOCase.INSENSITIVE; + + return FilenameUtils.wildcardMatch(resourceValue, getExpandedValue(evalContext), caseSensitivity); + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + IOCase caseSensitivity = startsWithAnyChar(resourceValue, quoteChars) ? IOCase.SENSITIVE : IOCase.INSENSITIVE; + + return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), caseSensitivity); + } + int getPriority() {return 6 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java index 8a444715c5..977fd49414 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcher.java @@ -20,69 +20,105 @@ package org.apache.ranger.plugin.resourcematcher; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Map; +import static org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType.NONE; + public class RangerDefaultResourceMatcher extends RangerAbstractResourceMatcher { - private static final Log LOG = LogFactory.getLog(RangerDefaultResourceMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultResourceMatcher.class); @Override - public boolean isMatch(Object resource, Map evalContext) { + public boolean isMatch(Object resource, ResourceElementMatchingScope matchingScope, Map evalContext) { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerDefaultResourceMatcher.isMatch(" + resource + ", " + evalContext + ")"); } - boolean ret = false; - boolean allValuesRequested = isAllValuesRequested(resource); + ResourceElementMatchType matchType = getMatchType(resource, matchingScope, evalContext); + boolean ret = ResourceMatcher.isMatch(matchType, matchingScope); + + if (ret == false) { + if(LOG.isDebugEnabled()) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (String policyValue: policyValues) { + sb.append(policyValue); + sb.append(" "); + } + sb.append("]"); + + LOG.debug("RangerDefaultResourceMatcher.isMatch returns FALSE, (resource=" + resource + ", policyValues=" + sb.toString() + ")"); + } + } + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerDefaultResourceMatcher.isMatch(" + resource + ", " + evalContext + "): " + ret); + } + + return ret; + } + + @Override + public ResourceElementMatchType getMatchType(Object resource, ResourceElementMatchingScope matchingScope, Map evalContext) { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerDefaultResourceMatcher.getMatchType(" + resource + ", " + evalContext + ")"); + } + + ResourceElementMatchType ret = NONE; + boolean allValuesRequested = isAllValuesRequested(resource); + boolean isPrefixMatch = matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX; - if(allValuesRequested || isMatchAny) { - ret = isMatchAny; + if (isMatchAny || (allValuesRequested && !isPrefixMatch)) { + ret = isMatchAny ? ResourceElementMatchType.SELF : NONE; } else { if (resource instanceof String) { String strValue = (String) resource; for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) { - ret = resourceMatcher.isMatch(strValue, evalContext); - if (ret) { + ResourceElementMatchType matchType = resourceMatcher.getMatchType(strValue, matchingScope, evalContext); + + if (matchType != NONE) { + ret = matchType; + } + + if (ret == ResourceElementMatchType.SELF) { break; } } } else if (resource instanceof Collection) { @SuppressWarnings("unchecked") - Collection collValue = (Collection) resource; + Collection resourceValues = (Collection) resource; for (ResourceMatcher resourceMatcher : resourceMatchers.getResourceMatchers()) { - ret = resourceMatcher.isMatchAny(collValue, evalContext); - if (ret) { + for (String resourceValue : resourceValues) { + ResourceElementMatchType matchType = resourceMatcher.getMatchType(resourceValue, matchingScope, evalContext); + + if (matchType != NONE) { + ret = matchType; + } + + if (ret == ResourceElementMatchType.SELF) { + break; + } + } + + if (ret == ResourceElementMatchType.SELF) { break; } } } - } ret = applyExcludes(allValuesRequested, ret); - if (ret == false) { - if(LOG.isDebugEnabled()) { - StringBuilder sb = new StringBuilder(); - sb.append("["); - for (String policyValue: policyValues) { - sb.append(policyValue); - sb.append(" "); - } - sb.append("]"); - - LOG.debug("RangerDefaultResourceMatcher.isMatch returns FALSE, (resource=" + resource + ", policyValues=" + sb.toString() + ")"); - } - } - if(LOG.isDebugEnabled()) { - LOG.debug("<== RangerDefaultResourceMatcher.isMatch(" + resource + ", " + evalContext + "): " + ret); + LOG.debug("<== RangerDefaultResourceMatcher.getMatchType(" + resource + ", " + evalContext + "): " + ret); } return ret; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java index 9cf31a2cba..d376e90250 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcher.java @@ -24,24 +24,25 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.BiFunction; public class RangerPathResourceMatcher extends RangerDefaultResourceMatcher { - private static final Log LOG = LogFactory.getLog(RangerPathResourceMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPathResourceMatcher.class); public static final String OPTION_PATH_SEPARATOR = "pathSeparatorChar"; public static final char DEFAULT_PATH_SEPARATOR_CHAR = org.apache.hadoop.fs.Path.SEPARATOR_CHAR; - private boolean policyIsRecursive; - private char pathSeparatorChar = '/'; + private boolean policyIsRecursive; + private Character pathSeparatorChar = '/'; @Override public void init() { @@ -96,8 +97,8 @@ protected ResourceMatcherWrapper buildResourceMatchers() { @Override ResourceMatcher getMatcher(String policyValue) { - if(! policyIsRecursive) { - return super.getMatcher(policyValue); + if (!policyIsRecursive) { + return getPathMatcher(policyValue); } final int len = policyValue != null ? policyValue.length() : 0; @@ -108,7 +109,7 @@ ResourceMatcher getMatcher(String policyValue) { // To ensure that when policyValue is single '*', ResourceMatcher created here returns true for isMatchAny() if (optWildCard && WILDCARD_ASTERISK.equals(policyValue)) { - return new CaseInsensitiveStringMatcher(""); + return new CaseInsensitiveStringMatcher("", getOptions()); } boolean isWildcardPresent = false; @@ -127,10 +128,9 @@ ResourceMatcher getMatcher(String policyValue) { final ResourceMatcher ret; if (isWildcardPresent) { - ret = optIgnoreCase ? new CaseInsensitiveRecursiveWildcardMatcher(policyValue, pathSeparatorChar) - : new CaseSensitiveRecursiveWildcardMatcher(policyValue, pathSeparatorChar); + ret = new RecursiveWildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, RangerPathResourceMatcher::isRecursiveWildCardMatch, optIgnoreCase ? 8 : 7); } else { - ret = optIgnoreCase ? new CaseInsensitiveRecursiveMatcher(policyValue, pathSeparatorChar) : new CaseSensitiveRecursiveMatcher(policyValue, pathSeparatorChar); + ret = new RecursivePathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, optIgnoreCase ? 8 : 7); } if (optReplaceTokens) { @@ -140,7 +140,7 @@ ResourceMatcher getMatcher(String policyValue) { return ret; } - static boolean isRecursiveWildCardMatch(String pathToCheck, String wildcardPath, char pathSeparatorChar, IOCase caseSensitivity) { + static boolean isRecursiveWildCardMatch(String pathToCheck, String wildcardPath, Character pathSeparatorChar, IOCase caseSensitivity, String[] wildcardPathElements) { boolean ret = false; @@ -154,16 +154,46 @@ static boolean isRecursiveWildCardMatch(String pathToCheck, String wildcardPath, sb.append(pathSeparatorChar); // preserve the initial pathSeparatorChar } - for(String p : pathElements) { + int pathElementIndex = 0; + boolean useStringMatching = true; + + for (String p : pathElements) { sb.append(p); - ret = FilenameUtils.wildcardMatch(sb.toString(), wildcardPath, caseSensitivity); + if (useStringMatching) { + if (wildcardPathElements.length > pathElementIndex) { + String wp = wildcardPathElements[pathElementIndex]; + + if (!(StringUtils.contains(wp, '*') || StringUtils.contains(wp, '?'))) { + boolean isMatch = caseSensitivity.isCaseSensitive() ? StringUtils.equals(p, wp) : StringUtils.equalsIgnoreCase(p, wp); + if (!isMatch) { + useStringMatching = false; + break; + } + } else { + useStringMatching = false; + } + } else { + useStringMatching = false; + } + } - if (ret) { - break; + if (!useStringMatching) { + ret = FilenameUtils.wildcardMatch(sb.toString(), wildcardPath, caseSensitivity); + if (ret) { + break; + } } sb.append(pathSeparatorChar); + pathElementIndex++; + } + if (useStringMatching) { + if (pathElements.length == wildcardPathElements.length) { // Loop finished normally and all sub-paths string-matched.. + ret = true; + } else if (pathToCheck.charAt(pathToCheck.length() - 1) == pathSeparatorChar) { // pathToCheck ends with separator, like /home/ + ret = pathElements.length == (wildcardPathElements.length - 1) && WILDCARD_ASTERISK.equals(wildcardPathElements[wildcardPathElements.length - 1]); + } } sb = null; @@ -185,118 +215,531 @@ public StringBuilder toString(StringBuilder sb) { return sb; } -} -final class CaseSensitiveRecursiveWildcardMatcher extends ResourceMatcher { - private final char levelSeparatorChar; - CaseSensitiveRecursiveWildcardMatcher(String value, char levelSeparatorChar) { - super(value); - this.levelSeparatorChar = levelSeparatorChar; + private ResourceMatcher getPathMatcher(String policyValue) { + final int len = policyValue != null ? policyValue.length() : 0; + + if (len == 0) { + return null; + } + + final ResourceMatcher ret; + + int wildcardStartIdx = -1; + int wildcardEndIdx = -1; + boolean needWildcardMatch = false; + + // If optWildcard is true + // If ('?' found or non-contiguous '*'s found in policyValue) + // needWildcardMatch = true + // End + // + // wildcardStartIdx is set to index of first '*' in policyValue or -1 if '*' is not found in policyValue, and + // wildcardEndIdx is set to index of last '*' in policyValue or -1 if '*' is not found in policyValue + // Else + // needWildcardMatch is set to false + // End + if (optWildCard) { + for (int i = 0; i < len; i++) { + final char c = policyValue.charAt(i); + + if (c == '?') { + needWildcardMatch = true; + break; + } else if (c == '*') { + if (wildcardEndIdx == -1 || wildcardEndIdx == (i - 1)) { + wildcardEndIdx = i; + if (wildcardStartIdx == -1) { + wildcardStartIdx = i; + } + } else { + needWildcardMatch = true; + break; + } + } + } + } + + if (needWildcardMatch) { // test?, test*a*, test*a*b, *test*a + ret = new WildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6); + } else if (wildcardStartIdx == -1) { // test, testa, testab + ret = new PathResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals, !optIgnoreCase, optIgnoreCase ? 2 : 1); + } else if (wildcardStartIdx == 0) { // *test, **test, *testa, *testab + String matchStr = policyValue.substring(wildcardEndIdx + 1); + ret = new PathEndsWithResourceMatcher(matchStr, getOptions(), pathSeparatorChar, !optIgnoreCase, optIgnoreCase ? 4 : 3); + } else if (wildcardEndIdx != (len - 1)) { // test*a, test*ab + ret = new WildcardResourceMatcher(policyValue, getOptions(), pathSeparatorChar, optIgnoreCase, FilenameUtils::wildcardMatch, 6); + } else { // test*, test**, testa*, testab* + String matchStr = policyValue.substring(0, wildcardStartIdx); + ret = new PathStartsWithResourceMatcher(matchStr, getOptions(), pathSeparatorChar, !optIgnoreCase, optIgnoreCase ? 4 : 3); + } + + if (optReplaceTokens) { + ret.setDelimiters(startDelimiterChar, endDelimiterChar, escapeChar, tokenPrefix); + } + + return ret; } - @Override - boolean isMatch(String resourceValue, Map evalContext) { - return RangerPathResourceMatcher.isRecursiveWildCardMatch(resourceValue, getExpandedValue(evalContext), levelSeparatorChar, IOCase.SENSITIVE); + interface TriFunction { + R apply(T t, U u, V v); } - int getPriority() { return 7 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} -} -final class CaseInsensitiveRecursiveWildcardMatcher extends ResourceMatcher { - private final char levelSeparatorChar; - CaseInsensitiveRecursiveWildcardMatcher(String value, char levelSeparatorChar) { - super(value); - this.levelSeparatorChar = levelSeparatorChar; + interface QuadFunction { + R apply(T t, U u, V v, W w); } - @Override - boolean isMatch(String resourceValue, Map evalContext) { - return RangerPathResourceMatcher.isRecursiveWildCardMatch(resourceValue, getExpandedValue(evalContext), levelSeparatorChar, IOCase.INSENSITIVE); + interface QuintFunction { + R apply(T t, U u, V v, W w, X x); } - int getPriority() { return 8 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} -} + static abstract class AbstractPathResourceMatcher extends ResourceMatcher { + final char pathSeparatorChar; + final int priority; + final boolean isCaseSensitive; -abstract class RecursiveMatcher extends ResourceMatcher { - final char levelSeparatorChar; - String valueWithoutSeparator; - String valueWithSeparator; + AbstractPathResourceMatcher(String value, Map options, char pathSeparatorChar, boolean isCaseSensitive, int priority) { + super(value, options); - RecursiveMatcher(String value, char levelSeparatorChar) { - super(value); - this.levelSeparatorChar = levelSeparatorChar; + this.pathSeparatorChar = pathSeparatorChar; + this.priority = priority; + this.isCaseSensitive = isCaseSensitive; + } + int getPriority() { + return priority + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0); + } } - String getStringToCompare(String policyValue) { - if (StringUtils.isEmpty(policyValue)) { - return policyValue; + static class PathResourceMatcher extends AbstractPathResourceMatcher { + final BiFunction function; + + PathResourceMatcher(String value, Map options, char pathSeparatorChar, BiFunction function, boolean isCaseSensitive, int priority) { + super(value, options, pathSeparatorChar, isCaseSensitive, priority); + + this.function = function; + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathResourceMatcher.isMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + String expandedValue = getExpandedValue(evalContext); + boolean ret = function.apply(resourceValue, expandedValue); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathResourceMatcher.isMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret ); + } + + return ret; + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathResourceMatcher.isPrefixMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = isCaseSensitive ? StringUtils.startsWith(getExpandedValue(evalContext), resourceValue) + : StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathResourceMatcher.isPrefixMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret ); + } + + return ret; + + } + + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathResourceMatcher.isChildMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = false; + String expandedValue = getExpandedValue(evalContext); + int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar); + + if (lastLevelSeparatorIndex != -1) { + String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex); + + if (resourceValue.charAt(resourceValue.length()-1) == pathSeparatorChar) { + resourceValue = resourceValue.substring(0, resourceValue.length()-1); + } + + ret = function.apply(resourceValue, shorterExpandedValue); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathResourceMatcher.isChildMatch(resourceValue={}, lastLevelSeparatorIndex={}): ret={}", resourceValue, lastLevelSeparatorIndex, ret ); + } + + return ret; } - return (policyValue.lastIndexOf(levelSeparatorChar) == policyValue.length()-1) ? - policyValue.substring(0, policyValue.length()-1) : policyValue; } -} -final class CaseSensitiveRecursiveMatcher extends RecursiveMatcher { - CaseSensitiveRecursiveMatcher(String value, char levelSeparatorChar) { - super(value, levelSeparatorChar); + static class PathStartsWithResourceMatcher extends AbstractPathResourceMatcher { + PathStartsWithResourceMatcher(String value, Map options, char pathSeparatorChar, boolean isCaseSensitive, int priority) { + super(value, options, pathSeparatorChar, isCaseSensitive, priority); + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathStartsWithResourceMatcher.isMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = isCaseSensitive ? StringUtils.startsWith(resourceValue, getExpandedValue(evalContext)) + : StringUtils.startsWithIgnoreCase(resourceValue, getExpandedValue(evalContext)); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathStartsWithResourceMatcher.isMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret ); + } + + return ret; + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathStartsWithResourceMatcher.isPrefixMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = isCaseSensitive ? StringUtils.startsWith(getExpandedValue(evalContext), resourceValue) + : StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathStartsWithResourceMatcher.isPrefixMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret ); + } + + return ret; + } + + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathStartsWithResourceMatcher.isChildMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = false; + String expandedValue = getExpandedValue(evalContext); + int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar); + + if (lastLevelSeparatorIndex != -1) { + String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex); + + if (resourceValue.charAt(resourceValue.length()-1) == pathSeparatorChar) { + resourceValue = resourceValue.substring(0, resourceValue.length()-1); + } + + ret = isCaseSensitive ? StringUtils.startsWith(resourceValue, shorterExpandedValue) + : StringUtils.startsWithIgnoreCase(resourceValue, shorterExpandedValue); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathStartsWithResourceMatcher.isChildMatch(resourceValue={}, lastLevelSeparatorIndex={}): ret={}", resourceValue, lastLevelSeparatorIndex, ret ); + } + + return ret; + } } - @Override - boolean isMatch(String resourceValue, Map evalContext) { + static class PathEndsWithResourceMatcher extends AbstractPathResourceMatcher { + PathEndsWithResourceMatcher(String value, Map options, char pathSeparatorChar, boolean isCaseSensitive, int priority) { + super(value, options, pathSeparatorChar, isCaseSensitive, priority); + } - final String noSeparator; - if (getNeedsDynamicEval()) { - String expandedPolicyValue = getExpandedValue(evalContext); - noSeparator = expandedPolicyValue != null ? getStringToCompare(expandedPolicyValue) : null; - } else { - if (valueWithoutSeparator == null && value != null) { - valueWithoutSeparator = getStringToCompare(value); - valueWithSeparator = valueWithoutSeparator + Character.toString(levelSeparatorChar); + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathEndsWithResourceMatcher.isMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = isCaseSensitive ? StringUtils.endsWith(resourceValue, getExpandedValue(evalContext)) + : StringUtils.endsWithIgnoreCase(resourceValue, getExpandedValue(evalContext)); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathEndsWithResourceMatcher.isMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret ); } - noSeparator = valueWithoutSeparator; + + return ret; } - boolean ret = StringUtils.equals(resourceValue, noSeparator); + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathEndsWithResourceMatcher.isPrefixMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = true; // isPrefixMatch() is always true for endsWith - if (!ret && noSeparator != null) { - final String withSeparator = getNeedsDynamicEval() ? noSeparator + Character.toString(levelSeparatorChar) : valueWithSeparator; - ret = StringUtils.startsWith(resourceValue, withSeparator); + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathEndsWithResourceMatcher.isPrefixMatch(resourceValue={}, expandedValue={}): ret={}", resourceValue, getExpandedValue(evalContext) , ret ); + } + + return ret; } - return ret; + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> PathEndsWithResourceMatcher.isChildMatch(resourceValue={}, evalContext={})", resourceValue, evalContext); + } + + boolean ret = false; + String expandedValue = getExpandedValue(evalContext); + int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar); + + if (lastLevelSeparatorIndex != -1) { + String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex); + + if (resourceValue.charAt(resourceValue.length()-1) == pathSeparatorChar) { + resourceValue = resourceValue.substring(0, resourceValue.length()-1); + } + + ret = isCaseSensitive ? StringUtils.endsWith(resourceValue, shorterExpandedValue) + : StringUtils.endsWithIgnoreCase(resourceValue, shorterExpandedValue); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== PathEndsWithResourceMatcher.isChildMatch(resourceValue={}, lastLevelSeparatorIndex={}): ret={}", resourceValue, lastLevelSeparatorIndex, ret ); + } + + return ret; + } } - int getPriority() { return 7 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} -} -final class CaseInsensitiveRecursiveMatcher extends RecursiveMatcher { - CaseInsensitiveRecursiveMatcher(String value, char levelSeparatorChar) { - super(value, levelSeparatorChar); + static class WildcardResourceMatcher extends AbstractPathResourceMatcher { + final TriFunction function; + final IOCase ioCase; + + WildcardResourceMatcher(String value, Map options, char pathSeparatorChar, boolean optIgnoreCase, TriFunction function, int priority) { + super(value, options, pathSeparatorChar, !optIgnoreCase, priority); + + this.function = function; + this.ioCase = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE; + } + + @Override + public boolean isMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> WildcardResourceMatcher.isMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")"); + } + + String expandedValue = getExpandedValue(evalContext); + boolean ret = function.apply(resourceValue, expandedValue, ioCase); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== WildcardResourceMatcher.isMatch(resourceValue=" + resourceValue + ", expandedValue=" + expandedValue + ") : result:[" + ret + "]"); + } + return ret; + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> WildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")"); + } + + boolean ret = ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), ioCase); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== WildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", expandedValue=" + getExpandedValue(evalContext) + ") : result:[" + ret + "]"); + } + return ret; + } + + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + boolean ret = false; + String expandedValue = getExpandedValue(evalContext); + int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar); + + if (lastLevelSeparatorIndex != -1) { + String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex); + + if (resourceValue.charAt(resourceValue.length()-1) == pathSeparatorChar) { + resourceValue = resourceValue.substring(0, resourceValue.length()-1); + } + + ret = function.apply(resourceValue, shorterExpandedValue, ioCase); + } + + return ret; + } } - @Override - boolean isMatch(String resourceValue, Map evalContext) { + static class RecursiveWildcardResourceMatcher extends AbstractPathResourceMatcher { + final QuintFunction function; + final IOCase ioCase; + String[] wildcardPathElements; - final String noSeparator; - if (getNeedsDynamicEval()) { - String expandedPolicyValue = getExpandedValue(evalContext); - noSeparator = expandedPolicyValue != null ? getStringToCompare(expandedPolicyValue) : null; - } else { - if (valueWithoutSeparator == null && value != null) { - valueWithoutSeparator = getStringToCompare(value); - valueWithSeparator = valueWithoutSeparator + Character.toString(levelSeparatorChar); + RecursiveWildcardResourceMatcher(String value, Map options, char pathSeparatorChar, boolean optIgnoreCase, QuintFunction function, int priority) { + super(value, options, pathSeparatorChar, !optIgnoreCase, priority); + + this.function = function; + this.ioCase = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE; + + if (!getNeedsDynamicEval()) { + wildcardPathElements = StringUtils.split(value, pathSeparatorChar); } - noSeparator = valueWithoutSeparator; } + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RecursiveWildcardResourceMatcher.isMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")"); + } + String expandedValue; + if (getNeedsDynamicEval()) { + expandedValue = getExpandedValue(evalContext); + wildcardPathElements = StringUtils.split(expandedValue, pathSeparatorChar); + } else { + expandedValue = value; + } - boolean ret = StringUtils.equalsIgnoreCase(resourceValue, noSeparator); + boolean ret = function.apply(resourceValue, expandedValue, pathSeparatorChar, ioCase, wildcardPathElements); - if (!ret && noSeparator != null) { - final String withSeparator = getNeedsDynamicEval() ? noSeparator + Character.toString(levelSeparatorChar) : valueWithSeparator; - ret = StringUtils.startsWithIgnoreCase(resourceValue, withSeparator); + if (LOG.isDebugEnabled()) { + LOG.debug("<== RecursiveWildcardResourceMatcher.isMatch(resourceValue=" + resourceValue + ", expandedValue=" + expandedValue + ") : result:[" + ret + "]"); + } + return ret; } - return ret; + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")"); + } + + boolean ret = ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), ioCase); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", expandedValue=" + getExpandedValue(evalContext) + ") : result:[" + ret + "]"); + } + return ret; + } + + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + boolean ret = false; + String expandedValue = getExpandedValue(evalContext); + int lastLevelSeparatorIndex = expandedValue.lastIndexOf(pathSeparatorChar); + + if (lastLevelSeparatorIndex != -1) { + String shorterExpandedValue = expandedValue.substring(0, lastLevelSeparatorIndex); + + if (resourceValue.charAt(resourceValue.length() - 1) == pathSeparatorChar) { + resourceValue = resourceValue.substring(0, resourceValue.length() - 1); + } + + String[] shorterWildCardPathElements = StringUtils.split(shorterExpandedValue, pathSeparatorChar); + + ret = function.apply(resourceValue, shorterExpandedValue, pathSeparatorChar, ioCase, shorterWildCardPathElements); + } + + return ret; + } } - int getPriority() { return 8 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} + static class RecursivePathResourceMatcher extends AbstractPathResourceMatcher { + String valueWithoutSeparator; + String valueWithSeparator; + + final IOCase ioCase; + final BiFunction primaryFunction; + final BiFunction fallbackFunction; + + RecursivePathResourceMatcher(String value, Map options, char pathSeparatorChar, boolean optIgnoreCase, int priority) { + super(value, options, pathSeparatorChar, true, priority); + + this.ioCase = optIgnoreCase ? IOCase.INSENSITIVE : IOCase.SENSITIVE; + this.primaryFunction = optIgnoreCase ? StringUtils::equalsIgnoreCase : StringUtils::equals; + this.fallbackFunction = optIgnoreCase ? StringUtils::startsWithIgnoreCase : StringUtils::startsWith; + } + + String getStringToCompare(String policyValue) { + if (StringUtils.isEmpty(policyValue)) { + return policyValue; + } + return (policyValue.lastIndexOf(pathSeparatorChar) == policyValue.length() - 1) ? + policyValue.substring(0, policyValue.length() - 1) : policyValue; + } + + @Override + boolean isMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RecursivePathResourceMatcher.isMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")"); + } + final String noSeparator; + if (getNeedsDynamicEval()) { + String expandedPolicyValue = getExpandedValue(evalContext); + noSeparator = expandedPolicyValue != null ? getStringToCompare(expandedPolicyValue) : null; + } else { + if (valueWithoutSeparator == null && value != null) { + valueWithoutSeparator = getStringToCompare(value); + valueWithSeparator = valueWithoutSeparator + pathSeparatorChar; + } + noSeparator = valueWithoutSeparator; + } + + boolean ret = primaryFunction.apply(resourceValue, noSeparator); + + if (!ret && noSeparator != null) { + final String withSeparator = getNeedsDynamicEval() ? noSeparator + pathSeparatorChar : valueWithSeparator; + + ret = fallbackFunction.apply(resourceValue, withSeparator); + } -} \ No newline at end of file + if (LOG.isDebugEnabled()) { + LOG.debug("<== RecursivePathResourceMatcher.isMatch(resourceValue=" + resourceValue + ", expandedValueWithoutTrailingSeparatorChar=" + noSeparator + ") : result:[" + ret + "]"); + } + + return ret; + } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", evalContext=" + evalContext + ")"); + } + + boolean ret = ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), ioCase); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RecursiveWildcardResourceMatcher.isPrefixMatch(resourceValue=" + resourceValue + ", expandedValue=" + getExpandedValue(evalContext) + ") : result:[" + ret + "]"); + } + return ret; + } + + @Override + public boolean isChildMatch(String resourceValue, Map evalContext) { + boolean ret = false; + final String noSeparator; + if (getNeedsDynamicEval()) { + String expandedPolicyValue = getExpandedValue(evalContext); + noSeparator = expandedPolicyValue != null ? getStringToCompare(expandedPolicyValue) : null; + } else { + if (valueWithoutSeparator == null && value != null) { + valueWithoutSeparator = getStringToCompare(value); + valueWithSeparator = valueWithoutSeparator + pathSeparatorChar; + } + noSeparator = valueWithoutSeparator; + } + final int lastLevelSeparatorIndex = noSeparator != null ? noSeparator.lastIndexOf(pathSeparatorChar) : -1; + + if (lastLevelSeparatorIndex != -1) { + final String shorterExpandedValue = noSeparator.substring(0, lastLevelSeparatorIndex); + + if (resourceValue.charAt(resourceValue.length() - 1) == pathSeparatorChar) { + resourceValue = resourceValue.substring(0, resourceValue.length() - 1); + } + + ret = primaryFunction.apply(resourceValue, shorterExpandedValue); + } + + return ret; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java index 0cb3e0fed4..ec22e01bfa 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerResourceMatcher.java @@ -21,6 +21,8 @@ import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType; import java.util.Map; @@ -33,7 +35,9 @@ public interface RangerResourceMatcher { boolean isMatchAny(); - boolean isMatch(Object resource, Map evalContext); + ResourceElementMatchType getMatchType(Object resource, ResourceElementMatchingScope matchingScope, Map evalContext); + + boolean isMatch(Object resource, ResourceElementMatchingScope matchingScope, Map evalContext); boolean isCompleteMatch(String resource, Map evalContext); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java index 40f3df98f0..0575636960 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcher.java @@ -24,9 +24,9 @@ import org.apache.commons.io.IOCase; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; @@ -37,7 +37,7 @@ public class RangerURLResourceMatcher extends RangerDefaultResourceMatcher { - private static final Log LOG = LogFactory.getLog(RangerURLResourceMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerURLResourceMatcher.class); public static final String OPTION_PATH_SEPARATOR = "pathSeparatorChar"; public static final char DEFAULT_PATH_SEPARATOR_CHAR = org.apache.hadoop.fs.Path.SEPARATOR_CHAR; @@ -110,7 +110,7 @@ ResourceMatcher getMatcher(String policyValue) { // To ensure that when policyValue is single '*', ResourceMatcher created here returns true for isMatchAny() if (optWildCard && WILDCARD_ASTERISK.equals(policyValue)) { - return new CaseInsensitiveStringMatcher(""); + return new CaseInsensitiveStringMatcher("", getOptions()); } boolean isWildcardPresent = false; @@ -129,10 +129,10 @@ ResourceMatcher getMatcher(String policyValue) { final ResourceMatcher ret; if (isWildcardPresent) { - ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveWildcardMatcher(policyValue, pathSeparatorChar) - : new CaseSensitiveURLRecursiveWildcardMatcher(policyValue, pathSeparatorChar); + ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveWildcardMatcher(policyValue, getOptions(), pathSeparatorChar) + : new CaseSensitiveURLRecursiveWildcardMatcher(policyValue, getOptions(), pathSeparatorChar); } else { - ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveMatcher(policyValue, pathSeparatorChar) : new CaseSensitiveURLRecursiveMatcher(policyValue, pathSeparatorChar); + ret = optIgnoreCase ? new CaseInsensitiveURLRecursiveMatcher(policyValue, getOptions(), pathSeparatorChar) : new CaseSensitiveURLRecursiveMatcher(policyValue, getOptions(), pathSeparatorChar); } if (optReplaceTokens) { @@ -179,6 +179,14 @@ static boolean isRecursiveWildCardMatch(String pathToCheck, String wildcardPath, sb.append(pathSeparatorChar); } + if (!ret) { + boolean isEndsWithPathSeparator = url.endsWith(Character.toString(pathSeparatorChar)); + if (!isEndsWithPathSeparator) { + sb.deleteCharAt(sb.length()-1); + } + ret = FilenameUtils.wildcardMatch(sb.toString(), wildcardPath, caseSensitivity); + } + sb = null; } else { // pathToCheck consists of only pathSeparatorChar ret = FilenameUtils.wildcardMatch(pathToCheck, wildcardPath, caseSensitivity); @@ -201,14 +209,19 @@ public StringBuilder toString(StringBuilder sb) { } static boolean isPathURLType(String url) { + boolean ret = false; + + if (url != null) { + Pattern p1 = Pattern.compile(":/{2}"); + Matcher m1 = p1.matcher(url); - Pattern p1 = Pattern.compile(":/{2}"); - Matcher m1 = p1.matcher(url); + Pattern p2 = Pattern.compile(":/{3,}"); + Matcher m2 = p2.matcher(url); - Pattern p2 = Pattern.compile(":/{3,}"); - Matcher m2 = p2.matcher(url); + ret = (m1.find() && !(m2.find())); + } - return (m1.find() && !(m2.find())); + return ret; } @@ -221,10 +234,10 @@ static String getPathWithOutScheme(String url) { } } -final class CaseSensitiveURLRecursiveWildcardMatcher extends ResourceMatcher { +final class CaseSensitiveURLRecursiveWildcardMatcher extends AbstractStringResourceMatcher { private final char levelSeparatorChar; - CaseSensitiveURLRecursiveWildcardMatcher(String value, char levelSeparatorChar) { - super(value); + CaseSensitiveURLRecursiveWildcardMatcher(String value, Map options, char levelSeparatorChar) { + super(value, options); this.levelSeparatorChar = levelSeparatorChar; } @@ -232,13 +245,19 @@ final class CaseSensitiveURLRecursiveWildcardMatcher extends ResourceMatcher { boolean isMatch(String resourceValue, Map evalContext) { return RangerURLResourceMatcher.isRecursiveWildCardMatch(resourceValue, getExpandedValue(evalContext), levelSeparatorChar, IOCase.SENSITIVE); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.SENSITIVE); + } + int getPriority() { return 7 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} } -final class CaseInsensitiveURLRecursiveWildcardMatcher extends ResourceMatcher { +final class CaseInsensitiveURLRecursiveWildcardMatcher extends AbstractStringResourceMatcher { private final char levelSeparatorChar; - CaseInsensitiveURLRecursiveWildcardMatcher(String value, char levelSeparatorChar) { - super(value); + CaseInsensitiveURLRecursiveWildcardMatcher(String value, Map options, char levelSeparatorChar) { + super(value, options); this.levelSeparatorChar = levelSeparatorChar; } @@ -246,14 +265,38 @@ final class CaseInsensitiveURLRecursiveWildcardMatcher extends ResourceMatcher { boolean isMatch(String resourceValue, Map evalContext) { return RangerURLResourceMatcher.isRecursiveWildCardMatch(resourceValue, getExpandedValue(evalContext), levelSeparatorChar, IOCase.INSENSITIVE); } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return ResourceMatcher.wildcardPrefixMatch(resourceValue, getExpandedValue(evalContext), IOCase.INSENSITIVE); + } + int getPriority() { return 8 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} } +abstract class RecursiveMatcher extends AbstractStringResourceMatcher { + final char levelSeparatorChar; + String valueWithoutSeparator; + String valueWithSeparator; + + RecursiveMatcher(String value, Map options, char levelSeparatorChar) { + super(value, options); + this.levelSeparatorChar = levelSeparatorChar; + } + + String getStringToCompare(String policyValue) { + if (StringUtils.isEmpty(policyValue)) { + return policyValue; + } + return (policyValue.lastIndexOf(levelSeparatorChar) == policyValue.length() - 1) ? + policyValue.substring(0, policyValue.length() - 1) : policyValue; + } +} final class CaseSensitiveURLRecursiveMatcher extends RecursiveMatcher { - CaseSensitiveURLRecursiveMatcher(String value, char levelSeparatorChar) { - super(value, levelSeparatorChar); + CaseSensitiveURLRecursiveMatcher(String value, Map options, char levelSeparatorChar) { + super(value, options, levelSeparatorChar); } @Override @@ -280,12 +323,18 @@ boolean isMatch(String resourceValue, Map evalContext) { return ret; } + + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return StringUtils.startsWith(getExpandedValue(evalContext), resourceValue); + } + int getPriority() { return 7 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} } final class CaseInsensitiveURLRecursiveMatcher extends RecursiveMatcher { - CaseInsensitiveURLRecursiveMatcher(String value, char levelSeparatorChar) { - super(value, levelSeparatorChar); + CaseInsensitiveURLRecursiveMatcher(String value, Map options, char levelSeparatorChar) { + super(value, options, levelSeparatorChar); } @Override @@ -313,5 +362,10 @@ boolean isMatch(String resourceValue, Map evalContext) { return ret; } + @Override + public boolean isPrefixMatch(String resourceValue, Map evalContext) { + return StringUtils.startsWithIgnoreCase(getExpandedValue(evalContext), resourceValue); + } + int getPriority() { return 8 + (getNeedsDynamicEval() ? DYNAMIC_EVALUATION_PENALTY : 0);} -} \ No newline at end of file +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java index 35856a96e0..e8d97b24ba 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/resourcematcher/ResourceMatcher.java @@ -19,44 +19,82 @@ package org.apache.ranger.plugin.resourcematcher; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.io.IOCase; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerRequestExprResolver; import org.apache.ranger.plugin.util.StringTokenReplacer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.Serializable; -import java.util.Collection; +import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; +import java.util.List; import java.util.Map; +import java.util.Stack; abstract class ResourceMatcher { - private static final Log LOG = LogFactory.getLog(ResourceMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(ResourceMatcher.class); - protected final String value; - protected StringTokenReplacer tokenReplacer; + protected final String value; + protected final RangerRequestExprResolver exprResolver; + protected StringTokenReplacer tokenReplacer; static final int DYNAMIC_EVALUATION_PENALTY = 8; - ResourceMatcher(String value) { this.value = value; } + ResourceMatcher(String value, Map options) { + this.value = value; + + if (RangerAbstractResourceMatcher.getOptionReplaceReqExpressions(options) && RangerRequestExprResolver.hasExpressions(value)) { + exprResolver = new RangerRequestExprResolver(value, null); // TODO: serviceType + } else { + exprResolver = null; + } + } abstract boolean isMatch(String resourceValue, Map evalContext); - abstract int getPriority(); - boolean isMatchAny() { return value != null && value.length() == 0; } + abstract boolean isPrefixMatch(String resourceValue, Map evalContext); - boolean getNeedsDynamicEval() { - return tokenReplacer != null; + abstract boolean isChildMatch(String resourceValue, Map evalContext); + + final boolean isMatch(String resourceValue, ResourceElementMatchingScope matchingScope, Map evalContext) { + final ResourceElementMatchType matchType = getMatchType(resourceValue, matchingScope, evalContext); + + return isMatch(matchType, matchingScope); } - public boolean isMatchAny(Collection resourceValues, Map evalContext) { - if (resourceValues != null) { - for (String resourceValue : resourceValues) { - if (isMatch(resourceValue, evalContext)) { - return true; + final ResourceElementMatchType getMatchType(String resourceValue, ResourceElementMatchingScope matchingScope, Map evalContext) { + ResourceElementMatchType ret = ResourceElementMatchType.NONE; + + if (isMatch(resourceValue, evalContext)) { + ret = ResourceElementMatchType.SELF; + } else { + if (matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX) { + if (isPrefixMatch(resourceValue, evalContext)) { + ret = ResourceElementMatchType.PREFIX; + } + } else if (matchingScope == ResourceElementMatchingScope.SELF_OR_CHILD) { + if (isChildMatch(resourceValue, evalContext)) { + ret = ResourceElementMatchType.CHILD; } } } - return false; + return ret; + } + + abstract int getPriority(); + + boolean isMatchAny() { return value != null && value.length() == 0; } + + boolean getNeedsDynamicEval() { + return exprResolver != null || tokenReplacer != null; } @Override @@ -70,7 +108,7 @@ void setDelimiters(char startDelimiterChar, char endDelimiterChar, char escapeCh ", endDelimiter=" + endDelimiterChar + ", escapeChar=" + escapeChar + ", prefix=" + tokenPrefix); } - if(value != null && (value.indexOf(escapeChar) != -1 || (value.indexOf(startDelimiterChar) != -1 && value.indexOf(endDelimiterChar) != -1))) { + if(exprResolver != null || StringTokenReplacer.hasToken(value, startDelimiterChar, endDelimiterChar, escapeChar)) { tokenReplacer = new StringTokenReplacer(startDelimiterChar, endDelimiterChar, escapeChar, tokenPrefix); } @@ -81,17 +119,184 @@ void setDelimiters(char startDelimiterChar, char endDelimiterChar, char escapeCh } String getExpandedValue(Map evalContext) { - final String ret; + String ret = value; - if(tokenReplacer != null) { - ret = tokenReplacer.replaceTokens(value, evalContext); - } else { - ret = value; + if (exprResolver != null) { + RangerAccessRequest accessRequest = RangerAccessRequestUtil.getRequestFromContext(evalContext); + + if (accessRequest != null) { + ret = exprResolver.resolveExpressions(accessRequest); + } + } + + if (tokenReplacer != null) { + ret = tokenReplacer.replaceTokens(ret, evalContext); + } + + return ret; + } + + public static boolean startsWithAnyChar(String value, String startChars) { + boolean ret = false; + + if (value != null && value.length() > 0 && startChars != null) { + ret = StringUtils.contains(startChars, value.charAt(0)); + } + + return ret; + } + + public static boolean isMatch(ResourceElementMatchType matchType, ResourceElementMatchingScope matchingScope) { + final boolean ret; + + switch (matchType) { + case SELF: + ret = true; + break; + + case CHILD: + ret = matchingScope == ResourceElementMatchingScope.SELF_OR_CHILD; + break; + + case PREFIX: + ret = matchingScope == ResourceElementMatchingScope.SELF_OR_PREFIX; + break; + + case NONE: + ret = false; + break; + + default: + LOG.error("invalid ResourceElementMatchType: {}}", matchType); + + ret = false; } return ret; } + // modified version of FilenameUtils.wildcardMatch(), to check if value is a prefix match for wildcardMatcher + public static boolean wildcardPrefixMatch(String value, String wildcardMatcher, IOCase caseSensitivity) { + if (value == null && wildcardMatcher == null) { + return true; + } else if (value == null || wildcardMatcher == null) { + return false; + } + + if (caseSensitivity == null) { + caseSensitivity = IOCase.SENSITIVE; + } + + List wcsTokens = splitOnTokens(wildcardMatcher); + boolean anyChars = false; + int textIdx = 0; + int wcsIdx = 0; + Stack backtrack = new Stack<>(); + + do { + if (backtrack.size() > 0) { + int[] array = backtrack.pop(); + + wcsIdx = array[0]; + textIdx = array[1]; + anyChars = true; + } + + for(; wcsIdx < wcsTokens.size(); ++wcsIdx) { + String wcsToken = wcsTokens.get(wcsIdx); + + if (wcsToken.equals("?")) { + ++textIdx; + + if (textIdx > value.length()) { + break; + } + + anyChars = false; + } else if (wcsToken.equals("*")) { + anyChars = true; + + if (wcsIdx == wcsTokens.size() - 1) { + textIdx = value.length(); + } + } else { + // changes from FilenameUtils.wildcardMatch(): added following 3 lines to check if value is a prefix match for wildcardMatcher + if (wcsToken.length() > (value.length() - textIdx)) { + wcsToken = wcsToken.substring(0, value.length() - textIdx); + } + + if (anyChars) { + textIdx = caseSensitivity.checkIndexOf(value, textIdx, wcsToken); + + if (textIdx == -1) { + break; + } + + int repeat = caseSensitivity.checkIndexOf(value, textIdx + 1, wcsToken); + + if (repeat >= 0) { + backtrack.push(new int[]{wcsIdx, repeat}); + } + } else if (!caseSensitivity.checkRegionMatches(value, textIdx, wcsToken)) { + break; + } + + textIdx += wcsToken.length(); + + anyChars = false; + } + } + + // changes from FilenameUtils.wildcardMatch(): replaced the condition in 'if' below to check if value is a prefix match for wildcardMatcher + // original if: if (wcsIdx == wcsTokens.size() && textIdx == value.length()) + if (wcsIdx == wcsTokens.size() || textIdx == value.length()) { + return true; + } + } while (backtrack.size() > 0); + + return anyChars; + } + + static List splitOnTokens(String text) { + if (text.indexOf(63) == -1 && text.indexOf(42) == -1) { + return Collections.singletonList(text); + } else { + char[] array = text.toCharArray(); + List list = new ArrayList<>(2); + StringBuilder buffer = new StringBuilder(); + char prevChar = 0; + char[] arr$ = array; + int len$ = array.length; + + for(int i$ = 0; i$ < len$; ++i$) { + char ch = arr$[i$]; + + if (ch != '?' && ch != '*') { + buffer.append(ch); + } else { + if (buffer.length() != 0) { + list.add(buffer.toString()); + buffer.setLength(0); + } + + if (ch == '?') { + list.add("?"); + } else if (prevChar != '*') { + list.add("*"); + } + } + + prevChar = ch; + } + + if (buffer.length() != 0) { + list.add(buffer.toString()); + } + + return list; + } + } + public static class PriorityComparator implements Comparator, Serializable { @Override public int compare(ResourceMatcher me, ResourceMatcher other) { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java index 81b1971a8b..f3da19fe5a 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerAuthContext.java @@ -24,8 +24,11 @@ import org.apache.commons.lang.StringUtils; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; import org.apache.ranger.plugin.policyengine.RangerPolicyEngine; +import org.apache.ranger.plugin.policyengine.RangerSecurityZoneMatcher; import org.apache.ranger.plugin.util.RangerRoles; import org.apache.ranger.plugin.util.RangerRolesUtil; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.apache.ranger.plugin.util.RangerUserStoreUtil; import java.util.HashSet; import java.util.Map; @@ -34,23 +37,34 @@ public class RangerAuthContext { private final Map requestContextEnrichers; + private final RangerSecurityZoneMatcher zoneMatcher; private RangerRolesUtil rolesUtil; + private RangerUserStoreUtil userStoreUtil; - - public RangerAuthContext(Map requestContextEnrichers, RangerRoles roles) { + public RangerAuthContext(Map requestContextEnrichers, RangerSecurityZoneMatcher zoneMatcher, RangerRoles roles, RangerUserStore userStore) { this.requestContextEnrichers = requestContextEnrichers != null ? requestContextEnrichers : new ConcurrentHashMap<>(); + this.zoneMatcher = zoneMatcher;; setRoles(roles); + setUserStore(userStore); } public Map getRequestContextEnrichers() { return requestContextEnrichers; } + public RangerSecurityZoneMatcher getZoneMatcher() { + return zoneMatcher; + } + public void addOrReplaceRequestContextEnricher(RangerContextEnricher enricher, Object database) { // concurrentHashMap does not allow null to be inserted into it, so insert a dummy which is checked // when enrich() is called requestContextEnrichers.put(enricher, database != null ? database : enricher); + + if (database instanceof RangerUserStore) { + setUserStore((RangerUserStore) database); + } } public void cleanupRequestContextEnricher(RangerContextEnricher enricher) { @@ -58,7 +72,7 @@ public void cleanupRequestContextEnricher(RangerContextEnricher enricher) { } public void setRoles(RangerRoles roles) { - this.rolesUtil = roles != null ? new RangerRolesUtil(roles) : new RangerRolesUtil(null); + this.rolesUtil = new RangerRolesUtil(roles); } public Set getRolesForUserAndGroups(String user, Set groups) { @@ -97,4 +111,20 @@ public Set getRolesForUserAndGroups(String user, Set groups) { } public long getRoleVersion() { return this.rolesUtil.getRoleVersion(); } + + public RangerRolesUtil getRangerRolesUtil() { + return this.rolesUtil; + } + + public long getUserStoreVersion() { + return this.userStoreUtil.getUserStoreVersion(); + } + + public RangerUserStoreUtil getUserStoreUtil() { + return this.userStoreUtil; + } + + public void setUserStore(RangerUserStore userStore) { + this.userStoreUtil = new RangerUserStoreUtil(userStore); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java index 794d3cba4c..6a614bf2d6 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBasePlugin.java @@ -23,8 +23,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.admin.client.RangerAdminClient; import org.apache.ranger.admin.client.RangerAdminRESTClient; import org.apache.ranger.audit.provider.AuditHandler; @@ -33,9 +31,14 @@ import org.apache.ranger.authorization.hadoop.config.RangerAuditConfig; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.authorization.utils.StringUtil; -import org.apache.ranger.plugin.conditionevaluator.RangerScriptExecutionContext; +import org.apache.ranger.plugin.contextenricher.RangerAdminGdsInfoRetriever; +import org.apache.ranger.plugin.contextenricher.RangerAdminUserStoreRetriever; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; +import org.apache.ranger.plugin.contextenricher.RangerGdsEnricher; import org.apache.ranger.plugin.contextenricher.RangerTagEnricher; +import org.apache.ranger.plugin.contextenricher.RangerUserStoreEnricher; +import org.apache.ranger.plugin.model.RangerBaseModelObject; +import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerRole; import org.apache.ranger.plugin.model.RangerServiceDef; @@ -49,12 +52,16 @@ import org.apache.ranger.plugin.policyengine.RangerPolicyEngineImpl; import org.apache.ranger.plugin.policyengine.RangerResourceACLs; import org.apache.ranger.plugin.policyengine.RangerResourceAccessInfo; +import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.util.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerBasePlugin { - private static final Log LOG = LogFactory.getLog(RangerBasePlugin.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerBasePlugin.class); private final RangerPluginConfig pluginConfig; private final RangerPluginContext pluginContext; @@ -66,6 +73,9 @@ public class RangerBasePlugin { private RangerAuthContext currentAuthContext; private RangerAccessResultProcessor resultProcessor; private RangerRoles roles; + private final List chainedPlugins; + private final boolean dedupStrings; + private boolean isUserStoreEnricherAddedImplcitly = false; public RangerBasePlugin(String serviceType, String appId) { @@ -80,16 +90,80 @@ public RangerBasePlugin(RangerPluginConfig pluginConfig) { this.pluginConfig = pluginConfig; this.pluginContext = new RangerPluginContext(pluginConfig); + boolean usePerfDataRecorder = pluginConfig.getBoolean("ranger.perf.aggregate.data", false); + int perfDataDumpInterval = pluginConfig.getInt("ranger.perf.aggregate.data.dump.interval", 0); + boolean usePerfDataLock = pluginConfig.getBoolean("ranger.perf.aggregate.data.lock.enabled", false); + String nullSafeSupplier = pluginConfig.get(pluginConfig.getPropertyPrefix() + ".null_safe.supplier", RangerBaseModelObject.NULL_SAFE_SUPPLIER_V2); + + LOG.info(pluginConfig.getPropertyPrefix() + ".null_safe.supplier=" + nullSafeSupplier); + + RangerBaseModelObject.setNullSafeSupplier(nullSafeSupplier); + + PerfDataRecorder.initialize(usePerfDataRecorder, perfDataDumpInterval, usePerfDataLock, null); + Set superUsers = toSet(pluginConfig.get(pluginConfig.getPropertyPrefix() + ".super.users")); Set superGroups = toSet(pluginConfig.get(pluginConfig.getPropertyPrefix() + ".super.groups")); Set auditExcludeUsers = toSet(pluginConfig.get(pluginConfig.getPropertyPrefix() + ".audit.exclude.users")); Set auditExcludeGroups = toSet(pluginConfig.get(pluginConfig.getPropertyPrefix() + ".audit.exclude.groups")); Set auditExcludeRoles = toSet(pluginConfig.get(pluginConfig.getPropertyPrefix() + ".audit.exclude.roles")); + Set serviceAdmins = toSet(pluginConfig.get(pluginConfig.getPropertyPrefix() + ".service.admins")); setSuperUsersAndGroups(superUsers, superGroups); setAuditExcludedUsersGroupsRoles(auditExcludeUsers, auditExcludeGroups, auditExcludeRoles); + setIsFallbackSupported(pluginConfig.getBoolean(pluginConfig.getPropertyPrefix() + ".is.fallback.supported", false)); + setServiceAdmins(serviceAdmins); + + RangerRequestScriptEvaluator.init(pluginConfig); - RangerScriptExecutionContext.init(pluginConfig); + this.dedupStrings = pluginConfig.getBoolean(pluginConfig.getPropertyPrefix() + ".dedup.strings", true); + this.chainedPlugins = initChainedPlugins(); + } + + public RangerBasePlugin(RangerPluginConfig pluginConfig, ServicePolicies policies, ServiceTags tags, RangerRoles roles) { + this(pluginConfig, policies, tags, roles, null, null); + } + + public RangerBasePlugin(RangerPluginConfig pluginConfig, ServicePolicies policies, ServiceTags tags, RangerRoles roles, RangerUserStore userStore) { + this(pluginConfig, policies, tags, roles, userStore, null); + } + + public RangerBasePlugin(RangerPluginConfig pluginConfig, ServicePolicies policies, ServiceTags tags, RangerRoles roles, RangerUserStore userStore, ServiceGdsInfo gdsInfo) { + this(pluginConfig); + + init(); + + setPolicies(policies); + setRoles(roles); + + if (tags != null) { + RangerTagEnricher tagEnricher = getTagEnricher(); + + if (tagEnricher != null) { + tagEnricher.setServiceTags(tags); + } else { + LOG.warn("RangerBasePlugin(tagsVersion=" + tags.getTagVersion() + "): no tag enricher found. Plugin will not enforce tag-based policies"); + } + } + + if (userStore != null) { + RangerUserStoreEnricher userStoreEnricher = getUserStoreEnricher(); + + if (userStoreEnricher != null) { + userStoreEnricher.setRangerUserStore(userStore); + } else { + LOG.warn("RangerBasePlugin(userStoreVersion=" + userStore.getUserStoreVersion() + "): no userstore enricher found. Plugin will not enforce user/group attribute-based policies"); + } + } + + if (gdsInfo != null) { + RangerGdsEnricher gdsEnricher = getGdsEnricher(); + + if (gdsEnricher != null) { + gdsEnricher.setGdsInfo(gdsInfo); + } else { + LOG.warn("RangerBasePlugin(gdsInfo=" + gdsInfo.getGdsVersion() + "): no GDS enricher found. Plugin will not enforce GDS policies"); + } + } } public static AuditHandler getAuditProvider(String serviceName) { @@ -115,8 +189,14 @@ public String getClusterName() { return pluginConfig.getClusterName(); } + public RangerPluginContext getPluginContext() { + return pluginContext; + } + public RangerAuthContext getCurrentRangerAuthContext() { return currentAuthContext; } + public List getChainedPlugins() { return chainedPlugins; } + // For backward compatibility public RangerAuthContext createRangerAuthContext() { return currentAuthContext; } @@ -144,6 +224,14 @@ public void setSuperUsersAndGroups(Set users, Set groups) { pluginConfig.setSuperUsersGroups(users, groups); } + public void setIsFallbackSupported(boolean isFallbackSupported) { + pluginConfig.setIsFallbackSupported(isFallbackSupported); + } + + public void setServiceAdmins(Set users) { + pluginConfig.setServiceAdmins(users); + } + public RangerServiceDef getServiceDef() { RangerPolicyEngine policyEngine = this.policyEngine; @@ -176,10 +264,44 @@ public void init() { } } - refresher = new PolicyRefresher(this); - LOG.info("Created PolicyRefresher Thread(" + refresher.getName() + ")"); - refresher.setDaemon(true); - refresher.startRefresher(); + if (!pluginConfig.getPolicyEngineOptions().disablePolicyRefresher) { + refresher = new PolicyRefresher(this); + LOG.info("Created PolicyRefresher Thread(" + refresher.getName() + ")"); + refresher.setDaemon(true); + refresher.startRefresher(); + } + + for (RangerChainedPlugin chainedPlugin : chainedPlugins) { + chainedPlugin.init(); + } + } + + public long getPoliciesVersion() { + RangerPolicyEngine policyEngine = this.policyEngine; + Long ret = policyEngine != null ? policyEngine.getPolicyVersion() : null; + + return ret != null ? ret : -1L; + } + + public long getTagsVersion() { + RangerTagEnricher tagEnricher = getTagEnricher(); + Long ret = tagEnricher != null ? tagEnricher.getServiceTagsVersion() : null; + + return ret != null ? ret : -1L; + } + + public long getRolesVersion() { + RangerPolicyEngine policyEngine = this.policyEngine; + Long ret = policyEngine != null ? policyEngine.getRoleVersion() : null; + + return ret != null ? ret : -1L; + } + + public long getUserStoreVersion() { + RangerUserStoreEnricher userStoreEnricher = getUserStoreEnricher(); + Long ret = userStoreEnricher != null ? userStoreEnricher.getUserStoreVersion() : null; + + return ret != null ? ret : -1L; } public void setPolicies(ServicePolicies policies) { @@ -187,6 +309,27 @@ public void setPolicies(ServicePolicies policies) { LOG.debug("==> setPolicies(" + policies + ")"); } + if (pluginConfig.isEnableImplicitUserStoreEnricher() && policies != null && !ServiceDefUtil.isUserStoreEnricherPresent(policies)) { + String retrieverClassName = pluginConfig.get(RangerUserStoreEnricher.USERSTORE_RETRIEVER_CLASSNAME_OPTION, RangerAdminUserStoreRetriever.class.getCanonicalName()); + String retrieverPollIntMs = pluginConfig.get(RangerUserStoreEnricher.USERSTORE_REFRESHER_POLLINGINTERVAL_OPTION, Integer.toString(60 * 1000)); + + // in case of delta, policies will only have changes; hence add userStoreEnricher if it was implicitly added previous calls to setPolicies() + if (RangerPolicyDeltaUtil.hasPolicyDeltas(policies) == Boolean.TRUE && isUserStoreEnricherAddedImplcitly) { + ServiceDefUtil.addUserStoreEnricher(policies, retrieverClassName, retrieverPollIntMs); + } else if (pluginConfig.isUseRangerGroups() || pluginConfig.isConvertEmailToUsername()) { + isUserStoreEnricherAddedImplcitly = ServiceDefUtil.addUserStoreEnricher(policies, retrieverClassName, retrieverPollIntMs); + } else { + isUserStoreEnricherAddedImplcitly = ServiceDefUtil.addUserStoreEnricherIfNeeded(policies, retrieverClassName, retrieverPollIntMs); + } + } + + if (pluginConfig.isEnableImplicitGdsInfoEnricher() && policies != null && !ServiceDefUtil.isGdsInfoEnricherPresent(policies)) { + String retrieverClassName = pluginConfig.get(RangerGdsEnricher.RETRIEVER_CLASSNAME_OPTION, RangerAdminGdsInfoRetriever.class.getCanonicalName()); + String retrieverPollIntMs = pluginConfig.get(RangerGdsEnricher.REFRESHER_POLLINGINTERVAL_OPTION, Integer.toString(60 * 1000)); + + ServiceDefUtil.addGdsInfoEnricher(policies, retrieverClassName, retrieverPollIntMs); + } + // guard against catastrophic failure during policy engine Initialization or try { RangerPolicyEngine oldPolicyEngine = this.policyEngine; @@ -202,11 +345,32 @@ public void setPolicies(ServicePolicies policies) { isNewEngineNeeded = false; } } else { + if (dedupStrings) { + policies.dedupStrings(); + } + Boolean hasPolicyDeltas = RangerPolicyDeltaUtil.hasPolicyDeltas(policies); if (hasPolicyDeltas == null) { - LOG.warn("Downloaded policies are internally inconsistent!! [" + policies + "]. Please check server-side code! Keeping old policy-engine!"); - isNewEngineNeeded = false; + LOG.info("Downloaded policies do not require policy change !! [" + policies + "]"); + + if (this.policyEngine == null) { + + LOG.info("There are no material changes, and current policy-engine is null! Creating a policy-engine with default service policies"); + ServicePolicies defaultSvcPolicies = getDefaultSvcPolicies(); + + if (defaultSvcPolicies == null) { + LOG.error("Could not get default Service Policies. Keeping old policy-engine! This is a FATAL error as the old policy-engine is null!"); + isNewEngineNeeded = false; + } else { + defaultSvcPolicies.setPolicyVersion(policies.getPolicyVersion()); + policies = defaultSvcPolicies; + isNewEngineNeeded = true; + } + } else { + LOG.info("Keeping old policy-engine!"); + isNewEngineNeeded = false; + } } else { if (hasPolicyDeltas.equals(Boolean.TRUE)) { // Rebuild policies from deltas @@ -222,7 +386,16 @@ public void setPolicies(ServicePolicies policies) { isNewEngineNeeded = false; } } else { - usePolicyDeltas = false; + if (policies.getPolicies() == null) { + policies.setPolicies(new ArrayList<>()); + } + if (MapUtils.isNotEmpty(policies.getSecurityZones())) { + for (ServicePolicies.SecurityZoneInfo element : policies.getSecurityZones().values()) { + if (element.getPolicies() == null) { + element.setPolicies(new ArrayList<>()); + } + } + } } } } @@ -280,22 +453,36 @@ public void setPolicies(ServicePolicies policies) { newPolicyEngine.setTrustedProxyAddresses(pluginConfig.getTrustedProxyAddresses()); } + LOG.info("Switching policy engine from [" + getPolicyVersion() + "]"); this.policyEngine = newPolicyEngine; + LOG.info("Switched policy engine to [" + getPolicyVersion() + "]"); this.currentAuthContext = pluginContext.getAuthContext(); pluginContext.notifyAuthContextChanged(); - if (oldPolicyEngine != null) { + if (oldPolicyEngine != null && oldPolicyEngine != newPolicyEngine) { ((RangerPolicyEngineImpl) oldPolicyEngine).releaseResources(!isPolicyEngineShared); } if (this.refresher != null) { - this.refresher.saveToCache(usePolicyDeltas ? servicePolicies : policies); + boolean doPreserveDeltas = pluginConfig.getBoolean (pluginConfig.getPropertyPrefix() + ".preserve.deltas", false); + if (!doPreserveDeltas) { + this.refresher.saveToCache(usePolicyDeltas ? servicePolicies : policies); + } else { + // Save both deltas and all policies to cache for verification + this.refresher.saveToCache(policies); + + if (usePolicyDeltas) { + this.refresher.saveToCache(servicePolicies); + } + } } } } else { - LOG.warn("Returning without saving policies to cache. Leaving current policy engine as-is"); + LOG.warn("Leaving current policy engine as-is"); + LOG.warn("Policies are not saved to cache. policyVersion in the policy-cache may be different than in Ranger-admin, even though the policies are the same!"); + LOG.warn("Ranger-PolicyVersion:[" + (policies != null ? policies.getPolicyVersion() : -1L) + "], Cached-PolicyVersion:[" + (this.policyEngine != null ? this.policyEngine.getPolicyVersion() : -1L) + "]"); } } catch (Exception e) { @@ -339,43 +526,169 @@ public Collection isAccessAllowed(Collection isAccessAllowed(Collection requests, RangerAccessResultProcessor resultProcessor) { - RangerPolicyEngine policyEngine = this.policyEngine; + Collection ret = null; + RangerPolicyEngine policyEngine = this.policyEngine; - if(policyEngine != null) { - return policyEngine.evaluatePolicies(requests, RangerPolicy.POLICY_TYPE_ACCESS, resultProcessor); + if (policyEngine != null) { + ret = policyEngine.evaluatePolicies(requests, RangerPolicy.POLICY_TYPE_ACCESS, null); } - return null; + if (CollectionUtils.isNotEmpty(ret)) { + for (RangerChainedPlugin chainedPlugin : chainedPlugins) { + Collection chainedResults = chainedPlugin.isAccessAllowed(requests); + + if (CollectionUtils.isNotEmpty(chainedResults)) { + Iterator iterRet = ret.iterator(); + Iterator iterChainedResults = chainedResults.iterator(); + + while (iterRet.hasNext() && iterChainedResults.hasNext()) { + RangerAccessResult result = iterRet.next(); + RangerAccessResult chainedResult = iterChainedResults.next(); + + if (result != null && chainedResult != null) { + updateResultFromChainedResult(result, chainedResult); + } + } + } + } + } + + if (policyEngine != null && CollectionUtils.isNotEmpty(ret)) { + for (RangerAccessResult result : ret) { + policyEngine.evaluateAuditPolicies(result); + } + } + + if (resultProcessor != null) { + resultProcessor.processResults(ret); + } + + return ret; } public RangerAccessResult evalDataMaskPolicies(RangerAccessRequest request, RangerAccessResultProcessor resultProcessor) { RangerPolicyEngine policyEngine = this.policyEngine; + RangerAccessResult ret = null; if(policyEngine != null) { - return policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_DATAMASK, resultProcessor); + ret = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_DATAMASK, resultProcessor); + + if (ret != null) { + for (RangerChainedPlugin chainedPlugin : chainedPlugins) { + if (LOG.isDebugEnabled()) { + LOG.debug("BasePlugin.evalDataMaskPolicies result=[" + ret + "]"); + LOG.debug("Calling chainedPlugin.evalDataMaskPolicies for service:[" + chainedPlugin.plugin.pluginConfig.getServiceName() + "]"); + } + + RangerAccessResult chainedResult = chainedPlugin.evalDataMaskPolicies(request); + + if (chainedResult != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("chainedPlugin.evalDataMaskPolicies for service:[" + chainedPlugin.plugin.pluginConfig.getServiceName() + "] returned result=[" + chainedResult + "]"); + } + + updateResultFromChainedResult(ret, chainedResult); + + if (LOG.isDebugEnabled()) { + LOG.debug("After updating result from chainedPlugin.evalDataMaskPolicies for service:[" + chainedPlugin.plugin.pluginConfig.getServiceName() + "], result=" + ret + "]"); + } + } + } + } + + policyEngine.evaluateAuditPolicies(ret); } - return null; + return ret; } public RangerAccessResult evalRowFilterPolicies(RangerAccessRequest request, RangerAccessResultProcessor resultProcessor) { RangerPolicyEngine policyEngine = this.policyEngine; + RangerAccessResult ret = null; if(policyEngine != null) { - return policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ROWFILTER, resultProcessor); + ret = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ROWFILTER, resultProcessor); + + if (ret != null) { + for (RangerChainedPlugin chainedPlugin : chainedPlugins) { + if (LOG.isDebugEnabled()) { + LOG.debug("BasePlugin.evalRowFilterPolicies result=[" + ret + "]"); + LOG.debug("Calling chainedPlugin.evalRowFilterPolicies for service:[" + chainedPlugin.plugin.pluginConfig.getServiceName() + "]"); + } + + RangerAccessResult chainedResult = chainedPlugin.evalRowFilterPolicies(request); + + if (chainedResult != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("chainedPlugin.evalRowFilterPolicies for service:[" + chainedPlugin.plugin.pluginConfig.getServiceName() + "] returned result=[" + chainedResult + "]"); + } + + updateResultFromChainedResult(ret, chainedResult); + + if (LOG.isDebugEnabled()) { + LOG.debug("After updating result from chainedPlugin.evalRowFilterPolicies for service:[" + chainedPlugin.plugin.pluginConfig.getServiceName() + "], result=" + ret + "]"); + } + } + } + } + + policyEngine.evaluateAuditPolicies(ret); } - return null; + return ret; + } + + public void evalAuditPolicies(RangerAccessResult result) { + RangerPolicyEngine policyEngine = this.policyEngine; + + if (policyEngine != null) { + policyEngine.evaluateAuditPolicies(result); + } } public RangerResourceAccessInfo getResourceAccessInfo(RangerAccessRequest request) { @@ -389,13 +702,57 @@ public RangerResourceAccessInfo getResourceAccessInfo(RangerAccessRequest reques } public RangerResourceACLs getResourceACLs(RangerAccessRequest request) { + return getResourceACLs(request, null); + } + + public RangerResourceACLs getResourceACLs(RangerAccessRequest request, Integer policyType) { + RangerResourceACLs ret = null; RangerPolicyEngine policyEngine = this.policyEngine; if(policyEngine != null) { - return policyEngine.getResourceACLs(request); + ret = policyEngine.getResourceACLs(request, policyType); } - return null; + for (RangerChainedPlugin chainedPlugin : chainedPlugins) { + RangerResourceACLs chainedResourceACLs = chainedPlugin.getResourceACLs(request, policyType); + + if (chainedResourceACLs != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Chained-plugin returned non-null ACLs!!"); + } + if (chainedPlugin.isAuthorizeOnlyWithChainedPlugin()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Chained-plugin is configured to ignore Base-plugin's ACLs"); + } + ret = chainedResourceACLs; + break; + } else { + if (ret != null) { + ret = getMergedResourceACLs(ret, chainedResourceACLs); + } + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Chained-plugin returned null ACLs!!"); + } + } + } + + GdsPolicyEngine gdsPolicyEngine = getGdsPolicyEngine(); + + if (gdsPolicyEngine != null) { + RangerResourceACLs gdsACLs = gdsPolicyEngine.getResourceACLs(request); + + if (gdsACLs != null) { + if (ret != null) { + ret = getMergedResourceACLs(ret, gdsACLs); + } else { + ret = gdsACLs; + } + } + } + + return ret; } public Set getRolesFromUserAndGroups(String user, Set groups) { @@ -408,6 +765,75 @@ public Set getRolesFromUserAndGroups(String user, Set groups) { return null; } + public RangerRoles getRangerRoles() { + RangerPolicyEngine policyEngine = this.policyEngine; + + if(policyEngine != null) { + return policyEngine.getRangerRoles(); + } + + return null; + } + + public Set getRangerRoleForPrincipal(String principal, String type) { + Set ret = new HashSet<>(); + Set rangerRoles = null; + Map> roleMapping = null; + RangerRoles roles = getRangerRoles(); + if (roles != null) { + rangerRoles = roles.getRangerRoles(); + } + + if (rangerRoles != null) { + RangerPluginContext rangerPluginContext = policyEngine.getPluginContext(); + if (rangerPluginContext != null) { + RangerAuthContext rangerAuthContext = rangerPluginContext.getAuthContext(); + if (rangerAuthContext != null) { + RangerRolesUtil rangerRolesUtil = rangerAuthContext.getRangerRolesUtil(); + if (rangerRolesUtil != null) { + switch (type) { + case "USER": + roleMapping = rangerRolesUtil.getUserRoleMapping(); + break; + case "GROUP": + roleMapping = rangerRolesUtil.getGroupRoleMapping(); + break; + case "ROLE": + roleMapping = rangerRolesUtil.getRoleRoleMapping(); + break; + } + } + } + } + if (roleMapping != null) { + Set principalRoles = roleMapping.get(principal); + if (CollectionUtils.isNotEmpty(principalRoles)) { + for (String role : principalRoles) { + for (RangerRole rangerRole : rangerRoles) { + if (rangerRole.getName().equals(role)) { + ret.add(rangerRole); + } + } + } + } + } + } + return ret; + } + + public boolean isServiceAdmin(String userName) { + boolean ret = false; + + RangerPolicyEngine policyEngine = this.policyEngine; + + if(policyEngine != null) { + RangerPolicyEngineImpl rangerPolicyEngine = (RangerPolicyEngineImpl) policyEngine; + ret = rangerPolicyEngine.isServiceAdmin(userName); + } + + return ret; + } + public RangerRole createRole(RangerRole request, RangerAccessResultProcessor resultProcessor) throws Exception { if(LOG.isDebugEnabled()) { LOG.debug("==> RangerBasePlugin.createRole(" + request + ")"); @@ -603,7 +1029,9 @@ public void refreshPoliciesAndTags() { // Synch-up policies long oldPolicyVersion = policyEngine.getPolicyVersion(); - refresher.syncPoliciesWithAdmin(accessTrigger); + if (refresher != null) { + refresher.syncPoliciesWithAdmin(accessTrigger); + } policyEngine = this.policyEngine; // might be updated in syncPoliciesWithAdmin() @@ -633,7 +1061,7 @@ private void auditGrantRevoke(GrantRevokeRequest request, String action, boolean accessRequest.setResource(new RangerAccessResourceImpl(StringUtil.toStringObjectMap(request.getResource()))); accessRequest.setUser(request.getGrantor()); - accessRequest.setAccessType(RangerPolicyEngine.ADMIN_ACCESS); + accessRequest.setAccessType(RangerPolicyEngine.ANY_ACCESS); accessRequest.setAction(action); accessRequest.setClientIPAddress(request.getClientIPAddress()); accessRequest.setClientType(request.getClientType()); @@ -718,7 +1146,7 @@ static private final class LogHistory { int counter; } - private RangerTagEnricher getTagEnricher() { + public RangerTagEnricher getTagEnricher() { RangerTagEnricher ret = null; RangerAuthContext authContext = getCurrentRangerAuthContext(); @@ -740,6 +1168,83 @@ private RangerTagEnricher getTagEnricher() { return ret; } + public RangerUserStoreEnricher getUserStoreEnricher() { + RangerUserStoreEnricher ret = null; + RangerAuthContext authContext = getCurrentRangerAuthContext(); + + if (authContext != null) { + Map contextEnricherMap = authContext.getRequestContextEnrichers(); + + if (MapUtils.isNotEmpty(contextEnricherMap)) { + Set contextEnrichers = contextEnricherMap.keySet(); + + for (RangerContextEnricher enricher : contextEnrichers) { + if (enricher instanceof RangerUserStoreEnricher) { + ret = (RangerUserStoreEnricher) enricher; + + ret.getRangerUserStore(); + + break; + } + } + } + } + + return ret; + } + + public RangerGdsEnricher getGdsEnricher() { + RangerGdsEnricher ret = null; + RangerAuthContext authContext = getCurrentRangerAuthContext(); + + if (authContext != null) { + Map contextEnricherMap = authContext.getRequestContextEnrichers(); + + if (MapUtils.isNotEmpty(contextEnricherMap)) { + Set contextEnrichers = contextEnricherMap.keySet(); + + for (RangerContextEnricher enricher : contextEnrichers) { + if (enricher instanceof RangerGdsEnricher) { + ret = (RangerGdsEnricher) enricher; + + break; + } + } + } + } + + return ret; + } + + public GdsPolicyEngine getGdsPolicyEngine() { + RangerGdsEnricher gdsEnricher = getGdsEnricher(); + + return gdsEnricher != null ? gdsEnricher.getGdsPolicyEngine() : null; + } + + public static RangerResourceACLs getMergedResourceACLs(RangerResourceACLs baseACLs, RangerResourceACLs chainedACLs) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerBasePlugin.getMergedResourceACLs()"); + LOG.debug("baseACLs:[" + baseACLs + "]"); + LOG.debug("chainedACLS:[" + chainedACLs + "]"); + } + + overrideACLs(chainedACLs, baseACLs, RangerRolesUtil.ROLES_FOR.USER); + overrideACLs(chainedACLs, baseACLs, RangerRolesUtil.ROLES_FOR.GROUP); + overrideACLs(chainedACLs, baseACLs, RangerRolesUtil.ROLES_FOR.ROLE); + baseACLs.getDatasets().addAll(chainedACLs.getDatasets()); + baseACLs.getProjects().addAll(chainedACLs.getProjects()); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerBasePlugin.getMergedResourceACLs() : ret:[" + baseACLs + "]"); + } + return baseACLs; + } + + protected RangerPolicyEngine getPolicyEngine() { + return policyEngine; + } + private RangerAdminClient getAdminClient() throws Exception { PolicyRefresher refresher = this.refresher; RangerAdminClient admin = refresher == null ? null : refresher.getRangerAdminClient(); @@ -750,6 +1255,154 @@ private RangerAdminClient getAdminClient() throws Exception { return admin; } + private List initChainedPlugins() { + List ret = new ArrayList<>(); + String chainedServicePropPrefix = pluginConfig.getPropertyPrefix() + ".chained.services"; + + for (String chainedService : StringUtil.toList(pluginConfig.get(chainedServicePropPrefix))) { + if (StringUtils.isBlank(chainedService)) { + continue; + } + + String className = pluginConfig.get(chainedServicePropPrefix + "." + chainedService + ".impl"); + + if (StringUtils.isBlank(className)) { + LOG.error("Ignoring chained service " + chainedService + ": no impl class specified"); + + continue; + } + + try { + @SuppressWarnings("unchecked") + Class pluginClass = (Class) Class.forName(className); + RangerChainedPlugin chainedPlugin = pluginClass.getConstructor(RangerBasePlugin.class, String.class).newInstance(this, chainedService); + + ret.add(chainedPlugin); + } catch (Throwable t) { + LOG.error("initChainedPlugins(): error instantiating plugin impl " + className, t); + } + } + + return ret; + } + + private void updateResultFromChainedResult(RangerAccessResult result, RangerAccessResult chainedResult) { + boolean overrideResult = false; + int policyType = result.getPolicyType(); + + if (chainedResult.getIsAccessDetermined()) { // only if chained-result is definitive + // override if chained-result is by a higher priority policy or result is not definitive or the result is not-allowed and no matching Ranger policy found + overrideResult = chainedResult.getPolicyPriority() > result.getPolicyPriority() || !result.getIsAccessDetermined() || (!result.getIsAllowed() && result.getPolicyId() == -1L); + + if (!overrideResult) { + // override if chained-result is from the same policy priority, and if denies access with a specific policy id + if (chainedResult.getPolicyPriority() == result.getPolicyPriority() && (!chainedResult.getIsAllowed() && chainedResult.getPolicyId() != -1L)) { + // let's not override if result is already denied + if (result.getIsAllowed()) { + overrideResult = true; + } + } + } + } + + if (overrideResult) { + result.setIsAllowed(chainedResult.getIsAllowed()); + result.setIsAccessDetermined(chainedResult.getIsAccessDetermined()); + result.setPolicyId(chainedResult.getPolicyId()); + result.setPolicyVersion(chainedResult.getPolicyVersion()); + result.setPolicyPriority(chainedResult.getPolicyPriority()); + result.setZoneName(chainedResult.getZoneName()); + + if (policyType == RangerPolicy.POLICY_TYPE_DATAMASK) { + result.setMaskType(chainedResult.getMaskType()); + result.setMaskCondition(chainedResult.getMaskCondition()); + result.setMaskedValue(chainedResult.getMaskedValue()); + } else if (policyType == RangerPolicy.POLICY_TYPE_ROWFILTER) { + result.setFilterExpr(chainedResult.getFilterExpr()); + } + } + + if (!result.getIsAuditedDetermined() && chainedResult.getIsAuditedDetermined()) { + result.setIsAudited(chainedResult.getIsAudited()); + result.setAuditPolicyId(chainedResult.getAuditPolicyId()); + } + } + + private static void overrideACLs(final RangerResourceACLs chainedResourceACLs, RangerResourceACLs baseResourceACLs, final RangerRolesUtil.ROLES_FOR userType) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerBasePlugin.overrideACLs(isUser=" + userType.name() + ")"); + } + Map> chainedACLs = null; + Map> baseACLs = null; + + switch (userType) { + case USER: + chainedACLs = chainedResourceACLs.getUserACLs(); + baseACLs = baseResourceACLs.getUserACLs(); + break; + case GROUP: + chainedACLs = chainedResourceACLs.getGroupACLs(); + baseACLs = baseResourceACLs.getGroupACLs(); + break; + case ROLE: + chainedACLs = chainedResourceACLs.getRoleACLs(); + baseACLs = baseResourceACLs.getRoleACLs(); + break; + default: + break; + } + + for (Map.Entry> chainedPermissionsMap : chainedACLs.entrySet()) { + String name = chainedPermissionsMap.getKey(); + Map chainedPermissions = chainedPermissionsMap.getValue(); + Map basePermissions = baseACLs.get(name); + + for (Map.Entry chainedPermission : chainedPermissions.entrySet()) { + String chainedAccessType = chainedPermission.getKey(); + RangerResourceACLs.AccessResult chainedAccessResult = chainedPermission.getValue(); + RangerResourceACLs.AccessResult baseAccessResult = basePermissions == null ? null : basePermissions.get(chainedAccessType); + + final boolean useChainedAccessResult; + + if (baseAccessResult == null) { + useChainedAccessResult = true; + } else { + if (chainedAccessResult.getPolicy().getPolicyPriority() > baseAccessResult.getPolicy().getPolicyPriority()) { + useChainedAccessResult = true; + } else if (chainedAccessResult.getPolicy().getPolicyPriority().equals(baseAccessResult.getPolicy().getPolicyPriority())) { + if (chainedAccessResult.getResult() == baseAccessResult.getResult()) { + useChainedAccessResult = true; + } else { + useChainedAccessResult = chainedAccessResult.getResult() == RangerPolicyEvaluator.ACCESS_DENIED; + } + } else { // chainedAccessResult.getPolicy().getPolicyPriority() < baseAccessResult.getPolicy().getPolicyPriority() + useChainedAccessResult = false; + } + } + + final RangerResourceACLs.AccessResult finalAccessResult = useChainedAccessResult ? chainedAccessResult : baseAccessResult; + + switch (userType) { + case USER: + baseResourceACLs.setUserAccessInfo(name, chainedAccessType, finalAccessResult.getResult(), finalAccessResult.getPolicy()); + break; + case GROUP: + baseResourceACLs.setGroupAccessInfo(name, chainedAccessType, finalAccessResult.getResult(), finalAccessResult.getPolicy()); + break; + case ROLE: + baseResourceACLs.setRoleAccessInfo(name, chainedAccessType, finalAccessResult.getResult(), finalAccessResult.getPolicy()); + break; + default: + break; + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerBasePlugin.mergeACLsOneWay(isUser=" + userType.name() + ")"); + } + } + private static AuditProviderFactory getAuditProviderFactory(String serviceName) { AuditProviderFactory ret = AuditProviderFactory.getInstance(); @@ -769,4 +1422,8 @@ private static AuditProviderFactory getAuditProviderFactory(String serviceName) return ret; } + + public Long getPolicyVersion() { + return this.policyEngine == null ? -1L : this.policyEngine.getPolicyVersion(); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBaseService.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBaseService.java index 65c34e857d..36febb3411 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBaseService.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerBaseService.java @@ -32,8 +32,6 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.security.SecureClientLogin; import org.apache.hadoop.security.authentication.util.KerberosName; import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig; @@ -46,10 +44,12 @@ import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class RangerBaseService { - private static final Log LOG = LogFactory.getLog(RangerBaseService.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerBaseService.class); protected static final String ADMIN_USER_PRINCIPAL = "ranger.admin.kerberos.principal"; protected static final String ADMIN_USER_KEYTAB = "ranger.admin.kerberos.keytab"; @@ -59,9 +59,13 @@ public abstract class RangerBaseService { protected static final String KERBEROS_TYPE = "kerberos"; + protected static final String CONFIG_CREATE_DEFAULT_POLICY_PER_HIERARCHY = "create.default.policy.per.hierarchy"; + private static final String PROP_DEFAULT_POLICY_PREFIX = "default-policy."; private static final String PROP_DEFAULT_POLICY_NAME_SUFFIX = "name"; + private static final String PROP_RESOURCE_FLAG_SUFFIX_IS_EXCLUDES = ".is-excludes"; + private static final String PROP_RESOURCE_FLAG_SUFFIX_IS_RECURSIVE = ".is-recursive"; protected RangerServiceDef serviceDef; protected RangerService service; @@ -140,17 +144,19 @@ public List getDefaultRangerPolicies() throws Exception { List ret = new ArrayList(); - try { - // we need to create one policy for each resource hierarchy - RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef); - for (List aHierarchy : serviceDefHelper.filterHierarchies_containsOnlyMandatoryResources(RangerPolicy.POLICY_TYPE_ACCESS)) { - RangerPolicy policy = getDefaultPolicy(aHierarchy); - if (policy != null) { - ret.add(policy); + if (createDefaultPolicyPerHierarchy()) { + try { + // we need to create one policy for each resource hierarchy + RangerServiceDefHelper serviceDefHelper = new RangerServiceDefHelper(serviceDef); + for (List aHierarchy : serviceDefHelper.filterHierarchies_containsOnlyMandatoryResources(RangerPolicy.POLICY_TYPE_ACCESS)) { + RangerPolicy policy = getDefaultPolicy(aHierarchy); + if (policy != null) { + ret.add(policy); + } } + } catch (Exception e) { + LOG.error("Error getting default polcies for Service: " + service.getName(), e); } - } catch (Exception e) { - LOG.error("Error getting default polcies for Service: " + service.getName(), e); } final Boolean additionalDefaultPolicySetup = Boolean.valueOf(configs.get("setup.additional.default.policies")); @@ -196,13 +202,19 @@ private Map getResourcesForPrefix(String resourceP String configName = entry.getKey(); String configValue = entry.getValue(); - if(configName.startsWith(resourcePropertyPrefix) && StringUtils.isNotBlank(configValue)){ + if (configName.endsWith(PROP_RESOURCE_FLAG_SUFFIX_IS_EXCLUDES) || configName.endsWith(PROP_RESOURCE_FLAG_SUFFIX_IS_RECURSIVE)) { + continue; + } + + if (configName.startsWith(resourcePropertyPrefix) && StringUtils.isNotBlank(configValue)) { RangerPolicyResource rPolRes = new RangerPolicyResource(); String resourceKey = configName.substring(resourcePropertyPrefix.length()); List resourceList = new ArrayList(Arrays.asList(configValue.split(","))); + boolean isExcludes = Boolean.parseBoolean(configs.getOrDefault(configName + PROP_RESOURCE_FLAG_SUFFIX_IS_EXCLUDES, "false")); + boolean isRecursive = Boolean.parseBoolean(configs.getOrDefault(configName + PROP_RESOURCE_FLAG_SUFFIX_IS_RECURSIVE, "false")); - rPolRes.setIsExcludes(false); - rPolRes.setIsRecursive(false); + rPolRes.setIsExcludes(isExcludes); + rPolRes.setIsRecursive(isRecursive); rPolRes.setValues(resourceList); policyResourceMap.put(resourceKey, rPolRes); } @@ -215,6 +227,7 @@ private Map getResourcesForPrefix(String resourceP private void addCustomRangerDefaultPolicies(List ret, Map policyResourceMap, String policyPropertyPrefix) throws Exception { String policyName = configs.get(policyPropertyPrefix + PROP_DEFAULT_POLICY_NAME_SUFFIX); String description = configs.get(policyPropertyPrefix + "description"); + Boolean isDenyAllElse = Boolean.valueOf(configs.get(policyPropertyPrefix + "isDenyAllElse")); if (StringUtils.isEmpty(description)) { description = "Policy for " + policyName; @@ -230,6 +243,7 @@ private void addCustomRangerDefaultPolicies(List ret, Map ret, Map RangerChainedPlugin.init(" + serviceType + ", " + serviceName + ")"); + + this.plugin.init(); + + LOG.info("<== RangerChainedPlugin.init(" + serviceType + ", " + serviceName + ")"); + } + + protected RangerBasePlugin buildChainedPlugin(String serviceType, String serviceName, String appId) { + return new RangerBasePlugin(serviceType, serviceName, appId); + } + + public abstract RangerAccessResult isAccessAllowed(RangerAccessRequest request); + + public abstract Collection isAccessAllowed(Collection requests); + + public abstract RangerResourceACLs getResourceACLs(RangerAccessRequest request); + + public abstract RangerResourceACLs getResourceACLs(RangerAccessRequest request, Integer policyType); + + public boolean isAuthorizeOnlyWithChainedPlugin() { return false; } + + public RangerAccessResult evalDataMaskPolicies(RangerAccessRequest request) { + return null; // no-op + } + + public RangerAccessResult evalRowFilterPolicies(RangerAccessRequest request) { + return null; // no-op + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultRequestProcessor.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultRequestProcessor.java index facf05d434..3265f1011d 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultRequestProcessor.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultRequestProcessor.java @@ -21,6 +21,7 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; import org.apache.ranger.plugin.policyengine.PolicyEngine; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; @@ -28,38 +29,83 @@ import org.apache.ranger.plugin.policyengine.RangerAccessRequestProcessor; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyengine.RangerMutableResource; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerPerfTracer; +import org.apache.ranger.plugin.util.RangerUserStoreUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Set; public class RangerDefaultRequestProcessor implements RangerAccessRequestProcessor { + private static final Logger PERF_CONTEXTENRICHER_REQUEST_LOG = RangerPerfTracer.getPerfLogger("contextenricher.request"); + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultRequestProcessor.class); + protected final PolicyEngine policyEngine; + private final boolean useRangerGroups; + private final boolean useOnlyRangerGroups; + private final boolean convertEmailToUser; public RangerDefaultRequestProcessor(PolicyEngine policyEngine) { this.policyEngine = policyEngine; + + RangerPluginContext pluginContext = policyEngine.getPluginContext(); + RangerPluginConfig pluginConfig = pluginContext != null ? pluginContext.getConfig() : null; + + if (pluginConfig != null) { + useRangerGroups = pluginConfig.isUseRangerGroups(); + useOnlyRangerGroups = pluginConfig.isUseOnlyRangerGroups(); + convertEmailToUser = pluginConfig.isConvertEmailToUsername(); + } else { + useRangerGroups = false; + useOnlyRangerGroups = false; + convertEmailToUser = false; + } } @Override public void preProcess(RangerAccessRequest request) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> preProcess(" + request + ")"); + } + + if (RangerAccessRequestUtil.getIsRequestPreprocessed(request.getContext())) { + if (LOG.isDebugEnabled()) { + LOG.debug("<== preProcess(" + request + ")"); + } + return; + } + setResourceServiceDef(request); + + RangerPluginContext pluginContext = policyEngine.getPluginContext(); + RangerAccessRequestImpl reqImpl = null; + if (request instanceof RangerAccessRequestImpl) { - RangerAccessRequestImpl reqImpl = (RangerAccessRequestImpl) request; + reqImpl = (RangerAccessRequestImpl) request; if (reqImpl.getClientIPAddress() == null) { reqImpl.extractAndSetClientIPAddress(policyEngine.getUseForwardedIPAddress(), policyEngine.getTrustedProxyAddresses()); } - if(policyEngine.getPluginContext() != null) { + if (pluginContext != null) { if (reqImpl.getClusterName() == null) { - reqImpl.setClusterName(policyEngine.getPluginContext().getClusterName()); + reqImpl.setClusterName(pluginContext.getClusterName()); } if (reqImpl.getClusterType() == null) { - reqImpl.setClusterType(policyEngine.getPluginContext().getClusterType()); + reqImpl.setClusterType(pluginContext.getClusterType()); } + + convertEmailToUsername(reqImpl); + + updateUserGroups(reqImpl); } } @@ -72,15 +118,29 @@ public void preProcess(RangerAccessRequest request) { } Set roles = request.getUserRoles(); - if (CollectionUtils.isEmpty(roles)) { - roles = policyEngine.getPluginContext().getAuthContext().getRolesForUserAndGroups(request.getUser(), request.getUserGroups()); + if (pluginContext != null && CollectionUtils.isEmpty(roles)) { + roles = pluginContext.getAuthContext().getRolesForUserAndGroups(request.getUser(), request.getUserGroups()); + + if (reqImpl != null && roles != null && !roles.isEmpty()) { + reqImpl.setUserRoles(roles); + } } if (CollectionUtils.isNotEmpty(roles)) { RangerAccessRequestUtil.setCurrentUserRolesInContext(request.getContext(), roles); } + Set zoneNames = policyEngine.getMatchedZonesForResourceAndChildren(request.getResource()); + + RangerAccessRequestUtil.setResourceZoneNamesInContext(request, zoneNames); + enrich(request); + + RangerAccessRequestUtil.setIsRequestPreprocessed(request.getContext(), Boolean.TRUE); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== preProcess(" + request + ")"); + } } @Override @@ -89,7 +149,19 @@ public void enrich(RangerAccessRequest request) { if (!CollectionUtils.isEmpty(enrichers)) { for(RangerContextEnricher enricher : enrichers) { + RangerPerfTracer perf = null; + + if(RangerPerfTracer.isPerfTraceEnabled(PERF_CONTEXTENRICHER_REQUEST_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_CONTEXTENRICHER_REQUEST_LOG, "RangerContextEnricher.enrich(requestHashCode=" + Integer.toHexString(System.identityHashCode(request)) + ", enricherName=" + enricher.getName() + ")"); + } + enricher.enrich(request); + + RangerPerfTracer.log(perf); + } + } else { + if (LOG.isDebugEnabled()){ + LOG.debug("No context-enrichers!!!"); } } } @@ -105,4 +177,62 @@ private void setResourceServiceDef(RangerAccessRequest request) { } } + private void convertEmailToUsername(RangerAccessRequestImpl reqImpl) { + if (convertEmailToUser) { + RangerPluginContext pluginContext = policyEngine.getPluginContext(); + RangerUserStoreUtil userStoreUtil = pluginContext != null ? pluginContext.getAuthContext().getUserStoreUtil() : null; + + if (userStoreUtil != null) { + String userName = reqImpl.getUser(); + int idxSep = StringUtils.indexOf(userName, '@'); + + if (idxSep > 0) { + String userNameFromEmail = userStoreUtil.getUserNameFromEmail(userName); + + if (StringUtils.isBlank(userNameFromEmail)) { + userNameFromEmail = userName.substring(0, idxSep); + } + + LOG.debug("replacing req.user '{}' with '{}'", userName, userNameFromEmail); + + reqImpl.setUser(userNameFromEmail); + } + } + } + } + + private void updateUserGroups(RangerAccessRequestImpl reqImpl) { + if (useRangerGroups) { + RangerPluginContext pluginContext = policyEngine.getPluginContext(); + RangerUserStoreUtil userStoreUtil = pluginContext != null ? pluginContext.getAuthContext().getUserStoreUtil() : null; + String userName = reqImpl.getUser(); + + if (userStoreUtil != null && userName != null) { + Set userGroups = reqImpl.getUserGroups(); + Set rangerUserGroups = userStoreUtil.getUserGroups(userName); + + if (rangerUserGroups == null) { + rangerUserGroups = Collections.emptySet(); + } + + if (useOnlyRangerGroups) { + userGroups = new HashSet<>(rangerUserGroups); + + LOG.debug("replacing req.userGroups '{}' with '{}'", reqImpl.getUserGroups(), userGroups); + + reqImpl.setUserGroups(userGroups); + } else { + if (!rangerUserGroups.isEmpty()) { + userGroups = userGroups != null ? new HashSet<>(userGroups) : new HashSet<>(); + + userGroups.addAll(rangerUserGroups); + + LOG.debug("replacing req.userGroups '{}' with '{}'", reqImpl.getUserGroups(), userGroups); + + reqImpl.setUserGroups(userGroups); + } + } + } + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultService.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultService.java old mode 100644 new mode 100755 index c61ebd6deb..60f7a09df6 --- a/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultService.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/RangerDefaultService.java @@ -23,19 +23,21 @@ import java.util.Map; import org.apache.commons.collections.ListUtils; -import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerDefaultService extends RangerBaseService { - private static final Log LOG = LogFactory.getLog(RangerDefaultService.class); - + private static final Logger LOG = LoggerFactory.getLogger(RangerDefaultService.class); + + public static final String ERROR_MSG_VALIDATE_CONFIG_NOT_IMPLEMENTED = "Configuration validation is not implemented"; + @Override public Map validateConfig() throws Exception { - if(LOG.isDebugEnabled()) { - LOG.debug("RangerDefaultService.validateConfig Service: (" + serviceName + " ), returning empty map"); + if (LOG.isDebugEnabled()) { + LOG.debug("RangerDefaultService.validateConfig Service: (" + serviceName + " ), " + ERROR_MSG_VALIDATE_CONFIG_NOT_IMPLEMENTED); } - return MapUtils.EMPTY_MAP; + + throw new Exception(ERROR_MSG_VALIDATE_CONFIG_NOT_IMPLEMENTED); } @Override diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/service/ResourceLookupContext.java b/agents-common/src/main/java/org/apache/ranger/plugin/service/ResourceLookupContext.java index 25b5521837..82a20eae7c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/service/ResourceLookupContext.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/service/ResourceLookupContext.java @@ -22,20 +22,14 @@ import java.util.List; import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(getterVisibility=Visibility.NONE, setterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL ) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class ResourceLookupContext { private String userInput; private String resourceName; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractGdsStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractGdsStore.java new file mode 100644 index 0000000000..fd806c7468 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractGdsStore.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.store; + +import org.apache.ranger.plugin.model.RangerGds.RangerDataset; +import org.apache.ranger.plugin.model.RangerGds.RangerDatasetInProject; +import org.apache.ranger.plugin.model.RangerGds.RangerDataShare; +import org.apache.ranger.plugin.model.RangerGds.RangerDataShareInDataset; +import org.apache.ranger.plugin.model.RangerGds.RangerProject; +import org.apache.ranger.plugin.model.RangerGds.RangerSharedResource; +import org.apache.ranger.plugin.util.SearchFilter; + +import java.util.List; + +public abstract class AbstractGdsStore implements GdsStore { + @Override + public RangerDataset createDataset(RangerDataset dataset) throws Exception { return null; } + + @Override + public RangerDataset updateDataset(RangerDataset dataset) throws Exception { return null; } + + @Override + public void deleteDataset(Long datasetId, boolean forceDelete) throws Exception { } + + @Override + public RangerDataset getDataset(Long datasetId) throws Exception { return null; } + + @Override + public RangerDataset getDatasetByName(String name) throws Exception { return null; } + + @Override + public PList getDatasetNames(SearchFilter filter) throws Exception { return null; } + + @Override + public PList searchDatasets(SearchFilter filter) throws Exception { return null; } + + + @Override + public RangerProject createProject(RangerProject dataset) throws Exception { return null; } + + @Override + public RangerProject updateProject(RangerProject dataset) throws Exception { return null; } + + @Override + public void deleteProject(Long datasetId, boolean forceDelete) throws Exception { } + + @Override + public RangerProject getProject(Long projectId) throws Exception { return null; } + + @Override + public RangerProject getProjectByName(String name) throws Exception { return null; } + + @Override + public PList getProjectNames(SearchFilter filter) throws Exception { return null; } + + @Override + public PList searchProjects(SearchFilter filter) throws Exception { return null; } + + + @Override + public RangerDataShare createDataShare(RangerDataShare dataShare) throws Exception { return null; } + + @Override + public RangerDataShare updateDataShare(RangerDataShare dataShare) throws Exception { return null; } + + @Override + public void deleteDataShare(Long dataShareId, boolean forceDelete) throws Exception { } + + @Override + public RangerDataShare getDataShare(Long dataShareId) throws Exception { return null; } + + @Override + public PList searchDataShares(SearchFilter filter) throws Exception { return null; } + + + @Override + public List addSharedResources(List resources) throws Exception { return null; } + + @Override + public RangerSharedResource updateSharedResource(RangerSharedResource resource) throws Exception { return null; } + + @Override + public void removeSharedResources(List sharedResourceIds) throws Exception { } + + @Override + public RangerSharedResource getSharedResource(Long sharedResourceId) throws Exception { return null; } + + @Override + public PList searchSharedResources(SearchFilter filter) throws Exception { return null; } + + + @Override + public RangerDataShareInDataset addDataShareInDataset(RangerDataShareInDataset dataShareInDataset) throws Exception { return null; } + + @Override + public RangerDataShareInDataset updateDataShareInDataset(RangerDataShareInDataset dataShareInDataset) throws Exception { return null; } + + @Override + public void removeDataShareInDataset(Long dataShareInDatasetId) throws Exception { } + + @Override + public RangerDataShareInDataset getDataShareInDataset(Long dataShareInDatasetId) throws Exception { return null; } + + @Override + public PList searchDataShareInDatasets(SearchFilter filter) throws Exception { return null; } + + + @Override + public RangerDatasetInProject addDatasetInProject(RangerDatasetInProject datasetInProject) throws Exception { return null; } + + @Override + public RangerDatasetInProject updateDatasetInProject(RangerDatasetInProject datasetInProject) throws Exception { return null; } + + @Override + public void removeDatasetInProject(Long datasetInProjectId) throws Exception { } + + @Override public RangerDatasetInProject getDatasetInProject(Long datasetInProjectId) throws Exception { return null; } + + @Override + public PList searchDatasetInProjects(SearchFilter filter) throws Exception { return null; } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java index 85fa213831..39aa155138 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractPredicateUtil.java @@ -59,9 +59,12 @@ public void applyFilter(List objList, SearchFil } Comparator sorter = getSorter(filter); + boolean isDesc = (filter.getSortType() != null) && "desc".equalsIgnoreCase(filter.getSortType()); - if(sorter != null) { - Collections.sort(objList, sorter); + if (sorter != null){ + Collections.sort(objList, isDesc ? new ReverseComparator(sorter) : sorter); + }else if(isDesc) { + Collections.reverse(objList); } } @@ -92,6 +95,7 @@ public void addPredicates(SearchFilter filter, List predicates) { // addPredicateForTagServiceId(filter.getParam(SearchFilter.TAG_SERVICE_ID), predicates); // not supported addPredicateForUserName(filter.getParam(SearchFilter.USER), predicates); addPredicateForGroupName(filter.getParam(SearchFilter.GROUP), predicates); + addPredicateForRoleName(filter.getParam(SearchFilter.ROLE), predicates); addPredicateForResources(filter.getParamsWithPrefix(SearchFilter.RESOURCE_PREFIX, true), predicates); addPredicateForPolicyResource(filter.getParam(SearchFilter.POL_RESOURCE), predicates); addPredicateForPartialPolicyName(filter.getParam(SearchFilter.POLICY_NAME_PARTIAL), predicates); @@ -231,7 +235,6 @@ public int compare(RangerBaseModelObject o1, RangerBaseModelObject o2) { sorterMap.put(SearchFilter.SERVICE_TYPE, serviceDefNameComparator); sorterMap.put(SearchFilter.SERVICE_TYPE_ID, idComparator); sorterMap.put(SearchFilter.SERVICE_NAME, serviceNameComparator); - sorterMap.put(SearchFilter.SERVICE_TYPE_ID, idComparator); sorterMap.put(SearchFilter.POLICY_NAME, policyNameComparator); sorterMap.put(SearchFilter.POLICY_ID, idComparator); sorterMap.put(SearchFilter.CREATE_TIME, createTimeComparator); @@ -564,6 +567,64 @@ public boolean evaluate(Object object) { return ret; } + private Predicate addPredicateForRoleName(final String roleName, List predicates) { + if(StringUtils.isEmpty(roleName)) { + return null; + } + + Predicate ret = new Predicate() { + @Override + public boolean evaluate(Object object) { + if(object == null) { + return false; + } + + boolean ret = false; + + if(object instanceof RangerPolicy) { + RangerPolicy policy = (RangerPolicy)object; + + List[] policyItemsList = new List[] { policy.getPolicyItems(), + policy.getDenyPolicyItems(), + policy.getAllowExceptions(), + policy.getDenyExceptions(), + policy.getDataMaskPolicyItems(), + policy.getRowFilterPolicyItems() + }; + for(List policyItemsObj : policyItemsList) { + @SuppressWarnings("unchecked") + List policyItems = (List)policyItemsObj; + + for(RangerPolicyItem policyItem : policyItems) { + if(! policyItem.getRoles().isEmpty()) { + for(String role : policyItem.getRoles()) { + if(StringUtils.containsIgnoreCase(role, roleName)) { + ret = true; + break; + } + } + } + } + if (ret) { + break; + } + } + }else { + ret = true; + } + + return ret; + } + }; + + if(predicates != null) { + predicates.add(ret); + } + + return ret; + + } + private Predicate addPredicateForIsEnabled(final String status, List predicates) { if(StringUtils.isEmpty(status)) { return null; @@ -991,4 +1052,17 @@ public boolean evaluate(Object object) { return ret; } + + private static class ReverseComparator implements Comparator { + private final Comparator comparator; + + ReverseComparator(Comparator comparator) { + this.comparator = comparator; + } + + @Override + public int compare(RangerBaseModelObject o1, RangerBaseModelObject o2) { + return comparator.compare(o2, o1); + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractServiceStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractServiceStore.java index e9d199bfbd..6863e48c92 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractServiceStore.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/AbstractServiceStore.java @@ -21,15 +21,16 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig; import org.apache.ranger.plugin.model.RangerBaseModelObject; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerService; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.util.SearchFilter; +import org.apache.ranger.plugin.util.ServiceDefUtil; import org.apache.ranger.services.tag.RangerServiceTag; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; @@ -38,7 +39,7 @@ import java.util.Objects; public abstract class AbstractServiceStore implements ServiceStore { - private static final Log LOG = LogFactory.getLog(AbstractServiceStore.class); + private static final Logger LOG = LoggerFactory.getLogger(AbstractServiceStore.class); public static final String COMPONENT_ACCESSTYPE_SEPARATOR = ":"; @@ -65,7 +66,9 @@ public void updateTagServiceDefForAccessTypes() throws Exception { } List allServiceDefs = getServiceDefs(new SearchFilter()); for (RangerServiceDef serviceDef : allServiceDefs) { - updateTagServiceDefForUpdatingAccessTypes(serviceDef); + if (ServiceDefUtil.getOption_enableTagBasedPolicies(serviceDef, config)) { + updateTagServiceDefForUpdatingAccessTypes(serviceDef); + } } if (LOG.isDebugEnabled()) { LOG.debug("<== ServiceDefDBStore.updateTagServiceDefForAccessTypes()"); @@ -223,6 +226,7 @@ private boolean updateTagAccessTypeDefs(List()); if (CollectionUtils.isNotEmpty(svcAccessType.getImpliedGrants())) { @@ -275,7 +279,8 @@ private boolean updateTagAccessTypeDefs(List supportedServiceDefs; public static final String EMBEDDED_SERVICEDEF_TAG_NAME = "tag"; + public static final String EMBEDDED_SERVICEDEF_GDS_NAME = "gds"; public static final String EMBEDDED_SERVICEDEF_HDFS_NAME = "hdfs"; public static final String EMBEDDED_SERVICEDEF_HBASE_NAME = "hbase"; public static final String EMBEDDED_SERVICEDEF_HIVE_NAME = "hive"; @@ -72,8 +74,10 @@ public class EmbeddedServiceDefsUtil { public static final String EMBEDDED_SERVICEDEF_ABFS_NAME = "abfs"; public static final String EMBEDDED_SERVICEDEF_ELASTICSEARCH_NAME = "elasticsearch"; public static final String EMBEDDED_SERVICEDEF_PRESTO_NAME = "presto"; + public static final String EMBEDDED_SERVICEDEF_TRINO_NAME = "trino"; public static final String EMBEDDED_SERVICEDEF_OZONE_NAME = "ozone"; public static final String EMBEDDED_SERVICEDEF_KUDU_NAME = "kudu"; + public static final String EMBEDDED_SERVICEDEF_NESTEDSTRUCTURE_NAME = "nestedstructure"; public static final String PROPERTY_CREATE_EMBEDDED_SERVICE_DEFS = "ranger.service.store.create.embedded.service-defs"; @@ -90,6 +94,7 @@ public class EmbeddedServiceDefsUtil { public static final String NIFI_IMPL_CLASS_NAME = "org.apache.ranger.services.nifi.RangerServiceNiFi"; public static final String ATLAS_IMPL_CLASS_NAME = "org.apache.ranger.services.atlas.RangerServiceAtlas"; public static final String PRESTO_IMPL_CLASS_NAME = "org.apache.ranger.services.presto.RangerServicePresto"; + public static final String TRINO_IMPL_CLASS_NAME = "org.apache.ranger.services.trino.RangerServiceTrino"; public static final String OZONE_IMPL_CLASS_NAME = "org.apache.ranger.services.ozone.RangerServiceOzone"; public static final String KUDU_IMPL_CLASS_NAME = "org.apache.ranger.services.kudu.RangerServiceKudu"; @@ -115,17 +120,18 @@ public class EmbeddedServiceDefsUtil { private RangerServiceDef abfsServiceDef; private RangerServiceDef elasticsearchServiceDef; private RangerServiceDef prestoServiceDef; + private RangerServiceDef trinoServiceDef; private RangerServiceDef ozoneServiceDef; private RangerServiceDef kuduServiceDef; + private RangerServiceDef nestedStructureServiveDef; private RangerServiceDef tagServiceDef; + private RangerServiceDef gdsServiceDef; - private final Gson gsonBuilder; private final RangerAdminConfig config; /** Private constructor to restrict instantiation of this singleton utility class. */ private EmbeddedServiceDefsUtil() { - gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").setPrettyPrinting().create(); config = RangerAdminConfig.getInstance(); } @@ -157,21 +163,26 @@ public void init(ServiceStore store) { nifiServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_NIFI_NAME); nifiRegistryServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_NIFI_REGISTRY_NAME); atlasServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_ATLAS_NAME); - - tagServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_TAG_NAME); wasbServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_WASB_NAME); sqoopServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_SQOOP_NAME); kylinServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_KYLIN_NAME); abfsServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_ABFS_NAME); elasticsearchServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_ELASTICSEARCH_NAME); + trinoServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_TRINO_NAME); prestoServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_PRESTO_NAME); ozoneServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_OZONE_NAME); kuduServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_KUDU_NAME); + nestedStructureServiveDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_NESTEDSTRUCTURE_NAME); + + tagServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_TAG_NAME); + gdsServiceDef = getOrCreateServiceDef(store, EMBEDDED_SERVICEDEF_GDS_NAME); // Ensure that tag service def is updated with access types of all service defs store.updateTagServiceDefForAccessTypes(); + + getOrCreateService(store, EMBEDDED_SERVICEDEF_GDS_NAME, GdsPolicyEngine.GDS_SERVICE_NAME); } catch(Throwable excp) { - LOG.fatal("EmbeddedServiceDefsUtil.init(): failed", excp); + LOG.error("EmbeddedServiceDefsUtil.init(): failed", excp); } LOG.info("<== EmbeddedServiceDefsUtil.init()"); @@ -240,18 +251,25 @@ public long getKylinServiceDefId() { public long getElasticsearchServiceDefId() { return getId(elasticsearchServiceDef); } - public long getTagServiceDefId() { return getId(tagServiceDef); } public long getWasbServiceDefId() { return getId(wasbServiceDef); } public long getAbfsServiceDefId() { return getId(abfsServiceDef); } + public long getTrinoServiceDefId() { return getId(trinoServiceDef); } + public long getPrestoServiceDefId() { return getId(prestoServiceDef); } public long getOzoneServiceDefId() { return getId(ozoneServiceDef); } public long getKuduServiceDefId() { return getId(kuduServiceDef); } + public long getNestedStructureServiceDefId() { return getId(nestedStructureServiveDef); } + + public long getTagServiceDefId() { return getId(tagServiceDef); } + + public long getGdsServiceDefId() { return getId(gdsServiceDef); } + public RangerServiceDef getEmbeddedServiceDef(String defType) throws Exception { RangerServiceDef serviceDef=null; if(StringUtils.isNotEmpty(defType)){ @@ -302,7 +320,7 @@ private RangerServiceDef getOrCreateServiceDef(ServiceStore store, String servic LOG.info("created embedded service-def " + serviceDefName); } } catch(Exception excp) { - LOG.fatal("EmbeddedServiceDefsUtil.getOrCreateServiceDef(): failed to load/create serviceType " + serviceDefName, excp); + LOG.error("EmbeddedServiceDefsUtil.getOrCreateServiceDef(): failed to load/create serviceType " + serviceDefName, excp); } if(LOG.isDebugEnabled()) { @@ -325,7 +343,7 @@ private RangerServiceDef loadEmbeddedServiceDef(String serviceType) throws Excep InputStreamReader reader = new InputStreamReader(inStream); - ret = gsonBuilder.fromJson(reader, RangerServiceDef.class); + ret = JsonUtils.jsonToObject(reader, RangerServiceDef.class); //Set DEFAULT displayName if missing if (ret != null && StringUtils.isBlank(ret.getDisplayName())) { @@ -360,4 +378,38 @@ private Set getSupportedServiceDef(){ } return supportedServiceDef; } + + private RangerService getOrCreateService(ServiceStore store, String serviceType, String serviceName) { + if(LOG.isDebugEnabled()) { + LOG.debug("==> EmbeddedServiceDefsUtil.getOrCreateService(" + serviceType + ", " + serviceName + ")"); + } + + RangerService ret = null; + + try { + ret = store.getServiceByName(serviceName); + + if(ret == null) { + LOG.info("Creating service " + serviceName + " of type " + serviceType); + + ret = new RangerService(); + + ret.setName(serviceName); + ret.setDisplayName(serviceName); + ret.setType(serviceType); + + ret = store.createService(ret); + + LOG.info("Created service " + serviceName + ". ID=" + (ret != null ? ret.getId() : null)); + } + } catch(Exception excp) { + LOG.error("EmbeddedServiceDefsUtil.getOrCreateService(): failed to load/create service " + serviceName, excp); + } + + if(LOG.isDebugEnabled()) { + LOG.debug("<== EmbeddedServiceDefsUtil.getOrCreateService(" + serviceType + ", " + serviceName + "): " + ret); + } + + return ret; + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/GdsStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/GdsStore.java new file mode 100644 index 0000000000..6bebfd0903 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/GdsStore.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.store; + +import org.apache.ranger.plugin.model.RangerGds.RangerDataShare; +import org.apache.ranger.plugin.model.RangerGds.RangerDataShareInDataset; +import org.apache.ranger.plugin.model.RangerGds.RangerDataset; +import org.apache.ranger.plugin.model.RangerGds.RangerDatasetInProject; +import org.apache.ranger.plugin.model.RangerGds.RangerProject; +import org.apache.ranger.plugin.model.RangerGds.RangerSharedResource; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.util.SearchFilter; + +import java.util.Collection; +import java.util.List; + +/** + * Interface to backing store for Data share model objects + */ + +public interface GdsStore { + RangerDataset createDataset(RangerDataset dataset) throws Exception; + + RangerDataset updateDataset(RangerDataset dataset) throws Exception; + + void deleteDataset(Long datasetId, boolean forceDelete) throws Exception; + + RangerDataset getDataset(Long datasetId) throws Exception; + + RangerDataset getDatasetByName(String name) throws Exception; + + PList getDatasetNames(SearchFilter filter) throws Exception; + + PList searchDatasets(SearchFilter filter) throws Exception; + + RangerPolicy addDatasetPolicy(Long datasetId, RangerPolicy policy) throws Exception; + + RangerPolicy updateDatasetPolicy(Long datasetId, RangerPolicy policy) throws Exception; + + void deleteDatasetPolicy(Long datasetId, Long policyId) throws Exception; + + void deleteDatasetPolicies(Long datasetId) throws Exception; + + RangerPolicy getDatasetPolicy(Long datasetId, Long policyId) throws Exception; + + List getDatasetPolicies(Long datasetId) throws Exception; + + + RangerProject createProject(RangerProject dataset) throws Exception; + + RangerProject updateProject(RangerProject dataset) throws Exception; + + void deleteProject(Long datasetId, boolean forceDelete) throws Exception; + + RangerProject getProject(Long projectId) throws Exception; + + RangerProject getProjectByName(String name) throws Exception; + + PList getProjectNames(SearchFilter filter) throws Exception; + + PList searchProjects(SearchFilter filter) throws Exception; + + RangerPolicy addProjectPolicy(Long projectId, RangerPolicy policy) throws Exception; + + RangerPolicy updateProjectPolicy(Long projectId, RangerPolicy policy) throws Exception; + + void deleteProjectPolicy(Long projectId, Long policyId) throws Exception; + + void deleteProjectPolicies(Long projectId) throws Exception; + + RangerPolicy getProjectPolicy(Long projectId, Long policyId) throws Exception; + + List getProjectPolicies(Long projectId) throws Exception; + + + RangerDataShare createDataShare(RangerDataShare dataShare) throws Exception; + + RangerDataShare updateDataShare(RangerDataShare dataShare) throws Exception; + + void deleteDataShare(Long dataShareId, boolean forceDelete) throws Exception; + + RangerDataShare getDataShare(Long dataShareId) throws Exception; + + PList searchDataShares(SearchFilter filter) throws Exception; + + + List addSharedResources(List resources) throws Exception; + + RangerSharedResource updateSharedResource(RangerSharedResource resource) throws Exception; + + void removeSharedResources(List sharedResourceIds) throws Exception; + + RangerSharedResource getSharedResource(Long sharedResourceId) throws Exception; + + PList searchSharedResources(SearchFilter filter) throws Exception; + + + RangerDataShareInDataset addDataShareInDataset(RangerDataShareInDataset dataShareInDataset) throws Exception; + + RangerDataShareInDataset updateDataShareInDataset(RangerDataShareInDataset dataShareInDataset) throws Exception; + + void removeDataShareInDataset(Long dataShareInDatasetId) throws Exception; + + RangerDataShareInDataset getDataShareInDataset(Long dataShareInDatasetId) throws Exception; + + PList searchDataShareInDatasets(SearchFilter filter) throws Exception; + + + RangerDatasetInProject addDatasetInProject(RangerDatasetInProject datasetInProject) throws Exception; + + RangerDatasetInProject updateDatasetInProject(RangerDatasetInProject datasetInProject) throws Exception; + + void removeDatasetInProject(Long datasetInProjectId) throws Exception; + + RangerDatasetInProject getDatasetInProject(Long datasetInProjectId) throws Exception; + + PList searchDatasetInProjects(SearchFilter filter) throws Exception; + + void deleteAllGdsObjectsForService(Long serviceId) throws Exception; + + void deleteAllGdsObjectsForSecurityZone(Long zoneId) throws Exception; + + void onSecurityZoneUpdate(Long zoneId, Collection updatedServices, Collection removedServices) throws Exception; +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/PList.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/PList.java index 2402fea9b6..918cd06ab5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/PList.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/PList.java @@ -172,11 +172,25 @@ public void setSortBy(String sortBy) { } public String getSortBy() { return sortBy; } + /** + * This method sets the value to the member attribute queryTimeMS. You + * cannot set null to the attribute. + * + * @param queryTimeMS + * Value to set member attribute queryTimeMS + */ + public void setQueryTimeMS(long queryTimeMS) { + this.queryTimeMS = queryTimeMS; + } - - - - + /** + * Returns the value for the member attribute queryTimeMS + * + * @return long - value of member attribute queryTimeMS. + */ + public long getQueryTimeMS() { + return queryTimeMS; + } /* * (non-Javadoc) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/RangerServiceResourceSignature.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/RangerServiceResourceSignature.java index d7fedf0539..63546f837c 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/RangerServiceResourceSignature.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/RangerServiceResourceSignature.java @@ -20,6 +20,7 @@ package org.apache.ranger.plugin.store; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.ranger.authorization.hadoop.config.RangerAdminConfig; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceResource; @@ -31,7 +32,11 @@ public class RangerServiceResourceSignature { public RangerServiceResourceSignature(RangerServiceResource serviceResource) { _string = ServiceResourceSerializer.toString(serviceResource); - _hash = DigestUtils.sha256Hex(_string); + if (RangerAdminConfig.getInstance().isFipsEnabled()) { + _hash = DigestUtils.sha512Hex(_string); + } else { + _hash = DigestUtils.sha256Hex(_string); + } } String asString() { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/RolePredicateUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/RolePredicateUtil.java index cda8d09995..48254987ea 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/RolePredicateUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/RolePredicateUtil.java @@ -67,7 +67,7 @@ public boolean evaluate(Object object) { List roles = role.getRoles(); for (RangerRole.RoleMember member : roles) { - ret = StringUtils.equals(role.getName(), roleName); + ret = StringUtils.equals(member.getName(), roleName); if (ret) { break; @@ -109,7 +109,7 @@ public boolean evaluate(Object object) { List roles = role.getRoles(); for (RangerRole.RoleMember member : roles) { - ret = StringUtils.containsIgnoreCase(role.getName(), roleNamePartial); + ret = StringUtils.containsIgnoreCase(member.getName(), roleNamePartial); if (ret) { break; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/SecurityZonePredicateUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/SecurityZonePredicateUtil.java index f2c381925b..1b66967168 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/SecurityZonePredicateUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/SecurityZonePredicateUtil.java @@ -27,7 +27,6 @@ import java.util.List; public class SecurityZonePredicateUtil extends AbstractPredicateUtil { - public SecurityZonePredicateUtil() { super(); } @@ -38,7 +37,10 @@ public void addPredicates(SearchFilter filter, List predicates) { addPredicateForServiceName(filter.getParam(SearchFilter.SERVICE_NAME), predicates); addPredicateForMatchingZoneId(filter.getParam(SearchFilter.ZONE_ID), predicates); - addPredicateForNonMatchingZoneName(filter.getParam(SearchFilter.ZONE_NAME), predicates); + addPredicateForMatchingZoneName(filter.getParam(SearchFilter.ZONE_NAME), predicates); + addPredicateForNonMatchingZoneName(filter.getParam(SearchFilter.NOT_ZONE_NAME), predicates); + addPredicateForMatchingZoneNamePartial(filter.getParam(SearchFilter.ZONE_NAME_PARTIAL), predicates); + addPredicateForCreatedBy(filter.getParam(SearchFilter.CREATED_BY), predicates); } private Predicate addPredicateForServiceName(final String serviceName, List predicates) { @@ -105,7 +107,43 @@ public boolean evaluate(Object object) { return ret; } + private Predicate addPredicateForMatchingZoneName(final String zoneName, List predicates) { + if (StringUtils.isEmpty(zoneName)) { + return null; + } + + Predicate ret = new Predicate() { + @Override + public boolean evaluate(Object object) { + if(object == null) { + return false; + } + + boolean ret = false; + + if(object instanceof RangerSecurityZone) { + RangerSecurityZone securityZone = (RangerSecurityZone) object; + + if (StringUtils.equals(zoneName, securityZone.getName())) { + ret = true; + } + } + + return ret; + } + }; + + if(predicates != null) { + predicates.add(ret); + } + + return ret; + } + private Predicate addPredicateForNonMatchingZoneName(final String zoneName, List predicates) { + if (StringUtils.isEmpty(zoneName)) { + return null; + } Predicate ret = new Predicate() { @Override @@ -119,7 +157,7 @@ public boolean evaluate(Object object) { if(object instanceof RangerSecurityZone) { RangerSecurityZone securityZone = (RangerSecurityZone) object; - if (StringUtils.isEmpty(zoneName) || !StringUtils.equals(zoneName, securityZone.getName())) { + if (!StringUtils.equals(zoneName, securityZone.getName())) { ret = true; } } @@ -134,5 +172,71 @@ public boolean evaluate(Object object) { return ret; } + + private Predicate addPredicateForMatchingZoneNamePartial(final String zoneName, List predicates) { + if (StringUtils.isEmpty(zoneName)) { + return null; + } + + Predicate ret = new Predicate() { + @Override + public boolean evaluate(Object object) { + if (object == null) { + return false; + } + + boolean ret = false; + + if (object instanceof RangerSecurityZone) { + RangerSecurityZone securityZone = (RangerSecurityZone) object; + + if (StringUtils.containsIgnoreCase(securityZone.getName(), zoneName)) { + ret = true; + } + } + + return ret; + } + }; + + if (predicates != null) { + predicates.add(ret); + } + + return ret; + } + + private Predicate addPredicateForCreatedBy(final String createdBy, List predicates) { + if (StringUtils.isEmpty(createdBy)) { + return null; + } + + Predicate ret = new Predicate() { + @Override + public boolean evaluate(Object object) { + if (object == null) { + return false; + } + + boolean ret = false; + + if (object instanceof RangerSecurityZone) { + RangerSecurityZone securityZone = (RangerSecurityZone) object; + + if (StringUtils.equals(securityZone.getCreatedBy(), createdBy)) { + ret = true; + } + } + + return ret; + } + }; + + if (predicates != null) { + predicates.add(ret); + } + + return ret; + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/ServiceStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/ServiceStore.java index 6283e02f2a..97072391e5 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/ServiceStore.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/ServiceStore.java @@ -59,6 +59,8 @@ public interface ServiceStore { void deleteService(Long id) throws Exception; + boolean serviceExists(String name) throws Exception; + RangerService getService(Long id) throws Exception; RangerService getServiceByName(String name) throws Exception; @@ -71,6 +73,8 @@ public interface ServiceStore { RangerPolicy createPolicy(RangerPolicy policy) throws Exception; + RangerPolicy createDefaultPolicy(RangerPolicy policy) throws Exception; + RangerPolicy updatePolicy(RangerPolicy policy) throws Exception; void deletePolicy(RangerPolicy policy, RangerService service) throws Exception; @@ -103,7 +107,7 @@ public interface ServiceStore { ServicePolicies getServicePolicyDeltasOrPolicies(String serviceName, Long lastKnownVersion) throws Exception; - ServicePolicies getServicePolicyDeltas(String serviceName, Long lastKnownVersion) throws Exception; + ServicePolicies getServicePolicyDeltas(String serviceName, Long lastKnownVersion, Long cachedPolicyVersion) throws Exception; ServicePolicies getServicePolicies(String serviceName, Long lastKnownVersion) throws Exception; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/StoredServiceResource.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/StoredServiceResource.java new file mode 100644 index 0000000000..26a4ec5254 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/StoredServiceResource.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.store; + +import org.apache.ranger.plugin.model.RangerPolicy; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.Map; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class StoredServiceResource implements java.io.Serializable { + private final Map resourceElements; + private final String ownerName; + private final Map additionalInfo; + + public StoredServiceResource() { + this(null, null, null); + } + + public StoredServiceResource(Map resourceElements, String ownerName, Map additionalInfo) { + this.resourceElements = resourceElements; + this.ownerName = ownerName; + this.additionalInfo = additionalInfo; + } + + public Map getResourceElements() { + return resourceElements; + } + public String getOwnerName() { + return ownerName; + } + public Map getAdditionalInfo() { + return additionalInfo; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/TagStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/TagStore.java index 795289b5c6..f9f80c9ac8 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/TagStore.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/TagStore.java @@ -139,4 +139,6 @@ public interface TagStore { void deleteAllTagObjectsForService(String serviceName) throws Exception; + boolean isInPlaceTagUpdateSupported(); + } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/TagValidator.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/TagValidator.java index 08b1e45fda..699e49e17b 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/TagValidator.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/TagValidator.java @@ -150,6 +150,7 @@ public RangerServiceResource preCreateServiceResource(RangerServiceResource reso if (ret == null) { RangerServiceResourceSignature serializer = new RangerServiceResourceSignature(resource); resource.setResourceSignature(serializer.getSignature()); + ret = tagStore.getServiceResourceByServiceAndResourceSignature(resource.getServiceName(), resource.getResourceSignature()); } return ret; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/store/file/GeolocationFileStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/store/file/GeolocationFileStore.java index 9b55e387b6..5f8e869cff 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/store/file/GeolocationFileStore.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/store/file/GeolocationFileStore.java @@ -20,12 +20,12 @@ package org.apache.ranger.plugin.store.file; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.geo.GeolocationMetadata; import org.apache.ranger.plugin.store.GeolocationStore; import org.apache.ranger.plugin.geo.RangerGeolocationDatabase; import org.apache.ranger.plugin.geo.RangerGeolocationData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.File; @@ -39,7 +39,7 @@ import java.util.Map; public class GeolocationFileStore implements GeolocationStore { - private static final Log LOG = LogFactory.getLog(GeolocationFileStore.class); + private static final Logger LOG = LoggerFactory.getLogger(GeolocationFileStore.class); public static final String GeoLineCommentIdentifier = "#"; public static final Character GeoFieldsSeparator = ','; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/AutoClosableLock.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/AutoClosableLock.java new file mode 100644 index 0000000000..5082bc8c72 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/AutoClosableLock.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; + +public class AutoClosableLock implements AutoCloseable { + private final Lock lock; + + public AutoClosableLock(Lock lock) { + this.lock = lock; + + this.lock.lock(); + } + + @Override + public void close() { + lock.unlock(); + } + + public static class AutoClosableTryLock implements AutoCloseable { + private final Lock lock; + private final boolean isLocked; + + public AutoClosableTryLock(Lock lock, long timeout, TimeUnit timeUnit) { + this.lock = lock; + + boolean isLocked = false; + + try { + isLocked = this.lock.tryLock(timeout, timeUnit); + } catch (InterruptedException excp) { + // ignored + } + + this.isLocked = isLocked; + } + + public boolean isLocked() { return isLocked; } + + @Override + public void close() { + if (isLocked) { + lock.unlock(); + } + } + } + + public static class AutoClosableReadLock implements AutoCloseable { + private final ReadWriteLock lock; + + public AutoClosableReadLock(ReadWriteLock lock) { + this.lock = lock; + + this.lock.readLock().lock(); + } + + @Override + public void close() { + lock.readLock().unlock(); + } + } + + public static class AutoClosableWriteLock implements AutoCloseable { + private final ReadWriteLock lock; + + public AutoClosableWriteLock(ReadWriteLock lock) { + this.lock = lock; + + this.lock.writeLock().lock(); + } + + @Override + public void close() { + lock.writeLock().unlock(); + } + } + + public static class AutoClosableTryWriteLock implements AutoCloseable { + private final ReadWriteLock lock; + private final boolean isLocked; + + public AutoClosableTryWriteLock(ReadWriteLock lock) { + this.lock = lock; + this.isLocked = this.lock.writeLock().tryLock(); + } + + public boolean isLocked() { return isLocked; } + + @Override + public void close() { + if (isLocked) { + lock.writeLock().unlock(); + } + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/CachedResourceEvaluators.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/CachedResourceEvaluators.java new file mode 100644 index 0000000000..9ce11e6e75 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/CachedResourceEvaluators.java @@ -0,0 +1,173 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.commons.collections.Predicate; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.contextenricher.RangerServiceResourceMatcher; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyengine.RangerResourceTrie; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class CachedResourceEvaluators { + private final Map, Collection>> cache = new HashMap<>(); + private final RangerReadWriteLock cacheLock = new RangerReadWriteLock(true); + + private static final Logger LOG = LoggerFactory.getLogger(CachedResourceEvaluators.class); + private static final Logger PERF_EVALUATORS_RETRIEVAL_LOG = RangerPerfTracer.getPerfLogger("CachedResourceEvaluators.retrieval"); + + public CachedResourceEvaluators() {} + + public Collection getEvaluators(String resourceKey, Map scopes) { + Collection ret; + + try (RangerReadWriteLock.RangerLock ignored = cacheLock.getReadLock()) { + ret = cache.getOrDefault(resourceKey, Collections.emptyMap()).get(scopes); + } + + return ret; + } + + public void cacheEvaluators(String resource, Map scopes, Collection evaluators) { + try (RangerReadWriteLock.RangerLock ignored = cacheLock.getWriteLock()) { + cache.computeIfAbsent(resource, k -> new HashMap<>()).put(scopes, evaluators); + } + } + + public void removeCacheEvaluators(Set resources) { + try (RangerReadWriteLock.RangerLock ignored = cacheLock.getWriteLock()) { + resources.forEach(cache::remove); + } + } + + public void clearCache() { + try (RangerReadWriteLock.RangerLock ignored = cacheLock.getWriteLock()) { + cache.clear(); + } + } + + public static Collection getEvaluators(RangerAccessRequest request, Map> serviceResourceTrie, CachedResourceEvaluators cache) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> CachedResourceEvaluators.getEvaluators(request=" + request + ")"); + } + + Collection ret = null; + final RangerAccessResource resource = request.getResource(); + RangerServiceDefHelper helper = new RangerServiceDefHelper(resource.getServiceDef()); + + RangerPerfTracer perf = null; + + if (RangerPerfTracer.isPerfTraceEnabled(PERF_EVALUATORS_RETRIEVAL_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_EVALUATORS_RETRIEVAL_LOG, "CachedResourceEvaluators.getEvaluators(resource=" + resource.getAsString() + ")"); + } + + final RangerAccessRequest.ResourceMatchingScope resourceMatchingScope = request.getResourceMatchingScope() != null ? request.getResourceMatchingScope() : RangerAccessRequest.ResourceMatchingScope.SELF; + final Predicate predicate = !(request.isAccessTypeAny() || resourceMatchingScope == RangerAccessRequest.ResourceMatchingScope.SELF_OR_DESCENDANTS) && excludeDescendantMatches(resource) ? new SelfOrAncestorPredicate(helper.getResourceDef(resource.getLeafName())) : null; + + if (predicate != null) { + ret = cache.getEvaluators(resource.getCacheKey(), request.getResourceElementMatchingScopes()); + } + + if (ret == null) { + ret = RangerResourceEvaluatorsRetriever.getEvaluators(serviceResourceTrie, resource.getAsMap(), request.getResourceElementMatchingScopes(), predicate); + + if (LOG.isDebugEnabled()) { + LOG.debug("Found [" + ret.size() + "] service-resource-matchers for service-resource [" + resource.getAsString() + "]"); + } + + if (predicate != null) { + cache.cacheEvaluators(resource.getCacheKey(), request.getResourceElementMatchingScopes(), ret); + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("Found [" + ret.size() + "] service-resource-matchers for service-resource [" + resource.getAsString() + "] in the cache"); + } + } + + RangerPerfTracer.logAlways(perf); + + if (ret == null) { + ret = new ArrayList<>(); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== CachedResourceEvaluators.getEvaluators(request=" + request + "): evaluators=" + ret); + } + + return ret; + } + + public static boolean excludeDescendantMatches(RangerAccessResource resource) { + final boolean ret; + + String leafName = resource.getLeafName(); + + if (StringUtils.isNotEmpty(leafName)) { + RangerServiceDefHelper helper = new RangerServiceDefHelper(resource.getServiceDef()); + Set> hierarchies = helper.getResourceHierarchies(RangerPolicy.POLICY_TYPE_ACCESS, resource.getKeys()); + + // skip caching if the leaf of accessed resource is the deepest in the only applicable hierarchy + if (hierarchies.size() == 1) { + List theHierarchy = hierarchies.iterator().next(); + RangerServiceDef.RangerResourceDef leafOfHierarchy = theHierarchy.get(theHierarchy.size() - 1); + + ret = !StringUtils.equals(leafOfHierarchy.getName(), leafName); + } else { + ret = true; + } + } else { + ret = false; + } + return ret; + } + + private static class SelfOrAncestorPredicate implements Predicate { + private final RangerServiceDef.RangerResourceDef leafResourceDef; + + public SelfOrAncestorPredicate(RangerServiceDef.RangerResourceDef leafResourceDef) { + this.leafResourceDef = leafResourceDef; + } + + @Override + public boolean evaluate(Object o) { + if (o instanceof RangerResourceEvaluator) { + RangerResourceEvaluator evaluator = (RangerResourceEvaluator) o; + + return evaluator.isLeaf(leafResourceDef.getName()) || evaluator.isAncestorOf(leafResourceDef); + } + + return false; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/DownloaderTask.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/DownloaderTask.java index 1345f6f751..f43e6b0164 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/DownloaderTask.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/DownloaderTask.java @@ -19,14 +19,14 @@ package org.apache.ranger.plugin.util; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.TimerTask; import java.util.concurrent.BlockingQueue; public final class DownloaderTask extends TimerTask { - private static final Log LOG = LogFactory.getLog(DownloaderTask.class); + private static final Logger LOG = LoggerFactory.getLogger(DownloaderTask.class); private final DownloadTrigger timerTrigger = new DownloadTrigger(); private final BlockingQueue queue; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/GraalScriptEngineCreator.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/GraalScriptEngineCreator.java new file mode 100644 index 0000000000..512d8d3ca4 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/GraalScriptEngineCreator.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.commons.lang.StringUtils; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.Path; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.io.File; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class GraalScriptEngineCreator implements ScriptEngineCreator { + private static final Logger LOG = LoggerFactory.getLogger(GraalScriptEngineCreator.class); + + private static final String ENGINE_NAME = "graal.js"; + private static final String CONFIG_PREDIX_JVM = "polyglot"; + private static final String CONFIG_PREDIX_PLUGIN = "ranger.plugin.script."; + private static final String CONFIG_JAVA_CLASS_PATH = "java.class.path"; + + // instance variables + private final Map graalVmConfigs = new HashMap<>(); + + // default constructor + public GraalScriptEngineCreator() { + Map graalVmConfigsDefault = new HashMap<>(4); //setting smallest size, which is big enough to avoid expand + Configuration configuration = new Configuration(); + FilenameFilter fileNameFilter = (dir, name) -> name.startsWith("ranger-") && name.endsWith("security.xml"); + + graalVmConfigsDefault.put("polyglot.js.allowHostAccess", Boolean.TRUE); //default is true for backward(Nashorn) compatibility + graalVmConfigsDefault.put("polyglot.js.nashorn-compat", Boolean.TRUE); //default is true for backward(Nashorn) compatibility + + for (String file: findFiles(fileNameFilter)) { + configuration.addResource(new Path(file)); + } + + graalVmConfigs.putAll(getGraalVmConfigs(configuration, graalVmConfigsDefault)); + } + + public ScriptEngine getScriptEngine(ClassLoader clsLoader) { + ScriptEngine ret = null; + + if (clsLoader == null) { + clsLoader = Thread.currentThread().getContextClassLoader(); + } + + try { + ScriptEngineManager mgr = new ScriptEngineManager(clsLoader); + + ret = mgr.getEngineByName(ENGINE_NAME); + + if (ret != null) { + // enable configured script features + Bindings bindings = ret.getBindings(ScriptContext.ENGINE_SCOPE); + bindings.putAll(graalVmConfigs); + ret.setBindings(bindings, ScriptContext.ENGINE_SCOPE); + } + } catch (Throwable t) { + LOG.debug("GraalScriptEngineCreator.getScriptEngine(): failed to create engine type {}", ENGINE_NAME, t); + } + + if (ret == null) { + LOG.debug("GraalScriptEngineCreator.getScriptEngine(): failed to create engine type {}", ENGINE_NAME); + } + return ret; + } + + private Map getGraalVmConfigs(Configuration configuration, Map graalVmConfigsDefault) { + if (LOG.isDebugEnabled()) { + LOG.debug("===>> GraalScriptEngineCreator.getGraalVmConfigs()"); + } + + Map ret = new HashMap<>(); + + // set configs from ranger security config values, if present + for (Map.Entry entry : configuration.getPropsWithPrefix(CONFIG_PREDIX_PLUGIN).entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + + if (StringUtils.isNotBlank(value)) { + ret.put(key, Boolean.valueOf(value)); + } + } + + // add JVM options if not already set + for (Map.Entry entry : System.getProperties().entrySet()) { + if (entry.getKey().toString().startsWith(CONFIG_PREDIX_JVM)) { + String key = entry.getKey().toString(); + String value = entry.getValue().toString(); + + if (StringUtils.isNotBlank(value) && ret.get(key) == null) { + ret.put(key, Boolean.valueOf(value)); + } + } + } + + // add default values if not already set + for (Map.Entry entry : graalVmConfigsDefault.entrySet()) { + String key = entry.getKey(); + Boolean value = entry.getValue(); + + ret.putIfAbsent(key, value); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<<=== GraalScriptEngineCreator.getGraalVmConfigs(): ret={}", ret); + } + + return ret; + } + + private Set findFiles(FilenameFilter filenameFilter) { + String classPath = System.getProperty(CONFIG_JAVA_CLASS_PATH); + List configDirs = new ArrayList<>(5); + Set ret = new HashSet<>(); + + for (String path : classPath.split(":")) { + if (!path.endsWith("jar")) { //ignore jars + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + configDirs.add(path); + } + } + + for (String configDir : configDirs) { + File confDir = new File(configDir); + if (confDir.isDirectory()) { + for (File file : Objects.requireNonNull(confDir.listFiles(filenameFilter))) { + ret.add(file.getAbsolutePath()); + } + } + } + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRequest.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRequest.java index 63f0f25988..948a8c97c8 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRequest.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRequest.java @@ -27,21 +27,15 @@ import java.util.Map; import java.util.Set; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(getterVisibility=Visibility.NONE, setterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL ) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class GrantRevokeRequest implements Serializable { private static final long serialVersionUID = 1L; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRoleRequest.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRoleRequest.java index d5bc57d251..8ba71f46f7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRoleRequest.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/GrantRevokeRoleRequest.java @@ -19,24 +19,19 @@ package org.apache.ranger.plugin.util; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + import java.io.Serializable; import java.util.HashSet; import java.util.Set; @JsonAutoDetect(getterVisibility=Visibility.NONE, setterVisibility=Visibility.NONE, fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL ) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class GrantRevokeRoleRequest implements Serializable { private static final long serialVersionUID = 1L; @@ -275,7 +270,7 @@ public StringBuilder toString(StringBuilder sb) { sb.append("} "); sb.append("groups={"); - if(roles != null) { + if(groups != null) { for(String group : groups) { sb.append(group).append(" "); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/JavaScriptEdits.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/JavaScriptEdits.java new file mode 100644 index 0000000000..a2dd18e4f4 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/JavaScriptEdits.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class JavaScriptEdits { + private static final Logger LOG = LoggerFactory.getLogger(JavaScriptEdits.class); + + private static final String DOUBLE_BRACKET_START = "[["; + private static final String DOUBLE_BRACKET_END = "]]"; + private static final String DOUBLE_BRACKET_REGEX = "\\[\\[([}{\\$\"a-zA-Z0-9_.\\[\\]]+)(\\,['\\\"](.+?)['\\\"])*\\]\\]"; // regex: /\[\[([a-zA-Z0-9_.\[\]]+)(\,['"](.+)['"])*\]\]/g; + private static final Pattern DOUBLE_BRACKET_PATTERN = Pattern.compile(DOUBLE_BRACKET_REGEX); + + public static boolean hasDoubleBrackets(String str) { + return StringUtils.contains(str, DOUBLE_BRACKET_START) && StringUtils.contains(str, DOUBLE_BRACKET_END); + } + + /* some examples: + tag-based access policy: + original: [[TAG.value]].intersects([[USER[TAG._type]]]) + replaced: TAG.value.split(",").intersects(USER[TAG._type].split(",")) + Row-filter policy: + original: ${{[["$USER.eventType",'|']]}}.includes(eventType) + replaced: ${{"$USER.eventType".split("|")}}.includes(jsonAttr.eventType) + */ + public static String replaceDoubleBrackets(String str) { + // Besides trivial inputs, re has been tested on ${{USER.x}} and multiple [[]]'s + String ret = str; + + for (Matcher m = DOUBLE_BRACKET_PATTERN.matcher(str); m.find(); ) { + String tokenToReplace = m.group(0); + String expr = m.group(1); + String delimiterSpec = m.group(2); + String delimiter = m.group(3); + + if (delimiter == null) { + delimiter = ","; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("replaceDoubleBrackets({}): tokenToReplace={} expr={} delimiterSpec={} delimiter={}", str, tokenToReplace, expr, delimiterSpec, delimiter); + } + + ret = ret.replace(tokenToReplace, expr + ".split(\"" + delimiter + "\")"); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== replaceDoubleBrackets({}): ret={}", str, ret); + } + + return ret; + } +} + diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/JavaScriptEngineCreator.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/JavaScriptEngineCreator.java new file mode 100644 index 0000000000..4a0081579d --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/JavaScriptEngineCreator.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; + + +public class JavaScriptEngineCreator implements ScriptEngineCreator { + private static final Logger LOG = LoggerFactory.getLogger(JavaScriptEngineCreator.class); + + static final String ENGINE_NAME = "JavaScript"; + + public ScriptEngine getScriptEngine(ClassLoader clsLoader) { + ScriptEngine ret = null; + + if (clsLoader == null) { + clsLoader = Thread.currentThread().getContextClassLoader(); + } + + try { + ScriptEngineManager mgr = new ScriptEngineManager(clsLoader); + + ret = mgr.getEngineByName(ENGINE_NAME); + } catch (Throwable t) { + LOG.debug("JavaScriptEngineCreator.getScriptEngine(): failed to create engine type {}", ENGINE_NAME, t); + } + + if (ret == null) { + LOG.debug("JavaScriptEngineCreator.getScriptEngine(): failed to create engine type {}", ENGINE_NAME); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/JsonUtilsV2.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/JsonUtilsV2.java index 9a8546b79b..1c2581de62 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/JsonUtilsV2.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/JsonUtilsV2.java @@ -19,10 +19,13 @@ package org.apache.ranger.plugin.util; -import org.codehaus.jackson.map.ObjectMapper; -import org.codehaus.jackson.type.TypeReference; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.sun.jersey.api.client.ClientResponse; +import java.io.Reader; import java.io.Serializable; +import java.io.Writer; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -68,4 +71,32 @@ static public T jsonToObj(String json, Class tClass) throws Exception { return getMapper().readValue(json, tClass); } + static public T jsonToObj(String json, TypeReference typeRef) throws Exception { + return getMapper().readValue(json, typeRef); + } + + + static public void writeValue(Writer writer, Object obj) throws Exception { + getMapper().writeValue(writer, obj); + } + + static public T readValue(Reader reader, Class tClass) throws Exception { + return getMapper().readValue(reader, tClass); + } + + static public String nonSerializableObjToJson(Object obj) throws Exception { + return getMapper().writeValueAsString(obj); + } + + static public T readResponse(ClientResponse response, Class cls) throws Exception { + String jsonStr = response.getEntity(String.class); + + return jsonToObj(jsonStr, cls); + } + + static public T readResponse(ClientResponse response, TypeReference cls) throws Exception { + String jsonStr = response.getEntity(String.class); + + return jsonToObj(jsonStr, cls); + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/MacroProcessor.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/MacroProcessor.java new file mode 100644 index 0000000000..8332cd93a1 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/MacroProcessor.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MacroProcessor { + private static final Logger LOG = LoggerFactory.getLogger(MacroProcessor.class); + + private final Map macrosMap; + private final Pattern macrosPattern; + + public MacroProcessor(Map macrosMap) { + this.macrosMap = macrosMap; + this.macrosPattern = getMacrosPattern(this.macrosMap); + } + + public String expandMacros(String expr) { + StringBuffer ret = null; + + if (expr != null) { + Matcher matcher = macrosPattern.matcher(expr); + + while (matcher.find()) { + if (ret == null) { + ret = new StringBuffer(); + } + + String keyword = matcher.group(); + String replacer = macrosMap.get(keyword); + + matcher.appendReplacement(ret, replacer); + } + + if (ret == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("expandMacros({}): no match found!", expr); + } + } else { + matcher.appendTail(ret); + + if (LOG.isDebugEnabled()) { + LOG.debug("expandMacros({}): match found. ret={}", expr, ret); + } + } + } + + return ret != null ? ret.toString() : expr; + } + + private Pattern getMacrosPattern(Map macros) { + StringBuilder sb = new StringBuilder(); + String sep = "\\b("; + + for (String macro : macros.keySet()) { + sb.append(sep).append(macro); + + sep = "|"; + } + + sb.append(")\\b"); + + return Pattern.compile(sb.toString()); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/NashornScriptEngineCreator.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/NashornScriptEngineCreator.java new file mode 100644 index 0000000000..db620df92b --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/NashornScriptEngineCreator.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptEngine; +import jdk.nashorn.api.scripting.ClassFilter; +import jdk.nashorn.api.scripting.NashornScriptEngineFactory; + +public class NashornScriptEngineCreator implements ScriptEngineCreator { + private static final Logger LOG = LoggerFactory.getLogger(NashornScriptEngineCreator.class); + + private static final String[] SCRIPT_ENGINE_ARGS = new String[] { "--no-java", "--no-syntax-extensions" }; + private static final String ENGINE_NAME = "NashornScriptEngine"; + + @Override + public ScriptEngine getScriptEngine(ClassLoader clsLoader) { + ScriptEngine ret = null; + + if (clsLoader == null) { + clsLoader = Thread.currentThread().getContextClassLoader(); + } + + try { + NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); + + ret = factory.getScriptEngine(SCRIPT_ENGINE_ARGS, clsLoader, RangerClassFilter.INSTANCE); + } catch (Throwable t) { + LOG.debug("NashornScriptEngineCreator.getScriptEngine(): failed to create engine type {}", ENGINE_NAME, t); + } + + return ret; + } + + private static class RangerClassFilter implements ClassFilter { + static final RangerClassFilter INSTANCE = new RangerClassFilter(); + + private RangerClassFilter() { + } + + @Override + public boolean exposeToScripts(String className) { + LOG.warn("script blocked: attempt to use Java class {}", className); + + return false; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java index f1f2b0920e..546412b530 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/PasswordUtils.java @@ -28,12 +28,12 @@ import javax.crypto.spec.PBEKeySpec; import javax.crypto.spec.PBEParameterSpec; +import org.apache.commons.lang3.StringUtils; +import org.apache.hadoop.thirdparty.com.google.common.base.Splitter; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Splitter; -import com.google.common.base.Strings; -import com.google.common.collect.Lists; import com.sun.jersey.core.util.Base64; public class PasswordUtils { @@ -169,7 +169,7 @@ private String decrypt() throws IOException { } public static boolean needsIv(String cryptoAlgo) { - if (Strings.isNullOrEmpty(cryptoAlgo)) + if (StringUtils.isEmpty(cryptoAlgo)) return false; return PBE_SHA512_AES_128.toLowerCase().equals(cryptoAlgo.toLowerCase()) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/PerfDataRecorder.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/PerfDataRecorder.java index 28163698d6..2b1e17a413 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/PerfDataRecorder.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/PerfDataRecorder.java @@ -20,10 +20,9 @@ package org.apache.ranger.plugin.util; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import com.google.common.collect.ImmutableMap; +import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collections; @@ -33,17 +32,31 @@ import java.util.concurrent.atomic.AtomicLong; public class PerfDataRecorder { - private static final Log LOG = LogFactory.getLog(PerfDataRecorder.class); - private static final Log PERF = RangerPerfTracer.getPerfLogger(PerfDataRecorder.class); + private static final Logger LOG = LoggerFactory.getLogger(PerfDataRecorder.class); + private static final Logger PERF = RangerPerfTracer.getPerfLogger(PerfDataRecorder.class); private static volatile PerfDataRecorder instance; - private Map perfStatistics = new HashMap<>(); + final private Map perfStatistics = Collections.synchronizedMap(new HashMap<>()); + private RangerReadWriteLock lock = null; public static void initialize(List names) { - if (instance == null) { - synchronized (PerfDataRecorder.class) { - if (instance == null) { - instance = new PerfDataRecorder(names); + initialize(true, 0, false, names); + } + + public static void initialize(final boolean useRecorder, final int collectionIntervalInSeconds, final boolean usePerfDataLock, List names) { + if (useRecorder) { + if (instance == null) { + synchronized (PerfDataRecorder.class) { + if (instance == null) { + instance = new PerfDataRecorder(names); + instance.lock = new RangerReadWriteLock(usePerfDataLock); + if (collectionIntervalInSeconds > 0) { + Thread statDumper = new StatisticsDumper(collectionIntervalInSeconds); + statDumper.setName("Perf-Statistics-Dumper"); + statDumper.setDaemon(true); + statDumper.start(); + } + } } } } @@ -61,32 +74,47 @@ public static void printStatistics() { public static void clearStatistics() { if (instance != null) { - instance.clear(); + try (RangerReadWriteLock.RangerLock writeLock = instance.lock.getWriteLock()) { + instance.clear(); + } } } - public static void recordStatistic(String tag, long elapsedTime) { + public static void recordStatistic(String tag, long cpuTime, long elapsedTime) { if (instance != null) { - instance.record(tag, elapsedTime); + instance.record(tag, cpuTime, elapsedTime); } } private void dumpStatistics() { - List tags = new ArrayList<>(perfStatistics.keySet()); + List tags; + + try (RangerReadWriteLock.RangerLock readLock = lock.getReadLock()) { + tags = new ArrayList<>(perfStatistics.keySet()); + } Collections.sort(tags); for (String tag : tags) { PerfStatistic perfStatistic = perfStatistics.get(tag); + long averageTimeSpentCpu = 0L; long averageTimeSpent = 0L; + if (perfStatistic.numberOfInvocations.get() != 0L) { + averageTimeSpentCpu = perfStatistic.microSecondsSpentCpu.get()/perfStatistic.numberOfInvocations.get(); + } + if (perfStatistic.numberOfInvocations.get() != 0L) { averageTimeSpent = perfStatistic.microSecondsSpent.get()/perfStatistic.numberOfInvocations.get(); } String logMsg = "[" + tag + "]" + " execCount: " + perfStatistic.numberOfInvocations.get() + + ", totalTimeTakenCpu: " + perfStatistic.microSecondsSpentCpu.get() + " μs" + + ", maxTimeTakenCpu: " + perfStatistic.maxTimeSpentCpu.get() + " μs" + + ", minTimeTakenCpu: " + perfStatistic.minTimeSpentCpu.get() + " μs" + + ", avgTimeTakenCpu: " + averageTimeSpentCpu + " μs" + ", totalTimeTaken: " + perfStatistic.microSecondsSpent.get() + " μs" + ", maxTimeTaken: " + perfStatistic.maxTimeSpent.get() + " μs" + ", minTimeTaken: " + perfStatistic.minTimeSpent.get() + " μs" + @@ -101,21 +129,25 @@ private void clear() { perfStatistics.clear(); } - private void record(String tag, long elapsedTime) { - PerfStatistic perfStatistic = perfStatistics.get(tag); + private void record(String tag, long cpuTime, long elapsedTime) { + try (RangerReadWriteLock.RangerLock writeLock = lock.getWriteLock()) { + + PerfStatistic perfStatistic = perfStatistics.get(tag); - if (perfStatistic == null) { - synchronized (PerfDataRecorder.class) { - perfStatistic = perfStatistics.get(tag); + if (perfStatistic == null) { + synchronized (PerfDataRecorder.class) { + perfStatistic = perfStatistics.get(tag); - if(perfStatistic == null) { - perfStatistic = new PerfStatistic(); - perfStatistics.put(tag, perfStatistic); + if (perfStatistic == null) { + perfStatistic = new PerfStatistic(); + perfStatistics.put(tag, perfStatistic); + } } } - } - perfStatistic.addPerfDataItem(elapsedTime); + perfStatistic.addPerfDataItem(cpuTime, elapsedTime); + + } } private PerfDataRecorder(List names) { @@ -136,20 +168,34 @@ public static Map exposeStatistics() { public static class PerfStatistic { private AtomicLong numberOfInvocations = new AtomicLong(0L); + + private AtomicLong microSecondsSpentCpu = new AtomicLong(0L); + private AtomicLong minTimeSpentCpu = new AtomicLong(Long.MAX_VALUE); + private AtomicLong maxTimeSpentCpu = new AtomicLong(Long.MIN_VALUE); + private AtomicLong microSecondsSpent = new AtomicLong(0L); private AtomicLong minTimeSpent = new AtomicLong(Long.MAX_VALUE); private AtomicLong maxTimeSpent = new AtomicLong(Long.MIN_VALUE); - void addPerfDataItem(final long timeTaken) { + void addPerfDataItem(final long cpuTime, final long timeTaken) { numberOfInvocations.getAndIncrement(); + microSecondsSpentCpu.getAndAdd(cpuTime); microSecondsSpent.getAndAdd(timeTaken); - long min = minTimeSpent.get(); + long min = minTimeSpentCpu.get(); + if(cpuTime < min) { + minTimeSpentCpu.compareAndSet(min, cpuTime); + } + min = minTimeSpent.get(); if(timeTaken < min) { minTimeSpent.compareAndSet(min, timeTaken); } - long max = maxTimeSpent.get(); + long max = maxTimeSpentCpu.get(); + if(cpuTime > max) { + maxTimeSpentCpu.compareAndSet(max, cpuTime); + } + max = maxTimeSpent.get(); if(timeTaken > max) { maxTimeSpent.compareAndSet(max, timeTaken); } @@ -159,6 +205,18 @@ public long getNumberOfInvocations() { return numberOfInvocations.get(); } + public long getMicroSecondsSpentCpu() { + return microSecondsSpentCpu.get(); + } + + public long getMinTimeSpentCpu() { + return minTimeSpentCpu.get(); + } + + public long getMaxTimeSpentCpu() { + return maxTimeSpentCpu.get(); + } + public long getMicroSecondsSpent() { return microSecondsSpent.get(); } @@ -171,4 +229,25 @@ public long getMaxTimeSpent() { return maxTimeSpent.get(); } } + + private static class StatisticsDumper extends Thread { + final int collectionIntervalInSeconds; + StatisticsDumper(final int collectionIntervalInSeconds) { + this.collectionIntervalInSeconds = collectionIntervalInSeconds; + } + @Override + public void run() { + while (true) { + try { + sleep(collectionIntervalInSeconds * 1000); + printStatistics(); + clearStatistics(); + } catch (InterruptedException exception) { + printStatistics(); + LOG.warn("Thread[" + this.getName() + "] was interrupted. Returning from thread. Performance statistics will NOT be dumped periodically!!"); + break; + } + } + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java index ed05e87805..ca93f27e77 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/PolicyRefresher.java @@ -20,29 +20,33 @@ package org.apache.ranger.plugin.util; import java.io.File; +import java.io.FileFilter; import java.io.FileReader; import java.io.FileWriter; import java.io.Reader; import java.io.Writer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; import java.util.Timer; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.admin.client.RangerAdminClient; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.authorization.utils.JsonUtils; +import org.apache.ranger.plugin.policyengine.RangerPluginContext; import org.apache.ranger.plugin.service.RangerBasePlugin; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class PolicyRefresher extends Thread { - private static final Log LOG = LogFactory.getLog(PolicyRefresher.class); + private static final Logger LOG = LoggerFactory.getLogger(PolicyRefresher.class); - private static final Log PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init"); + private static final Logger PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init"); private final RangerBasePlugin plugIn; private final String serviceType; @@ -52,8 +56,6 @@ public class PolicyRefresher extends Thread { private final long pollingIntervalMs; private final String cacheFileName; private final String cacheDir; - private final Gson gson; - private final boolean disableCacheIfServiceNotFound; private final BlockingQueue policyDownloadQueue = new LinkedBlockingQueue<>(); private Timer policyDownloadTimer; private long lastKnownVersion = -1L; @@ -83,18 +85,11 @@ public PolicyRefresher(RangerBasePlugin plugIn) { this.cacheFileName = cacheFilename; - Gson gson = null; - try { - gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create(); - } catch(Throwable excp) { - LOG.fatal("PolicyRefresher(): failed to create GsonBuilder object", excp); - } - - this.gson = gson; - this.disableCacheIfServiceNotFound = pluginConfig.getBoolean(propertyPrefix + ".disable.cache.if.servicenotfound", true); - this.rangerAdmin = RangerBasePlugin.createAdminClient(pluginConfig); + RangerPluginContext pluginContext = plugIn.getPluginContext(); + RangerAdminClient adminClient = pluginContext.getAdminClient(); + this.rangerAdmin = (adminClient != null) ? adminClient : pluginContext.createAdminClient(pluginConfig); this.rolesProvider = new RangerRolesProvider(getServiceType(), appId, getServiceName(), rangerAdmin, cacheDir, pluginConfig); - this.pollingIntervalMs = pluginConfig.getLong(propertyPrefix + ".policy.pollIntervalMs", 30 * 1000); + this.pollingIntervalMs = pluginConfig.getLong(propertyPrefix + ".policy.pollIntervalMs", 30 * 1000L); setName("PolicyRefresher(serviceName=" + serviceName + ")-" + getId()); @@ -207,7 +202,7 @@ public void run() { loadRoles(); loadPolicy(); } catch(InterruptedException excp) { - LOG.debug("PolicyRefresher(serviceName=" + serviceName + ").run(): interrupted! Exiting thread", excp); + LOG.info("PolicyRefresher(serviceName=" + serviceName + ").run(): interrupted! Exiting thread", excp); break; } finally { if (trigger != null) { @@ -261,6 +256,7 @@ private void loadPolicy() { if (svcPolicies != null) { plugIn.setPolicies(svcPolicies); policiesSetInPlugin = true; + serviceDefSetInPlugin = false; setLastActivationTimeInMillis(System.currentTimeMillis()); lastKnownVersion = svcPolicies.getPolicyVersion() != null ? svcPolicies.getPolicyVersion() : -1L; } else { @@ -270,12 +266,12 @@ private void loadPolicy() { } } } catch (RangerServiceNotFoundException snfe) { - if (disableCacheIfServiceNotFound) { + if (!serviceDefSetInPlugin) { disableCache(); plugIn.setPolicies(null); + serviceDefSetInPlugin = true; setLastActivationTimeInMillis(System.currentTimeMillis()); lastKnownVersion = -1; - serviceDefSetInPlugin = true; } } catch (Exception excp) { LOG.error("Encountered unexpected exception, ignoring..", excp); @@ -363,7 +359,7 @@ private ServicePolicies loadFromCache() { try { reader = new FileReader(cacheFile); - policies = gson.fromJson(reader, ServicePolicies.class); + policies = JsonUtils.jsonToObject(reader, ServicePolicies.class); if(policies != null) { if(!StringUtils.equals(serviceName, policies.getServiceName())) { @@ -402,22 +398,31 @@ public void saveToCache(ServicePolicies policies) { if(LOG.isDebugEnabled()) { LOG.debug("==> PolicyRefresher(serviceName=" + serviceName + ").saveToCache()"); } + boolean doPreserveDeltas = plugIn.getConfig().getBoolean(plugIn.getConfig().getPropertyPrefix() + ".preserve.deltas", false); if(policies != null) { File cacheFile = null; + File backupCacheFile = null; if (cacheDir != null) { + String realCacheDirName = CollectionUtils.isNotEmpty(policies.getPolicyDeltas()) ? cacheDir + File.separator + "deltas" : cacheDir; + String backupCacheFileName = cacheFileName + "_" + policies.getPolicyVersion(); + String realCacheFileName = CollectionUtils.isNotEmpty(policies.getPolicyDeltas()) ? backupCacheFileName : cacheFileName; + // Create the cacheDir if it doesn't already exist - File cacheDirTmp = new File(cacheDir); + File cacheDirTmp = new File(realCacheDirName); if (cacheDirTmp.exists()) { - cacheFile = new File(cacheDir + File.separator + cacheFileName); + cacheFile = new File(realCacheDirName + File.separator + realCacheFileName); } else { try { cacheDirTmp.mkdirs(); - cacheFile = new File(cacheDir + File.separator + cacheFileName); + cacheFile = new File(realCacheDirName + File.separator + realCacheFileName); } catch (SecurityException ex) { LOG.error("Cannot create cache directory", ex); } } + if (CollectionUtils.isEmpty(policies.getPolicyDeltas())) { + backupCacheFile = new File(realCacheDirName + File.separator + backupCacheFileName); + } } if(cacheFile != null) { @@ -432,23 +437,43 @@ public void saveToCache(ServicePolicies policies) { try { writer = new FileWriter(cacheFile); - - gson.toJson(policies, writer); + JsonUtils.objectToWriter(writer, policies); } catch (Exception excp) { LOG.error("failed to save policies to cache file '" + cacheFile.getAbsolutePath() + "'", excp); } finally { - if(writer != null) { - try { - writer.close(); - } catch(Exception excp) { - LOG.error("error while closing opened cache file '" + cacheFile.getAbsolutePath() + "'", excp); - } - } - } + if (writer != null) { + try { + writer.close(); + deleteOldestVersionCacheFileInCacheDirectory(cacheFile.getParentFile()); + } catch (Exception excp) { + LOG.error("error while closing opened cache file '" + cacheFile.getAbsolutePath() + "'", excp); + } + } + } RangerPerfTracer.log(perf); } + + if (doPreserveDeltas) { + if (backupCacheFile != null) { + + RangerPerfTracer perf = null; + + if (RangerPerfTracer.isPerfTraceEnabled(PERF_POLICYENGINE_INIT_LOG)) { + perf = RangerPerfTracer.getPerfTracer(PERF_POLICYENGINE_INIT_LOG, "PolicyRefresher.saveToCache(serviceName=" + serviceName + ")"); + } + + try (Writer writer = new FileWriter(backupCacheFile)) { + JsonUtils.objectToWriter(writer, policies); + } catch (Exception excp) { + LOG.error("failed to save policies to cache file '" + backupCacheFile.getAbsolutePath() + "'", excp); + } + + RangerPerfTracer.log(perf); + + } + } } else { LOG.info("policies is null. Nothing to save in cache"); } @@ -458,6 +483,51 @@ public void saveToCache(ServicePolicies policies) { } } + private void deleteOldestVersionCacheFileInCacheDirectory(File cacheDirectory) { + int maxVersionsToPreserve = plugIn.getConfig().getInt(plugIn.getConfig().getPropertyPrefix() + "max.versions.to.preserve", 1); + FileFilter logFileFilter = (file) -> file.getName().matches(".+json_.+"); + + File[] filesInParent = cacheDirectory.listFiles(logFileFilter); + List policyVersions = new ArrayList<>(); + + if (filesInParent != null && filesInParent.length > 0) { + for (File f : filesInParent) { + String fileName = f.getName(); + // Extract the part after json_ + int policyVersionIdx = fileName.lastIndexOf("json_"); + String policyVersionStr = fileName.substring(policyVersionIdx + 5); + Long policyVersion = Long.valueOf(policyVersionStr); + policyVersions.add(policyVersion); + } + } else { + LOG.info("No files matching '.+json_*' found"); + } + + if (!policyVersions.isEmpty()) { + policyVersions.sort(new Comparator() { + @Override + public int compare(Long o1, Long o2) { + if (o1.equals(o2)) return 0; + return o1 < o2 ? -1 : 1; + } + }); + } + + if (policyVersions.size() > maxVersionsToPreserve) { + String fileName = this.cacheFileName + "_" + Long.toString(policyVersions.get(0)); + String pathName = cacheDirectory.getAbsolutePath() + File.separator + fileName; + File toDelete = new File(pathName); + if (toDelete.exists()) { + boolean isDeleted = toDelete.delete(); + if (LOG.isDebugEnabled()) { + LOG.debug("file :[" + pathName + "] is deleted" + isDeleted); + } + } else { + LOG.info("File: " + pathName + " does not exist!"); + } + } + } + private void disableCache() { if (LOG.isDebugEnabled()) { LOG.debug("==> PolicyRefresher.disableCache(serviceName=" + serviceName + ")"); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java index bc52bdb406..72443b79d7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerAccessRequestUtil.java @@ -19,33 +19,44 @@ package org.apache.ranger.plugin.util; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.apache.ranger.plugin.policyengine.gds.GdsAccessResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerAccessRequestUtil { - private static final Log LOG = LogFactory.getLog(RangerAccessRequestUtil.class); - - public static final String KEY_CONTEXT_TAGS = "TAGS"; - public static final String KEY_CONTEXT_TAG_OBJECT = "TAG_OBJECT"; - public static final String KEY_CONTEXT_RESOURCE = "RESOURCE"; - public static final String KEY_CONTEXT_REQUESTED_RESOURCES = "REQUESTED_RESOURCES"; - public static final String KEY_CONTEXT_USERSTORE = "USERSTORE"; - public static final String KEY_TOKEN_NAMESPACE = "token:"; - public static final String KEY_USER = "USER"; - public static final String KEY_OWNER = "OWNER"; - public static final String KEY_ROLES = "ROLES"; + private static final Logger LOG = LoggerFactory.getLogger(RangerAccessRequestUtil.class); + + public static final String KEY_CONTEXT_TAGS = "TAGS"; + public static final String KEY_CONTEXT_TAG_OBJECT = "TAG_OBJECT"; + public static final String KEY_CONTEXT_RESOURCE = "RESOURCE"; + public static final String KEY_CONTEXT_REQUESTED_RESOURCES = "REQUESTED_RESOURCES"; + public static final String KEY_CONTEXT_USERSTORE = "USERSTORE"; + public static final String KEY_TOKEN_NAMESPACE = "token:"; + public static final String KEY_USER = "USER"; + public static final String KEY_OWNER = "OWNER"; + public static final String KEY_ROLES = "ROLES"; + public static final String KEY_CONTEXT_IS_ANY_ACCESS = "ISANYACCESS"; + public static final String KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS = "ALLACCESSTYPEGROUPS"; + public static final String KEY_CONTEXT_ALL_ACCESSTYPES = "ALLACCESSTYPES"; + public static final String KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES = "IGNOREIFNOTDENIEDACCESSTYPES"; + public static final String KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS = "ALL_ACCESS_TYPE_RESULTS"; + public static final String KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS = "ALL_ACCESS_TYPE_ACL_RESULTS"; + + public static final String KEY_CONTEXT_REQUEST = "_REQUEST"; + public static final String KEY_CONTEXT_GDS_RESULT = "_GDS_RESULT"; + public static final String KEY_CONTEXT_IS_REQUEST_PREPROCESSED = "ISREQUESTPREPROCESSED"; + public static final String KEY_CONTEXT_RESOURCE_ZONE_NAMES = "RESOURCE_ZONE_NAMES"; public static void setRequestTagsInContext(Map context, Set tags) { - if(CollectionUtils.isEmpty(tags)) { + if (CollectionUtils.isEmpty(tags)) { context.remove(KEY_CONTEXT_TAGS); } else { context.put(KEY_CONTEXT_TAGS, tags); @@ -54,12 +65,11 @@ public static void setRequestTagsInContext(Map context, Set getRequestTagsFromContext(Map context) { Set ret = null; - Object val = context.get(RangerAccessRequestUtil.KEY_CONTEXT_TAGS); + Object val = context.get(RangerAccessRequestUtil.KEY_CONTEXT_TAGS); if (val instanceof Set) { try { - @SuppressWarnings("unchecked") - Set tags = (Set) val; + @SuppressWarnings("unchecked") Set tags = (Set) val; ret = tags; } catch (Throwable t) { @@ -76,10 +86,10 @@ public static void setCurrentTagInContext(Map context, RangerTag public static RangerTagForEval getCurrentTagFromContext(Map context) { RangerTagForEval ret = null; - Object val = context.get(KEY_CONTEXT_TAGS); + Object val = context.get(KEY_CONTEXT_TAG_OBJECT); - if(val instanceof RangerTagForEval) { - ret = (RangerTagForEval)val; + if (val instanceof RangerTagForEval) { + ret = (RangerTagForEval) val; } return ret; @@ -93,8 +103,8 @@ public static RangerRequestedResources getRequestedResourcesFromContext(Map context, Rang public static RangerAccessResource getCurrentResourceFromContext(Map context) { RangerAccessResource ret = null; - Object val = context.get(KEY_CONTEXT_RESOURCE); + Object val = MapUtils.isNotEmpty(context) ? context.get(KEY_CONTEXT_RESOURCE) : null; - if(val instanceof RangerAccessResource) { - ret = (RangerAccessResource)val; + if (val instanceof RangerAccessResource) { + ret = (RangerAccessResource) val; } return ret; @@ -118,7 +128,7 @@ public static RangerAccessResource getCurrentResourceFromContext(Map copyContext(Map context) { final Map ret; - if(MapUtils.isEmpty(context)) { + if (MapUtils.isEmpty(context)) { ret = new HashMap<>(); } else { ret = new HashMap<>(context); @@ -126,6 +136,15 @@ public static Map copyContext(Map context) { ret.remove(KEY_CONTEXT_TAGS); ret.remove(KEY_CONTEXT_TAG_OBJECT); ret.remove(KEY_CONTEXT_RESOURCE); + ret.remove(KEY_CONTEXT_REQUEST); + ret.remove(KEY_CONTEXT_GDS_RESULT); + ret.remove(KEY_CONTEXT_IS_ANY_ACCESS); + ret.remove(KEY_CONTEXT_ALL_ACCESSTYPES); + ret.remove(KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS); + ret.remove(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS); + ret.remove(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS); + ret.remove(KEY_CONTEXT_IS_REQUEST_PREPROCESSED); + ret.remove(KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES); // don't remove REQUESTED_RESOURCES } @@ -135,9 +154,11 @@ public static Map copyContext(Map context) { public static void setCurrentUserInContext(Map context, String user) { setTokenInContext(context, KEY_USER, user); } + public static void setOwnerInContext(Map context, String owner) { setTokenInContext(context, KEY_OWNER, owner); } + public static String getCurrentUserFromContext(Map context) { Object ret = getTokenFromContext(context, KEY_USER); return ret != null ? ret.toString() : ""; @@ -147,6 +168,7 @@ public static void setTokenInContext(Map context, String tokenNa String tokenNameWithNamespace = KEY_TOKEN_NAMESPACE + tokenName; context.put(tokenNameWithNamespace, tokenValue); } + public static Object getTokenFromContext(Map context, String tokenName) { String tokenNameWithNamespace = KEY_TOKEN_NAMESPACE + tokenName; return MapUtils.isNotEmpty(context) ? context.get(tokenNameWithNamespace) : null; @@ -155,23 +177,272 @@ public static Object getTokenFromContext(Map context, String tok public static void setCurrentUserRolesInContext(Map context, Set roles) { setTokenInContext(context, KEY_ROLES, roles); } + public static Set getCurrentUserRolesFromContext(Map context) { Object ret = getTokenFromContext(context, KEY_ROLES); return ret != null ? (Set) ret : Collections.EMPTY_SET; } + public static Set getUserRoles(RangerAccessRequest request) { + Set ret = Collections.EMPTY_SET; + + if (request != null) { + ret = request.getUserRoles(); + + if (CollectionUtils.isEmpty(ret)) { + ret = RangerAccessRequestUtil.getCurrentUserRolesFromContext(request.getContext()); + } + } + + return ret; + } + public static void setRequestUserStoreInContext(Map context, RangerUserStore rangerUserStore) { context.put(KEY_CONTEXT_USERSTORE, rangerUserStore); } public static RangerUserStore getRequestUserStoreFromContext(Map context) { RangerUserStore ret = null; - Object val = context.get(KEY_CONTEXT_USERSTORE); + Object val = context.get(KEY_CONTEXT_USERSTORE); - if(val instanceof RangerUserStore) { + if (val instanceof RangerUserStore) { ret = (RangerUserStore) val; } return ret; } + + public static void setIsAnyAccessInContext(Map context, Boolean value) { + context.put(KEY_CONTEXT_IS_ANY_ACCESS, value); + } + + public static boolean getIsAnyAccessInContext(Map context) { + Boolean value = (Boolean) context.get(KEY_CONTEXT_IS_ANY_ACCESS); + return value != null && value; + } + + public static void setIsRequestPreprocessed(Map context, Boolean value) { + context.put(KEY_CONTEXT_IS_REQUEST_PREPROCESSED, value); + } + + public static boolean getIsRequestPreprocessed(Map context) { + Boolean value = (Boolean) context.get(KEY_CONTEXT_IS_REQUEST_PREPROCESSED); + return value != null && value; + } + + public static void setAllRequestedAccessTypes(Map context, Set accessTypes) { + context.put(KEY_CONTEXT_ALL_ACCESSTYPES, accessTypes); + } + + public static void setIgnoreIfNotDeniedAccessTypes(Map context, Set accessTypes) { + context.put(KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES, accessTypes); + } + + public static Set getIgnoreIfNotDeniedAccessTypes(RangerAccessRequest request) { + Set ret = Collections.emptySet(); + + Object val = request.getContext().get(KEY_CONTEXT_IGNORE_IF_NOT_DENIED_ACCESSTYPES); + + if (val != null) { + if (val instanceof Set) { + ret = (Set) val; + } else if (val instanceof List) { + ret = new TreeSet<>((List) val); + } else { + LOG.error("getNotDeniedRequestedAccessTypes(): failed to get NOTDENIEDACCESSTYPES from context"); + } + } + return ret; + } + + + @SuppressWarnings("unchecked") + public static Set getAllRequestedAccessTypes(RangerAccessRequest request) { + Set ret = null; + + Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESSTYPES); + + if (val != null) { + if (val instanceof Set) { + ret = (Set) val; + } else if (val instanceof List) { + ret = new TreeSet<>((List) val); + } else { + LOG.error("getAllRequestedAccessTypes(): failed to get ALLACCESSTYPES from context"); + } + } + + return ret != null ? ret : Collections.singleton(request.getAccessType()); + } + + public static Set> getAllRequestedAccessTypeGroups(RangerAccessRequest request) { + Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS); + return (Set>) val; + } + + public static void setAllRequestedAccessTypeGroups(RangerAccessRequest request, Set> accessTypeGroups) { + if (accessTypeGroups == null || accessTypeGroups.isEmpty()) { + request.getContext().put(KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS, new TreeSet<>()); + } else { + request.getContext().put(KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS, accessTypeGroups); + } + } + + public static Map getAccessTypeResults(RangerAccessRequest request) { + Map ret; + Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS); + if (val == null) { + ret = new HashMap<>(); + request.getContext().put(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS, ret); + } else { + ret = (Map) val; + } + return ret; + } + + public static Map getAccessTypeACLResults(RangerAccessRequest request) { + Map ret; + Object val = request.getContext().get(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS); + if (val == null) { + ret = new HashMap<>(); + request.getContext().put(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS, ret); + } else { + ret = (Map) val; + } + return ret; + } + + public static void setRequestInContext(RangerAccessRequest request) { + Map context = request.getContext(); + + if (context != null) { + context.put(KEY_CONTEXT_REQUEST, request); + } + } + + public static RangerAccessRequest getRequestFromContext(Map context) { + RangerAccessRequest ret = null; + + if (context != null) { + Object val = context.get(KEY_CONTEXT_REQUEST); + + if (val != null) { + if (val instanceof RangerAccessRequest) { + ret = (RangerAccessRequest) val; + } else { + LOG.error("getRequestFromContext(): expected RangerAccessRequest, but found " + val.getClass().getCanonicalName()); + } + } + } + + return ret; + } + + public static void setGdsResultInContext(RangerAccessRequest request, GdsAccessResult result) { + Map context = request.getContext(); + + if (context != null) { + context.put(KEY_CONTEXT_GDS_RESULT, result); + } + } + + public static GdsAccessResult getGdsResultFromContext(Map context) { + GdsAccessResult ret = null; + + if (context != null) { + Object val = context.get(KEY_CONTEXT_GDS_RESULT); + + if (val != null) { + if (val instanceof GdsAccessResult) { + ret = (GdsAccessResult) val; + } else { + LOG.error("getGdsResultFromContext(): expected RangerGdsAccessResult, but found " + val.getClass().getCanonicalName()); + } + } + } + + return ret; + } + + public static void setResourceZoneNamesInContext(RangerAccessRequest request, Set zoneNames) { + Map context = request.getContext(); + + if (context != null) { + context.put(KEY_CONTEXT_RESOURCE_ZONE_NAMES, zoneNames); + } else { + LOG.error("setResourceZoneNamesInContext({}): context is null", request); + } + } + + @SuppressWarnings("unchecked") + public static Set getResourceZoneNamesFromContext(Map context) { + Set ret = null; + + if (context != null) { + Object val = context.get(KEY_CONTEXT_RESOURCE_ZONE_NAMES); + + if (val instanceof Set) { + ret = (Set) val; + } else { + if (val != null) { + LOG.error("getResourceZoneNamesFromContext(): expected Set, but found {}", val.getClass().getCanonicalName()); + } + } + } + + return ret; + } + + public static String getResourceZoneNameFromContext(Map context) { + Set ret = getResourceZoneNamesFromContext(context); + + return ret != null && ret.size() == 1 ? ret.iterator().next() : null; + } + + public static void setAccessTypeResults(Map context, Map accessTypeResults) { + if (context != null) { + if (accessTypeResults != null) { + context.put(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS, accessTypeResults); + } else { + context.remove(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS); + } + } + } + + public static void setAccessTypeACLResults(Map context, Map accessTypeResults) { + if (context != null) { + if (accessTypeResults != null) { + context.put(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS, accessTypeResults); + } else { + context.remove(KEY_CONTEXT_ALL_ACCESS_TYPE_ACL_RESULTS); + } + } + } + + public static Map getAccessTypeResults(Map context) { + Map ret = null; + + if (context != null) { + Object o = context.get(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS); + if (o != null) { + ret = (Map) o; + } + } + + return ret; + } + + public static void setAccessTypeResult(Map context, String accessType, RangerAccessResult result) { + if (context != null) { + Map results = getAccessTypeResults(context); + + if (results == null) { + results = new HashMap<>(); + + context.put(KEY_CONTEXT_ALL_ACCESS_TYPE_RESULTS, results); + } + + results.putIfAbsent(accessType, result); + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCache.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCache.java new file mode 100644 index 0000000000..9769aaa522 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCache.java @@ -0,0 +1,387 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.util; + +import com.sun.istack.NotNull; +import org.apache.ranger.plugin.util.AutoClosableLock.AutoClosableTryLock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantLock; + + +public class RangerCache { + private static final Logger LOG = LoggerFactory.getLogger(RangerCache.class); + + public enum RefreshMode { ON_ACCESS, ON_SCHEDULE } // when to refresh the value: when a value is accessed? or on a scheduled interval? + + private static final AtomicInteger CACHE_NUMBER = new AtomicInteger(1); + private static final String CACHE_LOADER_THREAD_PREFIX = "ranger-cache-"; + private static final int DEFAULT_LOADER_THREADS_COUNT = 10; + private static final RefreshMode DEFAULT_REFRESH_MODE = RefreshMode.ON_ACCESS; + private static final int DEFAULT_VALUE_VALIDITY_PERIOD_MS = 30 * 1000; + private static final int DEFAULT_VALUE_INIT_TIMEOUT_MS = -1; // infinite timeout + private static final int DEFAULT_VALUE_REFRESH_TIMEOUT_MS = 10; + + private final String name; + private final Map cache; + private ValueLoader loader; // loader implementation that fetches the value for a given key from the source + private final int loaderThreadsCount; // number of threads to use for loading values into cache + private final RefreshMode refreshMode; // when to refresh a cached value: when a value is accessed? or on a scheduled interval? + private final long valueValidityPeriodMs; // minimum interval before a cached value is refreshed + private final long valueInitLoadTimeoutMs; // max time a caller would wait if cache doesn't have the value + private final long valueRefreshLoadTimeoutMs; // max time a caller would wait if cache already has the value, but needs refresh + private final ExecutorService loaderThreadPool; + + protected RangerCache(String name, ValueLoader loader) { + this(name, loader, DEFAULT_LOADER_THREADS_COUNT, DEFAULT_REFRESH_MODE, DEFAULT_VALUE_VALIDITY_PERIOD_MS, DEFAULT_VALUE_INIT_TIMEOUT_MS, DEFAULT_VALUE_REFRESH_TIMEOUT_MS); + } + + protected RangerCache(String name, ValueLoader loader, int loaderThreadsCount, RefreshMode refreshMode, long valueValidityPeriodMs, long valueInitLoadTimeoutMs, long valueRefreshLoadTimeoutMs) { + this.name = name; + this.cache = new ConcurrentHashMap<>(); + this.loader = loader; + this.loaderThreadsCount = loaderThreadsCount; + this.refreshMode = refreshMode; + this.valueValidityPeriodMs = valueValidityPeriodMs; + this.valueInitLoadTimeoutMs = valueInitLoadTimeoutMs; + this.valueRefreshLoadTimeoutMs = valueRefreshLoadTimeoutMs; + + if (this.refreshMode == RefreshMode.ON_SCHEDULE) { + this.loaderThreadPool = Executors.newScheduledThreadPool(loaderThreadsCount, createThreadFactory()); + } else { + this.loaderThreadPool = Executors.newFixedThreadPool(loaderThreadsCount, createThreadFactory()); + } + + LOG.info("Created RangerCache(name={}): loaderThreadsCount={}, refreshMode={}, valueValidityPeriodMs={}, valueInitLoadTimeoutMs={}, valueRefreshLoadTimeoutMs={}", name, loaderThreadsCount, refreshMode, valueValidityPeriodMs, valueInitLoadTimeoutMs, valueRefreshLoadTimeoutMs); + } + + protected void setLoader(ValueLoader loader) { this.loader = loader; } + + public String getName() { return name; } + + public ValueLoader getLoader() { return loader; } + + public int getLoaderThreadsCount() { return loaderThreadsCount; } + + public RefreshMode getRefreshMode() { return refreshMode; } + + public long getValueValidityPeriodMs() { return valueValidityPeriodMs; } + + public long getValueInitLoadTimeoutMs() { return valueInitLoadTimeoutMs; } + + public long getValueRefreshLoadTimeoutMs() { return valueRefreshLoadTimeoutMs; } + + public V get(K key) { + return get(key, null); + } + + public Set getKeys() { + return new HashSet<>(cache.keySet()); + } + + public void addIfAbsent(K key) { + cache.computeIfAbsent(key, f -> new CachedValue(key)); + } + + public V remove(K key) { + CachedValue value = cache.remove(key); + final V ret; + + if (value != null) { + value.isRemoved = true; // so that the refresher thread doesn't schedule next refresh + + ret = value.getCurrentValue(); + } else { + ret = null; + } + + return ret; + } + + public boolean isLoaded(K key) { + CachedValue entry = cache.get(key); + RefreshableValue value = entry != null ? entry.value : null; + + return value != null; + } + + protected V get(K key, Object context) { + final long startTime = System.currentTimeMillis(); + final CachedValue value = cache.computeIfAbsent(key, f -> new CachedValue(key)); + final long timeoutMs = value.isInitialized() ? valueRefreshLoadTimeoutMs : valueInitLoadTimeoutMs; + final V ret; + + if (timeoutMs >= 0) { + final long timeTaken = System.currentTimeMillis() - startTime; + + if (timeoutMs <= timeTaken) { + ret = value.getCurrentValue(); + + if (LOG.isDebugEnabled()) { + LOG.debug("key={}: cache-lookup={}ms took longer than timeout={}ms. Using current value {}", key, timeTaken, timeoutMs, ret); + } + } else { + ret = value.getValue(timeoutMs - timeTaken); + } + } else { + ret = value.getValue(context); + } + + return ret; + } + + public static class RefreshableValue { + private final V value; + private long nextRefreshTimeMs = -1; + + public RefreshableValue(V value) { + this.value = value; + } + + public V getValue() { return value; } + + public boolean needsRefresh() { + return nextRefreshTimeMs == -1 || System.currentTimeMillis() > nextRefreshTimeMs; + } + + private void setNextRefreshTimeMs(long nextRefreshTimeMs) {this.nextRefreshTimeMs = nextRefreshTimeMs; } + } + + public static abstract class ValueLoader { + public abstract RefreshableValue load(K key, RefreshableValue currentValue, Object context) throws Exception; + } + + private class CachedValue { + private final ReentrantLock lock = new ReentrantLock(); + private final K key; + private volatile boolean isRemoved = false; + private volatile RefreshableValue value = null; + private volatile Future refresher = null; + + private CachedValue(K key) { + if (LOG.isDebugEnabled()) { + LOG.debug("CachedValue({})", key); + } + + this.key = key; + } + + public K getKey() { return key; } + + public V getValue(Object context) { + refreshIfNeeded(context); + + return getCurrentValue(); + } + + public V getValue(long timeoutMs, Object context) { + if (timeoutMs < 0) { + refreshIfNeeded(context); + } else { + refreshIfNeeded(timeoutMs, context); + } + + return getCurrentValue(); + } + + public V getCurrentValue() { + RefreshableValue value = this.value; + + return value != null ? value.getValue() : null; + } + + public boolean needsRefresh() { + return !isInitialized() || (refreshMode == RefreshMode.ON_ACCESS && value.needsRefresh()); + } + + public boolean isInitialized() { + RefreshableValue value = this.value; + + return value != null; + } + + private void refreshIfNeeded(Object context) { + if (needsRefresh()) { + try (AutoClosableLock ignored = new AutoClosableLock(lock)) { + if (needsRefresh()) { + Future future = this.refresher; + + if (future == null) { // refresh from current thread + if (LOG.isDebugEnabled()) { + LOG.debug("refreshIfNeeded(key={}): using caller thread", key); + } + + refreshValue(context); + } else { // wait for the refresher to complete + try { + future.get(); + + this.refresher = null; + } catch (InterruptedException | ExecutionException excp) { + LOG.warn("refreshIfNeeded(key={}) failed", key, excp); + } + } + } + } + } + } + + private void refreshIfNeeded(long timeoutMs, Object context) { + if (needsRefresh()) { + long startTime = System.currentTimeMillis(); + + try (AutoClosableTryLock tryLock = new AutoClosableTryLock(lock, timeoutMs, TimeUnit.MILLISECONDS)) { + if (tryLock.isLocked()) { + if (needsRefresh()) { + Future future = this.refresher; + + if (future == null) { + future = this.refresher = loaderThreadPool.submit(new RefreshWithContext(context)); + + if (LOG.isDebugEnabled()) { + LOG.debug("refresher scheduled for key {}", key); + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("refresher already exists for key {}", key); + } + } + + long timeLeftMs = timeoutMs - (System.currentTimeMillis() - startTime); + + if (timeLeftMs > 0) { + try { + future.get(timeLeftMs, TimeUnit.MILLISECONDS); + + this.refresher = null; + } catch (TimeoutException | InterruptedException | ExecutionException excp) { + if (LOG.isDebugEnabled()) { + LOG.debug("refreshIfNeeded(key={}, timeoutMs={}) failed", key, timeoutMs, excp); + } + } + } + } + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("refreshIfNeeded(key={}, timeoutMs={}) couldn't obtain lock", key, timeoutMs); + } + } + } + } + } + + private Boolean refreshValue(Object context) { + long startTime = System.currentTimeMillis(); + boolean isSuccess = false; + RefreshableValue newValue = null; + + try { + ValueLoader loader = RangerCache.this.loader; + + if (loader != null) { + newValue = loader.load(key, value, context); + isSuccess = true; + } + } catch (KeyNotFoundException excp) { + LOG.debug("refreshValue(key={}) failed with KeyNotFoundException. Removing it", key, excp); + + remove(key); // remove the key from cache (so that next get() will try to load it again + } catch (Exception excp) { + LOG.warn("refreshValue(key={}) failed", key, excp); + + // retain the old value, update the loadTime + newValue = value; + } finally { + if (LOG.isDebugEnabled()) { + LOG.debug("refresher {} for key {}, timeTaken={}", (isSuccess ? "completed" : "failed"), key, (System.currentTimeMillis() - startTime)); + } + + setValue(newValue); + + if (refreshMode == RefreshMode.ON_SCHEDULE) { + if (!isRemoved) { + ScheduledExecutorService scheduledExecutor = ((ScheduledExecutorService) loaderThreadPool); + + scheduledExecutor.schedule(new RefreshWithContext(context), valueValidityPeriodMs, TimeUnit.MILLISECONDS); + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("key {} was removed. Not scheduling next refresh ", key); + } + } + } + } + + return Boolean.TRUE; + } + + private void setValue(RefreshableValue value) { + if (value != null) { + this.value = value; + + this.value.setNextRefreshTimeMs(System.currentTimeMillis() + valueValidityPeriodMs); + } + } + + private class RefreshWithContext implements Callable { + private final Object context; + + public RefreshWithContext(Object context) { + this.context = context; + } + + @Override + public Boolean call() { + return refreshValue(context); + } + } + } + + private ThreadFactory createThreadFactory() { + return new ThreadFactory() { + private final String namePrefix = CACHE_LOADER_THREAD_PREFIX + CACHE_NUMBER.getAndIncrement() + "-" + name; + private final AtomicInteger number = new AtomicInteger(1); + + @Override + public Thread newThread(@NotNull Runnable r) { + Thread t = new Thread(r, namePrefix + number.getAndIncrement()); + + if (!t.isDaemon()) { + t.setDaemon(true); + } + + if (t.getPriority() != Thread.NORM_PRIORITY) { + t.setPriority(Thread.NORM_PRIORITY); + } + + return t; + } + }; + } + + public static class KeyNotFoundException extends Exception { + public KeyNotFoundException(String msg) { + super(msg); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java index 5ecb280261..9d6e1f0b54 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerCommonConstants.java @@ -21,7 +21,171 @@ public class RangerCommonConstants { - public static final String PROP_COOKIE_NAME = "ranger.admin.cookie.name"; - public static final String DEFAULT_COOKIE_NAME = "RANGERADMINSESSIONID"; + private RangerCommonConstants() { -} \ No newline at end of file + } + + public static final String PROP_COOKIE_NAME = "ranger.admin.cookie.name"; + public static final String DEFAULT_COOKIE_NAME = "RANGERADMINSESSIONID"; + + public static final String RANGER_ADMIN_SUFFIX_POLICY_DELTA = ".supports.policy.deltas"; + public static final String PLUGIN_CONFIG_SUFFIX_POLICY_DELTA = ".supports.policy.deltas"; + + public static final String RANGER_ADMIN_SUFFIX_TAG_DELTA = ".supports.tag.deltas"; + public static final String PLUGIN_CONFIG_SUFFIX_TAG_DELTA = ".supports.tag.deltas"; + + public static final String RANGER_ADMIN_SUFFIX_IN_PLACE_POLICY_UPDATES = ".supports.in.place.policy.updates"; + public static final String PLUGIN_CONFIG_SUFFIX_IN_PLACE_POLICY_UPDATES = ".supports.in.place.policy.updates"; + + public static final String RANGER_ADMIN_SUFFIX_IN_PLACE_TAG_UPDATES = ".supports.in.place.tag.updates"; + public static final String PLUGIN_CONFIG_SUFFIX_IN_PLACE_TAG_UPDATES = ".supports.in.place.tag.updates"; + + public static final String RANGER_SUPPORTS_TAGS_DEDUP = ".supports.tags.dedup"; + + public static final boolean RANGER_ADMIN_SUFFIX_POLICY_DELTA_DEFAULT = false; + public static final boolean PLUGIN_CONFIG_SUFFIX_POLICY_DELTA_DEFAULT = false; + + public static final boolean RANGER_ADMIN_SUFFIX_TAG_DELTA_DEFAULT = false; + public static final boolean PLUGIN_CONFIG_SUFFIX_TAG_DELTA_DEFAULT = false; + + public static final boolean RANGER_ADMIN_SUFFIX_IN_PLACE_POLICY_UPDATES_DEFAULT = false; + public static final boolean PLUGIN_CONFIG_SUFFIX_IN_PLACE_POLICY_UPDATES_DEFAULT = false; + + public static final boolean RANGER_ADMIN_SUFFIX_IN_PLACE_TAG_UPDATES_DEFAULT = false; + public static final boolean PLUGIN_CONFIG_SUFFIX_IN_PLACE_TAG_UPDATES_DEFAULT = false; + + public static final boolean RANGER_SUPPORTS_TAGS_DEDUP_DEFAULT = true; + + public static final boolean POLICY_REST_CLIENT_SESSION_COOKIE_ENABLED = true; + + public static final String SCRIPT_OPTION_ENABLE_JSON_CTX = "enableJsonCtx"; + + public static final String SCRIPT_VAR_ctx = "ctx"; + public static final String SCRIPT_VAR_tag = "tag"; + public static final String SCRIPT_VAR_tagAttr = "tagAttr"; + public static final String SCRIPT_VAR__CTX = "_ctx"; + public static final String SCRIPT_VAR__CTX_JSON = "_ctx_json"; + public static final String SCRIPT_VAR_REQ = "REQ"; + public static final String SCRIPT_VAR_RES = "RES"; + public static final String SCRIPT_VAR_TAG = "TAG"; + public static final String SCRIPT_VAR_TAGNAMES = "TAGNAMES"; + public static final String SCRIPT_VAR_TAGS = "TAGS"; + public static final String SCRIPT_VAR_UGA = "UGA"; + public static final String SCRIPT_VAR_UG = "UG"; + public static final String SCRIPT_VAR_UGNAMES = "UGNAMES"; + public static final String SCRIPT_VAR_URNAMES = "URNAMES"; + public static final String SCRIPT_VAR_USER = "USER"; + + public static final String SCRIPT_FIELD_ACCESS_TIME = "accessTime"; + public static final String SCRIPT_FIELD_ACCESS_TYPE = "accessType"; + public static final String SCRIPT_FIELD_ACTION = "action"; + public static final String SCRIPT_FIELD_CLIENT_IP_ADDRESS = "clientIPAddress"; + public static final String SCRIPT_FIELD_CLIENT_TYPE = "clientType"; + public static final String SCRIPT_FIELD_CLUSTER_NAME = "clusterName"; + public static final String SCRIPT_FIELD_CLUSTER_TYPE = "clusterType"; + public static final String SCRIPT_FIELD_FORWARDED_ADDRESSES = "forwardedAddresses"; + public static final String SCRIPT_FIELD__MATCH_TYPE = "_matchType"; + public static final String SCRIPT_FIELD__NAME = "_name"; + public static final String SCRIPT_FIELD__SYNC_SOURCE = "_syncSource"; + public static final String SCRIPT_FIELD__IS_INTERNAL = "_isInternal"; + public static final String SCRIPT_FIELD__EMAIL_ADDRESS = "_emailAddress"; + public static final String SCRIPT_FIELD__OWNER_USER = "_ownerUser"; + public static final String SCRIPT_FIELD_REMOTE_IP_ADDRESS = "remoteIPAddress"; + public static final String SCRIPT_FIELD_REQUEST = "request"; + public static final String SCRIPT_FIELD_REQUEST_DATA = "requestData"; + public static final String SCRIPT_FIELD_RESOURCE = "resource"; + public static final String SCRIPT_FIELD_RESOURCE_MATCHING_SCOPE = "resourceMatchingScope"; + public static final String SCRIPT_FIELD_TAG = "tag"; + public static final String SCRIPT_FIELD_TAGS = "tags"; + public static final String SCRIPT_FIELD_TAG_NAMES = "tagNames"; + public static final String SCRIPT_FIELD__TYPE = "_type"; + public static final String SCRIPT_FIELD_USER = "user"; + public static final String SCRIPT_FIELD_USER_ATTRIBUTES = "userAttributes"; + public static final String SCRIPT_FIELD_USER_GROUPS = "userGroups"; + public static final String SCRIPT_FIELD_USER_GROUP_ATTRIBUTES = "userGroupAttributes"; + public static final String SCRIPT_FIELD_UGA = "uga"; + public static final String SCRIPT_FIELD_USER_ROLES = "userRoles"; + + public static final String SCRIPT_MACRO_GET_TAG_NAMES = "GET_TAG_NAMES"; + public static final String SCRIPT_MACRO_GET_TAG_NAMES_Q = "GET_TAG_NAMES_Q"; + public static final String SCRIPT_MACRO_GET_TAG_ATTR_NAMES = "GET_TAG_ATTR_NAMES"; + public static final String SCRIPT_MACRO_GET_TAG_ATTR_NAMES_Q = "GET_TAG_ATTR_NAMES_Q"; + public static final String SCRIPT_MACRO_GET_TAG_ATTR = "GET_TAG_ATTR"; + public static final String SCRIPT_MACRO_GET_TAG_ATTR_Q = "GET_TAG_ATTR_Q"; + public static final String SCRIPT_MACRO_GET_UG_NAMES = "GET_UG_NAMES"; + public static final String SCRIPT_MACRO_GET_UG_NAMES_Q = "GET_UG_NAMES_Q"; + public static final String SCRIPT_MACRO_GET_UG_ATTR_NAMES = "GET_UG_ATTR_NAMES"; + public static final String SCRIPT_MACRO_GET_UG_ATTR_NAMES_Q = "GET_UG_ATTR_NAMES_Q"; + public static final String SCRIPT_MACRO_GET_UG_ATTR = "GET_UG_ATTR"; + public static final String SCRIPT_MACRO_GET_UG_ATTR_Q = "GET_UG_ATTR_Q"; + public static final String SCRIPT_MACRO_GET_UR_NAMES = "GET_UR_NAMES"; + public static final String SCRIPT_MACRO_GET_UR_NAMES_Q = "GET_UR_NAMES_Q"; + public static final String SCRIPT_MACRO_GET_USER_ATTR_NAMES = "GET_USER_ATTR_NAMES"; + public static final String SCRIPT_MACRO_GET_USER_ATTR_NAMES_Q = "GET_USER_ATTR_NAMES_Q"; + public static final String SCRIPT_MACRO_GET_USER_ATTR = "GET_USER_ATTR"; + public static final String SCRIPT_MACRO_GET_USER_ATTR_Q = "GET_USER_ATTR_Q"; + + public static final String SCRIPT_MACRO_GET_TAG_ATTR_CSV = "GET_TAG_ATTR_CSV"; + public static final String SCRIPT_MACRO_GET_TAG_ATTR_Q_CSV = "GET_TAG_ATTR_Q_CSV"; + public static final String SCRIPT_MACRO_GET_UG_ATTR_CSV = "GET_UG_ATTR_CSV"; + public static final String SCRIPT_MACRO_GET_UG_ATTR_Q_CSV = "GET_UG_ATTR_Q_CSV"; + public static final String SCRIPT_MACRO_TAG_ATTR_NAMES_CSV = "TAG_ATTR_NAMES_CSV"; + public static final String SCRIPT_MACRO_TAG_ATTR_NAMES_Q_CSV = "TAG_ATTR_NAMES_Q_CSV"; + public static final String SCRIPT_MACRO_TAG_NAMES_CSV = "TAG_NAMES_CSV"; + public static final String SCRIPT_MACRO_TAG_NAMES_Q_CSV = "TAG_NAMES_Q_CSV"; + public static final String SCRIPT_MACRO_UG_ATTR_NAMES_CSV = "UG_ATTR_NAMES_CSV"; + public static final String SCRIPT_MACRO_UG_ATTR_NAMES_Q_CSV = "UG_ATTR_NAMES_Q_CSV"; + public static final String SCRIPT_MACRO_UG_NAMES_CSV = "UG_NAMES_CSV"; + public static final String SCRIPT_MACRO_UG_NAMES_Q_CSV = "UG_NAMES_Q_CSV"; + public static final String SCRIPT_MACRO_UR_NAMES_CSV = "UR_NAMES_CSV"; + public static final String SCRIPT_MACRO_UR_NAMES_Q_CSV = "UR_NAMES_Q_CSV"; + public static final String SCRIPT_MACRO_USER_ATTR_NAMES_CSV = "USER_ATTR_NAMES_CSV"; + public static final String SCRIPT_MACRO_USER_ATTR_NAMES_Q_CSV = "USER_ATTR_NAMES_Q_CSV"; + + public static final String SCRIPT_MACRO_HAS_TAG = "HAS_TAG"; + public static final String SCRIPT_MACRO_HAS_ANY_TAG = "HAS_ANY_TAG"; + public static final String SCRIPT_MACRO_HAS_NO_TAG = "HAS_NO_TAG"; + public static final String SCRIPT_MACRO_HAS_USER_ATTR = "HAS_USER_ATTR"; + public static final String SCRIPT_MACRO_HAS_UG_ATTR = "HAS_UG_ATTR"; + public static final String SCRIPT_MACRO_HAS_TAG_ATTR = "HAS_TAG_ATTR"; + public static final String SCRIPT_MACRO_IS_IN_GROUP = "IS_IN_GROUP"; + public static final String SCRIPT_MACRO_IS_IN_ROLE = "IS_IN_ROLE"; + public static final String SCRIPT_MACRO_IS_IN_ANY_GROUP = "IS_IN_ANY_GROUP"; + public static final String SCRIPT_MACRO_IS_IN_ANY_ROLE = "IS_IN_ANY_ROLE"; + public static final String SCRIPT_MACRO_IS_NOT_IN_ANY_GROUP = "IS_NOT_IN_ANY_GROUP"; + public static final String SCRIPT_MACRO_IS_NOT_IN_ANY_ROLE = "IS_NOT_IN_ANY_ROLE"; + public static final String SCRIPT_MACRO_IS_ACCESS_TIME_AFTER = "IS_ACCESS_TIME_AFTER"; + public static final String SCRIPT_MACRO_IS_ACCESS_TIME_BEFORE = "IS_ACCESS_TIME_BEFORE"; + public static final String SCRIPT_MACRO_IS_ACCESS_TIME_BETWEEN = "IS_ACCESS_TIME_BETWEEN"; + + public static final String SCRIPT_POLYFILL_INCLUDES = "if (!Array.prototype.includes) {\n" + + " Object.defineProperty(\n" + + " Array.prototype, 'includes', {\n" + + " value: function(valueToFind) {\n" + + " var o = Object(this); \n" + + " var len = o.length;\n" + + " if (len === 0) { return false; }\n" + + " for (var k=0; k < len; k++) {\n" + + " if (o[k]==valueToFind) {return true; }\n" + + " } \n" + + " return false;\n" + + " }\n" + + " }\n" + + " );\n" + + " }; "; + + public static final String SCRIPT_POLYFILL_INTERSECTS = "if (!Array.prototype.intersects) {\n" + + " Object.defineProperty(\n" + + " Array.prototype, 'intersects', {\n" + + " value: function (x) {\n" + + " if (x == null) {return false;}\n" + + " var o = Object(this);\n" + + " var len = o.length >>> 0;\n" + + " if (len === 0) { return false; }\n" + + " var result = o.filter(function(n) { return x.indexOf(n) > -1;})\n" + + " return result.length != 0;\n" + + " }\n" + + " }\n" + + " );\n" + + "}; "; +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.java index f6eef2f1f3..68a043e2c0 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerMetricsUtil.java @@ -20,10 +20,10 @@ package org.apache.ranger.plugin.util; import org.apache.commons.lang.StringUtils; -import org.apache.log4j.Logger; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.model.RangerMetrics; - -import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileWriter; @@ -43,7 +43,7 @@ */ public class RangerMetricsUtil { - private static final Logger LOG = Logger.getLogger(RangerMetricsUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerMetricsUtil.class); private static final OperatingSystemMXBean OS; private static final MemoryMXBean MEM_BEAN; public static final String NL = System.getProperty("line.separator"); @@ -54,7 +54,7 @@ public class RangerMetricsUtil { private static final String JVM_MACHINE_REPRESENTATION_NAME = RUNTIME.getName(); private static final long UP_TIME_OF_JVM = RUNTIME.getUptime(); private static final String JVM_VENDOR_NAME = RUNTIME.getVmVendor(); - + private static int IS_ROLE_ACTIVE = 0; static { OS = ManagementFactory.getOperatingSystemMXBean(); @@ -157,6 +157,7 @@ public RangerMetrics getVMStatus() { vmDetails.put("Up time of JVM", UP_TIME_OF_JVM); vmDetails.put("JVM Vendor Name", JVM_VENDOR_NAME); vmDetails.putAll(getValues()); + vmDetails.put("isRoleActive",getIsRoleActive()); jvm.put("jvm", vmDetails); if (LOG.isDebugEnabled()) { @@ -166,7 +167,15 @@ public RangerMetrics getVMStatus() { return new RangerMetrics(jvm); } - public void writeMetricsToFile(File filePath) throws Throwable { + public static int getIsRoleActive() { + return IS_ROLE_ACTIVE; + } + + public static void setIsRoleActive(int isRoleActive) { + IS_ROLE_ACTIVE = isRoleActive; + } + + public void writeMetricsToFile(File filePath) throws Throwable { RangerMetrics rangerMetrics = null; rangerMetrics = getVMStatus(); @@ -177,9 +186,8 @@ public void writeMetricsToFile(File filePath) throws Throwable { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerMetricsUtil.writeMetricsToFIle() for path: "+ filePath); } - Gson gson = new Gson(); try (FileWriter file = new FileWriter(filePath)) { - gson.toJson(rangerMetrics, file); + JsonUtils.objectToWriter(file, rangerMetrics); file.flush(); } catch (Exception e ) { LOG.error("RangerMetricsUtil.writeMetricsToFile() got an error",e); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfCollectorTracer.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfCollectorTracer.java index 353f7daec6..bc27ab24a9 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfCollectorTracer.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfCollectorTracer.java @@ -19,19 +19,33 @@ package org.apache.ranger.plugin.util; -import org.apache.commons.logging.Log; +import org.slf4j.Logger; + +import java.lang.management.ThreadInfo; public class RangerPerfCollectorTracer extends RangerPerfTracer { - private final long startTimeNanos; - public RangerPerfCollectorTracer(Log logger, String tag, String data) { - super(logger, tag, data); - startTimeNanos = System.nanoTime(); + public RangerPerfCollectorTracer(Logger logger, String tag, String data, ThreadInfo threadInfo) { + super(logger, tag, data, threadInfo); } @Override public void log() { - // Collect elapsed time in microseconds - PerfDataRecorder.recordStatistic(tag, ((System.nanoTime() - startTimeNanos) + 500) / 1000); + // Uncomment following line if the perf log for each individual call details to this needs to be logged in the perf log + //super.log(); + long elapsedTime = Math.max(getElapsedUserTime(), getElapsedCpuTime()); + long reportingThreshold = threadInfo == null ? 0L : (1000000/1000 - 1); // just about a microsecond + + if (elapsedTime > reportingThreshold) { + PerfDataRecorder.recordStatistic(tag, (getElapsedCpuTime()+500)/1000, (getElapsedUserTime() + 500)/1000); + } } + + @Override + public void logAlways() { + // Uncomment following line if the perf log for each individual call details to this needs to be logged in the perf log + //super.logAlways(); + + // Collect elapsed time in microseconds + PerfDataRecorder.recordStatistic(tag, (getElapsedCpuTime()+500)/1000, (getElapsedUserTime() + 500)/1000); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracer.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracer.java index 222c859793..6edcb12c0e 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracer.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracer.java @@ -19,49 +19,60 @@ package org.apache.ranger.plugin.util; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.management.ThreadInfo; public class RangerPerfTracer { - protected final Log logger; + protected final Logger logger; protected final String tag; protected final String data; - private final long startTimeMs; - - private static long reportingThresholdMs; + protected final ThreadInfo threadInfo; + protected final long startTime; + protected final long userStartTime; private final static String tagEndMarker = "("; - public static Log getPerfLogger(String name) { - return LogFactory.getLog("org.apache.ranger.perf." + name); + public static Logger getPerfLogger(String name) { + return LoggerFactory.getLogger("org.apache.ranger.perf." + name); } - public static Log getPerfLogger(Class cls) { + public static Logger getPerfLogger(Class cls) { return RangerPerfTracer.getPerfLogger(cls.getName()); } - public static boolean isPerfTraceEnabled(Log logger) { + public static boolean isPerfTraceEnabled(Logger logger) { return logger.isDebugEnabled(); } - public static RangerPerfTracer getPerfTracer(Log logger, String tag) { - String data = ""; - String realTag = ""; - - if (tag != null) { - int indexOfTagEndMarker = StringUtils.indexOf(tag, tagEndMarker); - if (indexOfTagEndMarker != -1) { - realTag = StringUtils.substring(tag, 0, indexOfTagEndMarker); - data = StringUtils.substring(tag, indexOfTagEndMarker); - } else { - realTag = tag; + public static RangerPerfTracer getPerfTracer(Logger logger, String tag) { + if (logger.isDebugEnabled()) { + String data = ""; + String realTag = ""; + + if (tag != null) { + int indexOfTagEndMarker = StringUtils.indexOf(tag, tagEndMarker); + + if (indexOfTagEndMarker != -1) { + realTag = StringUtils.substring(tag, 0, indexOfTagEndMarker); + + if (!PerfDataRecorder.collectStatistics()) { + data = StringUtils.substring(tag, indexOfTagEndMarker); + } + } else { + realTag = tag; + } } + + return RangerPerfTracerFactory.getPerfTracer(logger, realTag, data); + } else { + return null; } - return RangerPerfTracerFactory.getPerfTracer(logger, realTag, data); } - public static RangerPerfTracer getPerfTracer(Log logger, String tag, String data) { + public static RangerPerfTracer getPerfTracer(Logger logger, String tag, String data) { return RangerPerfTracerFactory.getPerfTracer(logger, tag, data); } @@ -76,33 +87,36 @@ public static void logAlways(RangerPerfTracer tracer) { tracer.logAlways(); } } - public RangerPerfTracer(Log logger, String tag, String data) { + public RangerPerfTracer(Logger logger, String tag, String data, ThreadInfo threadInfo) { this.logger = logger; this.tag = tag; this.data = data; - startTimeMs = System.currentTimeMillis(); - } + this.threadInfo = threadInfo; - public final String getTag() { - return tag; + startTime = threadInfo == null ? System.nanoTime() : RangerPerfTracerFactory.threadMgmtBean.getThreadCpuTime(threadInfo.getThreadId()); + userStartTime = System.nanoTime(); } - public final long getStartTime() { - return startTimeMs; + public final long getElapsedUserTime() { + return System.nanoTime() - userStartTime; } - - public final long getElapsedTime() { - return System.currentTimeMillis() - startTimeMs; + public final long getElapsedCpuTime() { + if (threadInfo == null) { + return getElapsedUserTime(); + } else { + return RangerPerfTracerFactory.threadMgmtBean.getThreadCpuTime(threadInfo.getThreadId()) - startTime; + } } public void log() { - long elapsedTime = getElapsedTime(); - if (elapsedTime > reportingThresholdMs) { - logger.debug("[PERF] " + tag + data + ": " + elapsedTime); + long elapsedTime = Math.max(getElapsedUserTime(), getElapsedCpuTime()); + long reportingThreshold = threadInfo == null ? 0L : (1000000/1000 - 1); // just about a microsecond + + if (elapsedTime > reportingThreshold) { + logger.debug("[PERF]:" + (threadInfo != null ? threadInfo.getThreadName() : "") + ":" + tag + data + ":" + getElapsedCpuTime() + ":" + getElapsedUserTime()); } } public void logAlways() { - long elapsedTime = getElapsedTime(); - logger.debug("[PERF] " + tag + data + ": " + elapsedTime); + logger.debug("[PERF]:" + (threadInfo != null ? threadInfo.getThreadName() : "") + ":" + tag + data + ":" + getElapsedCpuTime() + ":" + getElapsedUserTime()); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracerFactory.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracerFactory.java index 1153091b41..c42d631c06 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracerFactory.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPerfTracerFactory.java @@ -19,18 +19,54 @@ package org.apache.ranger.plugin.util; -import org.apache.commons.logging.Log; +import org.slf4j.Logger; + +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; public class RangerPerfTracerFactory { - static RangerPerfTracer getPerfTracer(Log logger, String tag, String data) { + static volatile ThreadMXBean threadMgmtBean = null; + private static boolean isThreadCPUTimeSupported = false; + private static boolean isThreadCPUTimeEnabled = false; + + static RangerPerfTracer getPerfTracer(Logger logger, String tag, String data) { RangerPerfTracer ret = null; + if (logger.isDebugEnabled()) { + if (threadMgmtBean == null) { + synchronized (RangerPerfTracerFactory.class) { + if (threadMgmtBean == null) { + threadMgmtBean = ManagementFactory.getThreadMXBean(); + isThreadCPUTimeSupported = threadMgmtBean.isThreadCpuTimeSupported(); + logger.info("ThreadCPUTimeSupported (by JVM) = " + isThreadCPUTimeSupported); + + isThreadCPUTimeEnabled = threadMgmtBean.isThreadCpuTimeEnabled(); + logger.info("ThreadCPUTimeEnabled = " + isThreadCPUTimeEnabled); + + if (isThreadCPUTimeSupported) { + if (!isThreadCPUTimeEnabled) { + threadMgmtBean.setThreadCpuTimeEnabled(true); + isThreadCPUTimeEnabled = threadMgmtBean.isThreadCpuTimeEnabled(); + } + logger.info("ThreadCPUTimeEnabled = " + isThreadCPUTimeEnabled); + } + } + } + } + } + + ThreadInfo threadInfo = null; + if (isThreadCPUTimeSupported && isThreadCPUTimeEnabled) { + threadInfo = threadMgmtBean.getThreadInfo(Thread.currentThread().getId()); + } + if (PerfDataRecorder.collectStatistics()) { - ret = new RangerPerfCollectorTracer(logger, tag, data); - } else if (logger.isDebugEnabled()) { - ret = new RangerPerfTracer(logger, tag, data); + ret = new RangerPerfCollectorTracer(logger, tag, data, threadInfo); + } else { + ret = new RangerPerfTracer(logger, tag, data, threadInfo); } return ret; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPluginCapability.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPluginCapability.java index 53e1a9b46e..16a861fa76 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPluginCapability.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPluginCapability.java @@ -28,11 +28,11 @@ public class RangerPluginCapability { /* - - tag-policies + - tag-policies - allowExceptions/deny/denyExceptions - masking/row-filtering - Macros - like ${USER} - - tag-based masking/row-filtering + - tag-based masking/row-filtering - audit mode support - service-def changes - isValidLeaf - validity periods @@ -42,6 +42,13 @@ public class RangerPluginCapability { - deny AllElse policies - roles - role download timer + - Audit-excluded-users + - Chained plugins + - Super-user permission + - UserStore download + - Audit-policies + - User/group/tag attributes in policy + - additional resources in policy */ private final long pluginCapabilities; private static final String baseRangerCapabilities = computeBaseCapabilities(); @@ -59,9 +66,17 @@ public enum RangerPluginFeature { RANGER_PLUGIN_CAPABILITY_POLICY_LEVEL_CONDITION("Policy-level Condition"), RANGER_PLUGIN_CAPABILITY_DENY_ALL_ELSE_POLICY("Deny-all-else Policy"), RANGER_PLUGIN_CAPABILITY_ROLE("Role"), - RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER("Role Timer"); - - private String name; + RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER("Role Timer"), + RANGER_PLUGIN_CAPABILITY_AUDIT_EXCLUDED_USERS("Audit-Excluded Users"), + RANGER_PLUGIN_CAPABILITY_CHAINED_PLUGINS("Chained Plugins"), + RANGER_PLUGIN_CAPABILITY_SUPERUSER_PERMISSIONS("Super-user Permissions"), + RANGER_PLUGIN_CAPABILITY_USERSTORE_DOWNLOAD("UserStore Download"), + RANGER_PLUGIN_CAPABILITY_AUDIT_POLICY("Audit Policy"), + RANGER_PLUGIN_CAPABILITY_UGT_ATTRIBUTES_IN_POLICY("User/group/tag attributes in policy"), + RANGER_PLUGIN_CAPABILITY_ADDITIONAL_RESOURCES_IN_POLICY("additional resources in policy"), + RANGER_PLUGIN_CAPABILITY_GDS_POLICIES("GDS Policies"); + + private final String name; RangerPluginFeature(String name) { this.name = name; } @@ -160,7 +175,12 @@ private static String computeBaseCapabilities() { , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_POLICY_LEVEL_CONDITION.getName() , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_DENY_ALL_ELSE_POLICY.getName() , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_ROLE.getName() - , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER.getName()); + , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER.getName() + , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_AUDIT_EXCLUDED_USERS.getName() + , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_CHAINED_PLUGINS.getName() + , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_SUPERUSER_PERMISSIONS.getName() + , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_USERSTORE_DOWNLOAD.getName() + , RangerPluginFeature.RANGER_PLUGIN_CAPABILITY_AUDIT_POLICY.getName()); return Long.toHexString(new RangerPluginCapability(baseCapabilities).getPluginCapabilities()); } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java index 02694617bf..d924272173 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPolicyDeltaUtil.java @@ -21,31 +21,32 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.lang3.StringUtils; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicyDelta; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class RangerPolicyDeltaUtil { - private static final Log LOG = LogFactory.getLog(RangerPolicyDeltaUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerPolicyDeltaUtil.class); - private static final Log PERF_POLICY_DELTA_LOG = RangerPerfTracer.getPerfLogger("policy.delta"); + private static final Logger PERF_POLICY_DELTA_LOG = RangerPerfTracer.getPerfLogger("policy.delta"); public static List applyDeltas(List policies, List deltas, String serviceType) { if (LOG.isDebugEnabled()) { - LOG.debug("==> applyDeltas(serviceType=" + serviceType + ")"); + LOG.debug("==> applyDeltas(serviceType=" + serviceType + ", deltas=" + deltas + ")"); } List ret; - - RangerPerfTracer perf = null; + RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_POLICY_DELTA_LOG)) { perf = RangerPerfTracer.getPerfTracer(PERF_POLICY_DELTA_LOG, "RangerPolicyDelta.applyDeltas()"); @@ -53,55 +54,68 @@ public static List applyDeltas(List policies, List retMap = new HashMap<>(); + + for (RangerPolicy policy : policies) { + retMap.put(policy.getId(), policy); } - ret = new ArrayList<>(policies); for (RangerPolicyDelta delta : deltas) { + if (!StringUtils.equals(serviceType, delta.getServiceType()) || delta.getPolicyId() == null) { + continue; + } + int changeType = delta.getChangeType(); - if (!serviceType.equals(delta.getServiceType())) { - if (!serviceType.equals(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME) && !delta.getServiceType().equals(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_TAG_NAME)) { - LOG.error("Found unexpected serviceType in policyDelta:[" + delta + "]. Was expecting serviceType:[" + serviceType + "]. Should NOT have come here!! Ignoring delta and continuing"); - } + + if (changeType != RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE && changeType != RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE && changeType != RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE) { + LOG.warn("Found unexpected changeType in policyDelta:[" + delta + "]. Ignoring delta"); + continue; } - if (changeType == RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE || changeType == RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE || changeType == RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE) { - if (changeType == RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE) { - if (delta.getPolicy() != null) { - ret.add(delta.getPolicy()); + + Long policyId = delta.getPolicyId(); + RangerPolicy deletedPolicy = retMap.remove(policyId); + + switch(changeType) { + case RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE: { + if (deletedPolicy != null) { + LOG.warn("Unexpected: found existing policy for CHANGE_TYPE_POLICY_CREATE: " + deletedPolicy); } - } else { - // Either UPDATE or DELETE - Long policyId = delta.getPolicyId(); - - Iterator iter = ret.iterator(); - while (iter.hasNext()) { - if (policyId.equals(iter.next().getId())) { - iter.remove(); - break; - } + break; + } + case RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE: { + if (deletedPolicy == null) { + LOG.warn("Unexpected: found no existing policy for CHANGE_TYPE_POLICY_UPDATE: " + deletedPolicy); } - if (changeType == RangerPolicyDelta.CHANGE_TYPE_POLICY_UPDATE) { - if (delta.getPolicy() != null) { - ret.add(delta.getPolicy()); - } + break; + } + case RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE: { + if (deletedPolicy == null) { + LOG.warn("Unexpected: found no existing policy for CHANGE_TYPE_POLICY_DELETE: " + deletedPolicy); } + break; } - } else { - LOG.warn("Found unexpected changeType in policyDelta:[" + delta +"]. Ignoring delta"); + default: + break; + } + + if (changeType != RangerPolicyDelta.CHANGE_TYPE_POLICY_DELETE) { + retMap.put(policyId, delta.getPolicy()); } } + + ret = new ArrayList<>(retMap.values()); + ret.sort(RangerPolicy.POLICY_ID_COMPARATOR); } else { if (LOG.isDebugEnabled()) { - LOG.debug("applyDeltas(deltas=null, serviceType=" + serviceType +")"); + LOG.debug("applyDeltas called with empty deltas. Will return policies without change"); } ret = policies; } - if (CollectionUtils.isNotEmpty(deltas) && CollectionUtils.isNotEmpty(ret)) { - ret.sort(RangerPolicy.POLICY_ID_COMPARATOR); - } - RangerPerfTracer.log(perf); if (LOG.isDebugEnabled()) { @@ -183,8 +197,27 @@ public static Boolean hasPolicyDeltas(final ServicePolicies servicePolicies) { boolean isPolicyDeltasExist = CollectionUtils.isNotEmpty(servicePolicies.getPolicyDeltas()) || isPolicyDeltasExistInSecurityZones; if (isPoliciesExist && isPolicyDeltasExist) { - LOG.warn("ServicePolicies contain both policies and policy-deltas!!"); + LOG.warn("ServicePolicies contain both policies and policy-deltas!! Cannot build policy-engine from these servicePolicies. Please check server-side code!"); + if(LOG.isDebugEnabled()) { + LOG.debug("Downloaded ServicePolicies are [" + servicePolicies + "]"); + } ret = null; + } else if (!isPoliciesExist && !isPolicyDeltasExist) { + LOG.warn("ServicePolicies do not contain any policies or policy-deltas!!"); + if(LOG.isDebugEnabled()) { + LOG.debug("Downloaded ServicePolicies are [" + servicePolicies + "]"); + } + if (servicePolicies.getPolicyDeltas() == null) { + if (LOG.isDebugEnabled()) { + LOG.debug("Complete set of servicePolicies is received. There may be a change to service. Forcing to create a new policy engine!"); + } + ret = false; // Force new policy engine creation from servicePolicies + } else { + if (LOG.isDebugEnabled()) { + LOG.debug("servicePolicy deltas are received. There are no material changes in the policies."); + } + ret = null; + } } else { ret = isPolicyDeltasExist; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPurgeResult.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPurgeResult.java new file mode 100644 index 0000000000..0b47bbcd29 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerPurgeResult.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + + +@JsonAutoDetect(getterVisibility= JsonAutoDetect.Visibility.NONE, setterVisibility= JsonAutoDetect.Visibility.NONE, fieldVisibility= JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerPurgeResult { + private String recordType; + private Long totalRecordCount; + private Long purgedRecordCount; + + public RangerPurgeResult() { } + + public RangerPurgeResult(String recordType, Long totalRecordCount, Long purgedRecordCount) { + this.recordType = recordType; + this.totalRecordCount = totalRecordCount; + this.purgedRecordCount = purgedRecordCount; + } + + public String getRecordType() { + return recordType; + } + + public void setRecordType(String recordType) { + this.recordType = recordType; + } + + public Long getTotalRecordCount() { + return totalRecordCount; + } + + public void setTotalRecordCount(Long totalRecordCount) { + this.totalRecordCount = totalRecordCount; + } + + public Long getPurgedRecordCount() { + return purgedRecordCount; + } + + public void setPurgedRecordCount(Long purgedRecordCount) { + this.purgedRecordCount = purgedRecordCount; + } + + @Override + public String toString( ) { + StringBuilder sb = new StringBuilder(); + + toString(sb); + + return sb.toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("RangerPurgeResult={"); + + sb.append("recordType={").append(recordType).append("} "); + sb.append("totalRecordCount={").append(totalRecordCount).append("} "); + sb.append("purgedRecordCount={").append(purgedRecordCount).append("} "); + + sb.append("}"); + + return sb; + } + +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java index 216b6b2a9e..e5461c2e68 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTClient.java @@ -45,17 +45,18 @@ import javax.net.ssl.TrustManagerFactory; import javax.ws.rs.core.Cookie; +import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider; +import com.sun.jersey.api.client.filter.ClientFilter; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.Validate; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.authorization.utils.StringUtil; -import org.codehaus.jackson.jaxrs.JacksonJsonProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientResponse; @@ -67,7 +68,7 @@ public class RangerRESTClient { - private static final Log LOG = LogFactory.getLog(RangerRESTClient.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerRESTClient.class); public static final String RANGER_PROP_POLICYMGR_URL = "ranger.service.store.rest.url"; public static final String RANGER_PROP_POLICYMGR_SSLCONFIG_FILENAME = "ranger.service.store.rest.ssl.config.file"; @@ -86,7 +87,7 @@ public class RangerRESTClient { public static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); public static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); - public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLS"; + public static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2"; private String mUrl; private String mSslConfigFileName; @@ -101,16 +102,18 @@ public class RangerRESTClient { private String mTrustStoreURL; private String mTrustStoreAlias; private String mTrustStoreFile; - private String mTrustStoreType; - private Gson gsonBuilder; - private int mRestClientConnTimeOutMs; + private String mTrustStoreType; + private int mRestClientConnTimeOutMs; private int mRestClientReadTimeOutMs; + private int maxRetryAttempts; + private int retryIntervalMs; private int lastKnownActiveUrlIndex; private final List configuredURLs; private volatile Client client; - + private volatile Client cookieAuthClient; + private ClientFilter basicAuthFilter = null; public RangerRESTClient(String url, String sslConfigFileName, Configuration config) { mUrl = url; @@ -154,23 +157,33 @@ public void setRestClientReadTimeOutMs(int mRestClientReadTimeOutMs) { this.mRestClientReadTimeOutMs = mRestClientReadTimeOutMs; } + public int getMaxRetryAttempts() { return maxRetryAttempts; } + + public void setMaxRetryAttempts(int maxRetryAttempts) { this.maxRetryAttempts = maxRetryAttempts; } + + public int getRetryIntervalMs() { return retryIntervalMs; } + + public void setRetryIntervalMs(int retryIntervalMs) { this.retryIntervalMs = retryIntervalMs; } + public void setBasicAuthInfo(String username, String password) { mUsername = username; mPassword = password; + + setBasicAuthFilter(username, password); } public WebResource getResource(String relativeUrl) { WebResource ret = getClient().resource(getUrl() + relativeUrl); - + return ret; } public String toJson(Object obj) { - return gsonBuilder.toJson(obj); + return JsonUtils.objectToJson(obj); } public T fromJson(String json, Class cls) { - return gsonBuilder.fromJson(json, cls); + return JsonUtils.jsonToObject(json, cls); } public Client getClient() { @@ -188,6 +201,28 @@ public Client getClient() { return result; } + private Client getCookieAuthClient() { + Client ret = cookieAuthClient; + + if (ret == null) { + synchronized (this) { + ret = cookieAuthClient; + + if (ret == null) { + cookieAuthClient = buildClient(); + + if (basicAuthFilter != null) { + cookieAuthClient.removeFilter(basicAuthFilter); + } + + ret = cookieAuthClient; + } + } + } + + return ret; + } + private Client buildClient() { Client client = null; @@ -218,8 +253,8 @@ public boolean verify(String urlHostName, SSLSession session) { client = Client.create(config); } - if(StringUtils.isNotEmpty(mUsername) && StringUtils.isNotEmpty(mPassword)) { - client.addFilter(new HTTPBasicAuthFilter(mUsername, mPassword)); + if (basicAuthFilter != null && !client.isFilterPresent(basicAuthFilter)) { + client.addFilter(basicAuthFilter); } // Set Connection Timeout and ReadTime for the PolicyRefresh @@ -229,18 +264,20 @@ public boolean verify(String urlHostName, SSLSession session) { return client; } + private void setBasicAuthFilter(String username, String password) { + if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) { + basicAuthFilter = new HTTPBasicAuthFilter(username, password); + } else { + basicAuthFilter = null; + } + } + public void resetClient(){ client = null; } private void init(Configuration config) { - try { - gsonBuilder = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create(); - } catch(Throwable excp) { - LOG.fatal("RangerRESTClient.init(): failed to create GsonBuilder object", excp); - } - - mIsSSL = StringUtils.containsIgnoreCase(mUrl, "https"); + mIsSSL = isSslEnabled(mUrl); if (mIsSSL) { @@ -269,6 +306,25 @@ private void init(Configuration config) { } } + + final String pluginPropertyPrefix; + + if (config instanceof RangerPluginConfig) { + pluginPropertyPrefix = ((RangerPluginConfig) config).getPropertyPrefix(); + } else { + pluginPropertyPrefix = "ranger.plugin"; + } + + String username = config.get(pluginPropertyPrefix + ".policy.rest.client.username"); + String password = config.get(pluginPropertyPrefix + ".policy.rest.client.password"); + + if (StringUtils.isNotBlank(username) && StringUtils.isNotBlank(password)) { + setBasicAuthFilter(username, password); + } + } + + private boolean isSslEnabled(String url) { + return !StringUtils.isEmpty(url) && url.toLowerCase().startsWith("https"); } private KeyManager[] getKeyManagers() { @@ -449,14 +505,13 @@ private void close(InputStream str, String filename) { public ClientResponse get(String relativeUrl, Map params) throws Exception { ClientResponse finalResponse = null; int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; + int retryAttempt = 0; for (int index = 0; index < configuredURLs.size(); index++) { - try { - currentIndex = (startIndex + index) % configuredURLs.size(); + int currentIndex = (startIndex + index) % configuredURLs.size(); - WebResource webResource = getClient().resource(configuredURLs.get(currentIndex) + relativeUrl); - webResource = setQueryParams(webResource, params); + try { + WebResource.Builder webResource = createWebResource(currentIndex, relativeUrl, params); finalResponse = webResource.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON).get(ClientResponse.class); @@ -465,8 +520,39 @@ public ClientResponse get(String relativeUrl, Map params) throws break; } } catch (ClientHandlerException ex) { - LOG.warn("Failed to communicate with Ranger Admin, URL : " + configuredURLs.get(currentIndex)); - processException(index, ex); + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } + } + } + return finalResponse; + } + + public ClientResponse get(String relativeUrl, Map params, Cookie sessionId) throws Exception{ + ClientResponse finalResponse = null; + int startIndex = this.lastKnownActiveUrlIndex; + int retryAttempt = 0; + + for (int index = 0; index < configuredURLs.size(); index++) { + int currentIndex = (startIndex + index) % configuredURLs.size(); + + try { + WebResource.Builder br = createWebResource(currentIndex, relativeUrl, params, sessionId); + + finalResponse = br.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON).get(ClientResponse.class); + + if (finalResponse != null) { + setLastKnownActiveUrlIndex(currentIndex); + break; + } + } catch (ClientHandlerException ex) { + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } } } return finalResponse; @@ -475,38 +561,69 @@ public ClientResponse get(String relativeUrl, Map params) throws public ClientResponse post(String relativeUrl, Map params, Object obj) throws Exception { ClientResponse finalResponse = null; int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; + int retryAttempt = 0; for (int index = 0; index < configuredURLs.size(); index++) { + int currentIndex = (startIndex + index) % configuredURLs.size(); + try { - currentIndex = (startIndex + index) % configuredURLs.size(); + WebResource.Builder webResource = createWebResource(currentIndex, relativeUrl, params); - WebResource webResource = getClient().resource(configuredURLs.get(currentIndex) + relativeUrl); - webResource = setQueryParams(webResource, params); finalResponse = webResource.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON).post(ClientResponse.class, toJson(obj)); if (finalResponse != null) { setLastKnownActiveUrlIndex(currentIndex); break; } } catch (ClientHandlerException ex) { - LOG.warn("Failed to communicate with Ranger Admin, URL : " + configuredURLs.get(currentIndex)); - processException(index, ex); + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } } } return finalResponse; } + public ClientResponse post(String relativeURL, Map params, Object obj, Cookie sessionId) throws Exception { + ClientResponse response = null; + int startIndex = this.lastKnownActiveUrlIndex; + int retryAttempt = 0; + + for (int index = 0; index < configuredURLs.size(); index++) { + int currentIndex = (startIndex + index) % configuredURLs.size(); + + try { + WebResource.Builder br = createWebResource(currentIndex, relativeURL, params, sessionId); + + response = br.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON) + .post(ClientResponse.class, toJson(obj)); + + if (response != null) { + setLastKnownActiveUrlIndex(currentIndex); + break; + } + } catch (ClientHandlerException ex) { + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } + } + } + return response; + } + public ClientResponse delete(String relativeUrl, Map params) throws Exception { ClientResponse finalResponse = null; int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; + int retryAttempt = 0; for (int index = 0; index < configuredURLs.size(); index++) { - try { - currentIndex = (startIndex + index) % configuredURLs.size(); + int currentIndex = (startIndex + index) % configuredURLs.size(); - WebResource webResource = getClient().resource(configuredURLs.get(currentIndex) + relativeUrl); - webResource = setQueryParams(webResource, params); + try { + WebResource.Builder webResource = createWebResource(currentIndex, relativeUrl, params); finalResponse = webResource.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON).delete(ClientResponse.class); if (finalResponse != null) { @@ -514,31 +631,66 @@ public ClientResponse delete(String relativeUrl, Map params) thr break; } } catch (ClientHandlerException ex) { - LOG.warn("Failed to communicate with Ranger Admin, URL : " + configuredURLs.get(currentIndex)); - processException(index, ex); + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } } } return finalResponse; } + public ClientResponse delete(String relativeURL, Map params, Cookie sessionId) throws Exception { + ClientResponse response = null; + int startIndex = this.lastKnownActiveUrlIndex; + int retryAttempt = 0; + + for (int index = 0; index < configuredURLs.size(); index++) { + int currentIndex = (startIndex + index) % configuredURLs.size(); + + try { + WebResource.Builder br = createWebResource(currentIndex, relativeURL, params, sessionId); + + response = br.delete(ClientResponse.class); + + if (response != null) { + setLastKnownActiveUrlIndex(currentIndex); + break; + } + } catch (ClientHandlerException ex) { + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } + } + } + return response; + } + public ClientResponse put(String relativeUrl, Map params, Object obj) throws Exception { ClientResponse finalResponse = null; int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; + int retryAttempt = 0; + for (int index = 0; index < configuredURLs.size(); index++) { + int currentIndex = (startIndex + index) % configuredURLs.size(); + try { - currentIndex = (startIndex + index) % configuredURLs.size(); + WebResource.Builder webResource = createWebResource(currentIndex, relativeUrl, params); - WebResource webResource = getClient().resource(configuredURLs.get(currentIndex) + relativeUrl); - webResource = setQueryParams(webResource, params); finalResponse = webResource.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON).put(ClientResponse.class, toJson(obj)); if (finalResponse != null) { setLastKnownActiveUrlIndex(currentIndex); break; } } catch (ClientHandlerException ex) { - LOG.warn("Failed to communicate with Ranger Admin, URL : " + configuredURLs.get(currentIndex)); - processException(index, ex); + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } } } return finalResponse; @@ -547,23 +699,27 @@ public ClientResponse put(String relativeUrl, Map params, Object public ClientResponse put(String relativeURL, Object request, Cookie sessionId) throws Exception { ClientResponse response = null; int startIndex = this.lastKnownActiveUrlIndex; - int currentIndex = 0; + int retryAttempt = 0; for (int index = 0; index < configuredURLs.size(); index++) { + int currentIndex = (startIndex + index) % configuredURLs.size(); + try { - currentIndex = (startIndex + index) % configuredURLs.size(); + WebResource.Builder br = createWebResource(currentIndex, relativeURL, null, sessionId); - WebResource webResource = createWebResourceForCookieAuth(currentIndex, relativeURL); - WebResource.Builder br = webResource.getRequestBuilder().cookie(sessionId); response = br.accept(RangerRESTUtils.REST_EXPECTED_MIME_TYPE).type(RangerRESTUtils.REST_MIME_TYPE_JSON) .put(ClientResponse.class, toJson(request)); + if (response != null) { setLastKnownActiveUrlIndex(currentIndex); break; } - } catch (ClientHandlerException e) { - LOG.warn("Failed to communicate with Ranger Admin, URL : " + configuredURLs.get(currentIndex)); - processException(index, e); + } catch (ClientHandlerException ex) { + if (shouldRetry(configuredURLs.get(currentIndex), index, retryAttempt, ex)) { + retryAttempt++; + + index = -1; // start from first url + } } } return response; @@ -584,18 +740,49 @@ protected void setLastKnownActiveUrlIndex(int lastKnownActiveUrlIndex) { this.lastKnownActiveUrlIndex = lastKnownActiveUrlIndex; } - protected WebResource createWebResourceForCookieAuth(int currentIndex, String relativeURL) { - Client cookieClient = getClient(); - cookieClient.removeAllFilters(); - WebResource ret = cookieClient.resource(configuredURLs.get(currentIndex) + relativeURL); - return ret; + protected WebResource.Builder createWebResource(int currentIndex, String relativeURL, Map params) { + WebResource webResource = getClient().resource(configuredURLs.get(currentIndex) + relativeURL); + + webResource = setQueryParams(webResource, params); + + return webResource.getRequestBuilder(); + } + + protected WebResource.Builder createWebResource(int currentIndex, String relativeURL, Map params, Cookie sessionId) { + if (sessionId == null) { + return createWebResource(currentIndex, relativeURL, params); + } else { + WebResource webResource = getCookieAuthClient().resource(configuredURLs.get(currentIndex) + relativeURL); + + webResource = setQueryParams(webResource, params); + + return webResource.getRequestBuilder().cookie(sessionId); + } } - protected void processException(int index, ClientHandlerException e) throws Exception { - if (index == configuredURLs.size() - 1) { + protected boolean shouldRetry(String currentUrl, int index, int retryAttemptCount, Exception ex) throws Exception { + LOG.warn("Failed to communicate with Ranger Admin. URL: " + currentUrl + ". Error: " + ex.getMessage()); + + boolean isLastUrl = index == (configuredURLs.size() - 1); + + // attempt retry after failure on the last url + boolean ret = isLastUrl && (retryAttemptCount < maxRetryAttempts); + + if (ret) { + LOG.warn("Waiting for " + retryIntervalMs + "ms before retry attempt #" + (retryAttemptCount + 1)); + + try { + Thread.sleep(retryIntervalMs); + } catch (InterruptedException excp) { + LOG.error("Failed while waiting to retry", excp); + } + } else if (isLastUrl) { LOG.error("Failed to communicate with all Ranger Admin's URL's : [ " + configuredURLs + " ]"); - throw e; + + throw ex; } + + return ret; } public int getLastKnownActiveUrlIndex() { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java index adf0c0a671..7407e46ce9 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRESTUtils.java @@ -24,8 +24,8 @@ import java.net.UnknownHostException; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import com.kstruct.gethostname4j.Hostname; /** @@ -34,7 +34,7 @@ */ public class RangerRESTUtils { - private static final Log LOG = LogFactory.getLog(RangerRESTUtils.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerRESTUtils.class); public static final String REST_URL_POLICY_GET_FOR_SERVICE_IF_UPDATED = "/service/plugins/policies/download/"; public static final String REST_URL_SERVICE_GRANT_ACCESS = "/service/plugins/services/grant/"; @@ -73,7 +73,12 @@ public class RangerRESTUtils { public static final String REST_PARAM_LAST_KNOWN_ROLE_VERSION = "lastKnownRoleVersion"; public static final String REST_PARAM_LAST_KNOWN_USERSTORE_VERSION = "lastKnownUserStoreVersion"; - public static final String REST_URL_SERVICE_SERCURE_GET_USERSTORE = "/service/xusers/secure/download/"; + public static final String REST_URL_SERVICE_GET_USERSTORE = "/service/xusers/download/"; + public static final String REST_URL_SERVICE_SERCURE_GET_USERSTORE = "/service/xusers/secure/download/"; + + public static final String REST_PARAM_LAST_KNOWN_GDS_VERSION = "lastKnownGdsVersion"; + public static final String REST_URL_SERVICE_GET_GDSINFO = "/service/gds/download/"; + public static final String REST_URL_SERVICE_SECURE_GET_GDSINFO = "/service/gds/secure/download/"; private static final int MAX_PLUGIN_ID_LEN = 255; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerReadWriteLock.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerReadWriteLock.java new file mode 100644 index 0000000000..7f17a52ffa --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerReadWriteLock.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +public class RangerReadWriteLock { + + private static final RangerLock NO_OP_LOCK = new RangerLock(null); + + private final ReentrantReadWriteLock lock; + + public RangerReadWriteLock(boolean isUseLock) { + lock = isUseLock ? new ReentrantReadWriteLock(true) : null; + } + + public RangerLock getReadLock() { + final RangerLock ret; + if (lock != null) { + ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); + readLock.lock(); + ret = new RangerLock(readLock); + } else { + ret = NO_OP_LOCK; + } + return ret; + } + + public RangerLock getWriteLock() { + final RangerLock ret; + if (lock != null) { + boolean isLocked = false; + ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); + while (!isLocked) { + isLocked = writeLock.tryLock(); + // Another thread has acquired write lock. Hint to scheduler to schedule some other thread if needed + if (!isLocked && lock.getReadLockCount() == 0) { + Thread.yield(); + } + } + ret = new RangerLock(writeLock); + } else { + ret = NO_OP_LOCK; + } + return ret; + } + + @Override + public String toString() { + if (lock != null) { + return "ReadWriteLock:[" + lock.toString() + "], ReadLock:[" + lock.readLock().toString() + "], WriteLock:[" + lock.writeLock().toString() + "]"; + } else { + return "ReadWriteLock:[null]"; + } + } + + static final public class RangerLock implements AutoCloseable { + private final Lock lock; + + private RangerLock(Lock lock) { + this.lock = lock; + } + + @Override + public void close() { + if (lock != null) { + lock.unlock(); + } + } + + public boolean isLockingEnabled() { + return lock != null; + } + + @Override + public String toString() { + return lock == null ? "null" : lock.toString(); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java new file mode 100644 index 0000000000..acf606aca5 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestExprResolver.java @@ -0,0 +1,246 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptEngine; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class RangerRequestExprResolver { + private static final Logger LOG = LoggerFactory.getLogger(RangerRequestExprResolver.class); + + private static final String REGEX_GROUP_EXPR = "expr"; + private static final Pattern PATTERN = Pattern.compile("\\$\\{\\{(?<" + REGEX_GROUP_EXPR + ">.*?)\\}\\}"); + public static final String EXPRESSION_START = "${{"; + + private final String str; + private final String serviceType; + private final boolean hasTokens; + + + public RangerRequestExprResolver(String str, String serviceType) { + this.str = str; + this.serviceType = serviceType; + this.hasTokens = hasExpressions(str); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerRequestExprResolver(" + str + "): hasTokens=" + hasTokens); + } + } + + /* + * replaces expressions in this.str with corresponding values in exprValues map (argument). + * For example, given the following: + * 1. this.str has value: "dept = '${{USER.dept}}'" + * 2. exprValues has value: { "dept": "marketing" } + * This method returns: "dept = 'marketing'" + */ + public String resolveExpressions(Map exprValues) { + String ret = str; + + if (hasTokens) { + StringBuffer sb = new StringBuffer(); + Matcher matcher = PATTERN.matcher(str); + + while (matcher.find()) { + String expr = matcher.group(REGEX_GROUP_EXPR); + Object oVal = exprValues.get(expr); + String val = oVal == null ? "" : Objects.toString(oVal); + + matcher.appendReplacement(sb, val); + } + + matcher.appendTail(sb); + + ret = sb.toString(); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerRequestExprResolver.resolveExpressions(" + str + "): ret=" + ret); + } + } + + return ret; + } + + /* + * replaces expressions in this.str with corresponding values in request (argument). + * For example, given the following: + * 1. this.str has value: "dept = '${{USER.dept}}'" + * 2. request.user has attribute dept with value: "marketing" + * This method returns: "dept = 'marketing'" + */ + public String resolveExpressions(RangerAccessRequest request) { + String ret = str; + + if (hasTokens) { + ScriptEngine scriptEngine = ScriptEngineUtil.createScriptEngine(serviceType); + RangerRequestScriptEvaluator scriptEvaluator = new RangerRequestScriptEvaluator(request, scriptEngine, RangerRequestScriptEvaluator.needsJsonCtxEnabled(str)); + StringBuffer sb = new StringBuffer(); + Matcher matcher = PATTERN.matcher(str); + + while (matcher.find()) { + String expr = matcher.group(REGEX_GROUP_EXPR); + Object oVal = scriptEvaluator.evaluateScript(expr); + String val = oVal == null ? "" : Objects.toString(oVal); + + matcher.appendReplacement(sb, val); + } + + matcher.appendTail(sb); + + ret = sb.toString(); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerRequestExprResolver.processExpressions(" + str + "): ret=" + ret); + } + } + + return ret; + } + + public static boolean hasExpressions(String str) { + boolean ret = false; + + if (str != null) { + Matcher matcher = PATTERN.matcher(str); + + ret = matcher.find(); + } + + return ret; + } + + public static boolean hasUserAttributeInExpression(String str) { + boolean ret = false; + + if (str != null) { + Matcher matcher = PATTERN.matcher(str); + + while (matcher.find()) { + String expr = matcher.group(REGEX_GROUP_EXPR); + + if (RangerRequestScriptEvaluator.hasUserAttributeReference(expr)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public static boolean hasGroupAttributeInExpression(String str) { + boolean ret = false; + + if (str != null) { + Matcher matcher = PATTERN.matcher(str); + + while (matcher.find()) { + String expr = matcher.group(REGEX_GROUP_EXPR); + + if (RangerRequestScriptEvaluator.hasGroupAttributeReference(expr)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public static boolean hasUserGroupAttributeInExpression(String str) { + boolean ret = false; + + if (str != null) { + Matcher matcher = PATTERN.matcher(str); + + while (matcher.find()) { + String expr = matcher.group(REGEX_GROUP_EXPR); + + if (RangerRequestScriptEvaluator.hasUserGroupAttributeReference(expr)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public static boolean hasUserAttributeInExpression(Collection values) { + boolean ret = false; + + if (values != null) { + for (String value : values) { + if (hasUserAttributeInExpression(value)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public static boolean hasGroupAttributeInExpression(Collection values) { + boolean ret = false; + + if (values != null) { + for (String value : values) { + if (hasGroupAttributeInExpression(value)) { + ret = true; + + break; + } + } + } + + return ret; + } + + public static boolean hasUserGroupAttributeInExpression(Collection values) { + boolean ret = false; + + if (values != null) { + for (String value : values) { + if (hasUserGroupAttributeInExpression(value)) { + ret = true; + + break; + } + } + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestedResources.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestedResources.java index a5107b3673..94db573f9f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestedResources.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRequestedResources.java @@ -22,23 +22,17 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.util.ArrayList; import java.util.List; import java.util.Map; @JsonAutoDetect(getterVisibility= JsonAutoDetect.Visibility.NONE, setterVisibility= JsonAutoDetect.Visibility.NONE, fieldVisibility= JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL ) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) - public class RangerRequestedResources { private List requestedResources = new ArrayList<>(); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerResourceEvaluatorsRetriever.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerResourceEvaluatorsRetriever.java new file mode 100644 index 0000000000..7f39dc2a2a --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerResourceEvaluatorsRetriever.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.collections.Predicate; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerResourceTrie; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +public class RangerResourceEvaluatorsRetriever { + private static final Logger LOG = LoggerFactory.getLogger(RangerResourceEvaluatorsRetriever.class); + + public static Collection getEvaluators(Map> resourceTrie, Map resource) { + return getEvaluators(resourceTrie, resource, null); + } + + public static Collection getEvaluators(Map> resourceTrie, Map resource, Map scopes) { + return getEvaluators(resourceTrie, resource, scopes, null); + } + + public static Collection getEvaluators(Map> resourceTrie, Map resource, Map scopes, Predicate predicate) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerPolicyResourceEvaluatorsRetriever.getEvaluators(" + resource + ")"); + } + Set ret = null; + + if (scopes == null) { + scopes = Collections.emptyMap(); + } + + if (MapUtils.isNotEmpty(resourceTrie) && MapUtils.isNotEmpty(resource)) { + Set resourceKeys = resource.keySet(); + String resourceWithMinEvals = null; + + if (resourceKeys.size() > 1) { + int minEvalCount = 0; // initial value doesn't matter, as the count for the first resource will be assigned later, in line #70 + + for (String resourceDefName : resourceKeys) { + RangerResourceTrie trie = resourceTrie.get(resourceDefName); + + if (trie == null) { + continue; + } + + Object resourceValues = resource.get(resourceDefName); + + int evalCount = trie.getEvaluatorsCountForResource(resourceValues, scopes.get(resourceDefName), predicate); + + if (resourceWithMinEvals == null || (evalCount < minEvalCount)) { + resourceWithMinEvals = resourceDefName; + minEvalCount = evalCount; + } + } + + if (minEvalCount == 0) { + resourceWithMinEvals = null; + ret = Collections.emptySet(); + } + } else if (resourceKeys.size() == 1) { // skip getEvaluatorsCountForResource() when there is only one resource + String resourceKey = resourceKeys.iterator().next(); + RangerResourceTrie trie = resourceTrie.get(resourceKey); + + if (trie != null) { + resourceWithMinEvals = resourceKey; + } + } + + if (resourceWithMinEvals != null) { + RangerResourceTrie trie = resourceTrie.get(resourceWithMinEvals); + + ret = trie.getEvaluatorsForResource(resource.get(resourceWithMinEvals), scopes.get(resourceWithMinEvals), predicate); + + for (String resourceDefName : resourceKeys) { + if (resourceWithMinEvals.equals(resourceDefName)) { + continue; + } + + trie = resourceTrie.get(resourceDefName); + + if (trie == null) { + continue; + } + + Set evaluators = trie.getEvaluatorsForResource(resource.get(resourceDefName), scopes.get(resourceDefName), ret, predicate); + + if (CollectionUtils.isEmpty(evaluators)) { + ret = Collections.emptySet(); + + break; + } else { + if (evaluators.size() < ret.size()) { + ret = evaluators; + } + } + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerResourceEvaluatorsRetriever.getEvaluators(" + resource + ") : evaluator:[" + ret + "]"); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java index 354bf4485a..51d64ba1dd 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRoles.java @@ -20,22 +20,17 @@ package org.apache.ranger.plugin.util; import org.apache.ranger.plugin.model.RangerRole; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; import java.util.Date; import java.util.Set; @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerRoles implements Serializable { private static final long serialVersionUID = 1L; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java index 46bfde2ad8..0caf65826d 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesProvider.java @@ -19,14 +19,13 @@ package org.apache.ranger.plugin.util; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.apache.hadoop.conf.Configuration; import org.apache.ranger.admin.client.RangerAdminClient; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.authorization.utils.JsonUtils; import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileReader; @@ -38,9 +37,9 @@ public class RangerRolesProvider { - private static final Log LOG = LogFactory.getLog(RangerRolesProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerRolesProvider.class); - private static final Log PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init"); + private static final Logger PERF_POLICYENGINE_INIT_LOG = RangerPerfTracer.getPerfLogger("policyengine.init"); private final String serviceType; private final String serviceName; @@ -49,7 +48,6 @@ public class RangerRolesProvider { private final String cacheFileName; private final String cacheFileNamePrefix; private final String cacheDir; - private final Gson gson; private final boolean disableCacheIfServiceNotFound; private long lastActivationTimeInMillis; @@ -57,7 +55,7 @@ public class RangerRolesProvider { private boolean rangerUserGroupRolesSetInPlugin; private boolean serviceDefSetInPlugin; - public RangerRolesProvider(String serviceType, String appId, String serviceName, RangerAdminClient rangerAdmin, String cacheDir, Configuration config) { + public RangerRolesProvider(String serviceType, String appId, String serviceName, RangerAdminClient rangerAdmin, String cacheDir, RangerPluginConfig config) { if (LOG.isDebugEnabled()) { LOG.debug("==> RangerRolesProvider(serviceName=" + serviceName + ").RangerRolesProvider()"); } @@ -78,16 +76,7 @@ public RangerRolesProvider(String serviceType, String appId, String serviceName, this.cacheFileName = cacheFilename; this.cacheDir = cacheDir; - - Gson gson = null; - try { - gson = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSS-Z").create(); - } catch (Throwable excp) { - LOG.fatal("RangerRolesProvider(): failed to create GsonBuilder object", excp); - } - this.gson = gson; - - String propertyPrefix = "ranger.plugin." + serviceType; + String propertyPrefix = config.getPropertyPrefix(); disableCacheIfServiceNotFound = config.getBoolean(propertyPrefix + ".disable.cache.if.servicenotfound", true); if (LOG.isDebugEnabled()) { @@ -139,7 +128,7 @@ public void loadUserGroupRoles(RangerBasePlugin plugIn) { plugIn.setRoles(roles); rangerUserGroupRolesSetInPlugin = true; setLastActivationTimeInMillis(System.currentTimeMillis()); - lastKnownRoleVersion = roles.getRoleVersion(); + lastKnownRoleVersion = roles.getRoleVersion() != null ? roles.getRoleVersion() : -1; } else { if (!rangerUserGroupRolesSetInPlugin && !serviceDefSetInPlugin) { plugIn.setRoles(null); @@ -232,7 +221,7 @@ private RangerRoles loadUserGroupRolesFromCache() { try { reader = new FileReader(cacheFile); - roles = gson.fromJson(reader, RangerRoles.class); + roles = JsonUtils.jsonToObject(reader, RangerRoles.class); if (roles != null) { if (!StringUtils.equals(serviceName, roles.getServiceName())) { @@ -306,8 +295,7 @@ public void saveToCache(RangerRoles roles) { try { writer = new FileWriter(cacheFile); - - gson.toJson(roles, writer); + JsonUtils.objectToWriter(writer, roles); } catch (Exception excp) { LOG.error("failed to save roles to cache file '" + cacheFile.getAbsolutePath() + "'", excp); } finally { diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java index 0268e2f309..40b2652a92 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerRolesUtil.java @@ -33,18 +33,42 @@ public class RangerRolesUtil { private final long roleVersion; private final Map> userRoleMapping = new HashMap<>(); private final Map> groupRoleMapping = new HashMap<>(); + private final Map> roleRoleMapping = new HashMap<>(); + + private final Map> roleToUserMapping = new HashMap<>(); + private final Map> roleToGroupMapping = new HashMap<>(); + + private RangerRoles roles = null; + public enum ROLES_FOR {USER, GROUP, ROLE} public RangerRolesUtil(RangerRoles roles) { if (roles != null) { - roleVersion = roles.getRoleVersion(); + this.roles = roles; + roleVersion = roles.getRoleVersion() != null ? roles.getRoleVersion() : -1; if (CollectionUtils.isNotEmpty(roles.getRangerRoles())) { for (RangerRole role : roles.getRangerRoles()) { Set containedRoles = getAllContainedRoles(roles.getRangerRoles(), role); - buildMap(userRoleMapping, role, containedRoles, true); - buildMap(groupRoleMapping, role, containedRoles, false); + buildMap(userRoleMapping, role, containedRoles, ROLES_FOR.USER); + buildMap(groupRoleMapping, role, containedRoles, ROLES_FOR.GROUP); + buildMap(roleRoleMapping, role, containedRoles, ROLES_FOR.ROLE); + + Set roleUsers = new HashSet<>(); + Set roleGroups = new HashSet<>(); + + addMemberNames(role.getUsers(), roleUsers); + addMemberNames(role.getGroups(), roleGroups); + + for (RangerRole containedRole : containedRoles) { + addMemberNames(containedRole.getUsers(), roleUsers); + addMemberNames(containedRole.getGroups(), roleGroups); + } + + roleToUserMapping.put(role.getName(), roleUsers); + roleToGroupMapping.put(role.getName(), roleGroups); } + } } else { roleVersion = -1L; @@ -53,6 +77,10 @@ public RangerRolesUtil(RangerRoles roles) { public long getRoleVersion() { return roleVersion; } + public RangerRoles getRoles() { + return this.roles; + } + public Map> getUserRoleMapping() { return this.userRoleMapping; } @@ -61,6 +89,18 @@ public Map> getGroupRoleMapping() { return this.groupRoleMapping; } + public Map> getRoleRoleMapping() { + return this.roleRoleMapping; + } + + public Map> getRoleToUserMapping() { + return this.roleToUserMapping; + } + + public Map> getRoleToGroupMapping() { + return this.roleToGroupMapping; + } + private Set getAllContainedRoles(Set roles, RangerRole role) { Set allRoles = new HashSet<>(); @@ -83,36 +123,49 @@ private void addContainedRoles(Set allRoles, Set roles, } } - private void buildMap(Map> map, RangerRole role, Set containedRoles, boolean isUser) { - buildMap(map, role, role.getName(), isUser); + private void buildMap(Map> map, RangerRole role, Set containedRoles, ROLES_FOR roleFor) { + buildMap(map, role, role.getName(), roleFor); for (RangerRole containedRole : containedRoles) { - buildMap(map, containedRole, role.getName(), isUser); + buildMap(map, containedRole, role.getName(), roleFor); } } - private void buildMap(Map> map, RangerRole role, String roleName, boolean isUser) { - for (RangerRole.RoleMember userOrGroup : isUser ? role.getUsers() : role.getGroups()) { - if (StringUtils.isNotEmpty(userOrGroup.getName())) { - Set roleNames = map.get(userOrGroup.getName()); - - if (roleNames == null) { - roleNames = new HashSet<>(); - - map.put(userOrGroup.getName(), roleNames); - } + private void buildMap(Map> map, RangerRole role, String roleName, ROLES_FOR roles_for) { + List userOrGroupOrRole = null; + switch(roles_for) { + case USER: + userOrGroupOrRole = role.getUsers(); + break; + case GROUP: + userOrGroupOrRole = role.getGroups(); + break; + case ROLE: + userOrGroupOrRole = role.getRoles(); + break; + } + if (CollectionUtils.isNotEmpty(userOrGroupOrRole)) { + getRoleMap(map, roleName, userOrGroupOrRole); + } + } + private void getRoleMap(Map> map, String roleName, List userOrGroupOrRole) { + for (RangerRole.RoleMember roleMember : userOrGroupOrRole) { + if (StringUtils.isNotEmpty(roleMember.getName())) { + Set roleNames = map.computeIfAbsent(roleMember.getName(), k -> new HashSet<>()); roleNames.add(roleName); } } } private RangerRole getContainedRole(Set roles, String role) { - return (roles - .stream() - .filter(containedRole -> role.equals(containedRole.getName())) - .findAny() - .orElse(null)); + return (roles.stream().filter(containedRole -> role.equals(containedRole.getName())).findAny().orElse(null)); + } + + private void addMemberNames(List members, Set names) { + for (RangerRole.RoleMember member : members) { + names.add(member.getName()); + } } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSecurityZoneHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSecurityZoneHelper.java new file mode 100644 index 0000000000..5c2f5ea0f4 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSecurityZoneHelper.java @@ -0,0 +1,449 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.model.RangerPrincipal; +import org.apache.ranger.plugin.model.RangerSecurityZone; +import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneChangeRequest; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResource; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResourceBase; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneServiceV2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class RangerSecurityZoneHelper { + private final RangerSecurityZone zone; + private final String currentUser; + private final Map services; + + + public RangerSecurityZoneHelper(RangerSecurityZone zone, String currentUser) { + this.zone = zone; + this.currentUser = currentUser; + this.services = new HashMap<>(); + + for (Map.Entry entry : zone.getServices().entrySet()) { + this.services.put(entry.getKey(), new RangerSecurityZoneServiceHelper(entry.getValue(), currentUser)); + } + } + + public RangerSecurityZone getZone() { return zone; } + + public RangerSecurityZoneServiceHelper getZoneService(String serviceName) { + return services.get(serviceName); + } + + public RangerSecurityZoneServiceHelper addZoneService(String serviceName) { + RangerSecurityZoneServiceHelper ret = services.get(serviceName); + + if (ret == null) { + RangerSecurityZoneService zoneService = zone.getServices().get(serviceName); + + if (zoneService == null) { + zoneService = new RangerSecurityZoneService(); + + zone.getServices().put(serviceName, zoneService); + } + + ret = new RangerSecurityZoneServiceHelper(zoneService, currentUser); + + services.put(serviceName, ret); + } + + return ret; + } + + public void removeService(String serviceName) { + services.remove(serviceName); + zone.getServices().remove(serviceName); + } + + public RangerSecurityZone updateZone(RangerSecurityZoneChangeRequest changeData) throws Exception { + if (changeData.getName() != null) { + zone.setName(changeData.getName()); + } + + if (changeData.getDescription() != null) { + zone.setDescription(changeData.getDescription()); + } + + if (changeData.getResourcesToUpdate() != null) { + for (Map.Entry entry : changeData.getResourcesToUpdate().entrySet()) { + String serviceName = entry.getKey(); + RangerSecurityZoneServiceV2 zoneService = entry.getValue(); + RangerSecurityZoneServiceHelper zoneServiceHelper = addZoneService(serviceName); + + if (zoneService != null && zoneService.getResources() != null) { + for (RangerSecurityZoneResource resource : zoneService.getResources()) { + if (resource != null) { + zoneServiceHelper.updateResource(resource); + } + } + } + } + } + + if (changeData.getResourcesToRemove() != null) { + for (Map.Entry entry : changeData.getResourcesToRemove().entrySet()) { + String serviceName = entry.getKey(); + RangerSecurityZoneServiceV2 zoneService = entry.getValue(); + RangerSecurityZoneServiceHelper zoneServiceHelper = getZoneService(serviceName); + + if (zoneServiceHelper != null && zoneService != null && zoneService.getResources() != null) { + for (RangerSecurityZoneResource resource : zoneService.getResources()) { + if (resource != null) { + final RangerSecurityZoneResource removedResource; + + if (resource.getId() != null) { + removedResource = zoneServiceHelper.removeResource(resource.getId()); + } else if (resource.getResource() != null) { + removedResource = zoneServiceHelper.removeResource(resource.getResource()); + } else { + removedResource = null; + } + + if (removedResource == null) { + throw new Exception(resource + ": resource not in zone"); + } + } + } + + if (zoneServiceHelper.getResourceCount() == 0) { + removeService(serviceName); + } + } else { + throw new Exception(serviceName + ": service not in zone"); + } + } + } + + if (changeData.getTagServicesToAdd() != null) { + for (String tagServiceToAdd : changeData.getTagServicesToAdd()) { + if (!addIfAbsent(tagServiceToAdd, zone.getTagServices())) { + throw new Exception(tagServiceToAdd + ": tag service already exists in zone"); + } + } + } + + if (changeData.getTagServicesToRemove() != null) { + for (String tagServiceToRemove : changeData.getTagServicesToRemove()) { + if (!zone.getTagServices().remove(tagServiceToRemove)) { + throw new Exception(tagServiceToRemove + ": tag service not in zone"); + } + } + } + + if (changeData.getAdminsToAdd() != null) { + addPrincipals(changeData.getAdminsToAdd(), zone.getAdminUsers(), zone.getAdminUserGroups(), zone.getAdminRoles()); + } + + if (changeData.getAdminsToRemove() != null) { + removePrincipals(changeData.getAdminsToRemove(), zone.getAdminUsers(), zone.getAdminUserGroups(), zone.getAdminRoles()); + } + + if (changeData.getAuditorsToAdd() != null) { + addPrincipals(changeData.getAuditorsToAdd(), zone.getAuditUsers(), zone.getAuditUserGroups(), zone.getAuditRoles()); + } + + if (changeData.getAuditorsToRemove() != null) { + removePrincipals(changeData.getAuditorsToRemove(), zone.getAuditUsers(), zone.getAuditUserGroups(), zone.getAuditRoles()); + } + + return zone; + } + + private void addPrincipals(List principals, List users, List groups, List roles) throws Exception { + for (RangerPrincipal principal : principals) { + boolean isAdded = false; + + if (principal.getType() == RangerPrincipal.PrincipalType.USER) { + isAdded = addIfAbsent(principal.getName(), users); + } else if (principal.getType() == RangerPrincipal.PrincipalType.GROUP) { + isAdded = addIfAbsent(principal.getName(), groups); + } else if (principal.getType() == RangerPrincipal.PrincipalType.ROLE) { + isAdded = addIfAbsent(principal.getName(), roles); + } + + if(!isAdded) { + throw new Exception(principal + ": principal already an admin or auditor in zone"); + } + } + } + + private void removePrincipals(List principals, List users, List groups, List roles) throws Exception { + for (RangerPrincipal principal : principals) { + boolean isRemoved = false; + + if (principal.getType() == RangerPrincipal.PrincipalType.USER) { + isRemoved = users.remove(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.GROUP) { + isRemoved = groups.remove(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.ROLE) { + isRemoved = roles.remove(principal.getName()); + } + + if(!isRemoved) { + throw new Exception(principal + ": principal not an admin or auditor in zone"); + } + } + } + + private boolean addIfAbsent(String item, List lst) { + final boolean ret; + + if (!lst.contains(item)) { + ret = lst.add(item); + } else { + ret = false; + } + + return ret; + } + + public static class RangerSecurityZoneServiceHelper { + private final RangerSecurityZoneService zoneService; + private final String currentUser; + private final List>> resources; + private final List resourcesBaseInfo; + private long nextResourceId = 1; + + public RangerSecurityZoneServiceHelper(RangerSecurityZoneService zoneService, String currentUser) { + this.zoneService = zoneService; + this.currentUser = currentUser; + + if (zoneService.getResources() != null) { + this.resources = zoneService.getResources(); + } else { + this.resources = new ArrayList<>(); + + zoneService.setResources(this.resources); + } + + if (zoneService.getResourcesBaseInfo() != null) { + this.resourcesBaseInfo = zoneService.getResourcesBaseInfo(); + } else { + this.resourcesBaseInfo = new ArrayList<>(); + + zoneService.setResourcesBaseInfo(this.resourcesBaseInfo); + } + + // compute nextResourceId + for (RangerSecurityZoneResourceBase baseInfo : resourcesBaseInfo) { + if (baseInfo.getId() != null && nextResourceId <= baseInfo.getId()) { + nextResourceId = baseInfo.getId() + 1; + } + } + + // make sure resourcesBaseInfo has as many entries as resources + for (int i = resourcesBaseInfo.size(); i < resources.size(); i++) { + RangerSecurityZoneResourceBase baseInfo = new RangerSecurityZoneResourceBase(); + + setCreated(baseInfo); + + resourcesBaseInfo.add(baseInfo); + } + + // remove any additional resourcesBaseInfo entries + for (int i = resources.size(); i < resourcesBaseInfo.size(); ) { + resourcesBaseInfo.remove(i); + } + + // set missing IDs + for (RangerSecurityZoneResourceBase baseInfo : resourcesBaseInfo) { + if (baseInfo.getId() == null) { + baseInfo.setId(nextResourceId++); + } + } + } + + public RangerSecurityZoneService getZoneService() { return zoneService; } + + public int getResourceCount() { + return resources != null ? resources.size() : 0; + } + + public List getResources() { + List ret = new ArrayList<>(); + + if (resources != null) { + for (int i = 0; i < resources.size(); i++) { + ret.add(getResourceAt(i)); + } + } + + return Collections.unmodifiableList(ret); + } + + public List getResources(int startIdx, int count) { + List ret = new ArrayList<>(); + + if (resources != null) { + for (int i = 0; i < count; i++) { + RangerSecurityZoneResource resource = getResourceAt(startIdx + i); + + if (resource == null) { + break; + } + + ret.add(resource); + } + } + + return Collections.unmodifiableList(ret); + } + + public RangerSecurityZoneResource getResource(long id) { + int idx = getResourceIdx(id); + + return idx != -1 ? getResourceAt(idx) : null; + } + + public RangerSecurityZoneResource getResource(Map> resource) { + int idx = getResourceIdx(resource); + + return idx != -1 ? getResourceAt(idx) : null; + } + + public RangerSecurityZoneResource addResource(RangerSecurityZoneResource resource) { + setCreated(resource); + + resources.add((HashMap>) resource.getResource()); + resourcesBaseInfo.add(new RangerSecurityZoneResourceBase(resource)); + + return resource; + } + + public RangerSecurityZoneResource updateResource(RangerSecurityZoneResource resource) { + Long resourceId = resource.getId(); + int resourceIdx = resourceId != null ? getResourceIdx(resourceId) : -1; + + if (resourceIdx == -1) { + addResource(resource); + } else { + setUpdated(resource, resourceIdx); + + resources.set(resourceIdx, (HashMap>) resource.getResource()); + resourcesBaseInfo.set(resourceIdx, new RangerSecurityZoneResourceBase(resource)); + } + + return resource; + } + + public RangerSecurityZoneResource removeResource(long id) { + int idx = getResourceIdx(id); + + return idx != -1 ? removeResourceAt(idx) : null; + } + + public RangerSecurityZoneResource removeResource(Map> resource) { + int idx = getResourceIdx(resource); + + return idx != -1 ? removeResourceAt(idx) : null; + } + + private RangerSecurityZoneResource getResourceAt(int idx) { + RangerSecurityZoneResource ret = null; + HashMap> resource = (resources != null && resources.size() > idx) ? resources.get(idx) : null; + RangerSecurityZoneResourceBase resourceBaseInfo = (resourcesBaseInfo != null && resourcesBaseInfo.size() > idx) ? resourcesBaseInfo.get(idx) : null; + + if (resource != null) { + ret = new RangerSecurityZoneResource(resource, resourceBaseInfo); + } + + return ret; + } + + private RangerSecurityZoneResource removeResourceAt(int idx) { + RangerSecurityZoneResource ret = null; + HashMap> resource = (resources != null && resources.size() > idx) ? resources.remove(idx) : null; + RangerSecurityZoneResourceBase resourceBaseInfo = (resourcesBaseInfo != null && resourcesBaseInfo.size() > idx) ? resourcesBaseInfo.remove(idx) : null; + + if (resource != null) { + ret = new RangerSecurityZoneResource(resource, resourceBaseInfo); + } + + return ret; + } + + private int getResourceIdx(long id) { + int ret = -1; + + if (resourcesBaseInfo != null) { + for (int i = 0; i < resourcesBaseInfo.size(); i++) { + RangerSecurityZoneResourceBase baseInfo = resourcesBaseInfo.get(i); + + if (baseInfo != null && baseInfo.getId() != null && baseInfo.getId().equals(id)) { + ret = i; + + break; + } + } + } + + return ret; + } + + private int getResourceIdx(Map> resource) { + int ret = -1; + + if (resources != null) { + for (int i = 0; i < resources.size(); i++) { + HashMap> res = resources.get(i); + + if (Objects.equals(resource, res)) { + ret = i; + + break; + } + } + } + + return ret; + } + + private void setCreated(RangerSecurityZoneResourceBase baseInfo) { + baseInfo.setId(nextResourceId++); + baseInfo.setCreatedBy(currentUser); + baseInfo.setCreateTime(new Date()); + baseInfo.setUpdatedBy(currentUser); + baseInfo.setUpdateTime(new Date()); + } + + private void setUpdated(RangerSecurityZoneResourceBase baseInfo, int idx) { + RangerSecurityZoneResourceBase resourceBase = (resourcesBaseInfo != null && resourcesBaseInfo.size() > idx) ? resourcesBaseInfo.get(idx) : null; + + if(resourceBase != null) { + baseInfo.setId(resourceBase.getId()); + baseInfo.setCreatedBy(resourceBase.getCreatedBy()); + baseInfo.setCreateTime(resourceBase.getCreateTime()); + } + + baseInfo.setUpdatedBy(currentUser); + baseInfo.setUpdateTime(new Date()); + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerServiceTagsDeltaUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerServiceTagsDeltaUtil.java index 6b70b22595..d5a0915e0a 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerServiceTagsDeltaUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerServiceTagsDeltaUtil.java @@ -19,113 +19,240 @@ package org.apache.ranger.plugin.util; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.apache.commons.lang3.tuple.MutablePair; import org.apache.ranger.plugin.model.RangerServiceResource; import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerTagDef; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; public class RangerServiceTagsDeltaUtil { - private static final Log LOG = LogFactory.getLog(RangerServiceTagsDeltaUtil.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceTagsDeltaUtil.class); - private static final Log PERF_TAGS_DELTA_LOG = RangerPerfTracer.getPerfLogger("tags.delta"); + private static final Logger PERF_TAGS_DELTA_LOG = RangerPerfTracer.getPerfLogger("tags.delta"); /* It should be possible to call applyDelta() multiple times with serviceTags and delta resulting from previous call to applyDelta() The end result should be same if called once or multiple times. */ - static public ServiceTags applyDelta(ServiceTags serviceTags, ServiceTags delta) { + static public ServiceTags applyDelta(ServiceTags serviceTags, ServiceTags delta, boolean supportsTagsDedup) { if (LOG.isDebugEnabled()) { - LOG.debug("==> RangerServiceTagsDeltaUtil.applyDelta()"); + LOG.debug("==> RangerServiceTagsDeltaUtil.applyDelta(): serviceTags:[" + serviceTags + "], delta:[" + delta + "], supportsTagsDedup:[" + supportsTagsDedup + "]"); } + + ServiceTags ret = serviceTags; RangerPerfTracer perf = null; if(RangerPerfTracer.isPerfTraceEnabled(PERF_TAGS_DELTA_LOG)) { perf = RangerPerfTracer.getPerfTracer(PERF_TAGS_DELTA_LOG, "RangerServiceTagsDeltaUtil.applyDelta()"); } + if (serviceTags != null && !serviceTags.getIsDelta() && delta != null && delta.getIsDelta()) { - serviceTags.setServiceName(delta.getServiceName()); - serviceTags.setTagVersion(delta.getTagVersion()); - - // merge - Map tags = serviceTags.getTags(); - List serviceResources = serviceTags.getServiceResources(); - Map> resourceToTagIds = serviceTags.getResourceToTagIds(); - boolean isAnyMaterialChange = false; - - for (Map.Entry tagEntry : delta.getTags().entrySet()) { - if (StringUtils.isEmpty(tagEntry.getValue().getType())) { - if (null != tags.remove(tagEntry.getKey())) { - isAnyMaterialChange = true; + ret = new ServiceTags(serviceTags); + + ret.setServiceName(delta.getServiceName()); + ret.setTagVersion(delta.getTagVersion()); + ret.setIsTagsDeduped(delta.getIsTagsDeduped()); + + int tagDefsAdded = 0, tagDefsUpdated = 0, tagDefsRemoved = 0; + int tagsAdded = 0, tagsUpdated = 0, tagsRemoved = 0; + + Map tagDefs = ret.getTagDefinitions(); + + for (Iterator> deltaTagDefIter = delta.getTagDefinitions().entrySet().iterator(); deltaTagDefIter.hasNext(); ) { + Map.Entry entry = deltaTagDefIter.next(); + Long deltaTagDefId = entry.getKey(); + RangerTagDef deltaTagDef = entry.getValue(); + + if (StringUtils.isEmpty(deltaTagDef.getName())) { // tagdef has been removed + RangerTagDef removedTagDef = tagDefs.remove(deltaTagDefId); + + if (removedTagDef != null) { + tagDefsRemoved++; } - } else { - tags.put(tagEntry.getKey(), tagEntry.getValue()); } - } - // This could be expensive - compute time is M x N ( M - number of old tagged service resources, N - number of changed service resources) + RangerTagDef existing = tagDefs.put(deltaTagDefId, deltaTagDef); - Map deletedServiceResources = new HashMap<>(); - List addedServiceResources = new ArrayList<>(); + if (existing == null) { + tagDefsAdded++; + } else if (!existing.equals(deltaTagDef)) { + tagDefsUpdated++; + } + } - for (RangerServiceResource serviceResource : delta.getServiceResources()) { + Map tags = ret.getTags(); + Map replacedTagIds = new HashMap<>(); - boolean found = false; - Iterator iter = serviceResources.iterator(); + for (Iterator> deltaTagIter = delta.getTags().entrySet().iterator(); deltaTagIter.hasNext(); ) { + Map.Entry entry = deltaTagIter.next(); + Long deltaTagId = entry.getKey(); + RangerTag deltaTag = entry.getValue(); - while (iter.hasNext()) { - RangerServiceResource existingResource = iter.next(); + if (StringUtils.isEmpty(deltaTag.getType())) { // tag has been removed + if (supportsTagsDedup) { + boolean found = false; - if (serviceResource.getId().equals(existingResource.getId())) { - if (!StringUtils.isEmpty(serviceResource.getResourceSignature())) { - if (!serviceResource.getResourceSignature().equals(existingResource.getResourceSignature())) { // ServiceResource changed - iter.remove(); - existingResource.setResourceSignature(null); - addedServiceResources.add(existingResource); + for (Iterator>> iterator = ret.cachedTags.entrySet().iterator(); iterator.hasNext(); ) { + MutablePair value = iterator.next().getValue(); + if (value.left.equals(deltaTagId)) { + if (--value.right == 0) { + // This may never be true when this tag is duplicated + // as the mapping between de-duplicated tags is not maintained - only the reference count is stored + // So, the tag with the smallest tag-id (among duplicate tags) will never be removed + if (tags.remove(deltaTagId) != null) { + tagsRemoved++; + } + iterator.remove(); + } + found = true; break; } + } + + if (!found) { + if (tags.remove(deltaTagId) != null) { + tagsRemoved++; + } + } + } else { + if (tags.remove(deltaTagId) != null) { + tagsRemoved++; + } + } + } else { + if (supportsTagsDedup) { + MutablePair cachedTag = ret.cachedTags.get(deltaTag); + + if (cachedTag == null) { + ret.cachedTags.put(deltaTag, new MutablePair<>(deltaTagId, 1L)); + tags.put(deltaTagId, deltaTag); + tagsAdded++; } else { - iter.remove(); - deletedServiceResources.put(serviceResource.getId(), serviceResource.getId()); + cachedTag.right++; + replacedTagIds.put(deltaTagId, cachedTag.left); + } + } else { + RangerTag existing = tags.put(deltaTagId, deltaTag); + + if (existing == null) { + tagsAdded++; + } else if (!existing.equals(deltaTag)) { + tagsUpdated++; } - found = true; - break; } } - if (!found) { - if (StringUtils.isNotEmpty(serviceResource.getResourceSignature())) { - serviceResources.add(serviceResource); - isAnyMaterialChange = true; + } + + List serviceResources = ret.getServiceResources(); + Map> resourceToTagIds = ret.getResourceToTagIds(); + Map idResourceMap = serviceResources.stream().collect(Collectors.toMap(RangerServiceResource::getId, Function.identity())); + Map resourcesToRemove = new HashMap<>(); + Map resourcesToAdd = new HashMap<>(); + + for (RangerServiceResource resource : delta.getServiceResources()) { + RangerServiceResource existingResource = idResourceMap.get(resource.getId()); + + if (existingResource != null) { + if (StringUtils.isNotEmpty(resource.getResourceSignature())) { + if (!StringUtils.equals(resource.getResourceSignature(), existingResource.getResourceSignature())) { // ServiceResource changed; replace existing instance + /* If the signature changed, we need to remove existing resource and add new resource */ + resourcesToRemove.put(resource.getId(), existingResource); + resourcesToAdd.put(resource.getId(), resource); + } + } else { // resource deleted + resourcesToRemove.put(resource.getId(), existingResource); + + resourceToTagIds.remove(existingResource.getId()); + } + } else { // resource added + if (StringUtils.isNotEmpty(resource.getResourceSignature())) { + resourcesToAdd.put(resource.getId(), resource); } } } - for (Long deletedServiceResourceId : deletedServiceResources.keySet()) { - resourceToTagIds.remove(deletedServiceResourceId); + if (!resourcesToRemove.isEmpty()) { + for (ListIterator iter = serviceResources.listIterator(); iter.hasNext(); ) { + RangerServiceResource resource = iter.next(); + RangerServiceResource deletedResource = resourcesToRemove.get(resource.getId()); + RangerServiceResource addedResource = resourcesToAdd.get(resource.getId()); + + if (addedResource == null && deletedResource == resource) { + iter.remove(); + } + } } - resourceToTagIds.putAll(delta.getResourceToTagIds()); + serviceResources.addAll(resourcesToAdd.values()); - // Ensure that any modified service-resources are at head of list of service-resources in delta - // So that in in setServiceTags(), they get cleaned out first, and service-resource with new spec gets added + if (!replacedTagIds.isEmpty()) { + for (Map.Entry> resourceEntry : delta.getResourceToTagIds().entrySet()) { + ListIterator listIter = resourceEntry.getValue().listIterator(); - addedServiceResources.addAll(delta.getServiceResources()); - delta.setServiceResources(addedServiceResources); + while (listIter.hasNext()) { + Long tagId = listIter.next(); + Long replacerTagId = replacedTagIds.get(tagId); - if (!isAnyMaterialChange) { + if (replacerTagId != null) { + listIter.set(replacerTagId); + } + } + } + } + + resourceToTagIds.putAll(delta.getResourceToTagIds()); + + if (MapUtils.isEmpty(resourceToTagIds)) { if (LOG.isDebugEnabled()) { - LOG.debug("No material change may have occurred because of applying this delta"); + LOG.debug("There are no resource->tag mappings!!"); } + if (MapUtils.isNotEmpty(ret.getTags())) { + LOG.warn("There are no resource->tag mappings, but there are tags in the ServiceTags!! Cleaning up"); + ret.getTags().clear(); + } + if (supportsTagsDedup) { + ret.cachedTags.clear(); + } + } + + // Ensure that any modified service-resources are at head of list of service-resources in delta + // So that in setServiceTags(), they get cleaned out first, and service-resource with new spec gets added + + List deltaServiceResources = new ArrayList<>(); + for (RangerServiceResource resourceToRemove : resourcesToRemove.values()) { + resourceToRemove.setResourceSignature(null); + deltaServiceResources.add(resourceToRemove); + } + if (!resourcesToAdd.isEmpty()) { + deltaServiceResources.addAll(resourcesToAdd.values()); } + delta.setServiceResources(deltaServiceResources); + + if (LOG.isDebugEnabled()) { + LOG.debug("RangerServiceTagsDeltaUtil.applyDelta(): delta(tagDefs={}, tags={}, resources={}), " + + "resources(total={}, added={}, removed={}), " + + "tags(total={}, added={}, updated={}, removed={}), " + + "tagDefs(total={}, added={}, updated={}, removed={})", + delta.getTagDefinitions().size(), delta.getTags().size(), delta.getServiceResources().size(), + serviceResources.size(), resourcesToAdd.size(), resourcesToRemove.size(), + tags.size(), tagsAdded, tagsUpdated, tagsRemoved, + tagDefs.size(), tagDefsAdded, tagDefsUpdated, tagDefsRemoved); + } } else { if (LOG.isDebugEnabled()) { LOG.debug("Cannot apply deltas to service-tags as one of preconditions is violated. Returning received serviceTags without applying delta!!"); @@ -133,12 +260,12 @@ static public ServiceTags applyDelta(ServiceTags serviceTags, ServiceTags delta) } if (LOG.isDebugEnabled()) { - LOG.debug("<== RangerServiceTagsDeltaUtil.applyDelta()"); + LOG.debug("<== RangerServiceTagsDeltaUtil.applyDelta(): serviceTags:[" + ret + "], delta:[" + delta + "], supportsTagsDedup:[" + supportsTagsDedup + "]"); } RangerPerfTracer.log(perf); - return serviceTags; + return ret; } public static void pruneUnusedAttributes(ServiceTags serviceTags) { @@ -146,32 +273,68 @@ public static void pruneUnusedAttributes(ServiceTags serviceTags) { serviceTags.setTagUpdateTime(null); for (Map.Entry entry : serviceTags.getTagDefinitions().entrySet()) { - RangerTagDef tagDef = entry.getValue(); - tagDef.setCreatedBy(null); - tagDef.setCreateTime(null); - tagDef.setUpdatedBy(null); - tagDef.setUpdateTime(null); - tagDef.setGuid(null); + pruneUnusedAttributes(entry.getValue()); } for (Map.Entry entry : serviceTags.getTags().entrySet()) { - RangerTag tag = entry.getValue(); - tag.setCreatedBy(null); - tag.setCreateTime(null); - tag.setUpdatedBy(null); - tag.setUpdateTime(null); - tag.setGuid(null); + pruneUnusedAttributes(entry.getValue()); } for (RangerServiceResource serviceResource : serviceTags.getServiceResources()) { - serviceResource.setCreatedBy(null); - serviceResource.setCreateTime(null); - serviceResource.setUpdatedBy(null); - serviceResource.setUpdateTime(null); - serviceResource.setGuid(null); - - serviceResource.setServiceName(null); + pruneUnusedAttributes(serviceResource); } } } + + public static void pruneUnusedAttributes(RangerTagDef tagDef) { + tagDef.setCreatedBy(null); + tagDef.setCreateTime(null); + tagDef.setUpdatedBy(null); + tagDef.setUpdateTime(null); + tagDef.setGuid(null); + tagDef.setVersion(null); + + if (tagDef.getAttributeDefs() != null && tagDef.getAttributeDefs().isEmpty()) { + tagDef.setAttributeDefs(null); + } + } + + public static void pruneUnusedAttributes(RangerTag tag) { + tag.setCreatedBy(null); + tag.setCreateTime(null); + tag.setUpdatedBy(null); + tag.setUpdateTime(null); + tag.setGuid(null); + tag.setVersion(null); + + if (tag.getOwner() != null && tag.getOwner().shortValue() == RangerTag.OWNER_SERVICERESOURCE) { + tag.setOwner(null); + } + + if (tag.getAttributes() != null && tag.getAttributes().isEmpty()) { + tag.setAttributes(null); + } + + if (tag.getOptions() != null && tag.getOptions().isEmpty()) { + tag.setOptions(null); + } + + if (tag.getValidityPeriods() != null && tag.getValidityPeriods().isEmpty()) { + tag.setValidityPeriods(null); + } + } + + public static void pruneUnusedAttributes(RangerServiceResource serviceResource) { + serviceResource.setCreatedBy(null); + serviceResource.setCreateTime(null); + serviceResource.setUpdatedBy(null); + serviceResource.setUpdateTime(null); + serviceResource.setGuid(null); + serviceResource.setVersion(null); + + if (serviceResource.getAdditionalInfo() != null && serviceResource.getAdditionalInfo().isEmpty()) { + serviceResource.setAdditionalInfo(null); + } + } + } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSslHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSslHelper.java index 26eaae20ca..8d803e32f7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSslHelper.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSslHelper.java @@ -39,14 +39,14 @@ import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.ranger.authorization.hadoop.utils.RangerCredentialProvider; import org.apache.ranger.authorization.utils.StringUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RangerSslHelper { - private static final Log LOG = LogFactory.getLog(RangerSslHelper.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerSslHelper.class); static final String RANGER_POLICYMGR_CLIENT_KEY_FILE = "xasecure.policymgr.clientssl.keystore"; static final String RANGER_POLICYMGR_CLIENT_KEY_FILE_TYPE = "xasecure.policymgr.clientssl.keystore.type"; @@ -62,7 +62,7 @@ public class RangerSslHelper { static final String RANGER_SSL_KEYMANAGER_ALGO_TYPE = KeyManagerFactory.getDefaultAlgorithm(); static final String RANGER_SSL_TRUSTMANAGER_ALGO_TYPE = TrustManagerFactory.getDefaultAlgorithm(); - static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLS"; + static final String RANGER_SSL_CONTEXT_ALGO_TYPE = "TLSv1.2"; private String mKeyStoreURL; private String mKeyStoreAlias; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerTimeRangeChecker.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerTimeRangeChecker.java new file mode 100644 index 0000000000..e6e9b1528e --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerTimeRangeChecker.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class RangerTimeRangeChecker { + private static final Logger LOG = LoggerFactory.getLogger(RangerTimeRangeChecker.class); + + private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); + private static final ThreadLocal DATE_TIME_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")); + private static final ThreadLocal DATE_TIME_FORMAT_2 = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy/MM/dd HH:mm")); + private static final ThreadLocal DATE_FORMAT = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy/MM/dd")); + private static final ThreadLocal[] DATE_FORMATS = new ThreadLocal[] {DATE_TIME_FORMAT, DATE_TIME_FORMAT_2, DATE_FORMAT}; + + private final long fromTime; + private final long toTime; + + + public RangerTimeRangeChecker(String fromTime, String toTime, String timeZone) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> RangerTimeRangeChecker({}, {})", fromTime, toTime); + } + + TimeZone tz = StringUtils.isNotBlank(timeZone) ? TimeZone.getTimeZone(timeZone) : null;; + + this.fromTime = parseDateTime(fromTime, tz); + this.toTime = parseDateTime(toTime, tz); + + if (LOG.isDebugEnabled()) { + LOG.debug("<== RangerTimeRangeChecker({}, {}): fromTime={}, toTime={}", fromTime, toTime, this.fromTime, this.toTime); + } + } + + public boolean isInRange(long time) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> isInRange({})", time); + } + + final boolean ret; + + if (toTime < 0) { // IS_ACCESS_TIME_AFTER + ret = time > fromTime; + } else if (fromTime < 0) { // IS_ACCESS_TIME_BEFORE + ret = time < toTime; + } else { // IS_ACCESS_TIME_BETWEEN + ret = time >= fromTime && time < toTime; + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== isInRange({}): fromTime={}, toTime={} ret={}", time, fromTime, toTime, ret); + } + + return ret; + } + + private static long parseDateTime(String str, TimeZone tz) { + final long ret; + + if (StringUtils.isNotBlank(str)) { + Date date = null; + ParseException excp = null; + + for (ThreadLocal formatter : DATE_FORMATS) { + try { + date = formatter.get().parse(str); + + break; + } catch (ParseException exception) { + excp = exception; + } + } + + if (date != null) { + ret = tz != null ? getAdjustedTime(date.getTime(), tz) : date.getTime(); + } else { + LOG.error("Error parsing date:[{}]", str, excp); + + ret = -1; + } + } else { + ret = -1; + } + + return ret; + } + + private static long getAdjustedTime(long localTime, TimeZone timeZone) { + final long ret; + + if (!DEFAULT_TIMEZONE.equals(timeZone)) { + int targetOffset = timeZone.getOffset(localTime); + int defaultOffset = DEFAULT_TIMEZONE.getOffset(localTime); + int diff = defaultOffset - targetOffset; + + ret = localTime + diff; + } else { + ret = localTime; + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java index 97e05d150c..18737fa64f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStore.java @@ -22,15 +22,13 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; +import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.model.GroupInfo; import org.apache.ranger.plugin.model.UserInfo; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; import java.io.Serializable; import java.util.Date; import java.util.HashMap; @@ -38,10 +36,8 @@ import java.util.Set; @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class RangerUserStore implements Serializable { private static final long serialVersionUID = 1L; public static final String CLOUD_IDENTITY_NAME = "cloud_id"; @@ -118,6 +114,16 @@ public void setGroupCloudIdMapping(Map groupCloudIdMapping) { this.groupCloudIdMapping = groupCloudIdMapping; } + public void dedupStrings() { + Map strTbl = new HashMap<>(); + + userAttrMapping = StringUtil.dedupStringsMapOfMap(userAttrMapping, strTbl); + groupAttrMapping = StringUtil.dedupStringsMapOfMap(groupAttrMapping, strTbl); + userGroupMapping = StringUtil.dedupStringsMapOfSet(userGroupMapping, strTbl); + userCloudIdMapping = StringUtil.dedupStringsMap(userCloudIdMapping, strTbl); + groupCloudIdMapping = StringUtil.dedupStringsMap(groupCloudIdMapping, strTbl); + } + @Override public String toString( ) { StringBuilder sb = new StringBuilder(); @@ -131,20 +137,40 @@ public StringBuilder toString(StringBuilder sb) { sb.append("RangerUserStore={") .append("userStoreVersion=").append(userStoreVersion).append(", ") .append("userStoreUpdateTime=").append(userStoreUpdateTime).append(", "); + sb.append("users={"); if(MapUtils.isNotEmpty(userAttrMapping)) { for(String user : userAttrMapping.keySet()) { - sb.append(user); + sb.append(user).append(" "); } } sb.append("}, "); + sb.append("groups={"); if(MapUtils.isNotEmpty(groupAttrMapping)) { for(String group : groupAttrMapping.keySet()) { - sb.append(group); + sb.append(group).append(" "); } } sb.append("}"); + + sb.append(", userGroupMapping={"); + if(MapUtils.isNotEmpty(userGroupMapping)) { + for(Map.Entry> entry : userGroupMapping.entrySet()) { + String user = entry.getKey(); + Set userGroups = entry.getValue(); + + sb.append(user).append("["); + if (CollectionUtils.isNotEmpty(userGroups)) { + for (String userGroup : userGroups) { + sb.append(userGroup).append(" "); + } + } + sb.append("] "); + } + } + sb.append("}"); + sb.append("}"); return sb; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java index f66eb1fb00..70f2c66291 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerUserStoreUtil.java @@ -22,11 +22,89 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; +import java.util.Set; public class RangerUserStoreUtil { public static final String CLOUD_IDENTITY_NAME = "cloud_id"; + private final RangerUserStore userStore; + private final long userStoreVersion; + private final Map> userGroups; + private final Map> userAttributes; + private final Map> groupAttributes; + + private volatile Map userEmailToName = null; + + public RangerUserStoreUtil(RangerUserStore userStore) { + this.userStore = userStore; + + if (userStore != null) { + this.userStoreVersion = userStore.getUserStoreVersion() != null ? userStore.getUserStoreVersion() : -1; + this.userGroups = userStore.getUserGroupMapping() != null ? userStore.getUserGroupMapping() : Collections.emptyMap(); + this.userAttributes = userStore.getUserAttrMapping() != null ? userStore.getUserAttrMapping() : Collections.emptyMap(); + this.groupAttributes = userStore.getGroupAttrMapping() != null ? userStore.getGroupAttrMapping() : Collections.emptyMap(); + } else { + this.userStoreVersion = -1; + this.userGroups = Collections.emptyMap(); + this.userAttributes = Collections.emptyMap(); + this.groupAttributes = Collections.emptyMap(); + this.userEmailToName = Collections.emptyMap(); + } + } + + public RangerUserStore getUserStore() { return userStore; } + + public long getUserStoreVersion() { return userStoreVersion; } + + public Set getUserGroups(String userName) { return userGroups.get(userName); } + + public Map getUserAttributes(String userName) { return userAttributes.get(userName); } + + public Map getGroupAttributes(String groupName) { return groupAttributes.get(groupName); } + + public String getUserNameFromEmail(String emailAddress) { + Map userEmailToName = this.userEmailToName; + + if (userEmailToName == null) { + synchronized (this) { + userEmailToName = this.userEmailToName; + + if (userEmailToName == null) { + this.userEmailToName = buildUserEmailToNameMap(); + + userEmailToName = this.userEmailToName; + } + } + } + + return userEmailToName != null ? userEmailToName.get(emailAddress) : null; + } + + private Map buildUserEmailToNameMap() { + final Map ret; + + if (!userAttributes.isEmpty()) { + ret = new HashMap<>(); + + for (Map.Entry> entry : userAttributes.entrySet()) { + String userName = entry.getKey(); + Map userAttrs = entry.getValue(); + String emailAddr = userAttrs != null ? userAttrs.get(RangerCommonConstants.SCRIPT_FIELD__EMAIL_ADDRESS) : null; + + if (StringUtils.isNotBlank(emailAddr)) { + ret.put(emailAddr, userName); + } + } + } else { + ret = Collections.emptyMap(); + } + + return ret; + } + public static String getPrintableOptions(Map otherAttributes) { if (MapUtils.isEmpty(otherAttributes)) return "{}"; StringBuilder ret = new StringBuilder(); @@ -51,9 +129,7 @@ public static String getAttrVal(Map> attrMap, String } public String getCloudId(Map> attrMap, String name) { - String cloudId = null; - cloudId = getAttrVal(attrMap, name, CLOUD_IDENTITY_NAME); - return cloudId; + return getAttrVal(attrMap, name, CLOUD_IDENTITY_NAME); } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineCreator.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineCreator.java new file mode 100644 index 0000000000..a4a35d313e --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineCreator.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import javax.script.ScriptEngine; + +public interface ScriptEngineCreator { + ScriptEngine getScriptEngine(ClassLoader clsLoader); +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java new file mode 100644 index 0000000000..8d76c1d81f --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ScriptEngineUtil.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + + +import org.apache.ranger.plugin.classloader.RangerPluginClassLoader; +import org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.script.ScriptEngine; + + +public class ScriptEngineUtil { + private static final Logger LOG = LoggerFactory.getLogger(RangerScriptConditionEvaluator.class); + + private static volatile ScriptEngineCreator SCRIPT_ENGINE_CREATOR = null; + private static volatile boolean SCRIPT_ENGINE_CREATOR_INITIALIZED = false; + + // for backward compatibility with any plugin that might use this API + public static ScriptEngine createScriptEngine(String engineName, String serviceType) { + if (LOG.isDebugEnabled()) { + LOG.debug("ScriptEngineUtil.createScriptEngine(engineName=" + engineName + ", serviceType=" + serviceType + "): engineName ignored"); + } + + return createScriptEngine(serviceType); + } + + public static ScriptEngine createScriptEngine(String serviceType) { + if (LOG.isDebugEnabled()) { + LOG.debug("==> ScriptEngineUtil.createScriptEngine(serviceType=" + serviceType + ")"); + } + + ScriptEngine ret = null; + ScriptEngineCreator creator = getScriptEngineCreator(serviceType); + + if (creator != null) { + ret = creator.getScriptEngine(null); + + if (ret == null) { + ClassLoader pluginClsLoader = getPrevActiveClassLoader(serviceType); + + if (pluginClsLoader != null) { + ret = creator.getScriptEngine(pluginClsLoader); + } + } + } else { + LOG.info("createScriptEngine(serviceType={}): no engine creator found", serviceType); + } + + if (ret == null) { + LOG.warn("createScriptEngine(serviceType={}): failed to create script engine", serviceType); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("<== ScriptEngineUtil.createScriptEngine(serviceType={}): ret={}", serviceType, ret); + } + + return ret; + } + + private static ScriptEngineCreator getScriptEngineCreator(String serviceType) { + boolean isInitialized = SCRIPT_ENGINE_CREATOR_INITIALIZED; + + if (!isInitialized) { + synchronized (ScriptEngineUtil.class) { + isInitialized = SCRIPT_ENGINE_CREATOR_INITIALIZED; + + if (!isInitialized) { + initScriptEngineCreator(serviceType); + } + + SCRIPT_ENGINE_CREATOR_INITIALIZED = true; + } + } + + return SCRIPT_ENGINE_CREATOR; + } + + private static void initScriptEngineCreator(String serviceType) { + String[] engineCreators = new String[] { "org.apache.ranger.plugin.util.NashornScriptEngineCreator", + "org.apache.ranger.plugin.util.GraalScriptEngineCreator", + "org.apache.ranger.plugin.util.JavaScriptEngineCreator" + }; + + for (String creatorClsName : engineCreators) { + ScriptEngineCreator creator = null; + + try { + Class creatorClass = (Class) Class.forName(creatorClsName); + + creator = creatorClass.newInstance(); + } catch (Throwable t) { + LOG.warn("initScriptEngineCreator(): failed to instantiate engine creator {}", creatorClsName, t); + } + + if (creator == null) { + continue; + } + + ScriptEngine engine = creator.getScriptEngine(null); + + if (engine == null) { + ClassLoader prevActiveClassLoader = getPrevActiveClassLoader(serviceType); + + if (prevActiveClassLoader != null) { + LOG.debug("initScriptEngineCreator(): trying to create engine using plugin-class-loader for service-type {}", serviceType); + + engine = creator.getScriptEngine(prevActiveClassLoader); + + if (engine == null) { + LOG.warn("initScriptEngineCreator(): failed to create engine using plugin-class-loader by creator {}", creatorClsName); + } + } + } + + if (engine != null) { + SCRIPT_ENGINE_CREATOR = creator; + + break; + } + } + } + + private static ClassLoader getPrevActiveClassLoader(String serviceType) { + ClassLoader ret = null; + + try { + RangerPluginClassLoader pluginClassLoader = RangerPluginClassLoader.getInstance(serviceType, null); + + if (pluginClassLoader != null) { + ret = pluginClassLoader.getPrevActiveClassLoader(); + } else { + LOG.debug("Cannot get plugin-class-loader for serviceType {}", serviceType); + } + } catch (Throwable excp) { + LOG.debug("Failed to get plugin-class-loader for serviceType {}", serviceType, excp); + } + + return ret; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java old mode 100644 new mode 100755 index c17c63bd37..6c5550bfa7 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/SearchFilter.java @@ -37,7 +37,10 @@ public class SearchFilter { public static final String IS_ENABLED = "isEnabled"; // search public static final String IS_RECURSIVE = "isRecursive"; // search public static final String TAG_SERVICE_NAME = "tagServiceName"; // search + public static final String TAG_SERVICE_NAME_PARTIAL = "tagServiceNamePartial"; // search public static final String TAG_SERVICE_ID = "tagServiceId"; // search + public static final String GDS_SERVICE_NAME = "gdsServiceName"; // search + public static final String GDS_SERVICE_ID = "gdsServiceId"; // search public static final String USER = "user"; // search public static final String GROUP = "group"; // search public static final String ROLE = "role"; // search @@ -59,23 +62,34 @@ public class SearchFilter { public static final String POLICY_LABEL_ID = "policyLabelId"; // search, sort public static final String ZONE_ID = "zoneId"; // search, sort public static final String ZONE_NAME = "zoneName"; // search, sort + public static final String ZONE_NAME_PARTIAL = "zoneNamePartial"; // search, sort + public static final String NOT_ZONE_NAME = "notZoneName"; // search public static final String ROLE_ID = "roleId"; // search, sort public static final String ROLE_NAME = "roleName"; // search, sort public static final String GROUP_NAME = "groupName"; // search, sort public static final String USER_NAME = "userName"; // search, sort public static final String ROLE_NAME_PARTIAL = "roleNamePartial"; // search - public static final String GROUP_NAME_PARTIAL = "groupNamePartial"; // search + public static final String GROUP_NAME_PARTIAL = "groupNamePartial"; // search public static final String USER_NAME_PARTIAL = "userNamePartial"; // search + public static final String SERVICE_NAME_PREFIX = "serviceNamePrefix"; // search + public static final String ZONE_NAME_PREFIX = "zoneNamePrefix"; // search public static final String TAG_DEF_ID = "tagDefId"; // search public static final String TAG_DEF_GUID = "tagDefGuid"; // search + public static final String TAG_NAMES = "tagNames"; // search public static final String TAG_TYPE = "tagType"; // search + public static final String TAG_TYPE_PARTIAL = "tagTypePartial"; // search + public static final String TAG_SOURCE = "tagSource"; // search + public static final String TAG_SOURCE_PARTIAL = "tagSourcePartial"; // search public static final String TAG_ID = "tagId"; // search + public static final String TAG_IDS = "tagIds"; // search public static final String TAG_GUID = "tagGuid"; // search public static final String TAG_RESOURCE_ID = "resourceId"; // search + public static final String TAG_RESOURCE_IDS = "resourceIds"; // search public static final String TAG_RESOURCE_GUID = "resourceGuid"; // search public static final String TAG_RESOURCE_SERVICE_NAME = "resourceServiceName"; // search public static final String TAG_RESOURCE_SIGNATURE = "resourceSignature"; // search + public static final String TAG_RESOURCE_ELEMENTS = "resourceElements"; // search public static final String TAG_MAP_ID = "tagResourceMapId"; // search public static final String TAG_MAP_GUID = "tagResourceMapGuid"; // search @@ -86,22 +100,74 @@ public class SearchFilter { public static final String PLUGIN_ENTITY_TYPE = "pluginEntityType"; public static final String PLUGIN_IP_ADDRESS = "pluginIpAddress"; public static final String CLUSTER_NAME = "clusterName"; - public static final String FETCH_ZONE_UNZONE_POLICIES = "fetchZoneAndUnzonePolicies"; + public static final String FETCH_ZONE_UNZONE_POLICIES= "fetchZoneAndUnzonePolicies"; public static final String FETCH_TAG_POLICIES = "fetchTagPolicies"; + public static final String FETCH_ZONE_NAME = "zoneName"; + public static final String FETCH_DENY_CONDITION = "denyCondition"; public static final String SERVICE_DISPLAY_NAME = "serviceDisplayName"; // search, sort public static final String SERVICE_DISPLAY_NAME_PARTIAL = "serviceDisplayNamePartial"; // search public static final String SERVICE_TYPE_DISPLAY_NAME = "serviceTypeDisplayName"; // search, sort - private Map params; - private int startIndex; - private int maxRows = Integer.MAX_VALUE; - private boolean getCount = true; - private String sortBy; - private String sortType; + public static final String DATASET_NAME = "datasetName"; // search, sort + public static final String DATASET_NAME_PARTIAL = "datasetNamePartial"; // search, sort + public static final String DATASET_ID = "datasetId"; // search, sort + public static final String DATASET_LABEL = "datasetLabel"; + public static final String DATASET_KEYWORD = "datasetKeyword"; + public static final String PROJECT_NAME = "projectName"; // search, sort + public static final String PROJECT_NAME_PARTIAL = "projectNamePartial"; // search, sort + public static final String PROJECT_ID = "projectId"; // search, sort + public static final String DATA_SHARE_NAME = "dataShareName"; // search, sort + public static final String DATA_SHARE_NAME_PARTIAL = "dataShareNamePartial"; // search, sort + public static final String DATA_SHARE_ID = "dataShareId"; // search, sort + public static final String EXCLUDE_DATASET_ID = "excludeDatasetId"; // search + public static final String SHARED_RESOURCE_NAME = "sharedResourceName"; // search, sort + public static final String SHARED_RESOURCE_NAME_PARTIAL = "sharedResourceNamePartial"; // search, sort + public static final String RESOURCE_CONTAINS = "resourceContains"; // search + public static final String SHARED_RESOURCE_ID = "sharedResourceId"; // search, sort + public static final String APPROVER = "approver"; // search, sort + public static final String SHARE_STATUS = "shareStatus"; // search, sort + public static final String PROFILE_NAME = "profileName"; // search + public static final String OWNER_NAME = "ownerName"; // search + public static final String OWNER_TYPE = "ownerType"; // search: valid-values(user, group, role) + public static final String DATA_SHARE_IN_DATASET_ID = "dataShareInDatasetId"; // search, sort + public static final String DATASET_IN_PROJECT_ID = "datasetInProjectId"; // search, sort + public static final String GDS_PERMISSION = "gdsPermission"; // search, sort + public static final String CREATE_TIME_START = "createdTimeStart"; // search + public static final String CREATE_TIME_END = "createdTimeEnd"; // search + public static final String CREATED_BY = "createdBy"; // search + public static final String UPDATE_TIME_START = "updatedTimeStart"; // search + public static final String UPDATE_TIME_END = "updatedTimeEnd"; // search + public static final String IS_DISTINCT = "isDistinct"; // search, sort + public static final String RETRIEVE_ALL_PAGES = "retrieveAllPages"; // search + public static final String SHARED_WITH_ME = "sharedWithMe"; // search + + private Map params; + private Map multiValueParams; + private int startIndex; + private int maxRows = Integer.MAX_VALUE; + private boolean getCount = true; + private String sortBy; + private String sortType; + private boolean isDistinct = true; public SearchFilter() { - this(null); + this((Map) null); + } + + public SearchFilter(SearchFilter other) { + if (other != null) { + setParams(other.params != null ? new HashMap<>(other.params) : null); + setMultiValueParams(other.multiValueParams != null ? new HashMap<>(other.multiValueParams) : null); + setStartIndex(other.startIndex); + setMaxRows(other.maxRows); + setGetCount(other.getCount); + setSortBy(other.sortBy); + setSortType(other.sortType); + } else { + setParams(null); + setMultiValueParams(null); + } } public SearchFilter(String name, String value) { @@ -171,6 +237,26 @@ public Map getParamsWithPrefix(String prefix, boolean stripPrefi public boolean isEmpty() { return MapUtils.isEmpty(params); } + + public Map getMultiValueParams() { + return multiValueParams; + } + + public void setMultiValueParams(Map multiValueParams) { + this.multiValueParams = multiValueParams; + } + + public void setMultiValueParam(String name, Object[] value) { + if (multiValueParams == null) { + multiValueParams = new HashMap<>(); + } + + multiValueParams.put(name, value); + } + + public Object[] getMultiValueParam(String name) { + return multiValueParams != null ? multiValueParams.get(name) : null; + } public int getStartIndex() { return startIndex; @@ -212,6 +298,14 @@ public void setSortType(String sortType) { this.sortType = sortType; } + public boolean isDistinct() { + return isDistinct; + } + + public void setDistinct(boolean isDistinct) { + this.isDistinct = isDistinct; + } + @Override public boolean equals(Object object) { if (object == null || !(object instanceof SearchFilter)) { @@ -244,6 +338,7 @@ public StringBuilder toString(StringBuilder sb) { sb.append("sortBy={").append(sortBy).append("} "); sb.append("sortType={").append(sortType).append("} "); sb.append("startIndex={").append(startIndex).append("} "); + sb.append("isDistinct={").append(isDistinct).append("} "); sb.append("}"); return sb; diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceDefUtil.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceDefUtil.java index cd6c18ba79..2bb667e315 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceDefUtil.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceDefUtil.java @@ -23,22 +23,76 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.hadoop.conf.Configuration; +import org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator; +import org.apache.ranger.plugin.contextenricher.RangerGdsEnricher; +import org.apache.ranger.plugin.contextenricher.RangerUserStoreEnricher; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicyDelta; import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerContextEnricherDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerDataMaskTypeDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.policyengine.RangerPluginContext; +import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator; +import org.apache.ranger.plugin.resourcematcher.RangerAbstractResourceMatcher; import org.apache.ranger.plugin.store.AbstractServiceStore; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.apache.ranger.plugin.util.ServicePolicies.SecurityZoneInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class ServiceDefUtil { + private static final Logger LOG = LoggerFactory.getLogger(ServiceDefUtil.class); + + public static final String IMPLICIT_CONDITION_EXPRESSION_EVALUATOR = RangerScriptConditionEvaluator.class.getCanonicalName(); + public static final String IMPLICIT_CONDITION_EXPRESSION_NAME = "_expression"; + public static final String IMPLICIT_CONDITION_EXPRESSION_LABEL = "Enter boolean expression"; + public static final String IMPLICIT_CONDITION_EXPRESSION_DESC = "Boolean expression"; + public static final String IMPLICIT_GDS_ENRICHER_NAME = "gdsInfoEnricher"; + + private static final String USER_STORE_ENRICHER = RangerUserStoreEnricher.class.getCanonicalName(); + private static final String GDSINFO_ENRICHER = RangerGdsEnricher.class.getCanonicalName(); + + + public static final String ACCESS_TYPE_MARKER_CREATE = "_CREATE"; + public static final String ACCESS_TYPE_MARKER_READ = "_READ"; + public static final String ACCESS_TYPE_MARKER_UPDATE = "_UPDATE"; + public static final String ACCESS_TYPE_MARKER_DELETE = "_DELETE"; + public static final String ACCESS_TYPE_MARKER_MANAGE = "_MANAGE"; + public static final String ACCESS_TYPE_MARKER_ALL = "_ALL"; + public static final Set ACCESS_TYPE_MARKERS; + + static { + Set typeMarkers = new LinkedHashSet<>(); + + typeMarkers.add(ACCESS_TYPE_MARKER_CREATE); + typeMarkers.add(ACCESS_TYPE_MARKER_READ); + typeMarkers.add(ACCESS_TYPE_MARKER_UPDATE); + typeMarkers.add(ACCESS_TYPE_MARKER_DELETE); + typeMarkers.add(ACCESS_TYPE_MARKER_MANAGE); + typeMarkers.add(ACCESS_TYPE_MARKER_ALL); + + ACCESS_TYPE_MARKERS = Collections.unmodifiableSet(typeMarkers); + } public static boolean getOption_enableDenyAndExceptionsInPolicies(RangerServiceDef serviceDef, RangerPluginContext pluginContext) { boolean ret = false; @@ -54,6 +108,17 @@ public static boolean getOption_enableDenyAndExceptionsInPolicies(RangerServiceD return ret; } + public static boolean getOption_enableTagBasedPolicies(RangerServiceDef serviceDef, Configuration config) { + boolean ret = false; + + if(serviceDef != null) { + boolean defaultValue = config == null || config.getBoolean("ranger.servicedef.enableTagBasedPolicies", true); + ret = ServiceDefUtil.getBooleanValue(serviceDef.getOptions(), RangerServiceDef.OPTION_ENABLE_TAG_BASED_POLICIES, defaultValue); + } + + return ret; + } + public static RangerDataMaskTypeDef getDataMaskType(RangerServiceDef serviceDef, String typeName) { RangerDataMaskTypeDef ret = null; @@ -80,6 +145,22 @@ public static RangerServiceDef normalize(RangerServiceDef serviceDef) { return serviceDef; } + public static RangerPolicyConditionDef getConditionDef(RangerServiceDef serviceDef, String conditionName) { + RangerPolicyConditionDef ret = null; + + if (serviceDef != null && serviceDef.getPolicyConditions() != null) { + for (RangerPolicyConditionDef conditionDef : serviceDef.getPolicyConditions()) { + if (StringUtils.equals(conditionDef.getName(), conditionName)) { + ret = conditionDef; + + break; + } + } + } + + return ret; + } + public static RangerResourceDef getResourceDef(RangerServiceDef serviceDef, String resource) { RangerResourceDef ret = null; @@ -95,7 +176,7 @@ public static RangerResourceDef getResourceDef(RangerServiceDef serviceDef, Stri return ret; } - public static Integer getLeafResourceLevel(RangerServiceDef serviceDef, Map policyResource) { + public static Integer getLeafResourceLevel(RangerServiceDef serviceDef, Map policyResource) { Integer ret = null; RangerResourceDef resourceDef = getLeafResourceDef(serviceDef, policyResource); @@ -107,20 +188,26 @@ public static Integer getLeafResourceLevel(RangerServiceDef serviceDef, Map policyResource) { + public static RangerResourceDef getLeafResourceDef(RangerServiceDef serviceDef, Map policyResource) { + return getLeafResourceDef(serviceDef, policyResource, false); + } + + public static RangerResourceDef getLeafResourceDef(RangerServiceDef serviceDef, Map policyResource, boolean excludeWildcardLeaves) { RangerResourceDef ret = null; if(serviceDef != null && policyResource != null) { - for(Map.Entry entry : policyResource.entrySet()) { + for(Map.Entry entry : policyResource.entrySet()) { if (!isEmpty(entry.getValue())) { String resource = entry.getKey(); RangerResourceDef resourceDef = ServiceDefUtil.getResourceDef(serviceDef, resource); if (resourceDef != null && resourceDef.getLevel() != null) { - if (ret == null) { - ret = resourceDef; - } else if(ret.getLevel() < resourceDef.getLevel()) { - ret = resourceDef; + if (ret == null || ret.getLevel() < resourceDef.getLevel()) { + if (StringUtils.isEmpty(resourceDef.getParent())) { + ret = resourceDef; + } else if (!excludeWildcardLeaves || !hasWildcardValue(entry.getValue().getValues())) { + ret = resourceDef; + } } } } @@ -130,6 +217,19 @@ public static RangerResourceDef getLeafResourceDef(RangerServiceDef serviceDef, return ret; } + private static boolean hasWildcardValue(List values) { + boolean ret = false; + + for (String strValue : values) { + ret = StringUtils.equals(strValue, RangerAbstractResourceMatcher.WILDCARD_ASTERISK); + if (ret) { + break; + } + } + + return ret; + } + public static boolean isAncestorOf(RangerServiceDef serviceDef, RangerResourceDef ancestor, RangerResourceDef descendant) { boolean ret = false; @@ -148,7 +248,7 @@ public static boolean isAncestorOf(RangerServiceDef serviceDef, RangerResourceDe return ret; } - public static boolean isEmpty(RangerPolicy.RangerPolicyResource policyResource) { + public static boolean isEmpty(RangerPolicyResource policyResource) { boolean ret = true; if (policyResource != null) { List resourceValues = policyResource.getValues(); @@ -187,65 +287,67 @@ public static char getCharOption(Map options, String name, char } public static RangerServiceDef normalizeAccessTypeDefs(RangerServiceDef serviceDef, final String componentType) { - if (serviceDef != null && StringUtils.isNotBlank(componentType)) { + normalizeAccessTypeDefs(serviceDef.getAccessTypes(), componentType); + normalizeAccessTypeDefs(serviceDef.getMarkerAccessTypes(), componentType); - List accessTypeDefs = serviceDef.getAccessTypes(); - - if (CollectionUtils.isNotEmpty(accessTypeDefs)) { - - String prefix = componentType + AbstractServiceStore.COMPONENT_ACCESSTYPE_SEPARATOR; - - List unneededAccessTypeDefs = null; - - for (RangerServiceDef.RangerAccessTypeDef accessTypeDef : accessTypeDefs) { - - String accessType = accessTypeDef.getName(); + if (serviceDef.getDataMaskDef() != null) { + normalizeAccessTypeDefs(serviceDef.getDataMaskDef().getAccessTypes(), componentType); + } - if (StringUtils.startsWith(accessType, prefix)) { + if (serviceDef.getRowFilterDef() != null) { + normalizeAccessTypeDefs(serviceDef.getRowFilterDef().getAccessTypes(), componentType); + } + } - String newAccessType = StringUtils.removeStart(accessType, prefix); + return serviceDef; + } - accessTypeDef.setName(newAccessType); + private static void normalizeAccessTypeDefs(List accessTypeDefs, String componentType) { + if (CollectionUtils.isNotEmpty(accessTypeDefs)) { + String prefix = componentType + AbstractServiceStore.COMPONENT_ACCESSTYPE_SEPARATOR; + List unneededAccessTypeDefs = null; - Collection impliedGrants = accessTypeDef.getImpliedGrants(); + for (RangerAccessTypeDef accessTypeDef : accessTypeDefs) { + String accessType = accessTypeDef.getName(); - if (CollectionUtils.isNotEmpty(impliedGrants)) { + if (StringUtils.startsWith(accessType, prefix)) { + String newAccessType = StringUtils.removeStart(accessType, prefix); - Collection newImpliedGrants = null; + accessTypeDef.setName(newAccessType); + } else if (StringUtils.contains(accessType, AbstractServiceStore.COMPONENT_ACCESSTYPE_SEPARATOR)) { + if (unneededAccessTypeDefs == null) { + unneededAccessTypeDefs = new ArrayList<>(); + } - for (String impliedGrant : impliedGrants) { + unneededAccessTypeDefs.add(accessTypeDef); - if (StringUtils.startsWith(impliedGrant, prefix)) { + continue; + } - String newImpliedGrant = StringUtils.removeStart(impliedGrant, prefix); + Collection impliedGrants = accessTypeDef.getImpliedGrants(); - if (newImpliedGrants == null) { - newImpliedGrants = new ArrayList<>(); - } + if (CollectionUtils.isNotEmpty(impliedGrants)) { + Set newImpliedGrants = new HashSet<>(); - newImpliedGrants.add(newImpliedGrant); - } - } - accessTypeDef.setImpliedGrants(newImpliedGrants); + for (String impliedGrant : impliedGrants) { + if (StringUtils.startsWith(impliedGrant, prefix)) { + String newImpliedGrant = StringUtils.removeStart(impliedGrant, prefix); + newImpliedGrants.add(newImpliedGrant); + } else if (!StringUtils.contains(impliedGrant, AbstractServiceStore.COMPONENT_ACCESSTYPE_SEPARATOR)) { + newImpliedGrants.add(impliedGrant); } - } else if (StringUtils.contains(accessType, AbstractServiceStore.COMPONENT_ACCESSTYPE_SEPARATOR)) { - if(unneededAccessTypeDefs == null) { - unneededAccessTypeDefs = new ArrayList<>(); - } - - unneededAccessTypeDefs.add(accessTypeDef); } - } - if(unneededAccessTypeDefs != null) { - accessTypeDefs.removeAll(unneededAccessTypeDefs); + accessTypeDef.setImpliedGrants(newImpliedGrants); } } - } - return serviceDef; + if (unneededAccessTypeDefs != null) { + accessTypeDefs.removeAll(unneededAccessTypeDefs); + } + } } private static void normalizeDataMaskDef(RangerServiceDef serviceDef) { @@ -425,7 +527,7 @@ private static RangerAccessTypeDef mergeAccessTypeDef(RangerAccessTypeDef base, return ret; } - private static boolean getBooleanValue(Map map, String elementName, boolean defaultValue) { + public static boolean getBooleanValue(Map map, String elementName, boolean defaultValue) { boolean ret = defaultValue; if(MapUtils.isNotEmpty(map) && map.containsKey(elementName)) { @@ -439,4 +541,425 @@ private static boolean getBooleanValue(Map map, String elementNa return ret; } + public static Map> getExpandedImpliedGrants(RangerServiceDef serviceDef) { + Map> ret = new HashMap<>(); + + if(serviceDef != null && !CollectionUtils.isEmpty(serviceDef.getAccessTypes())) { + for(RangerAccessTypeDef accessTypeDef : serviceDef.getAccessTypes()) { + if(!CollectionUtils.isEmpty(accessTypeDef.getImpliedGrants())) { + + Collection impliedAccessGrants = ret.get(accessTypeDef.getName()); + + if(impliedAccessGrants == null) { + impliedAccessGrants = new HashSet<>(); + + ret.put(accessTypeDef.getName(), impliedAccessGrants); + } + + impliedAccessGrants.addAll(accessTypeDef.getImpliedGrants()); + impliedAccessGrants.add(accessTypeDef.getName()); + } else { + ret.put(accessTypeDef.getName(), new HashSet<>(Collections.singleton(accessTypeDef.getName()))); + } + } + } + return ret; + } + + public static boolean isGdsInfoEnricherPresent(ServicePolicies policies) { + boolean ret = false; + RangerServiceDef serviceDef = policies != null ? policies.getServiceDef() : null; + List enricherDefs = serviceDef != null ? serviceDef.getContextEnrichers() : null; + + if (enricherDefs != null) { + for (RangerContextEnricherDef enricherDef : enricherDefs) { + if (StringUtils.equals(enricherDef.getEnricher(), GDSINFO_ENRICHER)) { + ret = true; + + break; + } + } + } + + LOG.debug("isGdsInfoEnricherPresent(service={}): ret={}", policies.getServiceName(), ret); + + return ret; + } + + public static boolean addGdsInfoEnricher(ServicePolicies policies, String retrieverClassName, String retrieverPollIntMs) { + boolean ret = false; + RangerServiceDef serviceDef = policies != null ? policies.getServiceDef() : null; + + if (serviceDef != null && !isGdsInfoEnricherPresent(policies)) { + List enricherDefs = serviceDef.getContextEnrichers(); + + if (enricherDefs == null) { + enricherDefs = new ArrayList<>(); + } + + long enricherItemId = enricherDefs.size() + 1L; + + for (RangerServiceDef.RangerContextEnricherDef enricherDef : enricherDefs) { + if (enricherDef.getItemId() >= enricherItemId) { + enricherItemId = enricherDef.getItemId() + 1; + } + } + + Map enricherOptions = new HashMap<>(); + + enricherOptions.put(RangerGdsEnricher.RETRIEVER_CLASSNAME_OPTION, retrieverClassName); + enricherOptions.put(RangerGdsEnricher.REFRESHER_POLLINGINTERVAL_OPTION, retrieverPollIntMs); + + RangerServiceDef.RangerContextEnricherDef gdsInfoEnricher = new RangerServiceDef.RangerContextEnricherDef(enricherItemId, IMPLICIT_GDS_ENRICHER_NAME, GDSINFO_ENRICHER, enricherOptions); + + enricherDefs.add(gdsInfoEnricher); + + serviceDef.setContextEnrichers(enricherDefs); + + ret = true; + + LOG.info("addGdsInfoEnricher(serviceName={}): added gdsInfoEnricher {}", policies.getServiceName(), gdsInfoEnricher); + } + + return ret; + } + + public static boolean isUserStoreEnricherPresent(ServicePolicies policies) { + boolean ret = false; + RangerServiceDef serviceDef = policies != null ? policies.getServiceDef() : null; + List enricherDefs = serviceDef != null ? serviceDef.getContextEnrichers() : null; + + if (enricherDefs != null) { + for (RangerContextEnricherDef enricherDef : enricherDefs) { + if (StringUtils.equals(enricherDef.getEnricher(), USER_STORE_ENRICHER)) { + ret = true; + + break; + } + } + } + + if (LOG.isDebugEnabled()) { + LOG.debug("isUserStoreEnricherPresent(service={}): ret={}", policies.getServiceName(), ret); + } + + return ret; + } + + public static boolean addUserStoreEnricher(ServicePolicies policies, String retrieverClassName, String retrieverPollIntMs) { + boolean ret = false; + RangerServiceDef serviceDef = policies != null ? policies.getServiceDef() : null; + + if (serviceDef != null && !isUserStoreEnricherPresent(policies)) { + List enricherDefs = serviceDef.getContextEnrichers(); + + if (enricherDefs == null) { + enricherDefs = new ArrayList<>(); + } + + long enricherItemId = enricherDefs.size() + 1; + + for (RangerServiceDef.RangerContextEnricherDef enricherDef : enricherDefs) { + if (enricherDef.getItemId() >= enricherItemId) { + enricherItemId = enricherDef.getItemId() + 1; + } + } + + Map enricherOptions = new HashMap<>(); + + enricherOptions.put(RangerUserStoreEnricher.USERSTORE_RETRIEVER_CLASSNAME_OPTION, retrieverClassName); + enricherOptions.put(RangerUserStoreEnricher.USERSTORE_REFRESHER_POLLINGINTERVAL_OPTION, retrieverPollIntMs); + + RangerServiceDef.RangerContextEnricherDef userStoreEnricher = new RangerServiceDef.RangerContextEnricherDef(enricherItemId, "userStoreEnricher", USER_STORE_ENRICHER, enricherOptions); + + enricherDefs.add(userStoreEnricher); + + serviceDef.setContextEnrichers(enricherDefs); + + ret = true; + + LOG.info("addUserStoreEnricher(serviceName={}): added userStoreEnricher {}", policies.getServiceName(), userStoreEnricher); + } + + return ret; + } + + + public static boolean addUserStoreEnricherIfNeeded(ServicePolicies policies, String retrieverClassName, String retrieverPollIntMs) { + boolean ret = false; + RangerServiceDef serviceDef = policies != null ? policies.getServiceDef() : null; + + if (serviceDef != null && !isUserStoreEnricherPresent(policies)) { + boolean addEnricher = anyPolicyHasUserGroupAttributeExpression(policies.getPolicies()); + + if (!addEnricher) { + List tagPolicies = policies.getTagPolicies() != null ? policies.getTagPolicies().getPolicies() : null; + + addEnricher = anyPolicyHasUserGroupAttributeExpression(tagPolicies); + } + + if (!addEnricher) { + addEnricher = anyPolicyDeltaHasUserGroupAttributeExpression(policies.getPolicyDeltas()); + } + + if (!addEnricher) { + Map zoneInfos = policies.getSecurityZones(); + + if (zoneInfos != null) { + for (SecurityZoneInfo zoneInfo : zoneInfos.values()) { + addEnricher = anyPolicyHasUserGroupAttributeExpression(zoneInfo.getPolicies()); + + if (!addEnricher) { + addEnricher = anyPolicyDeltaHasUserGroupAttributeExpression(zoneInfo.getPolicyDeltas()); + } + + if (addEnricher) { + break; + } + } + } + } + + if (addEnricher) { + addUserStoreEnricher(policies, retrieverClassName, retrieverPollIntMs); + + ret = true; + } + } + + return ret; + } + + public static List getMarkerAccessTypes(List accessTypeDefs) { + List ret = new ArrayList<>(); + Map> markerTypeGrants = getMarkerAccessTypeGrants(accessTypeDefs); + long maxItemId = getMaxItemId(accessTypeDefs); + + for (String accessTypeMarker : ACCESS_TYPE_MARKERS) { + RangerAccessTypeDef accessTypeDef = new RangerAccessTypeDef(++maxItemId, accessTypeMarker, accessTypeMarker, null, markerTypeGrants.get(accessTypeMarker)); + + ret.add(accessTypeDef); + } + + return ret; + } + + public static RangerPolicyConditionDef createImplicitExpressionConditionDef(Long itemId) { + RangerPolicyConditionDef ret = new RangerPolicyConditionDef(itemId, IMPLICIT_CONDITION_EXPRESSION_NAME, IMPLICIT_CONDITION_EXPRESSION_EVALUATOR, new HashMap<>()); + + ret.getEvaluatorOptions().put("engineName", "JavaScript"); + ret.getEvaluatorOptions().put("ui.isMultiline", "true"); + ret.setLabel(IMPLICIT_CONDITION_EXPRESSION_LABEL); + ret.setDescription(IMPLICIT_CONDITION_EXPRESSION_DESC); + ret.setUiHint("{ \"isMultiline\":true }"); + + return ret; + } + + private static Map> getMarkerAccessTypeGrants(List accessTypeDefs) { + Map> ret = new HashMap<>(); + + for (String accessTypeMarker : ACCESS_TYPE_MARKERS) { + ret.put(accessTypeMarker, new HashSet<>()); + } + + if (CollectionUtils.isNotEmpty(accessTypeDefs)) { + for (RangerAccessTypeDef accessTypeDef : accessTypeDefs) { + if (accessTypeDef == null || StringUtils.isBlank(accessTypeDef.getName()) || ACCESS_TYPE_MARKERS.contains(accessTypeDef.getName())) { + continue; + } + + addToMarkerGrants(accessTypeDef, ret.get(ACCESS_TYPE_MARKER_ALL)); + + if (accessTypeDef.getCategory() == null) { + continue; + } else if (accessTypeDef.getCategory() == RangerAccessTypeDef.AccessTypeCategory.CREATE) { + addToMarkerGrants(accessTypeDef, ret.get(ACCESS_TYPE_MARKER_CREATE)); + } else if (accessTypeDef.getCategory() == RangerAccessTypeDef.AccessTypeCategory.READ) { + addToMarkerGrants(accessTypeDef, ret.get(ACCESS_TYPE_MARKER_READ)); + } else if (accessTypeDef.getCategory() == RangerAccessTypeDef.AccessTypeCategory.UPDATE) { + addToMarkerGrants(accessTypeDef, ret.get(ACCESS_TYPE_MARKER_UPDATE)); + } else if (accessTypeDef.getCategory() == RangerAccessTypeDef.AccessTypeCategory.DELETE) { + addToMarkerGrants(accessTypeDef, ret.get(ACCESS_TYPE_MARKER_DELETE)); + } else if (accessTypeDef.getCategory() == RangerAccessTypeDef.AccessTypeCategory.MANAGE) { + addToMarkerGrants(accessTypeDef, ret.get(ACCESS_TYPE_MARKER_MANAGE)); + } + } + } + + return ret; + } + + private static void addToMarkerGrants(RangerAccessTypeDef accessTypeDef, Set markerGrants) { + markerGrants.add(accessTypeDef.getName()); + + if (CollectionUtils.isNotEmpty(accessTypeDef.getImpliedGrants())) { + markerGrants.addAll(accessTypeDef.getImpliedGrants()); + } + } + + private static long getMaxItemId(List accessTypeDefs) { + long ret = -1; + + if (CollectionUtils.isNotEmpty(accessTypeDefs)) { + for (RangerAccessTypeDef accessTypeDef : accessTypeDefs) { + if (accessTypeDef.getItemId() != null && ret < accessTypeDef.getItemId()) { + ret = accessTypeDef.getItemId(); + } + } + } + + return ret; + } + + public static long getConditionsMaxItemId(List conditions) { + long ret = 0; + + if (conditions != null) { + for (RangerPolicyConditionDef condition : conditions) { + if (condition != null && condition.getItemId() != null && ret < condition.getItemId()) { + ret = condition.getItemId(); + } + } + } + + return ret; + } + + private static boolean anyPolicyHasUserGroupAttributeExpression(List policies) { + boolean ret = false; + + if (policies != null) { + for (RangerPolicy policy : policies) { + if (policyHasUserGroupAttributeExpression(policy)) { + if (LOG.isDebugEnabled()) { + LOG.debug("addUserStoreEnricherIfNeeded(service={}): policy(id={}, name={}) has reference to user/group attribute. Adding enricher", policy.getService(), policy.getId(), policy.getName()); + } + + ret = true; + + break; + } + } + } + + return ret; + } + + private static boolean anyPolicyDeltaHasUserGroupAttributeExpression(List policyDeltas) { + boolean ret = false; + + if (policyDeltas != null) { + for (RangerPolicyDelta policyDelta : policyDeltas) { + RangerPolicy policy = policyDelta.getPolicy(); + + if (policyHasUserGroupAttributeExpression(policy)) { + if (LOG.isDebugEnabled()) { + LOG.debug("addUserStoreEnricherIfNeeded(service={}): policy(id={}, name={}) has reference to user/group attribute. Adding enricher", policy.getService(), policy.getId(), policy.getName()); + } + + ret = true; + + break; + } + } + } + + return ret; + } + + private static boolean policyHasUserGroupAttributeExpression(RangerPolicy policy) { + boolean ret = false; + + if (policy != null) { + if (MapUtils.isNotEmpty(policy.getResources())) { + for (RangerPolicyResource resource : policy.getResources().values()) { + ret = RangerRequestExprResolver.hasUserGroupAttributeInExpression(resource.getValues()); + + if (ret) { + break; + } + } + } + + if (!ret) { + ret = anyPolicyConditionHasUserGroupAttributeReference(policy.getConditions()); + } + + if (!ret) { + ret = anyPolicyItemHasUserGroupAttributeExpression(policy.getPolicyItems()) || + anyPolicyItemHasUserGroupAttributeExpression(policy.getDenyPolicyItems()) || + anyPolicyItemHasUserGroupAttributeExpression(policy.getAllowExceptions()) || + anyPolicyItemHasUserGroupAttributeExpression(policy.getDenyExceptions()) || + anyPolicyItemHasUserGroupAttributeExpression(policy.getDataMaskPolicyItems()) || + anyPolicyItemHasUserGroupAttributeExpression(policy.getRowFilterPolicyItems()); + } + } + + return ret; + } + + private static boolean anyPolicyItemHasUserGroupAttributeExpression(List policyItems) { + boolean ret = false; + + if (policyItems != null) { + for (RangerPolicyItem policyItem : policyItems) { + if (policyItemHasUserGroupAttributeExpression(policyItem)) { + ret = true; + + break; + } + } + } + + return ret; + } + + private static boolean policyItemHasUserGroupAttributeExpression(RangerPolicyItem policyItem) { + boolean ret = false; + + if (policyItem != null) { + ret = anyPolicyConditionHasUserGroupAttributeReference(policyItem.getConditions()); + + if (!ret && policyItem instanceof RangerRowFilterPolicyItem) { + RangerRowFilterPolicyItem rowFilterPolicyItem = (RangerRowFilterPolicyItem) policyItem; + RangerPolicyItemRowFilterInfo rowFilterInfo = rowFilterPolicyItem.getRowFilterInfo(); + String filterExpr = rowFilterInfo != null ? rowFilterInfo.getFilterExpr() : ""; + + ret = RangerRequestExprResolver.hasUserGroupAttributeInExpression(filterExpr); + } + + if (!ret && policyItem instanceof RangerDataMaskPolicyItem) { + RangerDataMaskPolicyItem dataMaskPolicyItem = (RangerDataMaskPolicyItem) policyItem; + RangerPolicyItemDataMaskInfo dataMaskInfo = dataMaskPolicyItem.getDataMaskInfo(); + String maskedValue = dataMaskInfo != null ? dataMaskInfo.getValueExpr() : null; + + ret = RangerRequestExprResolver.hasUserGroupAttributeInExpression(maskedValue); + + if (!ret) { + String maskCondition = dataMaskInfo != null ? dataMaskInfo.getConditionExpr() : null; + + ret = RangerRequestExprResolver.hasUserGroupAttributeInExpression(maskCondition); + } + } + } + + return ret; + } + + private static boolean anyPolicyConditionHasUserGroupAttributeReference(List conditions) { + boolean ret = false; + + if (conditions != null) { + for (RangerPolicyItemCondition condition : conditions) { + if (RangerRequestScriptEvaluator.hasUserGroupAttributeReference(condition.getValues())) { + ret = true; + + break; + } + } + } + + return ret; + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java new file mode 100644 index 0000000000..f76e7d5445 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceGdsInfo.java @@ -0,0 +1,890 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.model.RangerGds.GdsShareStatus; +import org.apache.ranger.plugin.model.RangerGds.RangerTagDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerValiditySchedule; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; + +import java.util.*; + + +@JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) +@JsonInclude(JsonInclude.Include.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class ServiceGdsInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String serviceName; + private List dataShares; + private List resources; + private List datasets; + private List projects; + private List dshids; + private List dips; + private Boolean isDelta = Boolean.FALSE; + private List deltaLogs; + private RangerServiceDef gdsServiceDef; + private Long gdsLastUpdateTime; + private Long gdsVersion; + + public ServiceGdsInfo() { + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public List getDataShares() { + return dataShares; + } + + public void setDataShares(List dataShares) { + this.dataShares = dataShares; + } + + public List getResources() { + return resources; + } + + public void setResources(List resources) { + this.resources = resources; + } + + public List getDatasets() { + return datasets; + } + + public void setDatasets(List datasets) { + this.datasets = datasets; + } + + public List getProjects() { + return projects; + } + + public void setProjects(List projects) { + this.projects = projects; + } + + public List getDshids() { + return dshids; + } + + public void setDshids(List dshids) { + this.dshids = dshids; + } + + public List getDips() { + return dips; + } + + public void setDips(List dips) { + this.dips = dips; + } + + public Boolean getIsDelta() { + return isDelta; + } + + public void setIsDelta(Boolean delta) { + isDelta = delta == null ? Boolean.FALSE : delta; + } + + public List getDeltaLogs() { + return deltaLogs; + } + + public void setDeltaLogs(List deltaLogs) { + this.deltaLogs = deltaLogs; + } + + public RangerServiceDef getGdsServiceDef() { + return gdsServiceDef; + } + + public void setGdsServiceDef(RangerServiceDef gdsServiceDef) { + this.gdsServiceDef = gdsServiceDef; + } + + public Long getGdsLastUpdateTime() { + return gdsLastUpdateTime; + } + + public void setGdsLastUpdateTime(Long gdsLastUpdateTime) { + this.gdsLastUpdateTime = gdsLastUpdateTime; + } + + public Long getGdsVersion() { return gdsVersion; } + + public void setGdsVersion(Long gdsVersion) {this.gdsVersion = gdsVersion; } + + public void dedupStrings() { + // TODO: implement this + } + + public void addDataShare(DataShareInfo dataShare) { + if (dataShares == null) { + dataShares = new ArrayList<>(); + } + + dataShares.add(dataShare); + } + + public void addResource(SharedResourceInfo resource) { + if (resources == null) { + resources = new ArrayList<>(); + } + + resources.add(resource); + } + + public void addDataset(DatasetInfo dataset) { + if (datasets == null) { + datasets = new ArrayList<>(); + } + + datasets.add(dataset); + } + + public void addProject(ProjectInfo project) { + if (projects == null) { + projects = new ArrayList<>(); + } + + projects.add(project); + } + + public void addDataShareInDataset(DataShareInDatasetInfo dshid) { + if (dshids == null) { + dshids = new ArrayList<>(); + } + + dshids.add(dshid); + } + + public void addDatasetInProjectInfo(DatasetInProjectInfo dip) { + if (dips == null) { + dips = new ArrayList<>(); + } + + dips.add(dip); + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("ServiceGdsInfo={"); + + sb.append("serviceName={").append(serviceName).append("}"); + sb.append(", dataShares=["); + if (dataShares != null) { + for (DataShareInfo dataShare : dataShares) { + dataShare.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append(", datasets=["); + if (datasets != null) { + for (DatasetInfo dataset : datasets) { + dataset.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append(", projects=["); + if (projects != null) { + for (ProjectInfo project : projects) { + project.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append(", dshids=["); + if (dshids != null) { + for (DataShareInDatasetInfo dshid : dshids) { + dshid.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append(", dshInDs=["); + if (dips != null) { + for (DatasetInProjectInfo dip : dips) { + dip.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append(", isDelta={").append(isDelta).append("}"); + + sb.append(", deltaLogs=["); + if (deltaLogs != null) { + for (ObjectChangeLog changeLog : deltaLogs) { + changeLog.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append("serviceDef={"); + if (gdsServiceDef != null) { + gdsServiceDef.toString(sb); + } + sb.append("}"); + + sb.append(", gdsLastUpdateTime={").append(gdsLastUpdateTime).append("}"); + sb.append(", gdsVersion={").append(gdsVersion).append("}"); + + sb.append("}"); + + return sb; + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class DataShareInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + private String zoneName; + private String conditionExpr; + private Set defaultAccessTypes; + private List defaultTagMasks; + + public DataShareInfo() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public String getConditionExpr() { + return conditionExpr; + } + + public void setConditionExpr(String conditionExpr) { + this.conditionExpr = conditionExpr; + } + + public Set getDefaultAccessTypes() { + return defaultAccessTypes; + } + + public void setDefaultAccessTypes(Set defaultAccessTypes) { + this.defaultAccessTypes = defaultAccessTypes; + } + + public List getDefaultTagMasks() { + return defaultTagMasks; + } + + public void setDefaultTagMasks(List defaultTagMasks) { + this.defaultTagMasks = defaultTagMasks; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DataShareInfo={") + .append("id=").append(id) + .append(", name=").append(name) + .append(", zoneName=").append(zoneName); + + sb.append(", conditionExpr=").append(conditionExpr); + + sb.append(", defaultAccessTypes=["); + if (defaultAccessTypes != null) { + for (String defaultAccessType : defaultAccessTypes) { + sb.append(defaultAccessType).append(", "); + } + } + sb.append("]"); + + sb.append(", defaultTagMasks=["); + if (defaultTagMasks != null) { + for (RangerTagDataMaskInfo defaultTagMask : defaultTagMasks) { + defaultTagMask.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class SharedResourceInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + private Long dataShareId; + private Map resource; + private RangerPolicyResource subResource; + private String subResourceType; + private String conditionExpr; + private Set accessTypes; + private RangerPolicyItemRowFilterInfo rowFilter; + private Map subResourceMasks; + private Set profiles; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Long getDataShareId() { + return dataShareId; + } + + public void setDataShareId(Long dataShareId) { + this.dataShareId = dataShareId; + } + + public Map getResource() { + return resource; + } + + public void setResource(Map resource) { + this.resource = resource; + } + + public RangerPolicyResource getSubResource() { + return subResource; + } + + public void setSubResource(RangerPolicyResource subResource) { + this.subResource = subResource; + } + + public String getSubResourceType() { + return subResourceType; + } + + public void setSubResourceType(String subResourceType) { + this.subResourceType = subResourceType; + } + + public String getConditionExpr() { + return conditionExpr; + } + + public void setConditionExpr(String conditionExpr) { + this.conditionExpr = conditionExpr; + } + + public Set getAccessTypes() { + return accessTypes; + } + + public void setAccessTypes(Set accessTypes) { + this.accessTypes = accessTypes; + } + + public RangerPolicyItemRowFilterInfo getRowFilter() { + return rowFilter; + } + + public void setRowFilter(RangerPolicyItemRowFilterInfo rowFilter) { + this.rowFilter = rowFilter; + } + + public Map getSubResourceMasks() { + return subResourceMasks; + } + + public void setSubResourceMasks(Map subResourceMasks) { + this.subResourceMasks = subResourceMasks; + } + + public Set getProfiles() { + return profiles; + } + + public void setProfiles(Set profiles) { + this.profiles = profiles; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("SharedResourceInfo={") + .append("id=").append(id) + .append(", name=").append(name) + .append(", dataShareId=").append(dataShareId) + .append(", resource=").append(resource) + .append(", subResource=").append(subResource) + .append(", subResourceType=").append(subResourceType) + .append(", conditionExpr=").append(conditionExpr); + + sb.append(", accessTypes=["); + if (accessTypes != null) { + for (String accessType : accessTypes) { + sb.append(accessType).append(", "); + } + } + sb.append("]"); + + sb.append(", rowFilter="); + if (rowFilter != null) { + rowFilter.toString(sb); + } + + sb.append(", subResourceMasks={").append(subResourceMasks); + if (subResourceMasks != null) { + for (Map.Entry entry : subResourceMasks.entrySet()) { + String subResourceName = entry.getKey(); + RangerPolicyItemDataMaskInfo subResourceMask = entry.getValue(); + + sb.append(subResourceName).append(": "); + subResourceMask.toString(sb).append(", "); + } + } + sb.append("}"); + + sb.append(", profiles=["); + if (profiles != null) { + for (String profile : profiles) { + sb.append(profile).append(", "); + } + } + sb.append("]"); + + return sb; + } + + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class DatasetInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + private RangerValiditySchedule validitySchedule; + private List policies; + + public DatasetInfo() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RangerValiditySchedule getValiditySchedule() { return validitySchedule; } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { this.validitySchedule = validitySchedule; } + + public List getPolicies() { + return policies; + } + + public void setPolicies(List policies) { + this.policies = policies; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DatasetInfo={") + .append("id=").append(id) + .append(", name=").append(name) + .append(", validitySchedule=").append(validitySchedule); + + sb.append(", policies=["); + if (policies != null) { + for (RangerPolicy policy : policies) { + policy.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class ProjectInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + private RangerValiditySchedule validitySchedule; + private List policies; + + public ProjectInfo() { + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RangerValiditySchedule getValiditySchedule() { return validitySchedule; } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { this.validitySchedule = validitySchedule; } + + public List getPolicies() { + return policies; + } + + public void setPolicies(List policies) { + this.policies = policies; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("ProjectInfo={") + .append("id=").append(id) + .append(", name=").append(name) + .append(", validitySchedule=").append(validitySchedule); + + sb.append(", policies=["); + if (policies != null) { + for (RangerPolicy policy : policies) { + policy.toString(sb).append(", "); + } + } + sb.append("]"); + + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class DataShareInDatasetInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long dataShareId; + private Long datasetId; + private GdsShareStatus status; + private RangerValiditySchedule validitySchedule; + private Set profiles; + + public DataShareInDatasetInfo() { + } + + public Long getDataShareId() { + return dataShareId; + } + + public void setDataShareId(Long dataShareId) { + this.dataShareId = dataShareId; + } + + public Long getDatasetId() { + return datasetId; + } + + public void setDatasetId(Long datasetId) { + this.datasetId = datasetId; + } + + public GdsShareStatus getStatus() { + return status; + } + + public void setStatus(GdsShareStatus status) { + this.status = status; + } + + public RangerValiditySchedule getValiditySchedule() { + return validitySchedule; + } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { + this.validitySchedule = validitySchedule; + } + + public Set getProfiles() { + return profiles; + } + + public void setProfiles(Set profiles) { + this.profiles = profiles; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DataShareInDatasetInfo={") + .append("dataShareId=").append(dataShareId) + .append(", datasetId=").append(datasetId) + .append(", status=").append(status) + .append(", validitySchedule=").append(validitySchedule); + + sb.append(", profiles=["); + if (profiles != null) { + for (String profile : profiles) { + sb.append(profile).append(", "); + } + } + sb.append("]"); + + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class DatasetInProjectInfo implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Long datasetId; + private Long projectId; + private GdsShareStatus status; + private RangerValiditySchedule validitySchedule; + private Set profiles; + + public DatasetInProjectInfo() { + } + + public Long getDatasetId() { + return datasetId; + } + + public void setDatasetId(Long datasetId) { + this.datasetId = datasetId; + } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public GdsShareStatus getStatus() { + return status; + } + + public void setStatus(GdsShareStatus status) { + this.status = status; + } + + public RangerValiditySchedule getValiditySchedule() { + return validitySchedule; + } + + public void setValiditySchedule(RangerValiditySchedule validitySchedule) { + this.validitySchedule = validitySchedule; + } + + public Set getProfiles() { + return profiles; + } + + public void setProfiles(Set profiles) { + this.profiles = profiles; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("DatasetInProjectInfo={") + .append("datasetId=").append(datasetId) + .append(", projectId=").append(projectId) + .append(", status=").append(status) + .append(", validitySchedule=").append(validitySchedule); + + sb.append(", profiles=["); + if (profiles != null) { + for (String profile : profiles) { + sb.append(profile).append(", "); + } + } + sb.append("]"); + + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility= JsonAutoDetect.Visibility.ANY) + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class ObjectChangeLog { + public static final Integer CHANGE_TYPE_CREATE = 1; + public static final Integer CHANGE_TYPE_UPDATE = 2; + public static final Integer CHANGE_TYPE_DELETE = 3; + + private Integer objectType; + private Integer objectId; + private Integer changeType; + + public ObjectChangeLog() { + } + + public ObjectChangeLog(Integer objectType, Integer objectId, Integer changeType) { + this.objectType = objectType; + this.objectId = objectId; + this.changeType = changeType; + } + + public Integer getObjectType() { + return objectType; + } + + public void setObjectType(Integer objectType) { + this.objectType = objectType; + } + + public Integer getObjectId() { + return objectId; + } + + public void setObjectId(Integer objectId) { + this.objectId = objectId; + } + + public Integer getChangeType() { + return changeType; + } + + public void setChangeType(Integer changeType) { + this.changeType = changeType; + } + + @Override + public String toString( ) { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + sb.append("ObjectChangeLog={") + .append("objectType=").append(objectType) + .append(", objectId=").append(objectId) + .append(", changeType=").append(changeType) + .append("}"); + + return sb; + } + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java index 6ab068f6f3..51480fae3f 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServicePolicies.java @@ -20,34 +20,34 @@ package org.apache.ranger.plugin.util; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - import org.apache.commons.collections.MapUtils; +import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicyDelta; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.policyengine.RangerPolicyEngine; import org.apache.ranger.plugin.policyengine.RangerPolicyEngineImpl; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class ServicePolicies implements java.io.Serializable { private static final long serialVersionUID = 1L; + private static final Logger LOG = LoggerFactory.getLogger(ServicePolicies.class); private String serviceName; private Long serviceId; @@ -58,6 +58,7 @@ public class ServicePolicies implements java.io.Serializable { private String auditMode = RangerPolicyEngine.AUDIT_DEFAULT; private TagPolicies tagPolicies; private Map securityZones; + @JsonInclude(JsonInclude.Include.NON_NULL) private List policyDeltas; private Map serviceConfig; @@ -168,6 +169,40 @@ public void setSecurityZones(Map securityZones) { this.securityZones = securityZones; } + public void dedupStrings() { + Map strTbl = new HashMap<>(); + + serviceName = StringUtil.dedupString(serviceName, strTbl); + auditMode = StringUtil.dedupString(auditMode, strTbl); + serviceConfig = StringUtil.dedupStringsMap(serviceConfig, strTbl); + + if (policies != null) { + for (RangerPolicy policy : policies) { + policy.dedupStrings(strTbl); + } + } + + if (serviceDef != null) { + serviceDef.dedupStrings(strTbl); + } + + if (tagPolicies != null) { + tagPolicies.dedupStrings(strTbl); + } + + if (securityZones != null) { + for (SecurityZoneInfo securityZoneInfo : securityZones.values()) { + securityZoneInfo.dedupStrings(strTbl); + } + } + + if (policyDeltas != null) { + for (RangerPolicyDelta policyDelta : policyDeltas) { + policyDelta.dedupStrings(strTbl); + } + } + } + @Override public String toString() { return "serviceName=" + serviceName + ", " @@ -187,10 +222,8 @@ public String toString() { public void setPolicyDeltas(List policyDeltas) { this.policyDeltas = policyDeltas; } @JsonAutoDetect(fieldVisibility=Visibility.ANY) - @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class TagPolicies implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -201,6 +234,8 @@ public static class TagPolicies implements java.io.Serializable { private List policies; private RangerServiceDef serviceDef; private String auditMode = RangerPolicyEngine.AUDIT_DEFAULT; + private Map serviceConfig; + /** * @return the serviceName */ @@ -282,6 +317,30 @@ public void setAuditMode(String auditMode) { this.auditMode = auditMode; } + public Map getServiceConfig() { + return serviceConfig; + } + + public void setServiceConfig(Map serviceConfig) { + this.serviceConfig = serviceConfig; + } + + public void dedupStrings(Map strTbl) { + serviceName = StringUtil.dedupString(serviceName, strTbl); + auditMode = StringUtil.dedupString(auditMode, strTbl); + serviceConfig = StringUtil.dedupStringsMap(serviceConfig, strTbl); + + if (policies != null) { + for (RangerPolicy policy : policies) { + policy.dedupStrings(strTbl); + } + } + + if (serviceDef != null) { + serviceDef.dedupStrings(strTbl); + } + } + @Override public String toString() { return "serviceName=" + serviceName + ", " @@ -291,20 +350,20 @@ public String toString() { + "policies=" + policies + ", " + "serviceDef=" + serviceDef + ", " + "auditMode=" + auditMode + + "serviceConfig=" + serviceConfig ; } } @JsonAutoDetect(fieldVisibility = Visibility.ANY) - @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) + @JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown = true) - @XmlRootElement - @XmlAccessorType(XmlAccessType.FIELD) public static class SecurityZoneInfo implements java.io.Serializable { private static final long serialVersionUID = 1L; private String zoneName; private List>> resources; private List policies; + @JsonInclude(JsonInclude.Include.NON_NULL) private List policyDeltas; private Boolean containsAssociatedTagService; @@ -340,6 +399,32 @@ public void setPolicies(List policies) { public void setContainsAssociatedTagService(Boolean containsAssociatedTagService) { this.containsAssociatedTagService = containsAssociatedTagService; } + public void dedupStrings(Map strTbl) { + zoneName = StringUtil.dedupString(zoneName, strTbl); + + if (resources != null && resources.size() > 0) { + List>> updated = new ArrayList<>(resources.size()); + + for (HashMap> resource : resources) { + updated.add(StringUtil.dedupStringsHashMapOfList(resource, strTbl)); + } + + resources = updated; + } + + if (policies != null) { + for (RangerPolicy policy : policies) { + policy.dedupStrings(strTbl); + } + } + + if (policyDeltas != null) { + for (RangerPolicyDelta policyDelta : policyDeltas) { + policyDelta.dedupStrings(strTbl); + } + } + } + @Override public String toString() { return "zoneName=" + zoneName + ", " @@ -397,11 +482,21 @@ public static ServicePolicies applyDelta(final ServicePolicies servicePolicies, final List newTagPolicies; if (servicePolicies.getTagPolicies() != null) { + if (LOG.isDebugEnabled()) { + LOG.debug("applyingDeltas for tag policies"); + } newTagPolicies = RangerPolicyDeltaUtil.applyDeltas(oldTagPolicies, servicePolicies.getPolicyDeltas(), servicePolicies.getTagPolicies().getServiceDef().getName()); } else { + if (LOG.isDebugEnabled()) { + LOG.debug("No need to apply deltas for tag policies"); + } newTagPolicies = oldTagPolicies; } + if (LOG.isDebugEnabled()) { + LOG.debug("New tag policies:[" + Arrays.toString(newTagPolicies.toArray()) + "]"); + } + if (ret.getTagPolicies() != null) { ret.getTagPolicies().setPolicies(newTagPolicies); } @@ -416,8 +511,16 @@ public static ServicePolicies applyDelta(final ServicePolicies servicePolicies, List zoneResourcePolicies = policyEngine.getResourcePolicies(zoneName); // There are no separate tag-policy-repositories for each zone + if (LOG.isDebugEnabled()) { + LOG.debug("Applying deltas for security-zone:[" + zoneName + "]"); + } + final List newZonePolicies = RangerPolicyDeltaUtil.applyDeltas(zoneResourcePolicies, zoneInfo.getPolicyDeltas(), servicePolicies.getServiceDef().getName()); + if (LOG.isDebugEnabled()) { + LOG.debug("New resource policies for security-zone:[" + zoneName + "], zoneResourcePolicies:[" + Arrays.toString(newZonePolicies.toArray())+ "]"); + } + SecurityZoneInfo newZoneInfo = new SecurityZoneInfo(); newZoneInfo.setZoneName(zoneName); diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceTags.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceTags.java index 9e8e0cf6cc..bca352e8b2 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceTags.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/ServiceTags.java @@ -21,28 +21,27 @@ import java.util.Date; +import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.HashMap; import java.util.ArrayList; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlRootElement; - +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.ranger.authorization.utils.StringUtil; import org.apache.ranger.plugin.model.RangerServiceResource; import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerTagDef; -import org.codehaus.jackson.annotate.JsonAutoDetect; -import org.codehaus.jackson.annotate.JsonIgnoreProperties; -import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility; -import org.codehaus.jackson.map.annotate.JsonSerialize; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility; +import com.fasterxml.jackson.annotation.JsonInclude; @JsonAutoDetect(fieldVisibility=Visibility.ANY) -@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL) +@JsonInclude(JsonInclude.Include.NON_EMPTY) @JsonIgnoreProperties(ignoreUnknown=true) -@XmlRootElement -@XmlAccessorType(XmlAccessType.FIELD) public class ServiceTags implements java.io.Serializable { private static final long serialVersionUID = 1L; @@ -63,6 +62,11 @@ public enum TagsChangeType { NONE, SERVICE_RESOURCE_UPDATE, TAG_UPDATE, TAG_RESO private Map> resourceToTagIds; private Boolean isDelta; private TagsChangeExtent tagsChangeExtent; + private Boolean isTagsDeduped; + + // MutablePair.left is the tag-id, MutablePair.right is the reference-count + @JsonIgnore + Map> cachedTags = new HashMap<>(); public ServiceTags() { this(OP_ADD_OR_UPDATE, null, 0L, null, null, null, null, null); @@ -70,10 +74,10 @@ public ServiceTags() { public ServiceTags(String op, String serviceName, Long tagVersion, Date tagUpdateTime, Map tagDefinitions, Map tags, List serviceResources, Map> resourceToTagIds) { - this(op, serviceName, tagVersion, tagUpdateTime, tagDefinitions, tags, serviceResources, resourceToTagIds, false, TagsChangeExtent.ALL); + this(op, serviceName, tagVersion, tagUpdateTime, tagDefinitions, tags, serviceResources, resourceToTagIds, false, TagsChangeExtent.ALL, false); } public ServiceTags(String op, String serviceName, Long tagVersion, Date tagUpdateTime, Map tagDefinitions, - Map tags, List serviceResources, Map> resourceToTagIds, Boolean isDelta, TagsChangeExtent tagsChangeExtent) { + Map tags, List serviceResources, Map> resourceToTagIds, Boolean isDelta, TagsChangeExtent tagsChangeExtent, Boolean isTagsDeduped) { setOp(op); setServiceName(serviceName); setTagVersion(tagVersion); @@ -84,7 +88,25 @@ public ServiceTags(String op, String serviceName, Long tagVersion, Date tagUpdat setResourceToTagIds(resourceToTagIds); setIsDelta(isDelta); setTagsChangeExtent(tagsChangeExtent); + setIsTagsDeduped(isTagsDeduped); + } + + public ServiceTags(ServiceTags other) { + setOp(other.getOp()); + setServiceName(other.getServiceName()); + setTagVersion(other.getTagVersion()); + setTagUpdateTime(other.getTagUpdateTime()); + setTagDefinitions(other.getTagDefinitions() != null ? new HashMap<>(other.getTagDefinitions()) : null); + setTags(other.getTags() != null ? new HashMap<>(other.getTags()) : null); + setServiceResources(other.getServiceResources() != null ? new ArrayList<>(other.getServiceResources()) : null); + setResourceToTagIds(other.getResourceToTagIds() != null ? new HashMap<>(other.getResourceToTagIds()) : null); + setIsDelta(other.getIsDelta()); + setIsTagsDeduped(other.getIsTagsDeduped()); + setTagsChangeExtent(other.getTagsChangeExtent()); + + this.cachedTags = new HashMap<>(other.cachedTags); } + /** * @return the op */ @@ -181,6 +203,14 @@ public void setIsDelta(Boolean isDelta) { this.isDelta = isDelta; } + public Boolean getIsTagsDeduped() { + return isTagsDeduped == null ? Boolean.FALSE : isTagsDeduped; + } + + public void setIsTagsDeduped(Boolean isTagsDeduped) { + this.isTagsDeduped = isTagsDeduped; + } + public TagsChangeExtent getTagsChangeExtent() { return tagsChangeExtent; } @@ -205,8 +235,102 @@ public StringBuilder toString(StringBuilder sb) { .append("tagUpdateTime={").append(tagUpdateTime).append("}") .append("isDelta={").append(isDelta).append("}") .append("tagsChangeExtent={").append(tagsChangeExtent).append("}") + .append(", serviceResources={").append(serviceResources).append("}") + .append(", tags={").append(tags).append("}") + .append(", resourceToTagIds={").append(resourceToTagIds).append("}") + .append(", isTagsDeduped={").append(isTagsDeduped).append("}") + .append(", cachedTags={").append(cachedTags).append("}") .append("}"); return sb; } + + public int dedupTags() { + final int ret; + final Map replacedIds = new HashMap<>(); + final int initialTagsCount = tags.size(); + final List tagIdsToRemove = new ArrayList<>(); + + for (Iterator> iter = tags.entrySet().iterator(); iter.hasNext(); ) { + Map.Entry entry = iter.next(); + Long tagId = entry.getKey(); + RangerTag tag = entry.getValue(); + MutablePair cachedTag = cachedTags.get(tag); + + if (cachedTag == null) { + cachedTags.put(tag, new MutablePair<>(tagId, 1L)); + } else if (!tagId.equals(cachedTag.left)) { + if (tagId < cachedTag.left) { + replacedIds.put(cachedTag.left, tagId); + tagIdsToRemove.add(cachedTag.left); + cachedTag.left = tagId; + } else { + replacedIds.put(tagId, cachedTag.left); + iter.remove(); + } + } + } + + for (Long tagIdToRemove : tagIdsToRemove) { + tags.remove(tagIdToRemove); + } + + final int finalTagsCount = tags.size(); + + for (Map.Entry> resourceEntry : resourceToTagIds.entrySet()) { + for (ListIterator listIter = resourceEntry.getValue().listIterator(); listIter.hasNext(); ) { + final Long tagId = listIter.next(); + Long mappedTagId = null; + + for (Long replacerTagId = replacedIds.get(tagId); replacerTagId != null; replacerTagId = replacedIds.get(mappedTagId)) { + mappedTagId = replacerTagId; + } + + if (mappedTagId == null) { + continue; + } + + listIter.set(mappedTagId); + + RangerTag tag = tags.get(mappedTagId); + + if (tag != null) { // This should always be true + MutablePair cachedTag = cachedTags.get(tag); + + if (cachedTag != null) { // This should always be true + cachedTag.right++; + } + } + } + } + + ret = initialTagsCount - finalTagsCount; + + return ret; + } + + public void dedupStrings() { + Map strTbl = new HashMap<>(); + + op = StringUtil.dedupString(op, strTbl); + serviceName = StringUtil.dedupString(serviceName, strTbl); + + if (tagDefinitions != null) { + for (RangerTagDef tagDef : tagDefinitions.values()) { + tagDef.dedupStrings(strTbl); + } + } + + if (tags != null) { + for (RangerTag tag : tags.values()) { + tag.dedupStrings(strTbl); + } + } + + if (serviceResources != null) { + for (RangerServiceResource resource : serviceResources) { + resource.dedupStrings(strTbl); + } + } + } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java index 2d09d44e6d..bf482ff1f6 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/StringTokenReplacer.java @@ -34,9 +34,12 @@ public StringTokenReplacer(char startChar, char endChar, char escapeChar, String this.tokenPrefix = tokenPrefix; } + public static boolean hasToken(String value, char startDelimiterChar, char endDelimiterChar, char escapeChar) { + return value != null && (value.indexOf(escapeChar) != -1 || (value.indexOf(startDelimiterChar) != -1 && value.indexOf(endDelimiterChar) != -1)); + } + public String replaceTokens(String value, Map tokens) { - if(tokens == null || tokens.size() < 1 || value == null || value.length() < 1 || - (value.indexOf(startChar) == -1 && value.indexOf(endChar) == -1 && value.indexOf(escapeChar) == -1)) { + if(tokens == null || tokens.size() < 1 || !hasToken(value, startChar, endChar, escapeChar)) { return value; } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/XMLUtils.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/XMLUtils.java index 3b674f8661..62477cb4b9 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/util/XMLUtils.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/XMLUtils.java @@ -29,7 +29,8 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; -import org.apache.log4j.Logger; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -37,7 +38,7 @@ public class XMLUtils { - private static final Logger LOG = Logger.getLogger(XMLUtils.class); + private static final Logger LOG = LoggerFactory.getLogger(XMLUtils.class); private static final String XMLCONFIG_PROPERTY_TAGNAME = "property"; private static final String XMLCONFIG_NAME_TAGNAME = "name"; diff --git a/agents-common/src/main/java/org/apache/ranger/services/gds/RangerServiceGds.java b/agents-common/src/main/java/org/apache/ranger/services/gds/RangerServiceGds.java new file mode 100644 index 0000000000..c67d3d3b74 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/services/gds/RangerServiceGds.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.services.gds; + +import org.apache.ranger.plugin.model.RangerService; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.service.RangerBaseService; +import org.apache.ranger.plugin.service.ResourceLookupContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RangerServiceGds extends RangerBaseService { + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceGds.class); + + public RangerServiceGds() { + super(); + } + + @Override + public void init(RangerServiceDef serviceDef, RangerService service) { + super.init(serviceDef, service); + } + + @Override + public Map validateConfig() throws Exception { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerServiceGds.validateConfig(" + serviceName + " )"); + } + + Map ret = new HashMap<>(); + + ret.put("connectivityStatus", true); + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerServiceGds.validateConfig(" + serviceName + " ): " + ret); + } + + return ret; + } + + @Override + public List lookupResource(ResourceLookupContext context) throws Exception { + if(LOG.isDebugEnabled()) { + LOG.debug("==> RangerServiceGds.lookupResource(" + context + ")"); + } + + if(LOG.isDebugEnabled()) { + LOG.debug("<== RangerServiceGds.lookupResource()"); + } + + return Collections.emptyList(); + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/services/tag/RangerServiceTag.java b/agents-common/src/main/java/org/apache/ranger/services/tag/RangerServiceTag.java index 678e72f1aa..036de11e20 100644 --- a/agents-common/src/main/java/org/apache/ranger/services/tag/RangerServiceTag.java +++ b/agents-common/src/main/java/org/apache/ranger/services/tag/RangerServiceTag.java @@ -29,8 +29,8 @@ import org.apache.ranger.plugin.service.RangerBaseService; import org.apache.ranger.plugin.service.ResourceLookupContext; import org.apache.ranger.plugin.store.TagStore; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Map; @@ -41,7 +41,7 @@ public class RangerServiceTag extends RangerBaseService { - private static final Log LOG = LogFactory.getLog(RangerServiceTag.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerServiceTag.class); public static final String TAG_RESOURCE_NAME = "tag"; public static final String RANGER_TAG_NAME_EXPIRES_ON = "EXPIRES_ON"; diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json index 7dcf388958..5dc5fccca4 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-abfs.json @@ -66,12 +66,14 @@ { "itemId": 1, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 2, "name": "write", - "label": "Write" + "label": "Write", + "category": "UPDATE" } ], "configs": @@ -118,7 +120,8 @@ "name": "ip-range", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerIpMatcher", "label": "IP Address Range", - "description": "IP Address Range" + "description": "IP Address Range", + "uiHint" : "{ \"isMultiValue\":true }" } ] } \ No newline at end of file diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-atlas.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-atlas.json index 7672be05ad..e9edfd5ba9 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-atlas.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-atlas.json @@ -21,8 +21,8 @@ "wildCard": "true", "ignoreCase": "true" }, - "label": "Type Catagory", - "description": "Type Catagory" + "label": "Type Category", + "description": "Type Category" }, { "itemId": 2, @@ -38,11 +38,11 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "Type Name", "description": "Type Name", - "accessTypeRestrictions": ["type-create", "type-update", "type-delete"] + "accessTypeRestrictions": ["type-read" ,"type-create", "type-update", "type-delete" ] }, { "itemId": 3, @@ -56,7 +56,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "Entity Type", "description": "Entity Type" @@ -74,7 +74,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "Entity Classification", "description": "Entity Classification" @@ -97,7 +97,7 @@ }, "label": "Entity ID", "description": "Entity ID", - "accessTypeRestrictions": ["entity-read", "entity-create", "entity-update", "entity-delete", "entity-add-classification", "entity-update-classification", "entity-remove-classification"] + "accessTypeRestrictions": ["entity-read", "entity-create", "entity-update", "entity-delete"] }, { "itemId": 6, @@ -115,7 +115,7 @@ }, "label": "Atlas Service", "description": "Atlas Service", - "accessTypeRestrictions": ["admin-import", "admin-export", "admin-purge"] + "accessTypeRestrictions": ["admin-import", "admin-export", "admin-purge", "admin-audits"] }, { "itemId": 7, @@ -129,7 +129,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "Relationship Type", "description": "Relationship Type" @@ -147,7 +147,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "End1 Entity Type", "description": "End1 Entity Type" @@ -165,7 +165,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "End1 Entity Classification", "description": "End1 Entity Classification" @@ -201,7 +201,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "End2 Entity Type", "description": "End2 Entity Type" @@ -219,7 +219,7 @@ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", - "ignoreCase": "true" + "ignoreCase": "false" }, "label": "End2 Entity Classification", "description": "End2 Entity Classification" @@ -292,103 +292,170 @@ "accessTypeRestrictions": [ "entity-update-business-metadata" ] + }, + { + "itemId": 16, + "name": "classification", + "type": "string", + "level": 40, + "mandatory": true, + "parent": "entity", + "isValidLeaf": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": "true", + "ignoreCase": "false" + }, + "label": "Targetted classifications", + "description": "Targetted classifications", + "accessTypeRestrictions": [ + "entity-add-classification", + "entity-update-classification", + "entity-remove-classification" + ] } ], "accessTypes": [ { "itemId": 1, "name": "type-create", - "label": "Create Type" + "label": "Create Type", + "category": "MANAGE", + "impliedGrants": + [ + "type-read" + ] }, { "itemId": 2, "name": "type-update", - "label": "Update Type" + "label": "Update Type", + "category": "MANAGE", + "impliedGrants": + [ + "type-read" + ] }, { "itemId": 3, "name": "type-delete", - "label": "Delete Type" + "label": "Delete Type", + "category": "MANAGE", + "impliedGrants": + [ + "type-read" + ] }, { "itemId": 4, "name": "entity-read", - "label": "Read Entity" + "label": "Read Entity", + "category": "READ" }, { "itemId": 5, "name": "entity-create", - "label": "Create Entity" + "label": "Create Entity", + "category": "CREATE" }, { "itemId": 6, "name": "entity-update", - "label": "Update Entity" + "label": "Update Entity", + "category": "UPDATE" }, { "itemId": 7, "name": "entity-delete", - "label": "Delete Entity" + "label": "Delete Entity", + "category": "DELETE" }, { "itemId": 8, "name": "entity-add-classification", - "label": "Add Classification" + "label": "Add Classification", + "category": "UPDATE" }, { "itemId": 9, "name": "entity-update-classification", - "label": "Update Classification" + "label": "Update Classification", + "category": "UPDATE" }, { "itemId": 10, "name": "entity-remove-classification", - "label": "Remove Classification" + "label": "Remove Classification", + "category": "UPDATE" }, { "itemId": 11, "name": "admin-export", - "label": "Admin Export" + "label": "Admin Export", + "category": "MANAGE" }, { "itemId": 12, "name": "admin-import", - "label": "Admin Import" + "label": "Admin Import", + "category": "MANAGE" }, { "itemId": 13, "name": "add-relationship", - "label": "Add Relationship" + "label": "Add Relationship", + "category": "UPDATE" }, { "itemId": 14, "name": "update-relationship", - "label": "Update Relationship" + "label": "Update Relationship", + "category": "UPDATE" }, { "itemId": 15, "name": "remove-relationship", - "label": "Remove Relationship" + "label": "Remove Relationship", + "category": "UPDATE" }, { "itemId": 16, "name": "admin-purge", - "label": "Admin Purge" + "label": "Admin Purge", + "category": "MANAGE" }, { "itemId": 17, "name": "entity-add-label", - "label": "Add Label" + "label": "Add Label", + "category": "UPDATE" }, { "itemId": 18, "name": "entity-remove-label", - "label": "Remove Label" + "label": "Remove Label", + "category": "UPDATE" }, { "itemId": 19, "name": "entity-update-business-metadata", - "label": "Update Business Metadata" + "label": "Update Business Metadata", + "category": "UPDATE" + }, + { + "itemId": 20, + "name": "type-read", + "label": "Read Type", + "category": "READ" + }, + { + "itemId": 21, + "name": "admin-audits", + "label": "Admin Audits", + "category": "MANAGE" } ], "configs": [ @@ -419,9 +486,23 @@ "type": "string", "mandatory": false, "label": "Common Name for Certificate" + }, + + { + "itemId": 5, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'users':['atlas'] ,'isAudited':false}, {'accessResult':'ALLOWED', 'isAudited':false, 'actions':['entity-read'], 'accessTypes':['entity-read'], 'users':['nifi']} ]" } ], "options": { - "enableDenyAndExceptionsInPolicies": "true" + "enableDenyAndExceptionsInPolicies": "true", + "enableTagBasedPolicies":"false" } } diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json index da621c7486..96cabe865b 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-elasticsearch.json @@ -57,13 +57,15 @@ { "itemId": 2, "name": "monitor", - "label": "monitor" + "label": "monitor", + "category": "MANAGE" }, { "itemId": 3, "name": "manage", "label": "manage", + "category": "MANAGE", "impliedGrants": [ "monitor" @@ -74,6 +76,7 @@ "itemId": 4, "name": "view_index_metadata", "label": "view_index_metadata", + "category": "MANAGE", "impliedGrants": [ "indices_search_shards" @@ -83,13 +86,15 @@ { "itemId": 5, "name": "read", - "label": "read" + "label": "read", + "category": "READ" }, { "itemId": 6, "name": "read_cross_cluster", "label": "read_cross_cluster", + "category": "READ", "impliedGrants": [ "indices_search_shards" @@ -100,6 +105,7 @@ "itemId": 7, "name": "index", "label": "index", + "category": "MANAGE", "impliedGrants": [ "indices_put", @@ -112,6 +118,7 @@ "itemId": 8, "name": "create", "label": "create", + "category": "CREATE", "impliedGrants": [ "indices_put", @@ -124,6 +131,7 @@ "itemId": 9, "name": "delete", "label": "delete", + "category": "DELETE", "impliedGrants": [ "indices_bulk" @@ -134,6 +142,7 @@ "itemId": 10, "name": "write", "label": "write", + "category": "UPDATE", "impliedGrants": [ "indices_put" @@ -143,13 +152,15 @@ { "itemId": 11, "name": "delete_index", - "label": "delete_index" + "label": "delete_index", + "category": "MANAGE" }, { "itemId": 12, "name": "create_index", - "label": "create_index" + "label": "create_index", + "category": "MANAGE" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-gds.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-gds.json new file mode 100644 index 0000000000..f3089bf30c --- /dev/null +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-gds.json @@ -0,0 +1,87 @@ +{ + "name": "gds", + "displayName": "Governed Data Sharing", + "implClass": "org.apache.ranger.services.gds.RangerServiceGds", + "label": "GDS", + "description": "GDS Service Definition", + "options": { + "enableDenyInPolicies": "false" + }, + "resources": [ + { + "itemId": 1, + "name": "dataset-id", + "type": "string", + "level": 1, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": false, "ignoreCase": false }, + "uiHint": "{ \"singleValue\": true }", + "label": "Dataset ID", + "description": "Dataset ID" + }, + { + "itemId": 2, + "name": "project-id", + "type": "string", + "level": 1, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": false, "ignoreCase": false }, + "uiHint": "{ \"singleValue\": true }", + "label": "Project ID", + "description": "Project ID" + } + ], + + "accessTypes": [ + { "itemId": 1, "name": "_CREATE", "label": "_CREATE" }, + { "itemId": 2, "name": "_READ", "label": "_READ" }, + { "itemId": 3, "name": "_UPDATE", "label": "_UPDATE" }, + { "itemId": 4, "name": "_DELETE", "label": "_DELETE" }, + { "itemId": 5, "name": "_MANAGE", "label": "_MANAGE" }, + { "itemId": 6, "name": "_ALL", "label": "_ALL" } + ], + + "configs": [ + { + "itemId": 1, + "name": "ranger.plugin.audit.filters", + "type": "string", + "mandatory": false, + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true} ]" + } + ], + + "enums": [ ], + + "contextEnrichers": [ ], + + "policyConditions": + [ + { + "itemId": 1, + "name": "expression", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions": { "engineName":"JavaScript", "ui.isMultiline":"true" }, + "label": "Enter boolean expression", + "description": "Boolean expression" + }, + { + "itemId": 2, + "name": "validitySchedule", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerValidityScheduleConditionEvaluator", + "label": "Validity schedule", + "description": "Validity schedule" + } + ] +} diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-hbase.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-hbase.json index 594e1751eb..7e5cfa1a6d 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-hbase.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-hbase.json @@ -71,26 +71,30 @@ { "itemId": 1, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 2, "name": "write", - "label": "Write" + "label": "Write", + "category": "UPDATE" }, { "itemId": 3, "name": "create", - "label": "Create" + "label": "Create", + "category": "CREATE" }, { "itemId": 4, "name": "admin", "label": "Admin", - "impliedGrants": + "category": "MANAGE", + "impliedGrants": [ "read", "write", @@ -100,7 +104,8 @@ { "itemId": 5, "name": "execute", - "label": "Execute" + "label": "Execute", + "category": "READ" } ], @@ -208,6 +213,19 @@ "validationMessage": "", "uiHint":"", "label": "Common Name for Certificate" + }, + + { + "itemId": 10, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[{'accessResult': 'DENIED', 'isAudited': true},{'resources':{'table':{'values':['*-ROOT-*','*.META.*', '*_acl_*', 'hbase:meta', 'hbase:acl', 'default', 'hbase']}}, 'users':['hbase'], 'isAudited': false }, {'resources':{'table':{'values':['atlas_janus','ATLAS_ENTITY_AUDIT_EVENTS']},'column-family':{'values':['*']},'column':{'values':['*']}},'users':['atlas', 'hbase'],'isAudited':false},{'users':['hbase'], 'actions':['balance'],'isAudited':false}]" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-hdfs.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-hdfs.json index fbb16d7659..8ed321649b 100755 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-hdfs.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-hdfs.json @@ -33,19 +33,22 @@ { "itemId": 1, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 2, "name": "write", - "label": "Write" + "label": "Write", + "category": "UPDATE" }, { "itemId": 3, "name": "execute", - "label": "Execute" + "label": "Execute", + "category": "READ" } ], @@ -180,6 +183,19 @@ "validationMessage": "", "uiHint":"", "label": "Common Name for Certificate" + }, + + { + "itemId": 12, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[{'accessResult': 'DENIED', 'isAudited': true}, {'actions':['delete','rename'],'isAudited':true}, {'users':['hdfs'], 'actions': ['listStatus', 'getfileinfo', 'listCachePools', 'listCacheDirectives', 'listCorruptFileBlocks', 'monitorHealth', 'rollEditLog', 'open'], 'isAudited': false}, {'users': ['oozie'],'resources': {'path': {'values': ['/user/oozie/share/lib'],'isRecursive': true}},'isAudited': false},{'users': ['spark'],'resources': {'path': {'values': ['/user/spark/applicationHistory'],'isRecursive': true}},'isAudited': false},{'users': ['hue'],'resources': {'path': {'values': ['/user/hue'],'isRecursive': true}},'isAudited': false},{'users': ['hbase'],'resources': {'path': {'values': ['/hbase'],'isRecursive': true}},'isAudited': false},{'users': ['mapred'],'resources': {'path': {'values': ['/user/history'],'isRecursive': true}},'isAudited': false}, {'actions': ['getfileinfo'], 'isAudited':false} ]" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-hive.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-hive.json index 42df0a8de7..aa7cb3e777 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-hive.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-hive.json @@ -129,6 +129,7 @@ "type": "string", "level": 10, "parent": "", + "mandatory": true, "lookupSupported": false, "recursiveSupported": false, "excludesSupported": false, @@ -150,43 +151,50 @@ { "itemId": 1, "name": "select", - "label": "select" + "label": "select", + "category": "READ" }, { "itemId": 2, "name": "update", - "label": "update" + "label": "update", + "category": "UPDATE" }, { "itemId": 3, "name": "create", - "label": "Create" + "label": "Create", + "category": "CREATE" }, { "itemId": 4, "name": "drop", - "label": "Drop" + "label": "Drop", + "category": "DELETE" }, { "itemId": 5, "name": "alter", - "label": "Alter" + "label": "Alter", + "category": "CREATE" }, { "itemId": 6, "name": "index", - "label": "Index" + "label": "Index", + "category": "MANAGE" }, { "itemId": 7, "name": "lock", - "label": "Lock" + "label": "Lock", + "category": "MANAGE" }, { @@ -213,37 +221,43 @@ { "itemId": 9, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 10, "name": "write", - "label": "Write" + "label": "Write", + "category": "UPDATE" }, { "itemId": 11, "name": "repladmin", - "label": "ReplAdmin" + "label": "ReplAdmin", + "category": "MANAGE" }, { "itemId": 12, "name": "serviceadmin", - "label": "Service Admin" + "label": "Service Admin", + "category": "MANAGE" }, { "itemId": 13, "name": "tempudfadmin", - "label": "Temporary UDF Admin" + "label": "Temporary UDF Admin", + "category": "MANAGE" }, { "itemId": 14, "name": "refresh", - "label": "Refresh" + "label": "Refresh", + "category": "MANAGE" } ], @@ -302,6 +316,19 @@ "validationMessage": "", "uiHint":"", "label": "Common Name for Certificate" + }, + + { + "itemId": 6, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'actions':['METADATA OPERATION'], 'isAudited': false}, {'users':['hive','hue'],'actions':['SHOW_ROLES'],'isAudited':false} ]" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json index 6ea52f77ea..d99819f555 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kafka.json @@ -97,6 +97,7 @@ "itemId":1, "name":"publish", "label":"Publish", + "category": "UPDATE", "impliedGrants":[ "describe" ] @@ -105,6 +106,7 @@ "itemId":2, "name":"consume", "label":"Consume", + "category": "READ", "impliedGrants":[ "describe" ] @@ -113,6 +115,7 @@ "itemId":5, "name":"configure", "label":"Configure", + "category": "MANAGE", "impliedGrants":[ "describe" ] @@ -120,12 +123,14 @@ { "itemId":6, "name":"describe", - "label":"Describe" + "label":"Describe", + "category": "READ" }, { "itemId":7, "name":"kafka_admin", "label":"Kafka Admin", + "category": "MANAGE", "impliedGrants":[ "publish", "consume", @@ -143,12 +148,14 @@ { "itemId":8, "name":"create", - "label":"Create" + "label":"Create", + "category": "CREATE" }, { "itemId":9, "name":"delete", "label":"Delete", + "category": "DELETE", "impliedGrants":[ "describe" ] @@ -156,17 +163,20 @@ { "itemId":10, "name":"idempotent_write", - "label":"Idempotent Write" + "label":"Idempotent Write", + "category": "UPDATE" }, { "itemId":11, "name":"describe_configs", - "label":"Describe Configs" + "label":"Describe Configs", + "category": "READ" }, { "itemId":12, "name":"alter_configs", "label":"Alter Configs", + "category": "MANAGE", "impliedGrants":[ "describe_configs" ] @@ -174,12 +184,14 @@ { "itemId":13, "name":"cluster_action", - "label":"Cluster Action" + "label":"Cluster Action", + "category": "MANAGE" }, { "itemId":14, "name":"alter", - "label":"Alter" + "label":"Alter", + "category": "MANAGE" } ], "configs":[ @@ -211,6 +223,19 @@ "type":"string", "mandatory":false, "label":"Ranger Plugin SSL CName" + }, + + { + "itemId": 5, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[{'accessResult': 'DENIED', 'isAudited': true},{'resources':{'topic':{'values':['ATLAS_ENTITIES','ATLAS_HOOK','ATLAS_SPARK_HOOK']}},'users':['atlas'],'actions':['describe','publish','consume'],'isAudited':false},{'resources':{'topic':{'values':['ATLAS_HOOK']}},'users':['hive','hbase','impala','nifi'],'actions':['publish','describe'],'isAudited':false},{'resources':{'topic':{'values':['ATLAS_ENTITIES']}},'users':['rangertagsync'],'actions':['consume','describe'],'isAudited':false},{'resources':{'consumergroup':{'values':['*']}},'users':['atlas','rangertagsync'],'actions':['consume'],'isAudited':false},{'users':['kafka'],'isAudited':false},{'resources':{'topic':{'values':['__CruiseControlMetrics']}},'users':['cc_metric_reporter'],'actions':['describe','publish','consume'],'isAudited':false}]" } ], "enums":[ @@ -226,9 +251,9 @@ }, "validationRegEx":"", "validationMessage":"", - "uiHint":"", "label":"IP Address Range", - "description":"IP Address Range" + "description":"IP Address Range", + "uiHint" : "{ \"isMultiValue\":true }" } ] } diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json index 5a2915cea6..48ba6b9d51 100755 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kms.json @@ -37,56 +37,65 @@ { "itemId": 1, "name": "create", - "label": "Create" + "label": "Create", + "category": "CREATE" }, { "itemId": 2, "name": "delete", - "label": "Delete" + "label": "Delete", + "category": "DELETE" }, { "itemId": 3, "name": "rollover", - "label": "Rollover" + "label": "Rollover", + "category": "UPDATE" }, { "itemId": 4, "name": "setkeymaterial", - "label": "Set Key Material" - }, + "label": "Set Key Material", + "category": "UPDATE" + }, { "itemId": 5, "name": "get", - "label": "Get" + "label": "Get", + "category": "READ" }, { "itemId": 6, "name": "getkeys", - "label": "Get Keys" + "label": "Get Keys", + "category": "READ" }, { "itemId": 7, "name": "getmetadata", - "label": "Get Metadata" + "label": "Get Metadata", + "category": "READ" }, { "itemId": 8, "name": "generateeek", - "label": "Generate EEK" + "label": "Generate EEK", + "category": "UPDATE" }, { "itemId": 9, "name": "decrypteek", - "label": "Decrypt EEK" - } + "label": "Decrypt EEK", + "category": "UPDATE" + } ], "configs": @@ -113,6 +122,18 @@ "type": "password", "mandatory": true, "label": "Password" + }, + + { + "itemId": 4, + "name": "ranger.plugin.audit.filters", + "type": "string", + "mandatory": false, + "validationRegEx":"", + "validationMessage":"", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'users':['keyadmin'] ,'isAudited':false} ]" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json index aa0f672fbd..d1832ecce2 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-knox.json @@ -52,7 +52,8 @@ { "itemId": 1, "name": "allow", - "label": "Allow" + "label": "Allow", + "category": "READ" } ], @@ -100,6 +101,19 @@ "validationMessage": "", "uiHint":"", "label": "Common Name for Certificate" + }, + + { + "itemId":5, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'users':['knox'] ,'isAudited':false} ]" } ], @@ -122,9 +136,9 @@ "evaluatorOptions": { }, "validationRegEx":"", "validationMessage": "", - "uiHint":"", "label": "IP Address Range", - "description": "IP Address Range" + "description": "IP Address Range", + "uiHint" : "{ \"isMultiValue\":true }" } ] } diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-kudu.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-kudu.json index e2a8b72a96..7143851d00 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kudu.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kudu.json @@ -72,6 +72,7 @@ "itemId": 1, "name": "select", "label": "SELECT", + "category": "READ", "impliedGrants": [ "metadata" @@ -82,6 +83,7 @@ "itemId": 2, "name": "insert", "label": "INSERT", + "category": "UPDATE", "impliedGrants": [ "metadata" @@ -91,6 +93,7 @@ "itemId": 3, "name": "update", "label": "UPDATE", + "category": "UPDATE", "impliedGrants": [ "metadata" @@ -100,6 +103,7 @@ "itemId": 4, "name": "delete", "label": "DELETE", + "category": "DELETE", "impliedGrants": [ "metadata" @@ -109,6 +113,7 @@ "itemId": 5, "name": "alter", "label": "ALTER", + "category": "CREATE", "impliedGrants": [ "metadata" @@ -118,6 +123,7 @@ "itemId": 6, "name": "create", "label": "CREATE", + "category": "CREATE", "impliedGrants": [ "metadata" @@ -127,6 +133,7 @@ "itemId": 7, "name": "drop", "label": "DROP", + "category": "DELETE", "impliedGrants": [ "metadata" @@ -135,7 +142,8 @@ { "itemId": 8, "name": "metadata", - "label": "METADATA" + "label": "METADATA", + "category": "MANAGE" }, { "itemId": 9, @@ -157,6 +165,18 @@ "configs": [ + { + "itemId": 1, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[]" + } ], "enums": @@ -171,6 +191,3 @@ [ ] } - - - diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-kylin.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-kylin.json index 70566dfd61..f852947b5d 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-kylin.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-kylin.json @@ -33,25 +33,29 @@ { "itemId": 1, "name": "QUERY", - "label": "QUERY" + "label": "QUERY", + "category": "READ" }, { "itemId": 2, "name": "OPERATION", - "label": "OPERATION" + "label": "OPERATION", + "category": "UPDATE" }, { "itemId": 3, "name": "MANAGEMENT", - "label": "MANAGEMENT" + "label": "MANAGEMENT", + "category": "MANAGE" }, { "itemId": 4, "name": "ADMIN", - "label": "ADMIN" + "label": "ADMIN", + "category": "MANAGE" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-nestedstructure.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-nestedstructure.json new file mode 100644 index 0000000000..8aa1b705bd --- /dev/null +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-nestedstructure.json @@ -0,0 +1,186 @@ +{ + "name": "nestedstructure", + "displayName": "nestedstructure", + "implClass": "", + "label": "NestedStructure", + "description": "Plugin to enforce READ and WRITE access control on nested structures such as JSON response objects from microservice API resource calls", + "options": { + "enableDenyAndExceptionsInPolicies": "true" + }, + "configs": [ + { "itemId": 1, "name": "commonNameForCertificate", "type": "string", "mandatory": false }, + { "itemId": 2, "name": "policy.download.auth.users", "type": "string", "mandatory": false } + ], + "resources": [ + { + "itemId": 1, + "name": "schema", + "type": "string", + "level": 10, + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": "true", "ignoreCase": "true" }, + "label": "NestedStructure Schema", + "description": "Schema of the nested structure returned from Microservice GET, etc", + "accessTypeRestrictions": [], + "isValidLeaf": true + }, + { + "itemId": 2, + "name": "field", + "type": "string", + "level": 20, + "parent": "schema", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": "true", "ignoreCase": "true" }, + "label": "NestedStructure Schema Field", + "description": "NestedStructure Schema Field", + "accessTypeRestrictions": [], + "isValidLeaf": true + } + ], + "accessTypes": [ + { "itemId": 1, "name": "read", "label": "Read", "category": "READ" }, + { "itemId": 2, "name": "write", "label": "Write", "category": "UPDATE" } + ], + "policyConditions": [], + "contextEnrichers": [], + "enums": [], + "dataMaskDef": { + "maskTypes": [ + { + "itemId": 1, + "name": "MASK", + "label": "Redact", + "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", + "transformer": "mask({field})", + "dataMaskOptions": {} + }, + { + "itemId": 2, + "name": "MASK_SHOW_LAST_4", + "label": "Partial mask: show last 4", + "description": "Show last 4 characters; replace rest with 'x'", + "transformer": "mask_show_last_n({field}, 4, 'x', 'x', 'x', -1, '1')", + "dataMaskOptions": {} + }, + { + "itemId": 3, + "name": "MASK_SHOW_FIRST_4", + "label": "Partial mask: show first 4", + "description": "Show first 4 characters; replace rest with 'x'", + "transformer": "mask_show_first_n({field}, 4, 'x', 'x', 'x', -1, '1')", + "dataMaskOptions": {} + }, + { + "itemId": 4, + "name": "MASK_HASH", + "label": "Hash", + "description": "Hash the value", + "transformer": "mask_hash({field})", + "dataMaskOptions": {} + }, + { + "itemId": 5, + "name": "MASK_NULL", + "label": "Nullify", + "description": "Replace with NULL", + "dataMaskOptions": {} + }, + { + "itemId": 6, + "name": "MASK_NONE", + "label": "Unmasked (retain original value)", + "description": "No masking", + "dataMaskOptions": {} + }, + { + "itemId": 12, + "name": "MASK_DATE_SHOW_YEAR", + "label": "Date: show only year", + "description": "Date: show only year", + "transformer": "mask({field}, 'x', 'x', 'x', -1, '1', 1, 0, -1)", + "dataMaskOptions": {} + }, + { + "itemId": 13, + "name": "CUSTOM", + "label": "Custom", + "description": "Custom", + "dataMaskOptions": {} + } + ], + "accessTypes": [ + { "itemId": 1, "name": "read", "label": "Read" } + ], + "resources": [ + { + "itemId": 1, + "name": "schema", + "type": "string", + "level": 10, + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, + "uiHint": "{ \"singleValue\":true }", + "label": "NestedStructure Schema", + "description": "NestedStructure Schema returned from Microservice GET, etc", + "accessTypeRestrictions": [], + "isValidLeaf": false + }, + { + "itemId": 2, + "name": "field", + "type": "string", + "level": 20, + "parent": "schema", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, + "uiHint": "{ \"singleValue\":true }", + "label": "NestedStructure Schema Field", + "description": "NestedStructure Schema Field", + "accessTypeRestrictions": [], + "isValidLeaf": true + } + ] + }, + "rowFilterDef": { + "accessTypes": [ + { "itemId": 1, "name": "read", "label": "Read" }, + { "itemId": 2, "name": "write", "label": "Write" } + ], + "resources": [ + { + "itemId": 1, + "name": "schema", + "type": "string", + "level": 10, + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, + "uiHint": "{ \"singleValue\":true }", + "label": "NestedStructure Schema", + "description": "NestedStructure Schema returned from Microservice GET, etc", + "accessTypeRestrictions": [], + "isValidLeaf": true + } + ] + } +} \ No newline at end of file diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json index bf2768ac0b..60babc6f7a 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi-registry.json @@ -33,17 +33,20 @@ { "itemId":100, "name":"READ", - "label":"Read" + "label":"Read", + "category": "READ" }, { "itemId":200, "name":"WRITE", - "label":"Write" + "label":"Write", + "category": "UPDATE" }, { "itemId":300, "name":"DELETE", - "label":"Delete" + "label":"Delete", + "category": "DELETE" } ], "configs":[ @@ -147,6 +150,18 @@ "validationMessage":"", "uiHint":"", "label":"Truststore Password" + }, + { + "itemId": 560, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[]" } ], "enums": diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json index c34533e30c..b481efd8ef 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-nifi.json @@ -33,12 +33,14 @@ { "itemId":100, "name":"READ", - "label":"Read" + "label":"Read", + "category": "READ" }, { "itemId":200, "name":"WRITE", - "label":"Write" + "label":"Write", + "category": "UPDATE" } ], "configs":[ @@ -142,6 +144,18 @@ "validationMessage":"", "uiHint":"", "label":"Truststore Password" + }, + { + "itemId": 560, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[]" } ], "enums": diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json index 4f2a432840..2bff90d47b 100755 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-ozone.json @@ -16,8 +16,8 @@ "parent": "", "mandatory": true, "lookupSupported": true, - "recursiveSupported": true, - "excludesSupported": false, + "recursiveSupported": false, + "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":false }, "validationRegEx":"", @@ -55,7 +55,7 @@ "parent": "bucket", "mandatory": true, "lookupSupported": true, - "recursiveSupported": false, + "recursiveSupported": true, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":true }, @@ -80,37 +80,56 @@ "write", "create", "list", - "delete" + "delete", + "read_acl", + "write_acl" ] }, { "itemId": 1, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 2, "name": "write", - "label": "Write" + "label": "Write", + "category": "UPDATE" }, { "itemId": 3, "name": "create", - "label": "Create" + "label": "Create", + "category": "CREATE" }, { "itemId": 4, "name": "list", - "label": "List" + "label": "List", + "category": "READ" }, { "itemId": 5, "name": "delete", - "label": "Delete" + "label": "Delete", + "category": "DELETE" + }, + { + "itemId": 6, + "name": "read_acl", + "label": "Read_ACL", + "category": "READ" + }, + { + "itemId": 7, + "name": "write_acl", + "label": "Write_ACL", + "category": "UPDATE" } ], @@ -157,7 +176,7 @@ "name": "hadoop.security.authorization", "type": "bool", "subType": "YesTrue:NoFalse", - "mandatory": true, + "mandatory": false, "validationRegEx":"", "validationMessage": "", "uiHint":"", @@ -191,47 +210,15 @@ { "itemId": 7, - "name": "dfs.datanode.kerberos.principal", - "type": "string", - "subType": "", - "mandatory": false, - "validationRegEx":"", - "validationMessage": "", - "uiHint":"" - }, - - { - "itemId": 8, - "name": "dfs.namenode.kerberos.principal", - "type": "string", - "subType": "", - "mandatory": false, - "validationRegEx":"", - "validationMessage": "", - "uiHint":"" - }, - - { - "itemId": 9, - "name": "dfs.secondary.namenode.kerberos.principal", - "type": "string", - "subType": "", - "mandatory": false, - "validationRegEx":"", - "validationMessage": "", - "uiHint":"" - }, - - { - "itemId": 10, - "name": "commonNameForCertificate", + "name": "ranger.plugin.audit.filters", "type": "string", "subType": "", "mandatory": false, "validationRegEx":"", "validationMessage": "", "uiHint":"", - "label": "Common Name for Certificate" + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true} ]" } ], @@ -274,9 +261,9 @@ "evaluatorOptions": { }, "validationRegEx":"", "validationMessage": "", - "uiHint":"", "label": "IP Address Range", - "description": "IP Address Range" + "description": "IP Address Range", + "uiHint" : "{ \"isMultiValue\":true }" } ] diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-presto.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-presto.json index b16b02a7be..330ff1485c 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-presto.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-presto.json @@ -209,57 +209,68 @@ { "itemId": 1, "name": "select", - "label": "Select" + "label": "Select", + "category": "READ" }, { "itemId": 2, "name": "insert", - "label": "Insert" + "label": "Insert", + "category": "UPDATE" }, { "itemId": 3, "name": "create", - "label": "Create" + "label": "Create", + "category": "CREATE" }, { "itemId": 4, "name": "drop", - "label": "Drop" + "label": "Drop", + "category": "DELETE" }, { "itemId": 5, "name": "delete", - "label": "Delete" + "label": "Delete", + "category": "DELETE" }, { "itemId": 6, "name": "use", - "label": "Use" + "label": "Use", + "category": "READ" }, { "itemId": 7, "name": "alter", - "label": "Alter" + "label": "Alter", + "category": "CREATE" }, { "itemId": 8, "name": "grant", - "label": "Grant" + "label": "Grant", + "category": "MANAGE" }, { "itemId": 9, "name": "revoke", - "label": "Revoke" + "label": "Revoke", + "category": "MANAGE" }, { "itemId": 10, "name": "show", - "label": "Show" + "label": "Show", + "category": "READ" }, { "itemId": 11, "name": "impersonate", - "label": "Impersonate" + "label": "Impersonate", + "category": "READ" }, { "itemId": 12, @@ -283,7 +294,8 @@ { "itemId": 13, "name": "execute", - "label": "execute" + "label": "execute", + "category": "READ" } ], "configs": [ diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json index 6ef253e089..250f9da70d 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-schema-registry.json @@ -149,25 +149,29 @@ { "itemId": 1, "name": "create", - "label": "Create" + "label": "Create", + "category": "CREATE" }, { "itemId": 2, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 3, "name": "update", - "label": "Update" + "label": "Update", + "category": "UPDATE" }, { "itemId": 4, "name": "delete", - "label": "Delete" + "label": "Delete", + "category": "DELETE" } ], @@ -207,6 +211,19 @@ "validationMessage": "", "uiHint":"", "label": "Ranger Plugin SSL CName" + }, + + { + "itemId": 4, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[]" } ], @@ -251,9 +268,9 @@ }, "validationRegEx":"", "validationMessage":"", - "uiHint":"", "label":"IP Address Range", - "description":"IP Address Range" + "description":"IP Address Range", + "uiHint" : "{ \"isMultiValue\":true }" } ] diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json index ec2ebcff99..315c03803d 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-solr.json @@ -26,6 +26,69 @@ "uiHint":"", "label":"Solr Collection", "description":"Solr Collection" + }, + { + "itemId":101, + "name":"config", + "type":"string", + "level":10, + "parent":"", + "mandatory":true, + "lookupSupported":true, + "recursiveSupported":false, + "excludesSupported":true, + "matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions":{ + "wildCard":true, + "ignoreCase":true + }, + "validationRegEx":"", + "validationMessage":"", + "uiHint":"", + "label":"Solr Config", + "description":"Solr Config" + }, + { + "itemId":102, + "name":"schema", + "type":"string", + "level":10, + "parent":"", + "mandatory":true, + "lookupSupported":true, + "recursiveSupported":false, + "excludesSupported":true, + "matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions":{ + "wildCard":true, + "ignoreCase":true + }, + "validationRegEx":"", + "validationMessage":"", + "uiHint":"", + "label":"Schema of a collection", + "description":"The schema of a collection" + }, + { + "itemId":103, + "name":"admin", + "type":"string", + "level":10, + "parent":"", + "mandatory":true, + "lookupSupported":true, + "recursiveSupported":false, + "excludesSupported":true, + "matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions":{ + "wildCard":true, + "ignoreCase":true + }, + "validationRegEx":"", + "validationMessage":"", + "uiHint":"", + "label":"Solr Admin", + "description":"Solr Admin" } ], @@ -33,25 +96,14 @@ { "itemId":100, "name":"query", - "label":"Query" + "label":"Query", + "category": "READ" }, { "itemId":200, "name":"update", - "label":"Update" - }, - { - "itemId":300, - "name":"others", - "label":"Others" - }, - { - "itemId":900, - "name":"solr_admin", - "label":"Solr Admin", - "impliedGrants":[ - "query","update","others" - ] + "label":"Update", + "category": "UPDATE" } ], "configs":[ @@ -75,6 +127,17 @@ "uiHint":"", "label":"Password" }, + { + "itemId":300, + "name":"solr.zookeeper.quorum", + "type":"string", + "mandatory":false, + "defaultValue":"", + "validationRegEx":"", + "validationMessage":"", + "uiHint":"", + "label":"Solr Zookeeper Quorum" + }, { "itemId":400, "name":"solr.url", @@ -95,6 +158,19 @@ "validationMessage":"", "uiHint":"", "label":"Ranger Plugin SSL CName" + }, + + { + "itemId":600, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'users':['hive','hdfs','kafka','hbase','solr','rangerraz','knox','atlas','yarn','impala'] ,'isAudited':false} ]" } ], @@ -114,9 +190,9 @@ }, "validationRegEx":"", "validationMessage":"", - "uiHint":"", "label":"IP Address Range", - "description":"IP Address Range" + "description":"IP Address Range", + "uiHint" : "{ \"isMultiValue\":true }" } ] diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-sqoop.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-sqoop.json index 544276e9a8..edbb619b2b 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-sqoop.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-sqoop.json @@ -71,13 +71,15 @@ { "itemId": 1, "name": "READ", - "label": "READ" + "label": "READ", + "category": "READ" }, { "itemId": 2, "name": "WRITE", - "label": "WRITE" + "label": "WRITE", + "category": "UPDATE" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-storm.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-storm.json index 4db4a7431d..cbe8812c83 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-storm.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-storm.json @@ -33,6 +33,7 @@ "itemId": 1, "name": "submitTopology", "label": "Submit Topology", + "category": "UPDATE", "impliedGrants": [ "fileUpload", @@ -43,67 +44,78 @@ { "itemId": 2, "name": "fileUpload", - "label": "File Upload" + "label": "File Upload", + "category": "UPDATE" }, { "itemId": 5, "name": "fileDownload", - "label": "File Download" + "label": "File Download", + "category": "READ" }, { "itemId": 6, "name": "killTopology", - "label": "Kill Topology" + "label": "Kill Topology", + "category": "MANAGE" }, { "itemId": 7, "name": "rebalance", - "label": "Rebalance" + "label": "Rebalance", + "category": "MANAGE" }, { "itemId": 8, "name": "activate", - "label": "Activate" + "label": "Activate", + "category": "MANAGE" }, { "itemId": 9, "name": "deactivate", - "label": "Deactivate" + "label": "Deactivate", + "category": "MANAGE" }, { "itemId": 10, "name": "getTopologyConf", - "label": "Get Topology Conf" + "label": "Get Topology Conf", + "category": "READ" }, { "itemId": 11, "name": "getTopology", - "label": "Get Topology" + "label": "Get Topology", + "category": "READ" }, { "itemId": 12, "name": "getUserTopology", - "label": "Get User Topology" + "label": "Get User Topology", + "category": "READ" }, { "itemId": 13, "name": "getTopologyInfo", - "label": "Get Topology Info" + "label": "Get Topology Info", + "category": "READ" }, { "itemId": 14, "name": "uploadNewCredentials", - "label": "Upload New Credential" + "label": "Upload New Credential", + "category": "MANAGE" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json index 7b72f45c15..add29feed4 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-tag.json @@ -39,7 +39,18 @@ "configs": [ - + { + "itemId":1, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true} ]" + } ], "enums": @@ -77,7 +88,8 @@ "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", "evaluatorOptions" : {"engineName":"JavaScript", "ui.isMultiline":"true"}, "label":"Enter boolean expression", - "description": "Boolean expression" + "description": "Boolean expression", + "uiHint" : "{ \"isMultiline\":true }" } ] } diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-trino.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-trino.json new file mode 100644 index 0000000000..352b26f101 --- /dev/null +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-trino.json @@ -0,0 +1,601 @@ +{ + "id": 203, + "name": "trino", + "displayName": "trino", + "implClass": "org.apache.ranger.services.trino.RangerServiceTrino", + "label": "Trino", + "description": "Trino", + "guid": "379a9fe5-1b6e-4091-a584-4890e245e6c1", + "resources": [ + { + "itemId": 1, + "name": "catalog", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "isValidLeaf": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Trino Catalog", + "description": "Trino Catalog" + }, + { + "itemId": 2, + "name": "schema", + "type": "string", + "level": 20, + "parent": "catalog", + "mandatory": true, + "isValidLeaf": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Trino Schema", + "description": "Trino Schema" + }, + { + "itemId": 3, + "name": "table", + "type": "string", + "level": 30, + "parent": "schema", + "mandatory": true, + "isValidLeaf": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Trino Table", + "description": "Trino Table" + }, + { + "itemId": 4, + "name": "column", + "type": "string", + "level": 40, + "parent": "table", + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Trino Column", + "description": "Trino Column" + }, + { + "itemId": 5, + "name": "trinouser", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Trino User", + "description": "Trino User", + "accessTypeRestrictions": ["impersonate"] + }, + { + "itemId": 6, + "name": "systemproperty", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "System Property", + "description": "Trino System Property", + "accessTypeRestrictions": ["alter"] + }, + { + "itemId": 7, + "name": "sessionproperty", + "type": "string", + "level": 20, + "parent": "catalog", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Catalog Session Property", + "description": "Trino Catalog Session Property", + "accessTypeRestrictions": ["alter"] + }, + { + "itemId": 8, + "name": "function", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Trino Function", + "description": "Trino Function", + "accessTypeRestrictions": ["execute", "grant"] + }, + { + "itemId": 9, + "name": "procedure", + "type": "string", + "level": 30, + "parent": "schema", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Schema Procedure", + "description": "Schema Procedure", + "accessTypeRestrictions": ["execute", "grant"] + }, + { + "itemId": 10, + "name": "schemafunction", + "type": "string", + "level": 30, + "parent": "schema", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Schema Function", + "description": "Schema Function", + "accessTypeRestrictions": [ "create", "drop", "show" ] + }, + { + "itemId": 11, + "name": "queryid", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Query ID", + "description": "Query ID", + "accessTypeRestrictions": [ "execute" ] + }, + { + "itemId": 12, + "name": "sysinfo", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "System Information", + "description": "Trino System Information", + "accessTypeRestrictions": [ "read_sysinfo", "write_sysinfo" ] + }, + { + "itemId": 13, + "name": "role", + "type": "string", + "level": 10, + "parent": "", + "mandatory": true, + "lookupSupported": false, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Role", + "description": "Trino Role", + "accessTypeRestrictions": [ "create", "drop", "show", "grant", "revoke" ] + } + ], + "accessTypes": [ + { + "itemId": 1, + "name": "select", + "label": "Select", + "category": "READ" + }, + { + "itemId": 2, + "name": "insert", + "label": "Insert", + "category": "UPDATE" + }, + { + "itemId": 3, + "name": "create", + "label": "Create", + "category": "CREATE" + }, + { + "itemId": 4, + "name": "drop", + "label": "Drop", + "category": "DELETE" + }, + { + "itemId": 5, + "name": "delete", + "label": "Delete", + "category": "DELETE" + }, + { + "itemId": 6, + "name": "use", + "label": "Use", + "category": "READ" + }, + { + "itemId": 7, + "name": "alter", + "label": "Alter", + "category": "CREATE" + }, + { + "itemId": 8, + "name": "grant", + "label": "Grant", + "category": "MANAGE" + }, + { + "itemId": 9, + "name": "revoke", + "label": "Revoke", + "category": "MANAGE" + }, + { + "itemId": 10, + "name": "show", + "label": "Show", + "category": "READ" + }, + { + "itemId": 11, + "name": "impersonate", + "label": "Impersonate", + "category": "READ" + }, + { + "itemId": 12, + "name": "all", + "label": "All", + "impliedGrants": [ + "select", + "insert", + "create", + "delete", + "drop", + "use", + "alter", + "grant", + "revoke", + "show", + "impersonate", + "execute", + "read_sysinfo", + "write_sysinfo" + ] + }, + { + "itemId": 13, + "name": "execute", + "label": "Execute", + "category": "READ" + }, + { + "itemId": 14, + "name": "read_sysinfo", + "label": "Read System Information", + "category": "MANAGE" + }, + { + "itemId": 15, + "name": "write_sysinfo", + "label": "Write System Information", + "category": "MANAGE" + } + ], + "configs": [ + { + "itemId": 1, + "name": "username", + "type": "string", + "mandatory": true, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Username" + }, + { + "itemId": 2, + "name": "password", + "type": "password", + "mandatory": false, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "Password" + }, + { + "itemId": 3, + "name": "jdbc.driverClassName", + "type": "string", + "mandatory": true, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "defaultValue": "io.trino.jdbc.TrinoDriver" + }, + { + "itemId": 4, + "name": "jdbc.url", + "type": "string", + "mandatory": true, + "defaultValue": "", + "validationRegEx": "", + "validationMessage": "", + "uiHint": "" + }, + { + "itemId": 5, + "name": "ranger.plugin.audit.filters", + "type": "string", + "defaultValue": "[{'accessResult':'DENIED','isAudited':true},{'isAudited':false,'resources':{'queryid':{'values':['*']}},'accessTypes':['execute']},{'isAudited':false,'resources':{'trinouser':{'values':['{USER}']}},'accessTypes':['impersonate']}]" + }, + { + "itemId": 6, + "name": "ranger.plugin.super.users", + "label": "Superusers", + "description": "Superusers will have full access to all resources in this Trino instance", + "type": "string", + "defaultValue": "trino" + }, + { + "itemId": 7, + "name": "ranger.plugin.super.groups", + "label": "Superuser groups", + "description": "Users in superuser groups will have full access to all resources in this Trino instance", + "type": "string", + "defaultValue": "trino" + }, + { + "itemId": 8, + "name": "service.admin.users", + "label": "Service admin users", + "description": "Service admin users can create policies for any resource in this Trino instance", + "type": "string", + "defaultValue": "trino" + }, + { + "itemId": 9, + "name": "service.admin.groups", + "label": "Service admin usergroups", + "description": "Users in service admin usergroups can create policies for any resource in this Trino instance", + "type": "string", + "defaultValue": "trino" + } + ], + "enums": [ + ], + "contextEnrichers": [ + ], + "policyConditions": + [ + ], + "dataMaskDef": { + "accessTypes": [ + { + "name": "select" + } + ], + "resources": [ + { + "name": "catalog", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "uiHint":"{ \"singleValue\":true }" + }, + { + "name": "schema", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "uiHint":"{ \"singleValue\":true }" + }, + { + "name": "table", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "uiHint":"{ \"singleValue\":true }" + }, + { + "name": "column", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "uiHint":"{ \"singleValue\":true }" + } + ], + "maskTypes": [ + { + "itemId": 1, + "name": "MASK", + "label": "Redact", + "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", + "transformer": "cast(regexp_replace(regexp_replace(regexp_replace({col},'([A-Z])', 'X'),'([a-z])','x'),'([0-9])','0') as {type})", + "dataMaskOptions": { + } + }, + { + "itemId": 2, + "name": "MASK_SHOW_LAST_4", + "label": "Partial mask: show last 4", + "description": "Show last 4 characters; replace rest with 'X'", + "transformer": "cast(regexp_replace({col}, '(.*)(.{4}$)', x -> regexp_replace(x[1], '.', 'X') || x[2]) as {type})" + }, + { + "itemId": 3, + "name": "MASK_SHOW_FIRST_4", + "label": "Partial mask: show first 4", + "description": "Show first 4 characters; replace rest with 'x'", + "transformer": "cast(regexp_replace({col}, '(^.{4})(.*)', x -> x[1] || regexp_replace(x[2], '.', 'X')) as {type})" + }, + { + "itemId": 4, + "name": "MASK_HASH", + "label": "Hash", + "description": "Hash the value of a varchar with sha256", + "transformer": "cast(to_hex(sha256(to_utf8({col}))) as {type})" + }, + { + "itemId": 5, + "name": "MASK_NULL", + "label": "Nullify", + "description": "Replace with NULL" + }, + { + "itemId": 6, + "name": "MASK_NONE", + "label": "Unmasked (retain original value)", + "description": "No masking" + }, + { + "itemId": 12, + "name": "MASK_DATE_SHOW_YEAR", + "label": "Date: show only year", + "description": "Date: show only year", + "transformer": "date_trunc('year', {col})" + }, + { + "itemId": 13, + "name": "CUSTOM", + "label": "Custom", + "description": "Custom" + } + ] + }, + "rowFilterDef": { + "accessTypes": [ + { + "name": "select" + } + ], + "resources": [ + { + "name": "catalog", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "mandatory": true, + "uiHint": "{ \"singleValue\":true }" + }, + { + "name": "schema", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "mandatory": true, + "uiHint": "{ \"singleValue\":true }" + }, + { + "name": "table", + "matcherOptions": { + "wildCard": "true" + }, + "lookupSupported": true, + "mandatory": true, + "uiHint": "{ \"singleValue\":true }" + } + ] + } + +} diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-wasb.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-wasb.json index fd30c51d8b..b8e55c8a40 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-wasb.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-wasb.json @@ -66,13 +66,15 @@ { "itemId": 1, "name": "read", - "label": "Read" + "label": "Read", + "category": "READ" }, { "itemId": 2, "name": "write", - "label": "Write" + "label": "Write", + "category": "UPDATE" } ], diff --git a/agents-common/src/main/resources/service-defs/ranger-servicedef-yarn.json b/agents-common/src/main/resources/service-defs/ranger-servicedef-yarn.json index 968f49e8bd..fd78e284a1 100644 --- a/agents-common/src/main/resources/service-defs/ranger-servicedef-yarn.json +++ b/agents-common/src/main/resources/service-defs/ranger-servicedef-yarn.json @@ -31,13 +31,15 @@ { "itemId": 1, "name": "submit-app", - "label": "submit-app" + "label": "submit-app", + "category": "UPDATE" }, { "itemId": 2, "name": "admin-queue", "label": "admin-queue", + "category": "MANAGE", "impliedGrants": [ "submit-app" @@ -93,7 +95,7 @@ "label": "Authentication Type", "defaultValue": "simple" }, - + { "itemId": 5, "name": "commonNameForCertificate", @@ -103,8 +105,21 @@ "validationMessage": "", "uiHint":"", "label": "Common Name for Certificate" + }, + + { + "itemId": 6, + "name": "ranger.plugin.audit.filters", + "type": "string", + "subType": "", + "mandatory": false, + "validationRegEx":"", + "validationMessage": "", + "uiHint":"", + "label": "Ranger Default Audit Filters", + "defaultValue": "[]" } - + ], "enums": diff --git a/agents-common/src/test/java/org/apache/ranger/authorization/utils/TestStringUtil.java b/agents-common/src/test/java/org/apache/ranger/authorization/utils/TestStringUtil.java new file mode 100644 index 0000000000..2f3caea9d5 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/authorization/utils/TestStringUtil.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.authorization.utils; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerSecurityZone; +import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService; +import org.apache.ranger.plugin.util.RangerSecurityZoneHelper; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class TestStringUtil { + @Test + public void testDedupString() { + Map strTbl = new HashMap<>(); + String s1 = getString("database"); + String s2 = getString("database"); + + // s1 and s2 point to different instances of String + Assert.assertNotSame("s1 != s2", s1, s2); + + // strTbl doesn't have s1; dedupString(s1) should return s1 + Assert.assertSame("s1 == dedupString(s1)", s1, StringUtil.dedupString(s1, strTbl)); + + // strTbl now has s1; s2 has same value as s1, hence dedupString() should return s1 + Assert.assertSame("s1 == dedupString(s2)", s1, StringUtil.dedupString(s2, strTbl)); + } + + @Test + public void testDedupStringsList() { + Map strTbl = new HashMap<>(); + List l1 = null; + + Assert.assertSame("null list - dedupStringsList() should return the same list", l1, StringUtil.dedupStringsList(l1, strTbl)); + + l1 = Collections.emptyList(); + Assert.assertSame("empty list - dedupStringsList() should return the same list", l1, StringUtil.dedupStringsList(l1, strTbl)); + + l1 = new ArrayList<>(); + Assert.assertSame("empty list - dedupStringsList() should return the same list", l1, StringUtil.dedupStringsList(l1, strTbl)); + + l1 = new ArrayList() {{ add(getString("*")); }}; + Assert.assertNotSame("non-empty list - dedupStringsList() should return a new list", l1, StringUtil.dedupStringsList(l1, strTbl)); + + l1 = new ArrayList() {{ add(getString("*")); add(getString("db1")); }}; + Assert.assertNotSame("non-empty list - dedupStringsList() should return a new list", l1, StringUtil.dedupStringsList(l1, strTbl)); + + List l2 = new ArrayList() {{ add(getString("*")); add(getString("db1")); }}; + + for (int i = 0; i < l1.size(); i++) { + Assert.assertNotSame("Before dedupStringsList(): l1[" + i + "] == l2[" + i + "]", l1.get(i), l2.get(i)); + } + + l1 = StringUtil.dedupStringsList(l1, strTbl); + l2 = StringUtil.dedupStringsList(l2, strTbl); + + for (int i = 0; i < l1.size(); i++) { + Assert.assertSame("After dedupStringsList(): l1[" + i + "] == l2[" + i + "]", l1.get(i), l2.get(i)); + } + } + + @Test + public void testDedupStringsSet() { + Map strTbl = new HashMap<>(); + Set s1 = null; + + Assert.assertSame("null set - dedupStringsList() should return the same set", s1, StringUtil.dedupStringsSet(s1, strTbl)); + + s1 = Collections.emptySet(); + Assert.assertSame("empty set - dedupStringsSet() should return the same set", s1, StringUtil.dedupStringsSet(s1, strTbl)); + + s1 = new HashSet<>(); + Assert.assertSame("empty set - dedupStringsSet() should return the same set", s1, StringUtil.dedupStringsSet(s1, strTbl)); + + s1 = new HashSet() {{ add(getString("*")); }}; + Assert.assertNotSame("non-empty set - dedupStringsSet() should return a new set", s1, StringUtil.dedupStringsSet(s1, strTbl)); + + s1 = new HashSet() {{ add(getString("*")); add(getString("db1")); }}; + Assert.assertNotSame("non-empty set - dedupStringsSet() should return a new set", s1, StringUtil.dedupStringsSet(s1, strTbl)); + + Set s2 = new HashSet() {{ add(getString("*")); add(getString("db1")); }}; + + for (String elem : s1) { + Assert.assertFalse("Before dedupStringsSet(): s1[" + elem + "] == s2[" + elem + "]", containsInstance(s2, elem)); + } + + s1 = StringUtil.dedupStringsSet(s1, strTbl); + s2 = StringUtil.dedupStringsSet(s2, strTbl); + + for (String elem : s1) { + Assert.assertTrue("After dedupStringsSet(): s1[" + elem + "] == s2[" + elem + "]", containsInstance(s2, elem)); + } + } + + @Test + public void testDedupStringsMap() { + Map strTbl = new HashMap<>(); + Map m1 = null; + + Assert.assertSame("null map - dedupStringsMap() should return the same map", m1, StringUtil.dedupStringsMap(m1, strTbl)); + + m1 = Collections.emptyMap(); + Assert.assertSame("empty map - dedupStringsMap() should return the same map", m1, StringUtil.dedupStringsMap(m1, strTbl)); + + m1 = new HashMap<>(); + Assert.assertSame("empty map - dedupStringsMap() should return the same map", m1, StringUtil.dedupStringsMap(m1, strTbl)); + + m1 = new HashMap() {{ put(getString("database"), getString("*")); }}; + Assert.assertNotSame("non-empty map - dedupStringsMap() should return a new map", m1, StringUtil.dedupStringsMap(m1, strTbl)); + + Map m2 = new HashMap() {{ put(getString("database"), getString("*")); }}; + + for (Map.Entry entry : m1.entrySet()) { + String key = entry.getKey(); + + Assert.assertFalse("Before dedupStringsMap(): m2 has same key as m1", containsInstance(m2.keySet(), key)); + Assert.assertNotSame("Before dedupStringsMap(): m1[" + key + "] == l2[" + key + "]", m1.get(key), m2.get(key)); + } + + m1 = StringUtil.dedupStringsMap(m1, strTbl); + m2 = StringUtil.dedupStringsMap(m2, strTbl); + + for (Map.Entry entry : m1.entrySet()) { + String key = entry.getKey(); + + Assert.assertTrue("After dedupStringsMap(): m2 has same key as m1", containsInstance(m2.keySet(), key)); + Assert.assertSame("After dedupStringsMap(): m1[" + key + "] == l2[" + key + "]", m1.get(key), m2.get(key)); + } + } + + @Test + public void testDedupMapOfPolicyResource() { + Map strTbl = new HashMap<>(); + Map m1 = null; + + Assert.assertSame("null map - dedupStringsMapOfPolicyResource() should return the same map", m1, StringUtil.dedupStringsMapOfPolicyResource(m1, strTbl)); + + m1 = Collections.emptyMap(); + Assert.assertSame("empty map - dedupStringsMapOfPolicyResource() should return the same map", m1, StringUtil.dedupStringsMapOfPolicyResource(m1, strTbl)); + + m1 = new HashMap<>(); + Assert.assertSame("empty map - dedupStringsMapOfPolicyResource() should return the same map", m1, StringUtil.dedupStringsMapOfPolicyResource(m1, strTbl)); + + m1 = new HashMap() {{ put(getString("database"), new RangerPolicyResource(getString("db1"))); put(getString("table"), new RangerPolicyResource(getString("*"))); }}; + Assert.assertNotSame("non-empty map - dedupStringsMapOfPolicyResource() should return a new map", m1, StringUtil.dedupStringsMapOfPolicyResource(m1, strTbl)); + + Map m2 = new HashMap() {{ put(getString("database"), new RangerPolicyResource(getString("db1"))); put(getString("table"), new RangerPolicyResource(getString("*"))); }}; + + for (Map.Entry entry : m1.entrySet()) { + String key = entry.getKey(); + RangerPolicyResource value1 = entry.getValue(); + RangerPolicyResource value2 = m2.get(key); + + Assert.assertFalse("Before dedupStringsMapOfPolicyResource(): m2 has same key as m1", containsInstance(m2.keySet(), key)); + + for (String value : value1.getValues()) { + Assert.assertFalse("Before dedupStringsMapOfPolicyResource(): m2.values not same values as m1.values for " + value, containsInstance(value2.getValues(), value)); + } + } + + m1 = StringUtil.dedupStringsMapOfPolicyResource(m1, strTbl); + m2 = StringUtil.dedupStringsMapOfPolicyResource(m2, strTbl); + + for (Map.Entry entry : m1.entrySet()) { + String key = entry.getKey(); + RangerPolicyResource value1 = entry.getValue(); + RangerPolicyResource value2 = m2.get(key); + + Assert.assertTrue("After dedupStringsMapOfPolicyResource(): m2 has same key as m1", containsInstance(m2.keySet(), key)); + + for (String value : value1.getValues()) { + Assert.assertTrue("After dedupStringsMapOfPolicyResource(): m2.values has same values as m1.values for " + value, containsInstance(value2.getValues(), value)); + } + } + } + + @Test + public void testJsonCompression() throws IOException { + int[] sizeFactors = new int[] { 1, 10, 50, 100, 250, 300, 400, 500 }; + + for (int sizeFactor : sizeFactors) { + RangerSecurityZone zone = generateLargeSecurityZone(sizeFactor); + String json = JsonUtils.objectToJson(zone); + String compressed = StringUtil.compressString(json); + String deCompressed = StringUtil.decompressString(compressed); + + System.out.println(String.format("%d: resourceCount=%d: len(json)=%,d, len(compressed)=%,d, savings=(%,d == %.03f%%)", sizeFactor, getResourceCount(zone), json.length(), compressed.length(), (json.length() - compressed.length()), ((json.length() - compressed.length()) / (float) json.length()) * 100)); + + Assert.assertTrue(compressed.length() < deCompressed.length()); + + Assert.assertEquals(json, deCompressed); + } + } + + private boolean containsInstance(Collection coll, String key) { + boolean ret = false; + + if (coll != null) { + for (String elem : coll) { + if (elem == key) { + ret = true; + + break; + } + } + } + + return ret; + } + + private String getString(String str) { + return str == null ? str : new String(str); + } + + private RangerSecurityZone generateLargeSecurityZone(int sizeFactor) { + RangerSecurityZone zone = new RangerSecurityZone(); + int svcCount = sizeFactor; + int resourceCount = sizeFactor; + int resNameLen = (sizeFactor / 10) + 1; + + zone.setName("test-zone"); + zone.setDescription("this is a test zone"); + zone.setTagServices(generateStrings("tag-service-", 25, 1)); + zone.setAdminUsers(generateStrings("admin-", 20, 10)); + zone.setAdminUserGroups(generateStrings("admin-group-", 20, 5)); + zone.setAdminRoles(generateStrings("admin-role-", 20, 5)); + zone.setAuditUsers(generateStrings("audit-", 20, 10)); + zone.setAuditUserGroups(generateStrings("audit-group-", 20, 5)); + zone.setAuditRoles(generateStrings("audit-role-", 20, 5)); + + for (int i = 0; i < svcCount; i++) { + RangerSecurityZoneService svc = new RangerSecurityZoneService(); + + for (int j = 0; j < resourceCount; j++) { + HashMap> resource = new HashMap<>(); + + resource.put("database", generateStrings("db-", resNameLen, 1)); + resource.put("table", generateStrings("tbl-", resNameLen, 2)); + resource.put("column", generateStrings("col-", resNameLen, 3)); + + svc.getResources().add(resource); + } + + zone.getServices().put("service-" + i, svc); + } + + return new RangerSecurityZoneHelper(zone, "testUser").getZone(); // add resourcesBaseInfo + } + + private int getResourceCount(RangerSecurityZone zone) { + int ret = 0; + + for (RangerSecurityZone.RangerSecurityZoneService svc : zone.getServices().values()) { + ret += svc.getResources().size(); + } + + return ret; + } + + private List generateStrings(String prefix, int maxLen, int count) { + List ret = new ArrayList<>(count); + + for (int i = 0; i < count; i++) { + ret.add(generateResourceName(prefix, maxLen)); + } + + return ret; + } + + private String generateResourceName(String prefix, int maxLen) { + return prefix.length() < maxLen ? (prefix + RandomStringUtils.random(maxLen - prefix.length(), true, true)) : prefix; + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java index 2c708d7b63..0c5e7fab72 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerCustomConditionMatcherTest.java @@ -21,23 +21,24 @@ import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; import org.apache.ranger.plugin.policyengine.RangerAccessResource; import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerUserStore; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.UUID; +import java.util.*; +import static org.apache.ranger.plugin.util.RangerCommonConstants.SCRIPT_OPTION_ENABLE_JSON_CTX; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -51,6 +52,63 @@ public void setUp() throws Exception { public void tearDown() throws Exception { } + @Test + public void testScriptConditionEvaluator() { + RangerAccessRequest request = createRequest(Arrays.asList("PCI", "PII")); + + RangerScriptConditionEvaluator resourceDbCondition = createScriptConditionEvaluator("_ctx.request.resource.database == 'db1'"); + RangerScriptConditionEvaluator resourceDbCondition2 = createScriptConditionEvaluator("_ctx.request.resource.database != 'db2'"); + RangerScriptConditionEvaluator resourceTblCondition = createScriptConditionEvaluator("_ctx.request.resource.table == 'tbl1'"); + RangerScriptConditionEvaluator resourceColCondition = createScriptConditionEvaluator("_ctx.request.resource.column == 'col1'"); + RangerScriptConditionEvaluator accessTypeCondition = createScriptConditionEvaluator("_ctx.request.accessType == 'select'"); + RangerScriptConditionEvaluator actionCondition = createScriptConditionEvaluator("_ctx.request.action == 'query'"); + RangerScriptConditionEvaluator userCondition = createScriptConditionEvaluator("_ctx.request.user == 'test-user'"); + RangerScriptConditionEvaluator userGroupsLenCondition = createScriptConditionEvaluator("_ctx.request.userGroups.length == 2"); + RangerScriptConditionEvaluator userGroupsHas1Condition = createScriptConditionEvaluator("_ctx.request.userGroups.indexOf('test-group1') != -1"); + RangerScriptConditionEvaluator userGroupsHas2Condition = createScriptConditionEvaluator("_ctx.request.userGroups.indexOf('test-group2') != -1"); + RangerScriptConditionEvaluator userRolesLenCondition = createScriptConditionEvaluator("_ctx.request.userRoles.length == 2"); + RangerScriptConditionEvaluator userRolesHas1Condition = createScriptConditionEvaluator("_ctx.request.userRoles.indexOf('test-role1') != -1"); + RangerScriptConditionEvaluator userRolesHas2Condition = createScriptConditionEvaluator("_ctx.request.userRoles.indexOf('test-role2') != -1"); + RangerScriptConditionEvaluator userAttrLenCondition = createScriptConditionEvaluator("Object.keys(_ctx.request.userAttributes).length == 3"); + RangerScriptConditionEvaluator userAttr1Condition = createScriptConditionEvaluator("_ctx.request.userAttributes['attr1'] == 'test-user-value1'"); + RangerScriptConditionEvaluator userAttr2Condition = createScriptConditionEvaluator("_ctx.request.userAttributes['attr2'] == 'test-user-value2'"); + RangerScriptConditionEvaluator userGroup1Attr1Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group1']['attr1'] == 'test-group1-value1'"); + RangerScriptConditionEvaluator userGroup1Attr2Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group1']['attr2'] == 'test-group1-value2'"); + RangerScriptConditionEvaluator userGroup2Attr1Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group2']['attr1'] == 'test-group2-value1'"); + RangerScriptConditionEvaluator userGroup2Attr2Condition = createScriptConditionEvaluator("_ctx.request.userGroupAttributes['test-group2']['attr2'] == 'test-group2-value2'"); + RangerScriptConditionEvaluator tagsLengthCondition = createScriptConditionEvaluator("Object.keys(_ctx.tags).length == 2"); + RangerScriptConditionEvaluator tagTypeCondition = createScriptConditionEvaluator("_ctx.tag._type == 'PCI'"); + RangerScriptConditionEvaluator tagAttributesCondition = createScriptConditionEvaluator("_ctx.tag.attr1 == 'PCI_value'"); + RangerScriptConditionEvaluator tagsTypeCondition = createScriptConditionEvaluator("_ctx.tags['PII']._type == 'PII' && _ctx.tags['PCI']._type == 'PCI'"); + RangerScriptConditionEvaluator tagsAttributesCondition = createScriptConditionEvaluator("_ctx.tags['PII'].attr1 == 'PII_value' && _ctx.tags['PCI'].attr1 == 'PCI_value'"); + + Assert.assertTrue("request.resource.database should be db1", resourceDbCondition.isMatched(request)); + Assert.assertTrue("request.resource.database should not be db2", resourceDbCondition2.isMatched(request)); + Assert.assertTrue("request.resource.table should be tbl1", resourceTblCondition.isMatched(request)); + Assert.assertTrue("request.resource.column should be col1", resourceColCondition.isMatched(request)); + Assert.assertTrue("request.accessType should be select", accessTypeCondition.isMatched(request)); + Assert.assertTrue("request.action should be query", actionCondition.isMatched(request)); + Assert.assertTrue("request.user should be testUser", userCondition.isMatched(request)); + Assert.assertTrue("request.userGroups should have 2 entries", userGroupsLenCondition.isMatched(request)); + Assert.assertTrue("request.userGroups should have test-group1", userGroupsHas1Condition.isMatched(request)); + Assert.assertTrue("request.userGroups should have test-group2", userGroupsHas2Condition.isMatched(request)); + Assert.assertTrue("request.userRoles should have 2 entries", userRolesLenCondition.isMatched(request)); + Assert.assertTrue("request.userRoles should have test-role1", userRolesHas1Condition.isMatched(request)); + Assert.assertTrue("request.userRoles should have test-role2", userRolesHas2Condition.isMatched(request)); + Assert.assertTrue("request.userAttributes should have 3 entries", userAttrLenCondition.isMatched(request)); + Assert.assertTrue("request.userAttributes[attr1] should be test-user-value1", userAttr1Condition.isMatched(request)); + Assert.assertTrue("request.userAttributes[attr2] should be test-user-value2", userAttr2Condition.isMatched(request)); + Assert.assertTrue("request.userGroup1Attributes[attr1] should be test-group1-value1", userGroup1Attr1Condition.isMatched(request)); + Assert.assertTrue("request.userGroup1Attributes[attr2] should be test-group1-value2", userGroup1Attr2Condition.isMatched(request)); + Assert.assertTrue("request.userGroup2Attributes[attr1] should be test-group2-value1", userGroup2Attr1Condition.isMatched(request)); + Assert.assertTrue("request.userGroup2Attributes[attr2] should be test-group2-value2", userGroup2Attr2Condition.isMatched(request)); + Assert.assertTrue("tag._type should be PCI", tagTypeCondition.isMatched(request)); + Assert.assertTrue("tag.attr1 should be PCI_value", tagAttributesCondition.isMatched(request)); + Assert.assertTrue("should have 2 tags", tagsLengthCondition.isMatched(request)); + Assert.assertTrue("tags PCI and PII should be found", tagsTypeCondition.isMatched(request)); + Assert.assertTrue("tag attributes for PCI and PII should be found", tagsAttributesCondition.isMatched(request)); + } + @Test public void testRangerAnyOfExpectedTagsPresentConditionEvaluator() { List policyConditionTags = Arrays.asList("PCI", "PII"); @@ -167,22 +225,95 @@ RangerNoneOfExpectedTagsPresentConditionEvaluator createRangerTagsNotPresentCond return matcher; } + RangerScriptConditionEvaluator createScriptConditionEvaluator(String script) { + RangerScriptConditionEvaluator ret = new RangerScriptConditionEvaluator(); + + RangerServiceDef serviceDef = mock(RangerServiceDef.class); + RangerPolicyConditionDef conditionDef = mock(RangerPolicyConditionDef.class); + RangerPolicyItemCondition condition = mock(RangerPolicyItemCondition.class); + + when(serviceDef.getName()).thenReturn("test"); + when(conditionDef.getEvaluatorOptions()).thenReturn(Collections.singletonMap(SCRIPT_OPTION_ENABLE_JSON_CTX, "true")); + when(condition.getValues()).thenReturn(Arrays.asList(script)); + + ret.setServiceDef(serviceDef); + ret.setConditionDef(conditionDef); + ret.setPolicyItemCondition(condition); + + ret.init(); + + return ret; + } + RangerAccessRequest createRequest(List resourceTags) { - RangerAccessResource resource = mock(RangerAccessResource.class); - RangerAccessRequest request = new RangerAccessRequestImpl(resource,"dummy","test", null, null); - Set rangerTagForEvals = new HashSet<>(); - RangerPolicyResourceMatcher.MatchType matchType = RangerPolicyResourceMatcher.MatchType.NONE; + RangerAccessResource resource = mock(RangerAccessResource.class); + + Map resourceMap = new HashMap<>(); + + resourceMap.put("database", "db1"); + resourceMap.put("table", "tbl1"); + resourceMap.put("column", "col1"); + + when(resource.getAsString()).thenReturn("db1/tbl1/col1"); + when(resource.getOwnerUser()).thenReturn("testUser"); + when(resource.getAsMap()).thenReturn(resourceMap); + when(resource.getReadOnlyCopy()).thenReturn(resource); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + + request.setResource(resource); + request.setResourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF); + request.setAccessType("select"); + request.setAction("query"); + request.setUser("test-user"); + request.setUserGroups(new HashSet<>(Arrays.asList("test-group1", "test-group2"))); + request.setUserRoles(new HashSet<>(Arrays.asList("test-role1", "test-role2"))); if (resourceTags != null) { + Set rangerTagForEvals = new HashSet<>(); + RangerTagForEval currentTag = null; + for (String resourceTag : resourceTags) { - RangerTag tag = new RangerTag(UUID.randomUUID().toString(), resourceTag, null, null, null, null); - rangerTagForEvals.add(new RangerTagForEval(tag, matchType)); + RangerTag tag = new RangerTag(UUID.randomUUID().toString(), resourceTag, Collections.singletonMap("attr1", resourceTag + "_value"), null, null, null); + RangerTagForEval tagForEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF); + + rangerTagForEvals.add(tagForEval); + + if (currentTag == null) { + currentTag = tagForEval; + } } - request.getContext().put("TAGS", rangerTagForEvals); + + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), rangerTagForEvals); + RangerAccessRequestUtil.setCurrentTagInContext(request.getContext(), currentTag); } else { - request.getContext().put("TAGS", null); + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), null); } + Map> userAttrMapping = new HashMap<>(); + Map> groupAttrMapping = new HashMap<>(); + Map testUserAttrs = new HashMap<>(); + Map testGroup1Attrs = new HashMap<>(); + Map testGroup2Attrs = new HashMap<>(); + + testUserAttrs.put("attr1", "test-user-value1"); + testUserAttrs.put("attr2", "test-user-value2"); + testGroup1Attrs.put("attr1", "test-group1-value1"); + testGroup1Attrs.put("attr2", "test-group1-value2"); + testGroup2Attrs.put("attr1", "test-group2-value1"); + testGroup2Attrs.put("attr2", "test-group2-value2"); + + userAttrMapping.put("test-user", testUserAttrs); + groupAttrMapping.put("test-group1", testGroup1Attrs); + groupAttrMapping.put("test-group2", testGroup2Attrs); + + RangerUserStore userStore = mock(RangerUserStore.class); + + when(userStore.getUserAttrMapping()).thenReturn(userAttrMapping); + when(userStore.getGroupAttrMapping()).thenReturn(groupAttrMapping); + + RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore); + return request; } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java new file mode 100644 index 0000000000..0059bef883 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerRequestScriptEvaluatorTest.java @@ -0,0 +1,533 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.conditionevaluator; + +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.model.RangerTag; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyengine.RangerRequestScriptEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerUserStore; +import org.apache.ranger.plugin.util.ScriptEngineUtil; +import org.junit.Assert; +import org.junit.Test; + +import javax.script.ScriptEngine; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class RangerRequestScriptEvaluatorTest { + final ScriptEngine scriptEngine = ScriptEngineUtil.createScriptEngine(null); + + @Test + public void testRequestAttributes() { + RangerTag tagPII = new RangerTag("PII", Collections.singletonMap("attr1", "PII_value")); + RangerTag tagPCI = new RangerTag("PCI", Collections.singletonMap("attr1", "PCI_value")); + RangerAccessRequest request = createRequest("test-user", new HashSet<>(Arrays.asList("test-group1", "test-group2")), new HashSet<>(Arrays.asList("test-role1", "test-role2")), Arrays.asList(tagPII, tagPCI)); + RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request, scriptEngine); + + Assert.assertEquals("test: UG_NAMES_CSV", "test-group1,test-group2", evaluator.evaluateScript("UG_NAMES_CSV")); + Assert.assertEquals("test: UR_NAMES_CSV", "test-role1,test-role2", evaluator.evaluateScript("UR_NAMES_CSV")); + Assert.assertEquals("test: TAG_NAMES_CSV", "PCI,PII", evaluator.evaluateScript("TAG_NAMES_CSV")); + Assert.assertEquals("test: USER_ATTR_NAMES_CSV", "state", evaluator.evaluateScript("USER_ATTR_NAMES_CSV")); + Assert.assertEquals("test: UG_ATTR_NAMES_CSV", "dept,site", evaluator.evaluateScript("UG_ATTR_NAMES_CSV")); + Assert.assertEquals("test: TAG_ATTR_NAMES_CSV", "attr1", evaluator.evaluateScript("TAG_ATTR_NAMES_CSV")); + Assert.assertEquals("test: GET_UG_ATTR_CSV('dept')", "ENGG,PROD", evaluator.evaluateScript("GET_UG_ATTR_CSV('dept')")); + Assert.assertEquals("test: GET_UG_ATTR_CSV('site')", "10,20", evaluator.evaluateScript("GET_UG_ATTR_CSV('site')")); + Assert.assertEquals("test: GET_TAG_ATTR_CSV('attr1')", "PCI_value,PII_value", evaluator.evaluateScript("GET_TAG_ATTR_CSV('attr1')")); + + Assert.assertEquals("test: UG_NAMES_Q_CSV", "'test-group1','test-group2'", evaluator.evaluateScript("UG_NAMES_Q_CSV")); + Assert.assertEquals("test: UR_NAMES_Q_CSV", "'test-role1','test-role2'", evaluator.evaluateScript("UR_NAMES_Q_CSV")); + Assert.assertEquals("test: TAG_NAMES_Q_CSV", "'PCI','PII'", evaluator.evaluateScript("TAG_NAMES_Q_CSV")); + Assert.assertEquals("test: USER_ATTR_NAMES_Q_CSV", "'state'", evaluator.evaluateScript("USER_ATTR_NAMES_Q_CSV")); + Assert.assertEquals("test: UG_ATTR_NAMES_Q_CSV", "'dept','site'", evaluator.evaluateScript("UG_ATTR_NAMES_Q_CSV")); + Assert.assertEquals("test: TAG_ATTR_NAMES_Q_CSV", "'attr1'", evaluator.evaluateScript("TAG_ATTR_NAMES_Q_CSV")); + Assert.assertEquals("test: GET_UG_ATTR_Q_CSV('dept')", "'ENGG','PROD'", evaluator.evaluateScript("GET_UG_ATTR_Q_CSV('dept')")); + Assert.assertEquals("test: GET_UG_ATTR_Q_CSV('site')", "'10','20'", evaluator.evaluateScript("GET_UG_ATTR_Q_CSV('site')")); + Assert.assertEquals("test: GET_TAG_ATTR_Q_CSV('attr1')", "'PCI_value','PII_value'", evaluator.evaluateScript("GET_TAG_ATTR_Q_CSV('attr1')")); + + Assert.assertTrue("test: USER._name is 'test-user'", (Boolean) evaluator.evaluateScript("USER._name == 'test-user'")); + Assert.assertTrue("test: HAS_USER_ATTR(state)", (Boolean)evaluator.evaluateScript("HAS_USER_ATTR('state')")); + Assert.assertFalse("test: HAS_USER_ATTR(notExists)", (Boolean)evaluator.evaluateScript("HAS_USER_ATTR('notExists')")); + Assert.assertTrue("test: USER['state'] is 'CA'", (Boolean) evaluator.evaluateScript("USER['state'] == 'CA'")); + Assert.assertTrue("test: USER.state is 'CA'", (Boolean) evaluator.evaluateScript("USER.state == 'CA'")); + + Assert.assertTrue("test: IS_IN_GROUP(test-group1)", (Boolean)evaluator.evaluateScript("IS_IN_GROUP('test-group1')")); + Assert.assertTrue("test: IS_IN_GROUP(test-group2)", (Boolean)evaluator.evaluateScript("IS_IN_GROUP('test-group2')")); + Assert.assertFalse("test: IS_IN_GROUP(notExists)", (Boolean)evaluator.evaluateScript("IS_IN_GROUP('notExists')")); + Assert.assertTrue("test: IS_IN_ANY_GROUP", (Boolean)evaluator.evaluateScript("IS_IN_ANY_GROUP")); + Assert.assertFalse("test: IS_NOT_IN_ANY_GROUP", (Boolean)evaluator.evaluateScript("IS_NOT_IN_ANY_GROUP")); + + Assert.assertTrue("test: UG['test-group1'].dept is 'ENGG'", (Boolean) evaluator.evaluateScript("UG['test-group1'].dept == 'ENGG'")); + Assert.assertTrue("test: UG['test-group1'].site is 10", (Boolean) evaluator.evaluateScript("UG['test-group1'].site == 10")); + Assert.assertTrue("test: UG['test-group2'].dept is 'PROD'", (Boolean) evaluator.evaluateScript("UG['test-group2'].dept == 'PROD'")); + Assert.assertTrue("test: UG['test-group2'].site is 20", (Boolean) evaluator.evaluateScript("UG['test-group2'].site == 20")); + Assert.assertTrue("test: UG['test-group3'] is null", (Boolean) evaluator.evaluateScript("UG['test-group3'] == null")); + Assert.assertTrue("test: UG['test-group1'].notExists is null", (Boolean) evaluator.evaluateScript("UG['test-group1'].notExists == null")); + + Assert.assertTrue("test: IS_IN_ROLE(test-role1)", (Boolean)evaluator.evaluateScript("IS_IN_ROLE('test-role1')")); + Assert.assertTrue("test: IS_IN_ROLE(test-role2)", (Boolean)evaluator.evaluateScript("IS_IN_ROLE('test-role2')")); + Assert.assertFalse("test: IS_IN_ROLE(notExists)", (Boolean)evaluator.evaluateScript("IS_IN_ROLE('notExists')")); + Assert.assertTrue("test: IS_IN_ANY_ROLE", (Boolean)evaluator.evaluateScript("IS_IN_ANY_ROLE")); + Assert.assertFalse("test: IS_NOT_IN_ANY_ROLE", (Boolean)evaluator.evaluateScript("IS_NOT_IN_ANY_ROLE")); + + Assert.assertTrue("test: UGA.sVal['dept'] is 'ENGG'", (Boolean)evaluator.evaluateScript("UGA.sVal['dept'] == 'ENGG'")); + Assert.assertTrue("test: UGA.sVal['site'] is 10", (Boolean) evaluator.evaluateScript("UGA.sVal['site'] == 10")); + Assert.assertTrue("test: UGA.sVal['notExists'] is null", (Boolean) evaluator.evaluateScript("UGA.sVal['notExists'] == null")); + Assert.assertTrue("test: UGA.mVal['dept'] is [\"ENGG\", \"PROD\"]", (Boolean) evaluator.evaluateScript("J(UGA.mVal['dept']) == '[\"ENGG\",\"PROD\"]'")); + Assert.assertTrue("test: UGA.mVal['site'] is [10, 20]", (Boolean) evaluator.evaluateScript("J(UGA.mVal['site']) == '[\"10\",\"20\"]'")); + Assert.assertTrue("test: UGA.mVal['notExists'] is null", (Boolean) evaluator.evaluateScript("UGA.mVal['notExists'] == null")); + Assert.assertTrue("test: UGA.mVal['dept'] has 'ENGG'", (Boolean) evaluator.evaluateScript("UGA.mVal['dept'].indexOf('ENGG') != -1")); + Assert.assertTrue("test: UGA.mVal['dept'] has 'PROD'", (Boolean) evaluator.evaluateScript("UGA.mVal['dept'].indexOf('PROD') != -1")); + Assert.assertTrue("test: UGA.mVal['dept'] doesn't have 'EXEC'", (Boolean) evaluator.evaluateScript("UGA.mVal['dept'].indexOf('EXEC') == -1")); + Assert.assertTrue("test: HAS_UG_ATTR(dept)", (Boolean)evaluator.evaluateScript("HAS_UG_ATTR('dept')")); + Assert.assertTrue("test: HAS_UG_ATTR(site)", (Boolean)evaluator.evaluateScript("HAS_UG_ATTR('site')")); + Assert.assertFalse("test: HAS_UG_ATTR(notExists)", (Boolean)evaluator.evaluateScript("HAS_UG_ATTR('notExists')")); + + Assert.assertTrue("test: REQ.accessTyp is 'select'", (Boolean) evaluator.evaluateScript("REQ.accessType == 'select'")); + Assert.assertTrue("test: REQ.action is 'query'", (Boolean) evaluator.evaluateScript("REQ.action == 'query'")); + + Assert.assertTrue("test: RES._ownerUser is 'testUser'", (Boolean) evaluator.evaluateScript("RES._ownerUser == 'testUser'")); + Assert.assertTrue("test: RES.database is 'db1'", (Boolean) evaluator.evaluateScript("RES.database == 'db1'")); + Assert.assertTrue("test: RES.table is 'tbl1'", (Boolean) evaluator.evaluateScript("RES.table == 'tbl1'")); + Assert.assertTrue("test: RES.column is 'col1'", (Boolean) evaluator.evaluateScript("RES.column == 'col1'")); + + Assert.assertTrue("test: TAG._type is 'PII'", (Boolean) evaluator.evaluateScript("TAG._type == 'PII'")); + Assert.assertTrue("test: TAG.attr1 is 'PII_value'", (Boolean) evaluator.evaluateScript("TAG.attr1 == 'PII_value'")); + Assert.assertTrue("test: TAGS.length is 2", (Boolean) evaluator.evaluateScript("Object.keys(TAGS).length == 2")); + Assert.assertEquals("test: TAG PII has attr1=PII_value", evaluator.evaluateScript("TAGS['PII'].attr1"), "PII_value"); + Assert.assertEquals("test: TAG PCI has attr1=PCI_value", evaluator.evaluateScript("TAGS['PCI'].attr1"), "PCI_value"); + Assert.assertTrue("test: TAG PII doesn't have PII.notExists", (Boolean) evaluator.evaluateScript("TAGS['PII'].notExists == undefined")); + Assert.assertTrue("test: HAS_TAG_ATTR(attr1)", (Boolean) evaluator.evaluateScript("HAS_TAG_ATTR('attr1')")); + Assert.assertFalse("test: HAS_TAG_ATTR(notExists)", (Boolean) evaluator.evaluateScript("HAS_TAG_ATTR('notExists')")); + + Assert.assertTrue("test: TAGNAMES.length is 2", (Boolean) evaluator.evaluateScript("TAGNAMES.length == 2")); + Assert.assertTrue("test: HAS_TAG(PII)", (Boolean) evaluator.evaluateScript("HAS_TAG('PII')")); + Assert.assertTrue("test: HAS_TAG(PCI)", (Boolean) evaluator.evaluateScript("HAS_TAG('PCI')")); + Assert.assertFalse("test: HAS_TAG(notExists)", (Boolean) evaluator.evaluateScript("HAS_TAG('notExists')")); + Assert.assertTrue("test: HAS_ANY_TAG", (Boolean) evaluator.evaluateScript("HAS_ANY_TAG")); + Assert.assertFalse("test: HAS_NO_TAG", (Boolean) evaluator.evaluateScript("HAS_NO_TAG")); + + Assert.assertEquals("GET_TAG_NAMES()", "PCI,PII", evaluator.evaluateScript("GET_TAG_NAMES()")); + Assert.assertEquals("GET_TAG_NAMES(null)", "PCI,PII", evaluator.evaluateScript("GET_TAG_NAMES(null)")); + Assert.assertEquals("GET_TAG_NAMES(null, '|')", "PCI|PII", evaluator.evaluateScript("GET_TAG_NAMES(null, '|')")); + Assert.assertEquals("GET_TAG_NAMES(null, null)", "PCIPII", evaluator.evaluateScript("GET_TAG_NAMES(null, null)")); + + Assert.assertEquals("GET_TAG_NAMES_Q()", "'PCI','PII'", evaluator.evaluateScript("GET_TAG_NAMES_Q()")); + Assert.assertEquals("GET_TAG_NAMES_Q(null)", "'PCI','PII'", evaluator.evaluateScript("GET_TAG_NAMES_Q(null)")); + Assert.assertEquals("GET_TAG_NAMES_Q(null, '|')", "'PCI'|'PII'", evaluator.evaluateScript("GET_TAG_NAMES_Q(null, '|')")); + Assert.assertEquals("GET_TAG_NAMES_Q(null, null)", "'PCI''PII'", evaluator.evaluateScript("GET_TAG_NAMES_Q(null, null)")); + Assert.assertEquals("GET_TAG_NAMES_Q(null, '|', null)", "PCI|PII", evaluator.evaluateScript("GET_TAG_NAMES_Q(null, '|', null)")); + Assert.assertEquals("GET_TAG_NAMES_Q(null, ',', '{', '}')", "{PCI},{PII}", evaluator.evaluateScript("GET_TAG_NAMES_Q(null, ',', '{', '}')")); + + Assert.assertEquals("GET_TAG_ATTR_NAMES()", "attr1", evaluator.evaluateScript("GET_TAG_ATTR_NAMES()")); + Assert.assertEquals("GET_TAG_ATTR_NAMES(null)", "attr1", evaluator.evaluateScript("GET_TAG_ATTR_NAMES(null)")); + Assert.assertEquals("GET_TAG_ATTR_NAMES(null, '|',)", "attr1", evaluator.evaluateScript("GET_TAG_ATTR_NAMES(null, '|')")); + Assert.assertEquals("GET_TAG_ATTR_NAMES(null, null)", "attr1", evaluator.evaluateScript("GET_TAG_ATTR_NAMES(null, null)")); + + Assert.assertEquals("GET_TAG_ATTR_NAMES_Q()", "'attr1'", evaluator.evaluateScript("GET_TAG_ATTR_NAMES_Q()")); + Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null)", "'attr1'", evaluator.evaluateScript("GET_TAG_ATTR_NAMES_Q(null)")); + Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, '|')", "'attr1'", evaluator.evaluateScript("GET_TAG_ATTR_NAMES_Q(null, '|')")); + Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, null)", "'attr1'", evaluator.evaluateScript("GET_TAG_ATTR_NAMES_Q(null, null)")); + Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, '|', null)", "attr1", evaluator.evaluateScript("GET_TAG_ATTR_NAMES_Q(null, '|', null)")); + Assert.assertEquals("GET_TAG_ATTR_NAMES_Q(null, ',', '{', '}')", "{attr1}", evaluator.evaluateScript("GET_TAG_ATTR_NAMES_Q(null, ',', '{', '}')")); + + Assert.assertEquals("GET_TAG_ATTR('attr1')", "PCI_value,PII_value", evaluator.evaluateScript("GET_TAG_ATTR('attr1')")); + Assert.assertEquals("GET_TAG_ATTR('attr1', null)", "PCI_value,PII_value", evaluator.evaluateScript("GET_TAG_ATTR('attr1', null)")); + Assert.assertEquals("GET_TAG_ATTR('attr1', null, '|')", "PCI_value|PII_value", evaluator.evaluateScript("GET_TAG_ATTR('attr1', null, '|')")); + Assert.assertEquals("GET_TAG_ATTR('attr1', null, null)", "PCI_valuePII_value", evaluator.evaluateScript("GET_TAG_ATTR('attr1', null, null)")); + + Assert.assertEquals("GET_TAG_ATTR_Q('attr1')", "'PCI_value','PII_value'", evaluator.evaluateScript("GET_TAG_ATTR_Q('attr1')")); + Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null)", "'PCI_value','PII_value'", evaluator.evaluateScript("GET_TAG_ATTR_Q('attr1', null)")); + Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, null)", "'PCI_value''PII_value'", evaluator.evaluateScript("GET_TAG_ATTR_Q('attr1', null, null)")); + Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, '|')", "'PCI_value'|'PII_value'", evaluator.evaluateScript("GET_TAG_ATTR_Q('attr1', null, '|')")); + Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, ',', null)", "PCI_value,PII_value", evaluator.evaluateScript("GET_TAG_ATTR_Q('attr1', null, ',', null)")); + Assert.assertEquals("GET_TAG_ATTR_Q('attr1', null, ',', '{', '}')", "{PCI_value},{PII_value}", evaluator.evaluateScript("GET_TAG_ATTR_Q('attr1', null, ',', '{', '}')")); + + Assert.assertEquals("GET_UG_NAMES()", "test-group1,test-group2", evaluator.evaluateScript("GET_UG_NAMES()")); + Assert.assertEquals("GET_UG_NAMES(null)", "test-group1,test-group2", evaluator.evaluateScript("GET_UG_NAMES(null)")); + Assert.assertEquals("GET_UG_NAMES(null, '|')", "test-group1|test-group2", evaluator.evaluateScript("GET_UG_NAMES(null, '|')")); + Assert.assertEquals("GET_UG_NAMES(null, null)", "test-group1test-group2", evaluator.evaluateScript("GET_UG_NAMES(null, null)")); + + Assert.assertEquals("GET_UG_NAMES_Q()", "'test-group1','test-group2'", evaluator.evaluateScript("GET_UG_NAMES_Q()")); + Assert.assertEquals("GET_UG_NAMES_Q(null)", "'test-group1','test-group2'", evaluator.evaluateScript("GET_UG_NAMES_Q(null)")); + Assert.assertEquals("GET_UG_NAMES_Q(null, null)", "'test-group1''test-group2'", evaluator.evaluateScript("GET_UG_NAMES_Q(null, null)")); + Assert.assertEquals("GET_UG_NAMES_Q(null, '|')", "'test-group1'|'test-group2'", evaluator.evaluateScript("GET_UG_NAMES_Q(null, '|')")); + Assert.assertEquals("GET_UG_NAMES_Q(null, ',', null)", "test-group1,test-group2", evaluator.evaluateScript("GET_UG_NAMES_Q(null, ',', null)")); + Assert.assertEquals("GET_UG_NAMES_Q(null, ',', '{', '}')", "{test-group1},{test-group2}", evaluator.evaluateScript("GET_UG_NAMES_Q(null, ',', '{', '}')")); + + Assert.assertEquals("GET_UG_ATTR_NAMES()", "dept,site", evaluator.evaluateScript("GET_UG_ATTR_NAMES()")); + Assert.assertEquals("GET_UG_ATTR_NAMES(null)", "dept,site", evaluator.evaluateScript("GET_UG_ATTR_NAMES(null)")); + Assert.assertEquals("GET_UG_ATTR_NAMES(null, '|')", "dept|site", evaluator.evaluateScript("GET_UG_ATTR_NAMES(null, '|')")); + Assert.assertEquals("GET_UG_ATTR_NAMES(null, null)", "deptsite", evaluator.evaluateScript("GET_UG_ATTR_NAMES(null, null)")); + + Assert.assertEquals("GET_UG_ATTR_NAMES_Q()", "'dept','site'", evaluator.evaluateScript("GET_UG_ATTR_NAMES_Q()")); + Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null)", "'dept','site'", evaluator.evaluateScript("GET_UG_ATTR_NAMES_Q(null)")); + Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, null)", "'dept''site'", evaluator.evaluateScript("GET_UG_ATTR_NAMES_Q(null, null)")); + Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, '|')", "'dept'|'site'", evaluator.evaluateScript("GET_UG_ATTR_NAMES_Q(null, '|')")); + Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, ',', null)", "dept,site", evaluator.evaluateScript("GET_UG_ATTR_NAMES_Q(null, ',', null)")); + Assert.assertEquals("GET_UG_ATTR_NAMES_Q(null, ',', '{', '}')", "{dept},{site}", evaluator.evaluateScript("GET_UG_ATTR_NAMES_Q(null, ',', '{', '}')")); + + Assert.assertEquals("GET_UG_ATTR('dept')", "ENGG,PROD", evaluator.evaluateScript("GET_UG_ATTR('dept')")); + Assert.assertEquals("GET_UG_ATTR('dept', null)", "ENGG,PROD", evaluator.evaluateScript("GET_UG_ATTR('dept', null)")); + Assert.assertEquals("GET_UG_ATTR('dept', null, '|')", "ENGG|PROD", evaluator.evaluateScript("GET_UG_ATTR('dept', null, '|')")); + Assert.assertEquals("GET_UG_ATTR('dept', null, null)", "ENGGPROD", evaluator.evaluateScript("GET_UG_ATTR('dept', null, null)")); + + Assert.assertEquals("GET_UG_ATTR_Q('dept')", "'ENGG','PROD'", evaluator.evaluateScript("GET_UG_ATTR_Q('dept')")); + Assert.assertEquals("GET_UG_ATTR_Q('dept', null)", "'ENGG','PROD'", evaluator.evaluateScript("GET_UG_ATTR_Q('dept', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('dept', null, null)", "'ENGG''PROD'", evaluator.evaluateScript("GET_UG_ATTR_Q('dept', null, null)")); + Assert.assertEquals("GET_UG_ATTR_Q('dept', null, '|')", "'ENGG'|'PROD'", evaluator.evaluateScript("GET_UG_ATTR_Q('dept', null, '|')")); + Assert.assertEquals("GET_UG_ATTR_Q('dept', null, ',', null)", "ENGG,PROD", evaluator.evaluateScript("GET_UG_ATTR_Q('dept', null, ',', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('dept', null, ',', '{', '}')", "{ENGG},{PROD}", evaluator.evaluateScript("GET_UG_ATTR_Q('dept', null, ',', '{', '}')")); + + Assert.assertEquals("GET_UG_ATTR('site')", "10,20", evaluator.evaluateScript("GET_UG_ATTR('site')")); + Assert.assertEquals("GET_UG_ATTR('site', null)", "10,20", evaluator.evaluateScript("GET_UG_ATTR('site', null)")); + Assert.assertEquals("GET_UG_ATTR('site', null, '|')", "10|20", evaluator.evaluateScript("GET_UG_ATTR('site', null, '|')")); + Assert.assertEquals("GET_UG_ATTR('site', null, null)", "1020", evaluator.evaluateScript("GET_UG_ATTR('site', null, null)")); + + Assert.assertEquals("GET_UG_ATTR_Q('site')", "'10','20'", evaluator.evaluateScript("GET_UG_ATTR_Q('site')")); + Assert.assertEquals("GET_UG_ATTR_Q('site', null)", "'10','20'", evaluator.evaluateScript("GET_UG_ATTR_Q('site', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('site', null, null)", "'10''20'", evaluator.evaluateScript("GET_UG_ATTR_Q('site', null, null)")); + Assert.assertEquals("GET_UG_ATTR_Q('site', null, '|')", "'10'|'20'", evaluator.evaluateScript("GET_UG_ATTR_Q('site', null, '|')")); + Assert.assertEquals("GET_UG_ATTR_Q('site', null, ',', null)", "10,20", evaluator.evaluateScript("GET_UG_ATTR_Q('site', null, ',', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('site', null, ',', '{', '}')", "{10},{20}", evaluator.evaluateScript("GET_UG_ATTR_Q('site', null, ',', '{', '}')")); + + Assert.assertEquals("GET_UR_NAMES()", "test-role1,test-role2", evaluator.evaluateScript("GET_UR_NAMES()")); + Assert.assertEquals("GET_UR_NAMES(null)", "test-role1,test-role2", evaluator.evaluateScript("GET_UR_NAMES(null)")); + Assert.assertEquals("GET_UR_NAMES(null, '|')", "test-role1|test-role2", evaluator.evaluateScript("GET_UR_NAMES(null, '|')")); + Assert.assertEquals("GET_UR_NAMES(null, null)", "test-role1test-role2", evaluator.evaluateScript("GET_UR_NAMES(null, null)")); + + Assert.assertEquals("GET_UR_NAMES_Q()", "'test-role1','test-role2'", evaluator.evaluateScript("GET_UR_NAMES_Q()")); + Assert.assertEquals("GET_UR_NAMES_Q(null)", "'test-role1','test-role2'", evaluator.evaluateScript("GET_UR_NAMES_Q(null)")); + Assert.assertEquals("GET_UR_NAMES_Q(null, null)", "'test-role1''test-role2'", evaluator.evaluateScript("GET_UR_NAMES_Q(null, null)")); + Assert.assertEquals("GET_UR_NAMES_Q(null, '|')", "'test-role1'|'test-role2'", evaluator.evaluateScript("GET_UR_NAMES_Q(null, '|')")); + Assert.assertEquals("GET_UR_NAMES_Q(null, ',', null)", "test-role1,test-role2", evaluator.evaluateScript("GET_UR_NAMES_Q(null, ',', null)")); + Assert.assertEquals("GET_UR_NAMES_Q(null, ',', '{', '}')", "{test-role1},{test-role2}", evaluator.evaluateScript("GET_UR_NAMES_Q(null, ',', '{', '}')")); + + Assert.assertEquals("GET_USER_ATTR_NAMES()", "state", evaluator.evaluateScript("GET_USER_ATTR_NAMES()")); + Assert.assertEquals("GET_USER_ATTR_NAMES(null)", "state", evaluator.evaluateScript("GET_USER_ATTR_NAMES(null)")); + Assert.assertEquals("GET_USER_ATTR_NAMES(null, '|')", "state", evaluator.evaluateScript("GET_USER_ATTR_NAMES(null, '|')")); + Assert.assertEquals("GET_USER_ATTR_NAMES(null, null)", "state", evaluator.evaluateScript("GET_USER_ATTR_NAMES(null, null)")); + + Assert.assertEquals("GET_USER_ATTR_NAMES_Q()", "'state'", evaluator.evaluateScript("GET_USER_ATTR_NAMES_Q()")); + Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null)", "'state'", evaluator.evaluateScript("GET_USER_ATTR_NAMES_Q(null)")); + Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, null)", "'state'", evaluator.evaluateScript("GET_USER_ATTR_NAMES_Q(null, null)")); + Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, '|')", "'state'", evaluator.evaluateScript("GET_USER_ATTR_NAMES_Q(null, '|')")); + Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, ',', null)", "state", evaluator.evaluateScript("GET_USER_ATTR_NAMES_Q(null, ',', null)")); + Assert.assertEquals("GET_USER_ATTR_NAMES_Q(null, ',', '{', '}')", "{state}", evaluator.evaluateScript("GET_USER_ATTR_NAMES_Q(null, ',', '{', '}')")); + + Assert.assertEquals("GET_USER_ATTR('state')", "CA", evaluator.evaluateScript("GET_USER_ATTR('state')")); + Assert.assertEquals("GET_USER_ATTR('state', null)", "CA", evaluator.evaluateScript("GET_USER_ATTR('state', null)")); + Assert.assertEquals("GET_USER_ATTR('state', null, '|')", "CA", evaluator.evaluateScript("GET_USER_ATTR('state', null, '|')")); + Assert.assertEquals("GET_USER_ATTR('state', null, null)", "CA", evaluator.evaluateScript("GET_USER_ATTR('state', null, null)")); + + Assert.assertEquals("GET_USER_ATTR_Q('state')", "'CA'", evaluator.evaluateScript("GET_USER_ATTR_Q('state')")); + Assert.assertEquals("GET_USER_ATTR_Q('state', null)", "'CA'", evaluator.evaluateScript("GET_USER_ATTR_Q('state', null)")); + Assert.assertEquals("GET_USER_ATTR_Q('state', null, null)", "'CA'", evaluator.evaluateScript("GET_USER_ATTR_Q('state', null, null)")); + Assert.assertEquals("GET_USER_ATTR_Q('state', null, '|')", "'CA'", evaluator.evaluateScript("GET_USER_ATTR_Q('state', null, '|')")); + Assert.assertEquals("GET_USER_ATTR_Q('state', null, ',', null)", "CA", evaluator.evaluateScript("GET_USER_ATTR_Q('state', null, ',', null)")); + Assert.assertEquals("GET_USER_ATTR_Q('state', null, ',', '{', '}')", "{CA}", evaluator.evaluateScript("GET_USER_ATTR_Q('state', null, ',', '{', '}')")); + } + + @Test + public void testNonExistentValues() { + RangerAccessRequest request = createRequest("test-user", Collections.emptySet(), Collections.emptySet(), Collections.emptyList()); + RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request, scriptEngine); + + // empty TAG names + Assert.assertEquals("GET_TAG_NAMES()", "", evaluator.evaluateScript("GET_TAG_NAMES()")); + Assert.assertEquals("GET_TAG_NAMES(null)", "", evaluator.evaluateScript("GET_TAG_NAMES(null)")); + Assert.assertEquals("GET_TAG_NAMES('empty')", "empty", evaluator.evaluateScript("GET_TAG_NAMES('empty')")); + Assert.assertEquals("GET_TAG_NAMES('empty', '|')", "empty", evaluator.evaluateScript("GET_TAG_NAMES('empty', '|')")); + Assert.assertEquals("GET_TAG_NAMES('empty', null)", "empty", evaluator.evaluateScript("GET_TAG_NAMES('empty', null)")); + + // empty TAG names + Assert.assertEquals("GET_TAG_NAMES_Q()", "", evaluator.evaluateScript("GET_TAG_NAMES_Q()")); + Assert.assertEquals("GET_TAG_NAMES_Q(null)", "", evaluator.evaluateScript("GET_TAG_NAMES_Q(null)")); + Assert.assertEquals("GET_TAG_NAMES_Q('empty')", "'empty'", evaluator.evaluateScript("GET_TAG_NAMES_Q('empty')")); + Assert.assertEquals("GET_TAG_NAMES_Q('empty', ',')", "'empty'", evaluator.evaluateScript("GET_TAG_NAMES_Q('empty', ',')")); + Assert.assertEquals("GET_TAG_NAMES_Q('empty', '|', null)", "'empty'", evaluator.evaluateScript("GET_TAG_NAMES_Q('empty', '|')")); + Assert.assertEquals("GET_TAG_NAMES_Q('empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript("GET_TAG_NAMES_Q('empty', ',', '{', '}')")); + + // empty UG names + Assert.assertEquals("GET_UG_NAMES()", "", evaluator.evaluateScript("GET_UG_NAMES()")); + Assert.assertEquals("GET_UG_NAMES(null)", "", evaluator.evaluateScript("GET_UG_NAMES(null)")); + Assert.assertEquals("GET_UG_NAMES('empty')", "empty", evaluator.evaluateScript("GET_UG_NAMES('empty')")); + Assert.assertEquals("GET_UG_NAMES('empty', '|')", "empty", evaluator.evaluateScript("GET_UG_NAMES('empty', '|')")); + Assert.assertEquals("GET_UG_NAMES('empty', null)", "empty", evaluator.evaluateScript("GET_UG_NAMES('empty', null)")); + + // empty UG names + Assert.assertEquals("GET_UG_NAMES_Q()", "", evaluator.evaluateScript("GET_UG_NAMES_Q()")); + Assert.assertEquals("GET_UG_NAMES_Q(null)", "", evaluator.evaluateScript("GET_UG_NAMES_Q(null)")); + Assert.assertEquals("GET_UG_NAMES_Q('empty')", "'empty'", evaluator.evaluateScript("GET_UG_NAMES_Q('empty')")); + Assert.assertEquals("GET_UG_NAMES_Q('empty', ',')", "'empty'", evaluator.evaluateScript("GET_UG_NAMES_Q('empty', ',')")); + Assert.assertEquals("GET_UG_NAMES_Q('empty', '|', null)", "'empty'", evaluator.evaluateScript("GET_UG_NAMES_Q('empty', '|')")); + Assert.assertEquals("GET_UG_NAMES_Q('empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript("GET_UG_NAMES_Q('empty', ',', '{', '}')")); + + // empty UR names + Assert.assertEquals("GET_UR_NAMES()", "", evaluator.evaluateScript("GET_UR_NAMES()")); + Assert.assertEquals("GET_UR_NAMES(null)", "", evaluator.evaluateScript("GET_UR_NAMES(null)")); + Assert.assertEquals("GET_UR_NAMES('empty')", "empty", evaluator.evaluateScript("GET_UR_NAMES('empty')")); + Assert.assertEquals("GET_UR_NAMES('empty', '|')", "empty", evaluator.evaluateScript("GET_UR_NAMES('empty', '|')")); + Assert.assertEquals("GET_UR_NAMES('empty', null)", "empty", evaluator.evaluateScript("GET_UR_NAMES('empty', null)")); + + // empty UR names + Assert.assertEquals("GET_UR_NAMES_Q()", "", evaluator.evaluateScript("GET_UR_NAMES_Q()")); + Assert.assertEquals("GET_UR_NAMES_Q(null)", "", evaluator.evaluateScript("GET_UR_NAMES_Q(null)")); + Assert.assertEquals("GET_UR_NAMES_Q('empty')", "'empty'", evaluator.evaluateScript("GET_UR_NAMES_Q('empty')")); + Assert.assertEquals("GET_UR_NAMES_Q('empty', ',')", "'empty'", evaluator.evaluateScript("GET_UR_NAMES_Q('empty', ',')")); + Assert.assertEquals("GET_UR_NAMES_Q('empty', '|', null)", "'empty'", evaluator.evaluateScript("GET_UR_NAMES_Q('empty', '|')")); + Assert.assertEquals("GET_UR_NAMES_Q('empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript("GET_UR_NAMES_Q('empty', ',', '{', '}')")); + + // non-existent attribute + Assert.assertEquals("GET_TAG_ATTR('noattr')", "", evaluator.evaluateScript("GET_TAG_ATTR('noattr')")); + Assert.assertEquals("GET_TAG_ATTR('noattr', null)", "", evaluator.evaluateScript("GET_TAG_ATTR('noattr', null)")); + Assert.assertEquals("GET_TAG_ATTR('noattr', 'empty')", "empty", evaluator.evaluateScript("GET_TAG_ATTR('noattr', 'empty')")); + Assert.assertEquals("GET_TAG_ATTR('noattr', 'empty', '|')", "empty", evaluator.evaluateScript("GET_TAG_ATTR('noattr', 'empty', '|')")); + Assert.assertEquals("GET_TAG_ATTR('noattr', 'empty', null)", "empty", evaluator.evaluateScript("GET_TAG_ATTR('noattr', 'empty', null)")); + + // non-existent attribute + Assert.assertEquals("GET_TAG_ATTR_Q('noattr')", "", evaluator.evaluateScript("GET_TAG_ATTR_Q('noattr')")); + Assert.assertEquals("GET_TAG_ATTR_Q('noattr', null)", "", evaluator.evaluateScript("GET_TAG_ATTR_Q('noattr', null)")); + Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty')", "'empty'", evaluator.evaluateScript("GET_TAG_ATTR_Q('noattr', 'empty')")); + Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty', ',')", "'empty'", evaluator.evaluateScript("GET_TAG_ATTR_Q('noattr', 'empty', ',')")); + Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty', '|', null)", "empty", evaluator.evaluateScript("GET_TAG_ATTR_Q('noattr', 'empty', '|', null)")); + Assert.assertEquals("GET_TAG_ATTR_Q('noattr', 'empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript("GET_TAG_ATTR_Q('noattr', 'empty', ',', '{', '}')")); + + // non-existent attribute + Assert.assertEquals("GET_UG_ATTR('noattr')", "", evaluator.evaluateScript("GET_UG_ATTR('noattr')")); + Assert.assertEquals("GET_UG_ATTR('noattr', null)", "", evaluator.evaluateScript("GET_UG_ATTR('noattr', null)")); + Assert.assertEquals("GET_UG_ATTR('noattr', 'empty', '|')", "empty", evaluator.evaluateScript("GET_UG_ATTR('noattr', 'empty', '|')")); + Assert.assertEquals("GET_UG_ATTR('noattr', 'empty', null)", "empty", evaluator.evaluateScript("GET_UG_ATTR('noattr', 'empty', null)")); + + // non-existent attribute + Assert.assertEquals("GET_UG_ATTR_Q('noattr')", "", evaluator.evaluateScript("GET_UG_ATTR_Q('noattr')")); + Assert.assertEquals("GET_UG_ATTR_Q('noattr', null)", "", evaluator.evaluateScript("GET_UG_ATTR_Q('noattr', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', null)", "'empty'", evaluator.evaluateScript("GET_UG_ATTR_Q('noattr', 'empty', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', '|')", "'empty'", evaluator.evaluateScript("GET_UG_ATTR_Q('noattr', 'empty', '|')")); + Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', ',', null)", "empty", evaluator.evaluateScript("GET_UG_ATTR_Q('noattr', 'empty', ',', null)")); + Assert.assertEquals("GET_UG_ATTR_Q('noattr', 'empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript("GET_UG_ATTR_Q('noattr', 'empty', ',', '{', '}')")); + + // non-existent attribute + Assert.assertEquals("GET_USER_ATTR('noattr')", "", evaluator.evaluateScript("GET_USER_ATTR('noattr')")); + Assert.assertEquals("GET_USER_ATTR('noattr', null)", "", evaluator.evaluateScript("GET_USER_ATTR('noattr', null)")); + Assert.assertEquals("GET_USER_ATTR('noattr', 'empty', '|')", "empty", evaluator.evaluateScript("GET_USER_ATTR('noattr', 'empty', '|')")); + Assert.assertEquals("GET_USER_ATTR('noattr', 'empty', null)", "empty", evaluator.evaluateScript("GET_USER_ATTR('noattr', 'empty', null)")); + + // non-existent attribute + Assert.assertEquals("GET_USER_ATTR_Q('noattr')", "", evaluator.evaluateScript("GET_USER_ATTR_Q('noattr')")); + Assert.assertEquals("GET_USER_ATTR_Q('noattr', null)", "", evaluator.evaluateScript("GET_USER_ATTR_Q('noattr', null)")); + Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', null)", "'empty'", evaluator.evaluateScript("GET_USER_ATTR_Q('noattr', 'empty', null)")); + Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', '|')", "'empty'", evaluator.evaluateScript("GET_USER_ATTR_Q('noattr', 'empty', '|')")); + Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', ',', null)", "empty", evaluator.evaluateScript("GET_USER_ATTR_Q('noattr', 'empty', ',', null)")); + Assert.assertEquals("GET_USER_ATTR_Q('noattr', 'empty', ',', '{', '}')", "{empty}", evaluator.evaluateScript("GET_USER_ATTR_Q('noattr', 'empty', ',', '{', '}')")); + } + + @Test + public void testIntersectsIncludes() { + RangerTag tagPartners = new RangerTag("PARTNERS", Collections.singletonMap("names", "partner-1,partner-2")); + RangerTag tagDepts = new RangerTag("DEPTS", Collections.singletonMap("names", "ENGG,SALES")); + RangerAccessRequest request = createRequest("test-user2", Collections.singleton("test-group2"), Collections.singleton("test-role2"), Arrays.asList(tagPartners, tagDepts)); + RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request, scriptEngine); + + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['sales'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['sales'])")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['mktg'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['mktg'])")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['products'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['products'])")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['sales', 'engineering'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['sales', 'engineering'])")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['mktg', 'engineering'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['mktg', 'engineering'])")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['products', 'engineering'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['products', 'engineering'])")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].intersects(['engineering', 'hr', 'sales'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['engineering', 'hr', 'sales'])")); + Assert.assertFalse("test: ['sales', 'mktg', 'products'].intersects(['engineering'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(['engineering'])")); + Assert.assertFalse("test: ['sales', 'mktg', 'products'].intersects([])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects([])")); + Assert.assertFalse("test: ['sales', 'mktg', 'products'].intersects(null)", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].intersects(null)")); + Assert.assertFalse("test: [].intersects(['engineering'])", (Boolean) evaluator.evaluateScript("[].intersects(['engineering'])")); + Assert.assertFalse("test: [].intersects([])", (Boolean) evaluator.evaluateScript("[].intersects([])")); + /* + TAGS.PARTNERS.names = partner-1,partner-2 + USER.partners = partner-1,partner-2,partners-3 + */ + Assert.assertTrue("test: TAGS.PARTNERS.names.split(',').intersects(USER.partners.split(','))", (Boolean) evaluator.evaluateScript("HAS_USER_ATTR('partners') && TAGS.PARTNERS.names.split(',').intersects(USER.partners.split(','))")); + + + Assert.assertTrue("test: ['sales', 'mktg', 'products'].includes('sales')", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].includes('sales')")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].includes('mktg')", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].includes('mktg')")); + Assert.assertTrue("test: ['sales', 'mktg', 'products'].includes('products')", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].includes('products')")); + Assert.assertFalse("test: ['sales', 'mktg', 'products'].includes(['engineering'])", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].includes('engineering')")); + Assert.assertFalse("test: ['sales', 'mktg', 'products'].includes('')", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].includes('')")); + Assert.assertFalse("test: ['sales', 'mktg', 'products'].includes(null)", (Boolean) evaluator.evaluateScript("['sales', 'mktg', 'products'].includes(null)")); + Assert.assertFalse("test: [].includes('engineering')", (Boolean) evaluator.evaluateScript("[].includes('engineering')")); + Assert.assertFalse("test: [].includes([])", (Boolean) evaluator.evaluateScript("[].includes([])")); + /* + TAGS.DEPTS.names = ENGG,SALES + USER.dept = ENGG + */ + Assert.assertTrue("test: TAGS.DEPTS.names.split(',').includes(USER.dept)", (Boolean) evaluator.evaluateScript("TAGS.DEPTS.names.split(',').includes(USER.dept)")); + + // switch context to user test-user3, who has different attribute values for partners and dept + request = createRequest("test-user3", Collections.singleton("test-group3"), Collections.singleton("test-role3"), Arrays.asList(tagPartners, tagDepts)); + evaluator = new RangerRequestScriptEvaluator(request, scriptEngine); + + /* + TAGS.PARTNERS.names = partner-1,partner-2 + USER.partners = partner-3 + */ + Assert.assertFalse("test: TAGS.PARTNERS.names.split(',').intersects(USER.partners.split(','))", (Boolean) evaluator.evaluateScript("HAS_USER_ATTR('partners') && TAGS.PARTNERS.names.split(',').intersects(USER.partners.split(','))")); + + /* + TAGS.DEPTS.names = ENGG,SALES + USER.dept = MKTG + */ + Assert.assertFalse("test: TAGS.DEPTS.names.split(',').includes(USER.dept)", (Boolean) evaluator.evaluateScript("TAGS.DEPTS.names.split(',').includes(USER.dept)")); + + // switch context to user test-user4, who doesn't have attribute partners and dept + request = createRequest("test-user4", Collections.singleton("test-group4"), Collections.singleton("test-role4"), Arrays.asList(tagPartners, tagDepts)); + evaluator = new RangerRequestScriptEvaluator(request, scriptEngine); + + /* + TAGS.PARTNERS.names = partner-1,partner-2 + USER.partners = null + */ + Assert.assertFalse("test: TAGS.PARTNERS.names.split(',').intersects(USER.partners.split(','))", (Boolean) evaluator.evaluateScript("HAS_USER_ATTR('partners') && TAGS.PARTNERS.names.split(',').intersects(USER.partners.split(','))")); + + /* + TAGS.DEPTS.names = ENGG,SALES + USER.dept = null + */ + Assert.assertFalse("test: TAGS.DEPTS.names.split(',').includes(USER.dept)", (Boolean) evaluator.evaluateScript("TAGS.DEPTS.names.split(',').includes(USER.dept)")); + } + + @Test + public void testBlockJavaClassReferences() { + RangerAccessRequest request = createRequest("test-user", Collections.EMPTY_SET, Collections.EMPTY_SET, Collections.EMPTY_LIST); + RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request, scriptEngine, false); + + Assert.assertNull("test: java.lang.System.out.println(\"test\");", evaluator.evaluateScript("java.lang.System.out.println(\"test\");")); + Assert.assertNull("test: java.lang.Runtime.getRuntime().exec(\"bash\");", evaluator.evaluateScript("java.lang.Runtime.getRuntime().exec(\"bash\");")); + } + + @Test + public void testIsTimeMacros() { + RangerAccessRequest request = createRequest("test-user", Collections.emptySet(), Collections.emptySet(), Collections.emptyList()); + RangerRequestScriptEvaluator evaluator = new RangerRequestScriptEvaluator(request, scriptEngine, false); + + // Date + Assert.assertTrue("test: IS_ACCESS_TIME_AFTER('2020/01/01')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_AFTER('2020/01/01')")); + Assert.assertTrue("test: IS_ACCESS_TIME_AFTER('2020/01/01', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_AFTER('2020/01/01', 'GMT')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BEFORE('2100/01/01')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BEFORE('2100/01/01')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BEFORE('2100/01/01', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BEFORE('2100/01/01', 'GMT')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BETWEEN('2010/01/01', '2100/01/01')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BETWEEN('2010/01/01', '2100/01/01')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BETWEEN('2010/01/01', '2100/01/01', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BETWEEN('2010/01/01', '2100/01/01', 'GMT')")); + + // Date hh:mm + Assert.assertTrue("test: IS_ACCESS_TIME_AFTER('2020/01/01 15:00')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_AFTER('2020/01/01 15:00')")); + Assert.assertTrue("test: IS_ACCESS_TIME_AFTER('2020/01/01 15:00', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_AFTER('2020/01/01 15:00', 'GMT')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BEFORE('2100/01/01 15:00')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BEFORE('2100/01/01 15:00')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BEFORE('2100/01/01 15:00', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BEFORE('2100/01/01 15:00', 'GMT')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00', '2100/01/01 15:00')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00', '2100/01/01 15:00')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00', '2100/01/01 15:00', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00', '2100/01/01 15:00', 'GMT')")); + + // Date hh:mm:ss + Assert.assertTrue("test: IS_ACCESS_TIME_AFTER('2020/01/01 15:00:42')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_AFTER('2020/01/01 15:00:42')")); + Assert.assertTrue("test: IS_ACCESS_TIME_AFTER('2020/01/01 15:00:42', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_AFTER('2020/01/01 15:00:42', 'GMT')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BEFORE('2100/01/01 15:00:42')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BEFORE('2100/01/01 15:00:42')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BEFORE('2100/01/01 15:00:42', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BEFORE('2100/01/01 15:00:42', 'GMT')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00:42', '2100/01/01 15:00:42')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00:42', '2100/01/01 15:00:42')")); + Assert.assertTrue("test: IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00:42', '2100/01/01 15:00:42', 'GMT')", (Boolean) evaluator.evaluateScript("IS_ACCESS_TIME_BETWEEN('2010/01/01 15:00:42', '2100/01/01 15:00:42', 'GMT')")); + } + + RangerAccessRequest createRequest(String userName, Set userGroups, Set userRoles, List resourceTags) { + RangerAccessResource resource = mock(RangerAccessResource.class); + + Map resourceMap = new HashMap<>(); + + resourceMap.put("database", "db1"); + resourceMap.put("table", "tbl1"); + resourceMap.put("column", "col1"); + + when(resource.getAsString()).thenReturn("db1/tbl1/col1"); + when(resource.getOwnerUser()).thenReturn("testUser"); + when(resource.getAsMap()).thenReturn(resourceMap); + when(resource.getReadOnlyCopy()).thenReturn(resource); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + + request.setResource(resource); + request.setResourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF); + request.setAccessType("select"); + request.setAction("query"); + request.setUser(userName); + request.setUserGroups(userGroups); + request.setUserRoles(userRoles); + + RangerAccessRequestUtil.setCurrentResourceInContext(request.getContext(), resource); + + if (resourceTags != null) { + Set rangerTagForEvals = new HashSet<>(); + RangerTagForEval currentTag = null; + + for (RangerTag tag : resourceTags) { + RangerTagForEval tagForEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF); + + rangerTagForEvals.add(tagForEval); + + if (currentTag == null) { + currentTag = tagForEval; + } + } + + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), rangerTagForEvals); + RangerAccessRequestUtil.setCurrentTagInContext(request.getContext(), currentTag); + } else { + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), null); + } + + RangerUserStore userStore = mock(RangerUserStore.class); + + RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore); + + Map> userAttrMapping = new HashMap<>(); + Map> groupAttrMapping = new HashMap<>(); + + userAttrMapping.put("test-user", Collections.singletonMap("state", "CA")); + userAttrMapping.put("test-user2", new HashMap() {{ put("partners", "partner-1,partner-2,partner-3"); put("dept", "ENGG"); }}); + userAttrMapping.put("test-user3", new HashMap() {{ put("partners", "partner-3"); put("dept", "MKTG"); }}); + + groupAttrMapping.put("test-group1", new HashMap() {{ put("dept", "ENGG"); put("site", "10"); }}); + groupAttrMapping.put("test-group2", new HashMap() {{ put("dept", "PROD"); put("site", "20"); }}); + groupAttrMapping.put("test-group3", new HashMap() {{ put("dept", "SALES"); put("site", "30"); }}); + + when(userStore.getUserAttrMapping()).thenReturn(userAttrMapping); + when(userStore.getGroupAttrMapping()).thenReturn(groupAttrMapping); + + return request; + } + +} \ No newline at end of file diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerSimpleMatcher.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerSimpleMatcher.java index fc19ed77d8..16e95d523b 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerSimpleMatcher.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerSimpleMatcher.java @@ -23,16 +23,16 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; public class RangerSimpleMatcher extends RangerAbstractConditionEvaluator { - private static final Log LOG = LogFactory.getLog(RangerSimpleMatcher.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerSimpleMatcher.class); public static final String CONTEXT_NAME = "CONTEXT_NAME"; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcherTest.java index ec64e33e4c..f2efa52536 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcherTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/RangerTimeOfDayMatcherTest.java @@ -30,12 +30,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; import org.apache.ranger.plugin.policyengine.RangerAccessRequest; import org.junit.Assert; import org.junit.Test; -import com.google.common.collect.Lists; public class RangerTimeOfDayMatcherTest { diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerValidityScheduleConditionEvaluator.java b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerValidityScheduleConditionEvaluator.java new file mode 100644 index 0000000000..4377c2645a --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/conditionevaluator/TestRangerValidityScheduleConditionEvaluator.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.conditionevaluator; + + +import org.apache.ranger.authorization.utils.JsonUtils; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +public class TestRangerValidityScheduleConditionEvaluator { + private static final String SERVICE_DEF_RESOURCE_PATH = "/admin/service-defs/test-hive-servicedef.json"; + private static final RangerPolicyConditionDef VALIDITY_SCHEDULE_CONDITION_DEF = new RangerPolicyConditionDef(1L, "__validitySchedule", RangerValidityScheduleEvaluator.class.getName(), null); + private static final RangerServiceDef TEST_SERVICE_DEF = initServiceDef(); + + + @Test + public void testBothStartAndEndTime() { + RangerValidityScheduleConditionEvaluator evaluator = getEvaluator("{ \"startTime\": \"2024/01/12 00:00:00\", \"endTime\": \"2024/01/13 00:00:00\" }"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + + // should match only for anytime on 2024/01/12 + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 11, 9, 0, 0)); + assertFalse(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 12, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 13, 9, 0, 0)); + assertFalse(evaluator.isMatched(request)); + } + + @Test + public void testOnlyStartTime() { + RangerValidityScheduleConditionEvaluator evaluator = getEvaluator("{ \"startTime\": \"2024/01/12 00:00:00\" }"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + + // should match only for anytime on or after 2024/01/12 + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 11, 9, 0, 0)); + assertFalse(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 12, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 13, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + } + + @Test + public void testOnlyEndTime() { + RangerValidityScheduleConditionEvaluator evaluator = getEvaluator("{ \"endTime\": \"2024/01/13 00:00:00\" }"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + + // should match only for anytime before 2024/01/13 + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 11, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 12, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 13, 9, 0, 0)); + assertFalse(evaluator.isMatched(request)); + } + + @Test + public void testMultipleSchedules() { + RangerValidityScheduleConditionEvaluator evaluator = getEvaluator("{ \"endTime\": \"2024/01/01 00:00:00\" }", "{ \"startTime\": \"2024/04/01 00:00:00\" }", "{ \"startTime\": \"2024/02/01 00:00:00\", \"endTime\": \"2024/02/15 00:00:00\" }"); + RangerAccessRequest request = mock(RangerAccessRequest.class); + + // match following time periods: + // - anytime before 2024/01/01 + // - anytime on or after 2024/04/01 + // - anytime between 2024/02/01 - 2024/02/15 + when(request.getAccessTime()).thenReturn(getDate(2023, 12, 31, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 4, 1, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 2, 1, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 2, 14, 9, 0, 0)); + assertTrue(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 1, 1, 9, 0, 0)); + assertFalse(evaluator.isMatched(request)); + + when(request.getAccessTime()).thenReturn(getDate(2024, 3, 31, 9, 0, 0)); + assertFalse(evaluator.isMatched(request)); + } + + private RangerValidityScheduleConditionEvaluator getEvaluator(String...schedules) { + RangerValidityScheduleConditionEvaluator evaluator = new RangerValidityScheduleConditionEvaluator(); + + evaluator.setServiceDef(TEST_SERVICE_DEF); + evaluator.setConditionDef(VALIDITY_SCHEDULE_CONDITION_DEF); + evaluator.setPolicyItemCondition(new RangerPolicyItemCondition(VALIDITY_SCHEDULE_CONDITION_DEF.getName(), Arrays.asList(schedules))); + + evaluator.init(); + + return evaluator; + } + + private static Date getDate(int year, int month, int day, int hour, int min, int sec) { + Calendar cal = Calendar.getInstance(); + + cal.set(year, month - 1, day, hour, min, sec); + + return cal.getTime(); + } + + private static RangerServiceDef initServiceDef() throws RuntimeException { + try (InputStream inStr = TestRangerValidityScheduleConditionEvaluator.class.getResourceAsStream(SERVICE_DEF_RESOURCE_PATH)) { + RangerServiceDef serviceDef = inStr != null ? JsonUtils.jsonToObject(new InputStreamReader(inStr), RangerServiceDef.class) : null; + + if (serviceDef == null) { + throw new RuntimeException("failed to load servicedef from " + SERVICE_DEF_RESOURCE_PATH); + } + + Long maxId = serviceDef.getPolicyConditions().stream().map(RangerPolicyConditionDef::getItemId).filter(Objects::nonNull).max(Long::compareTo).orElse(0L); + + VALIDITY_SCHEDULE_CONDITION_DEF.setItemId(maxId + 1L); + + serviceDef.getPolicyConditions().add(VALIDITY_SCHEDULE_CONDITION_DEF); + + return serviceDef; + } catch (IOException excp) { + throw new RuntimeException(excp); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java index 83b39eda07..3e0812a0f9 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/contextenricher/TestTagEnricher.java @@ -31,6 +31,8 @@ import org.apache.ranger.plugin.model.RangerTag; import org.apache.ranger.plugin.model.RangerTagDef; import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher.MatchType; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.ServiceTags; import org.junit.AfterClass; @@ -64,6 +66,27 @@ public static void setUpBeforeClass() throws Exception { public static void tearDownAfterClass() throws Exception { } + @Test + public void testRangerTagsForEvalSort() { + List matchTypes = new ArrayList<>(); + + matchTypes.add(null); + matchTypes.add(MatchType.NONE); + matchTypes.add(MatchType.DESCENDANT); + matchTypes.add(MatchType.ANCESTOR); + matchTypes.add(MatchType.SELF_AND_ALL_DESCENDANTS); + matchTypes.add(MatchType.SELF); + + matchTypes.sort(RangerPolicyResourceMatcher.MATCH_TYPE_COMPARATOR); + + assertEquals(matchTypes.get(0), MatchType.SELF); + assertEquals(matchTypes.get(1), MatchType.SELF_AND_ALL_DESCENDANTS); + assertEquals(matchTypes.get(2), MatchType.ANCESTOR); + assertEquals(matchTypes.get(3), MatchType.DESCENDANT); + assertEquals(matchTypes.get(4), MatchType.NONE); + assertEquals(matchTypes.get(5), null); + } + @Test public void testTagEnricher_hive() { String[] hiveTestResourceFiles = { "/contextenricher/test_tagenricher_hive.json" }; @@ -96,6 +119,7 @@ private void runTests(InputStreamReader reader, String testName) { tagEnricher.setServiceName(testCase.serviceName); tagEnricher.setServiceDef(testCase.serviceDef); + tagEnricher.init(); tagEnricher.setServiceTags(serviceTags); List expectedTags = new ArrayList<>(); @@ -104,6 +128,7 @@ private void runTests(InputStreamReader reader, String testName) { for (TestData test : testCase.tests) { RangerAccessRequestImpl request = new RangerAccessRequestImpl(test.resource, test.accessType, "testUser", null, null); + ((RangerMutableResource)request.getResource()).setServiceDef(testCase.serviceDef); tagEnricher.enrich(request); List expected = test.result; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/errors/TestValidationErrorCode.java b/agents-common/src/test/java/org/apache/ranger/plugin/errors/TestValidationErrorCode.java index 2c69bcc6fd..281080861c 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/errors/TestValidationErrorCode.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/errors/TestValidationErrorCode.java @@ -19,7 +19,7 @@ package org.apache.ranger.plugin.errors; -import com.google.common.collect.ImmutableSet; +import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableSet; import org.junit.Assert; import org.junit.Test; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerHealth.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerHealth.java new file mode 100644 index 0000000000..bb4aee146e --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerHealth.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import static org.apache.ranger.plugin.model.RangerServerHealth.RangerServerStatus.*; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import org.junit.Assert; +import org.junit.Test; + +public class TestRangerHealth { + @Test + public void testRangerStatusUP() { + Map componentsMap = new HashMap<>(); + Map dbMap = new LinkedHashMap<>(); + dbMap.put("status", UP); + Map dbDetailsMap = new LinkedHashMap<>(); + dbDetailsMap.put("database","Oracle 21.3c"); + dbDetailsMap.put("validationQuery","SELECT banner from v$version where rownum<2"); + dbMap.put("details",dbDetailsMap); + componentsMap.put("db",dbMap); + Map auditProviderMap = new LinkedHashMap<>(); + auditProviderMap.put("status", UP); + Map auditProviderDetailsMap = new LinkedHashMap<>(); + auditProviderDetailsMap.put("provider","Elastic Search"); + auditProviderDetailsMap.put("providerHealthCheckEndpoint","http://localhost:9200/_cluster/health?pretty"); + auditProviderDetailsMap.put("details", auditProviderDetailsMap); + componentsMap.put("auditProvider",auditProviderMap); + RangerServerHealth rangerHealth = RangerServerHealth.up().withDetail("components", componentsMap).build(); + Assert.assertEquals("RangerHealth.up()", UP, rangerHealth.getStatus()); + Assert.assertEquals("RangerHealth.getDetails()", 1, rangerHealth.getDetails().size()); + Assert.assertEquals("RangerHealth.getDetails('component')", 2, ((Map) rangerHealth.getDetails().get("components")).size()); + } + + @Test + public void testRangerStatusDOWN() { + Map componentsMap = new HashMap<>(); + Map dbMap = new LinkedHashMap<>(); + dbMap.put("status", DOWN); + Map dbDetailsMap = new LinkedHashMap<>(); + dbDetailsMap.put("database","Oracle 21.3c"); + dbDetailsMap.put("validationQuery","SELECT banner from v$version where rownum<2"); + dbMap.put("details",dbDetailsMap); + componentsMap.put("db",dbMap); + Map auditProviderMap = new LinkedHashMap<>(); + auditProviderMap.put("status", DOWN); + Map auditProviderDetailsMap = new LinkedHashMap<>(); + auditProviderDetailsMap.put("provider","Elastic Search"); + auditProviderDetailsMap.put("providerHealthCheckEndpoint","http://localhost:9200/_cluster/health?pretty"); + auditProviderDetailsMap.put("details", auditProviderDetailsMap); + componentsMap.put("auditProvider",auditProviderMap); + RangerServerHealth rangerHealth = RangerServerHealth.down().withDetail("components", componentsMap).build(); + Assert.assertEquals("RangerHealth.down()", DOWN, rangerHealth.getStatus()); + Assert.assertEquals("RangerHealth.getDetails()", 1, rangerHealth.getDetails().size()); + Assert.assertEquals("RangerHealth.getDetails('component')", 2, ((Map) rangerHealth.getDetails().get("components")).size()); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java index 57e710e616..fdd76e8973 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicy.java @@ -38,26 +38,26 @@ public void test_01_Policy_SetListMethods() { List policyItemList = getList(new RangerPolicyItem()); Assert.assertEquals("RangerPolicy.getPolicyItems()", 0, policy.getPolicyItems().size()); - policy.getPolicyItems().add(new RangerPolicyItem()); - Assert.assertEquals("RangerPolicy.getPolicyItems().add()", 1, policy.getPolicyItems().size()); + policy.addPolicyItem(new RangerPolicyItem()); + Assert.assertEquals("RangerPolicy.addPolicyItem()", 1, policy.getPolicyItems().size()); policy.setPolicyItems(policyItemList); Assert.assertEquals("RangerPolicy.setPolicyItems()", policyItemList.size(), policy.getPolicyItems().size()); Assert.assertEquals("RangerPolicy.getDenyPolicyItems()", 0, policy.getDenyPolicyItems().size()); - policy.getDenyPolicyItems().add(new RangerPolicyItem()); - Assert.assertEquals("RangerPolicy.getDenyPolicyItems().add()", 1, policy.getDenyPolicyItems().size()); + policy.addDenyPolicyItem(new RangerPolicyItem()); + Assert.assertEquals("RangerPolicy.addDenyPolicyItem()", 1, policy.getDenyPolicyItems().size()); policy.setDenyPolicyItems(policyItemList); Assert.assertEquals("RangerPolicy.setDenyPolicyItems()", policyItemList.size(), policy.getDenyPolicyItems().size()); Assert.assertEquals("RangerPolicy.getAllowExceptions()", 0, policy.getAllowExceptions().size()); - policy.getAllowExceptions().add(new RangerPolicyItem()); - Assert.assertEquals("RangerPolicy.getAllowExceptions().add()", 1, policy.getAllowExceptions().size()); + policy.addAllowException(new RangerPolicyItem()); + Assert.assertEquals("RangerPolicy.addAllowException()", 1, policy.getAllowExceptions().size()); policy.setAllowExceptions(policyItemList); Assert.assertEquals("RangerPolicy.setAllowExceptions()", policyItemList.size(), policy.getAllowExceptions().size()); Assert.assertEquals("RangerPolicy.getDenyExceptions()", 0, policy.getDenyExceptions().size()); - policy.getDenyExceptions().add(new RangerPolicyItem()); - Assert.assertEquals("RangerPolicy.getDenyExceptions().add()", 1, policy.getDenyExceptions().size()); + policy.addDenyException(new RangerPolicyItem()); + Assert.assertEquals("RangerPolicy.addDenyException()", 1, policy.getDenyExceptions().size()); policy.setDenyExceptions(policyItemList); Assert.assertEquals("RangerPolicy.setDenyExceptions()", policyItemList.size(), policy.getDenyExceptions().size()); } @@ -72,26 +72,26 @@ public void test_02_PolicyItem_SetListMethods() { Assert.assertEquals("RangerPolicyItem.getAccesses()", 0, policyItem.getAccesses().size()); - policyItem.getAccesses().add(new RangerPolicyItemAccess()); - Assert.assertEquals("RangerPolicyItem.getAccesses().add()", 1, policyItem.getAccesses().size()); + policyItem.addAccess(new RangerPolicyItemAccess()); + Assert.assertEquals("RangerPolicyItem.addAccess()", 1, policyItem.getAccesses().size()); policyItem.setAccesses(accesses); Assert.assertEquals("RangerPolicyItem.setAccesses()", accesses.size(), policyItem.getAccesses().size()); Assert.assertEquals("RangerPolicyItem.getUsers()", 0, policyItem.getUsers().size()); - policyItem.getUsers().add(new String()); - Assert.assertEquals("RangerPolicyItem.getUsers().add()", 1, policyItem.getUsers().size()); + policyItem.addUser(new String()); + Assert.assertEquals("RangerPolicyItem.addUser()", 1, policyItem.getUsers().size()); policyItem.setUsers(users); Assert.assertEquals("RangerPolicyItem.setUsers()", users.size(), policyItem.getUsers().size()); Assert.assertEquals("RangerPolicyItem.getGroups()", 0, policyItem.getGroups().size()); - policyItem.getGroups().add(new String()); - Assert.assertEquals("RangerPolicyItem.getGroups().add()", 1, policyItem.getGroups().size()); + policyItem.addGroup(new String()); + Assert.assertEquals("RangerPolicyItem.addGroup()", 1, policyItem.getGroups().size()); policyItem.setGroups(groups); Assert.assertEquals("RangerPolicyItem.setGroups()", groups.size(), policyItem.getGroups().size()); Assert.assertEquals("RangerPolicyItem.getConditions()", 0, policyItem.getConditions().size()); - policyItem.getConditions().add(new RangerPolicyItemCondition()); - Assert.assertEquals("RangerPolicyItem.getConditions().add()", 1, policyItem.getConditions().size()); + policyItem.addCondition(new RangerPolicyItemCondition()); + Assert.assertEquals("RangerPolicyItem.addCondition()", 1, policyItem.getConditions().size()); policyItem.setConditions(conditions); Assert.assertEquals("RangerPolicyItem.setConditions()", conditions.size(), policyItem.getConditions().size()); } @@ -102,8 +102,8 @@ public void test_03_PolicyResource_SetListMethods() { List values = getList("value"); Assert.assertEquals("RangerPolicyResource.getValues()", 0, policyResource.getValues().size()); - policyResource.getValues().add(new String()); - Assert.assertEquals("RangerPolicyResource.getValues().add()", 1, policyResource.getValues().size()); + policyResource.addValue(new String()); + Assert.assertEquals("RangerPolicyResource.addValue()", 1, policyResource.getValues().size()); policyResource.setValues(values); Assert.assertEquals("RangerPolicyResource.setValues()", values.size(), policyResource.getValues().size()); } @@ -114,8 +114,8 @@ public void test_04_PolicyItemCondition_SetListMethods() { List values = getList("value"); Assert.assertEquals("RangerPolicyItemCondition.getValues()", 0, policyItemCondition.getValues().size()); - policyItemCondition.getValues().add(new String()); - Assert.assertEquals("RangerPolicyItemCondition.getValues().add()", 1, policyItemCondition.getValues().size()); + policyItemCondition.addValue(new String()); + Assert.assertEquals("RangerPolicyItemCondition.addValue()", 1, policyItemCondition.getValues().size()); policyItemCondition.setValues(values); Assert.assertEquals("RangerPolicyItemCondition.setValues()", values.size(), policyItemCondition.getValues().size()); } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicyResourceSignature.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicyResourceSignature.java index bc9df31cc4..87075ecbfb 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicyResourceSignature.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerPolicyResourceSignature.java @@ -89,6 +89,7 @@ public void test_isPolicyValidForResourceSignatureComputation() { // empty resources map is ok! Map policyResources = new HashMap<>(); when(rangerPolicy.getResources()).thenReturn(policyResources); + when(rangerPolicy.getGuid()).thenReturn("TEST_GUID"); policySerializer = new PolicySerializer(rangerPolicy); Assert.assertTrue("policy.getResources().isEmpty()", policySerializer.isPolicyValidForResourceSignatureComputation()); @@ -159,6 +160,8 @@ public void test_getResourceSignature_happyPath() { Map policyResources = _utils.createPolicyResourceMap(first); when(policy.getResources()).thenReturn(policyResources); when(policy.getZoneName()).thenReturn(null); + when(policy.getGuid()).thenReturn("TEST_GUID"); + when(policy.getIsEnabled()).thenReturn(true); serializer = new PolicySerializer(policy); String expectedVersion = "version=1"; String expectedType = "type=0"; @@ -190,6 +193,9 @@ public void test_nullRecursiveFlagIsSameAsFlase() { RangerPolicy policy2 = createPolicy(first_recursive_null_or_false); RangerPolicyResourceSignature signature1 = new RangerPolicyResourceSignature(policy1); RangerPolicyResourceSignature signature2 = new RangerPolicyResourceSignature(policy2); + when(policy1.getGuid()).thenReturn("TEST_GUID-1"); + when(policy2.getGuid()).thenReturn("TEST_GUID-2"); + Assert.assertEquals("Recursive flag: null is same as false", signature1.toString(), signature2.toString()); } @@ -198,6 +204,8 @@ public void test_onlyDifferByRecursiveFlag() { // create two policies with resources that differ only in the recursive flag, i.e. null/false in one and true in another RangerPolicy policy1 = createPolicy(first); RangerPolicy policy2 = createPolicy(first_recursive_flag_different); + when(policy1.getGuid()).thenReturn("TEST_GUID-1"); + when(policy2.getGuid()).thenReturn("TEST_GUID-2"); RangerPolicyResourceSignature signature1 = new RangerPolicyResourceSignature(policy1); RangerPolicyResourceSignature signature2 = new RangerPolicyResourceSignature(policy2); Assert.assertFalse("Resources differ only by recursive flag true vs false/null", signature1.toString().equals(signature2.toString())); @@ -208,6 +216,8 @@ public void test_nullExcludesFlagIsSameAsFlase() { // create two policies with resources that differ only in the excludes flag such that flags are null in one and false in another RangerPolicy policy1 = createPolicy(first); RangerPolicy policy2 = createPolicy(first_excludes_null_or_false); + when(policy1.getGuid()).thenReturn("TEST_GUID-1"); + when(policy2.getGuid()).thenReturn("TEST_GUID-2"); RangerPolicyResourceSignature signature1 = new RangerPolicyResourceSignature(policy1); RangerPolicyResourceSignature signature2 = new RangerPolicyResourceSignature(policy2); Assert.assertEquals("Excludes flag: null is same as false", signature1.toString(), signature2.toString()); @@ -218,6 +228,8 @@ public void test_onlyDifferByExcludesFlag() { // create two policies with resources that differ only in the excludes flag, i.e. null/false in one and true in another RangerPolicy policy1 = createPolicy(first); RangerPolicy policy2 = createPolicy(first_excludes_flag_different); + when(policy1.getGuid()).thenReturn("TEST_GUID-1"); + when(policy2.getGuid()).thenReturn("TEST_GUID-2"); RangerPolicyResourceSignature signature1 = new RangerPolicyResourceSignature(policy1); RangerPolicyResourceSignature signature2 = new RangerPolicyResourceSignature(policy2); Assert.assertFalse("Resources differ only by excludes flag true vs false/null", signature1.toString().equals(signature2.toString())); @@ -227,6 +239,7 @@ RangerPolicy createPolicy(Object[][] data) { RangerPolicy policy = mock(RangerPolicy.class); Map resources = _utils.createPolicyResourceMap(data); when(policy.getResources()).thenReturn(resources); + when(policy.getIsEnabled()).thenReturn(true); return policy; } @@ -236,11 +249,16 @@ public void test_integration() { RangerPolicy aPolicy = mock(RangerPolicy.class); Map resources = _utils.createPolicyResourceMap(first); when(aPolicy.getResources()).thenReturn(resources); + when(aPolicy.getIsEnabled()).thenReturn(true); + when(aPolicy.getGuid()).thenReturn("TEST_GUID-1"); RangerPolicyResourceSignature signature = new RangerPolicyResourceSignature(aPolicy); RangerPolicy anotherPolicy = mock(RangerPolicy.class); resources = _utils.createPolicyResourceMap(data_second); when(anotherPolicy.getResources()).thenReturn(resources); + when(anotherPolicy.getIsEnabled()).thenReturn(true); + when(anotherPolicy.getGuid()).thenReturn("TEST_GUID-2"); + RangerPolicyResourceSignature anotherSignature = new RangerPolicyResourceSignature(anotherPolicy); Assert.assertTrue(signature.equals(anotherSignature)); Assert.assertTrue(anotherSignature.equals(signature)); diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerService.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerService.java new file mode 100644 index 0000000000..7ac1c20792 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/TestRangerService.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + + +import org.apache.commons.lang.StringUtils; +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; + + +public class TestRangerService { + + @Test + public void test_configToString() { + RangerService svc = new RangerService("hdfs", "dev_hdfs", "HDFS", "dev_tag", new HashMap<>()); + String str = svc.toString(); + + Assert.assertTrue(!StringUtils.containsIgnoreCase(str, RangerService.CONFIG_PASSWORD)); + + + svc.getConfigs().put(RangerService.CONFIG_PASSWORD, "test1234"); + + str = svc.toString(); + + Assert.assertTrue(StringUtils.containsIgnoreCase(str, RangerService.CONFIG_PASSWORD)); + Assert.assertTrue(!StringUtils.containsIgnoreCase(str, "test1234")); + Assert.assertTrue(StringUtils.containsIgnoreCase(str, RangerService.MASKED_PASSWORD_VALUE)); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/RangerSecurityZoneValidatorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/RangerSecurityZoneValidatorTest.java index 8bc77672cc..059ddfb21c 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/RangerSecurityZoneValidatorTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/RangerSecurityZoneValidatorTest.java @@ -19,11 +19,17 @@ package org.apache.ranger.plugin.model.validation; import static org.mockito.Mockito.mock; + import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ranger.plugin.errors.ValidationErrorCode; import org.apache.ranger.plugin.model.RangerSecurityZone; import org.apache.ranger.plugin.model.RangerService; import org.apache.ranger.plugin.model.RangerServiceDef; @@ -34,6 +40,7 @@ import org.apache.ranger.plugin.model.RangerServiceDef.RangerPolicyConditionDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef; +import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.apache.ranger.plugin.store.SecurityZoneStore; import org.apache.ranger.plugin.store.ServiceStore; import org.apache.ranger.plugin.util.SearchFilter; @@ -108,7 +115,7 @@ public void testValidateSecurityZoneForDeleteThrowsError() throws Exception{ try{ rangerSecurityZoneValidator.validate(suppliedSecurityZone, RangerValidator.Action.DELETE); }catch(IllegalArgumentException ex){ - Assert.assertEquals(ex.getMessage(), "isValid(RangerPolicy, ...) is only supported for create/update"); + Assert.assertEquals(ex.getMessage(), "isValid(RangerSecurityZone, ...) is only supported for create/update"); } } @@ -161,9 +168,11 @@ public void testValidateSecurityZoneWitoutServicesAdminUserAdminUserGroupAuditUs rangerSecurityZoneValidator.validate(suppliedSecurityZone, RangerValidator.Action.CREATE); } catch (Exception ex) { - Assert.assertEquals( - ex.getMessage(), - "(0) Validation failure: error code[3044], reason[No services specified for security-zone:[MyZone]], field[services], subfield[null], type[missing] (1) Validation failure: error code[3038], reason[both users and user-groups collections for the security zone were null/empty], field[security zone admin users/user-groups], subfield[null], type[missing] (2) Validation failure: error code[3038], reason[both users and user-groups collections for the security zone were null/empty], field[security zone audit users/user-groups], subfield[null], type[missing] "); + String failureMessage = ex.getMessage(); + ValidationErrorCode expectedError = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_MISSING_USER_AND_GROUPS_AND_ROLES; + boolean hasExpectedError = StringUtils.contains(failureMessage, expectedError.getErrorCode() + ""); + + Assert.assertTrue("validation failure message didn't include expected error code " + expectedError.getErrorCode() + ". Failure message: " + failureMessage, hasExpectedError); } } @@ -357,8 +366,154 @@ public void testIsValidSecurityZoneForDeleteWithWrongIdReturnFalse() throws Exce Assert.assertFalse(isValid); } - - + @Test + public void testValidatePathResourceInMultipleSecurityZones() throws Exception { + List>> zone1Resources = new ArrayList<>(); + List>> zone2Resources = new ArrayList<>(); + + zone1Resources.add(new HashMap>() {{ put("hdfs", Arrays.asList("/zone1")); }}); + zone2Resources.add(new HashMap>() {{ put("hdfs", Arrays.asList("/zone1/a")); }}); + + RangerServiceDef svcDef = rangerServiceDef(); + RangerService svc = getRangerService(); + RangerSecurityZoneService zone1HdfsSvc = new RangerSecurityZoneService(zone1Resources); + RangerSecurityZoneService zone2HdfsSvc = new RangerSecurityZoneService(zone2Resources); + + RangerSecurityZone zone1 = new RangerSecurityZone("zone1", Collections.singletonMap(svc.getName(), zone1HdfsSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + RangerSecurityZone zone2 = new RangerSecurityZone("zone2", Collections.singletonMap(svc.getName(), zone2HdfsSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + + zone1.setId(1L); + zone2.setId(2L); + + List zones = new ArrayList() {{ add(zone1); }}; + + Mockito.when(_store.getServiceByName(svc.getName())).thenReturn(svc); + Mockito.when(_store.getServiceDefByName(svc.getType())).thenReturn(svcDef); + Mockito.when(_store.getSecurityZone(2L)).thenReturn(zone2); + Mockito.when(_securityZoneStore.getSecurityZones(Mockito.any())).thenReturn(zones); + + try { + rangerSecurityZoneValidator.validate(zone2, RangerValidator.Action.UPDATE); + + Assert.assertFalse("security-zone update should have failed in validation", true); + } catch (Exception excp) { + String failureMessage = excp.getMessage(); + ValidationErrorCode expectedError = ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_ZONE_RESOURCE_CONFLICT; + boolean hasExpectedError = StringUtils.contains(failureMessage, expectedError.getErrorCode() + ""); + + Assert.assertTrue("validation failure message didn't include expected error code " + expectedError.getErrorCode() + ". Failure message: " + failureMessage, hasExpectedError); + } + } + + @Test + public void testValidateHiveResourceInMultipleSecurityZones() throws Exception { + List>> zone1Resources = new ArrayList<>(); + List>> zone2Resources = new ArrayList<>(); + + zone1Resources.add(new HashMap>() {{ put("database", Arrays.asList("db1")); }}); + zone2Resources.add(new HashMap>() {{ put("database", Arrays.asList("db1")); put("table", Arrays.asList("tbl1")); }}); + + RangerServiceDef svcDef = getHiveServiceDef(); + RangerService svc = getHiveService(); + RangerSecurityZoneService zone1HiveSvc = new RangerSecurityZoneService(zone1Resources); + RangerSecurityZoneService zone2HiveSvc = new RangerSecurityZoneService(zone2Resources); + + RangerSecurityZone zone1 = new RangerSecurityZone("zone1", Collections.singletonMap(svc.getName(), zone1HiveSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + RangerSecurityZone zone2 = new RangerSecurityZone("zone2", Collections.singletonMap(svc.getName(), zone2HiveSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + + zone1.setId(1L); + zone2.setId(2L); + + List zones = new ArrayList() {{ add(zone1); }}; + + Mockito.when(_store.getServiceByName(svc.getName())).thenReturn(svc); + Mockito.when(_store.getServiceDefByName(svc.getType())).thenReturn(svcDef); + Mockito.when(_store.getSecurityZone(2L)).thenReturn(zone2); + Mockito.when(_securityZoneStore.getSecurityZones(Mockito.any())).thenReturn(zones); + + try { + rangerSecurityZoneValidator.validate(zone2, RangerValidator.Action.UPDATE); + + Assert.assertFalse("security-zone update should have failed in validation", true); + } catch (Exception excp) { + String failureMessage = excp.getMessage(); + boolean hasResourceConflictError = StringUtils.contains(failureMessage, ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_ZONE_RESOURCE_CONFLICT.getErrorCode() + ""); + + Assert.assertTrue("validation failure message didn't include expected error code " + ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_ZONE_RESOURCE_CONFLICT.getErrorCode() + ". Failure message: " + excp.getMessage(), hasResourceConflictError); + } + } + + @Test + public void test2ValidateHiveResourceInMultipleSecurityZones() throws Exception { + List>> zone1Resources = new ArrayList<>(); + List>> zone2Resources = new ArrayList<>(); + + zone1Resources.add(new HashMap>() {{ put("database", Arrays.asList("*")); }}); + zone2Resources.add(new HashMap>() {{ put("database", Arrays.asList("db1")); put("table", Arrays.asList("tbl1")); }}); + + RangerServiceDef svcDef = getHiveServiceDef(); + RangerService svc = getHiveService(); + RangerSecurityZoneService zone1HiveSvc = new RangerSecurityZoneService(zone1Resources); + RangerSecurityZoneService zone2HiveSvc = new RangerSecurityZoneService(zone2Resources); + + RangerSecurityZone zone1 = new RangerSecurityZone("zone1", Collections.singletonMap(svc.getName(), zone1HiveSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + RangerSecurityZone zone2 = new RangerSecurityZone("zone2", Collections.singletonMap(svc.getName(), zone2HiveSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + + zone1.setId(1L); + zone2.setId(2L); + + List zones = new ArrayList() {{ add(zone1); }}; + + Mockito.when(_store.getServiceByName(svc.getName())).thenReturn(svc); + Mockito.when(_store.getServiceDefByName(svc.getType())).thenReturn(svcDef); + Mockito.when(_store.getSecurityZone(2L)).thenReturn(zone2); + Mockito.when(_securityZoneStore.getSecurityZones(Mockito.any())).thenReturn(zones); + + try { + rangerSecurityZoneValidator.validate(zone2, RangerValidator.Action.UPDATE); + + Assert.assertFalse("security-zone update should have failed in validation", true); + } catch (Exception excp) { + String failureMessage = excp.getMessage(); + boolean hasResourceConflictError = StringUtils.contains(failureMessage, ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_ZONE_RESOURCE_CONFLICT.getErrorCode() + ""); + + Assert.assertTrue("validation failure message didn't include expected error code " + ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_ZONE_RESOURCE_CONFLICT.getErrorCode() + ". Failure message: " + excp.getMessage(), hasResourceConflictError); + } + } + + @Test + public void testValidateDuplicateResourceEntries() throws Exception { + List>> zone1Resources = new ArrayList<>(); + + zone1Resources.add(new HashMap>() {{ put("database", Arrays.asList("db1")); put("table", Arrays.asList("tbl1")); }}); + zone1Resources.add(new HashMap>() {{ put("database", Arrays.asList("db1")); put("table", Arrays.asList("tbl1")); }}); + + RangerServiceDef svcDef = getHiveServiceDef(); + RangerService svc = getHiveService(); + RangerSecurityZoneService zone1HiveSvc = new RangerSecurityZoneService(zone1Resources); + + RangerSecurityZone zone1 = new RangerSecurityZone("zone1", Collections.singletonMap(svc.getName(), zone1HiveSvc), null, Arrays.asList("admin"), null, Arrays.asList("auditor"), null, "Zone 1"); + + zone1.setId(1L); + + List zones = new ArrayList() {{ add(zone1); }}; + + Mockito.when(_store.getServiceByName(svc.getName())).thenReturn(svc); + Mockito.when(_store.getServiceDefByName(svc.getType())).thenReturn(svcDef); + Mockito.when(_store.getSecurityZone(zone1.getId())).thenReturn(zone1); + + try { + rangerSecurityZoneValidator.validate(zone1, RangerValidator.Action.UPDATE); + + Assert.assertFalse("security-zone update should have failed in validation", true); + } catch (Exception excp) { + String failureMessage = excp.getMessage(); + boolean hasResourceConflictError = StringUtils.contains(failureMessage, ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_DUPLICATE_RESOURCE_ENTRY.getErrorCode() + ""); + + Assert.assertTrue("validation failure message didn't include expected error code " + ValidationErrorCode.SECURITY_ZONE_VALIDATION_ERR_DUPLICATE_RESOURCE_ENTRY.getErrorCode() + ". Failure message: " + excp.getMessage(), hasResourceConflictError); + } + } + private RangerService getRangerService() { Map configs = new HashMap(); configs.put("username", "servicemgr"); @@ -393,6 +548,8 @@ private RangerServiceDef rangerServiceDef() { RangerResourceDef rangerResourceDef = new RangerResourceDef(); rangerResourceDef.setName("hdfs"); + rangerResourceDef.setRecursiveSupported(true); + rangerResourceDef.setMatcher("org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher"); List configs = new ArrayList(); List resources = new ArrayList(); @@ -421,6 +578,18 @@ private RangerServiceDef rangerServiceDef() { return rangerServiceDef; } + private RangerService getHiveService() { + RangerService ret = new RangerService(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME, "hiveSvc", "Test Hive Service", null, new HashMap<>()); + + ret.setId(1L); + + return ret; + } + + private RangerServiceDef getHiveServiceDef() throws Exception { + return EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(EmbeddedServiceDefsUtil.EMBEDDED_SERVICEDEF_HIVE_NAME); + } + private RangerSecurityZone getRangerSecurityZone(){ List resourceList = new ArrayList(); resourceList.add("/path/myfolder"); @@ -466,7 +635,7 @@ private SearchFilter getSerachFilter(){ SearchFilter filter = new SearchFilter(); filter.setParam(SearchFilter.SERVICE_NAME, "hdfsSvc"); - filter.setParam(SearchFilter.ZONE_NAME, "MyZone"); + filter.setParam(SearchFilter.NOT_ZONE_NAME, "MyZone"); return filter; } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestDirectedGraph.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestDirectedGraph.java index 3ec20d99ad..ad989195e8 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestDirectedGraph.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestDirectedGraph.java @@ -23,12 +23,11 @@ import java.util.HashSet; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; +import org.apache.hadoop.thirdparty.com.google.common.collect.Sets; import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper.DirectedGraph; import org.junit.Test; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - public class TestDirectedGraph { @Test diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java index f0e2d07d9e..2372048cf6 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerPolicyValidator.java @@ -31,6 +31,8 @@ import java.util.Map; import java.util.Set; +import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableMap; +import org.apache.hadoop.thirdparty.com.google.common.collect.Sets; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemAccess; @@ -48,8 +50,6 @@ import org.junit.Test; import org.mockito.ArgumentMatcher; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; public class TestRangerPolicyValidator { @@ -140,6 +140,16 @@ public void setUp() throws Exception { {"extra", new String[] { "extra1", "extra2" }, null, null } // spurious "extra" specified }; + private final Object[][] policyResourceMap_bad_duplicate_values = new Object[][]{ + // resource-name, values, excludes, recursive + { "db", new String[] {"db1", "db2" }, null, true}, + { "tbl", new String[] {"tbl1", "tbl1"}, null, null} // invalid: there should not be any duplicates for any resource value + }; + private final Object[][] policyResourceMap_good_duplicate_values = new Object[][]{ + // resource-name, values, excludes, recursive + { "db", new String[] {"db1", "db2" }, null, true}, + { "tbl", new String[] {"tbl1", "tbl2"}, null, null} // valid: there should not be any duplicates for any resource value + }; private final Object[][] policyResourceMap_bad_multiple_hierarchies = new Object[][] { // resource-name, values, excludes, recursive { "db", new String[] { "db1", "db2" }, null, true }, @@ -205,6 +215,12 @@ public final void testIsValid_errorPaths() throws Exception { when(_store.getServiceByName("service-name2")).thenReturn(service2); when(_policy.getService()).thenReturn("service-name2"); + + RangerPolicyResourceSignature policySignature = mock(RangerPolicyResourceSignature.class); + when(policySignature.getSignature()).thenReturn("hash-1"); + + when(_factory.createPolicyResourceSignature(_policy)).thenReturn(policySignature); + when(_store.getServiceByName("service-name2")).thenReturn(service2); action = Action.UPDATE; @@ -527,6 +543,14 @@ public void test_isValidResourceValues() { policyResources = _utils.createPolicyResourceMap(policyResourceMap_good); Assert.assertTrue(_validator.isValidResourceValues(policyResources, _failures, _serviceDef)); + + policyResources = _utils.createPolicyResourceMap(policyResourceMap_bad_duplicate_values); + Assert.assertFalse(_validator.isValidResourceValues(policyResources, _failures, _serviceDef)); + _utils.checkFailureForSemanticError(_failures, "resource-values", "tbl"); + + policyResources = _utils.createPolicyResourceMap(policyResourceMap_good_duplicate_values); + Assert.assertTrue(_validator.isValidResourceValues(policyResources, _failures, _serviceDef)); + } @Test @@ -730,26 +754,26 @@ public final void test_isPolicyResourceUnique() throws Exception { when(_policy.getIsEnabled()).thenReturn(true); // ensure policy is enabled _failures.clear(); Assert.assertFalse(_validator.isPolicyResourceUnique(_policy, _failures, Action.CREATE)); _utils.checkFailureForSemanticError(_failures, "resources"); - // same check should pass if the policy is disabled + // same check should fail even if the policy is disabled when(_policy.getIsEnabled()).thenReturn(false); - _failures.clear(); Assert.assertTrue(_validator.isPolicyResourceUnique(_policy, _failures, Action.CREATE)); - Assert.assertTrue("failures collection wasn't empty!", _failures.isEmpty()); + _failures.clear(); Assert.assertFalse(_validator.isPolicyResourceUnique(_policy, _failures, Action.CREATE)); + _utils.checkFailureForSemanticError(_failures, "resources"); // For Update match with itself is not a problem as long as it isn't itself, i.e. same id. when(_policy.getIsEnabled()).thenReturn(true); // ensure policy is enabled when(policy1.getId()).thenReturn(103L); when(_policy.getId()).thenReturn(103L); Assert.assertTrue(_validator.isPolicyResourceUnique(_policy, _failures, Action.UPDATE)); - + // matching policy can't be some other policy (i.e. different id) because that implies a conflict. when(policy1.getId()).thenReturn(104L); Assert.assertFalse(_validator.isPolicyResourceUnique(_policy, _failures, Action.UPDATE)); _utils.checkFailureForSemanticError(_failures, "resources"); // same check should pass if the policy is disabled when(_policy.getIsEnabled()).thenReturn(false); - _failures.clear(); Assert.assertTrue(_validator.isPolicyResourceUnique(_policy, _failures, Action.UPDATE)); - Assert.assertTrue("failures collection wasn't empty!", _failures.isEmpty()); - + _failures.clear(); Assert.assertFalse(_validator.isPolicyResourceUnique(_policy, _failures, Action.UPDATE)); + _utils.checkFailureForSemanticError(_failures, "resources"); + // And validation should never pass if there are more than one policies with matching signature, regardless of their ID!! RangerPolicy policy2 = mock(RangerPolicy.class); when(policy2.getId()).thenReturn(103L); // has same id as the policy being tested (_policy) @@ -759,8 +783,8 @@ public final void test_isPolicyResourceUnique() throws Exception { _utils.checkFailureForSemanticError(_failures, "resources"); // same check should pass if the policy is disabled when(_policy.getIsEnabled()).thenReturn(false); - _failures.clear(); Assert.assertTrue(_validator.isPolicyResourceUnique(_policy, _failures, Action.UPDATE)); - Assert.assertTrue("failures collection wasn't empty!", _failures.isEmpty()); + _failures.clear(); Assert.assertFalse(_validator.isPolicyResourceUnique(_policy, _failures, Action.UPDATE)); + _utils.checkFailureForSemanticError(_failures, "resources"); } @Test @@ -805,7 +829,30 @@ public final void test_isValidResourceNames_failures() { _failures.clear(); Assert.assertFalse("Policy with resources for multiple hierarchies missing mandatory resources for all pontential matches", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); _utils.checkFailureForSemanticError(_failures, "policy resources", "missing mandatory"); } - + + @Test + public void test_isValidResource_additionalResources() throws Exception { + String serviceName = "a-service-def"; + Date now = new Date(); + List resourceDefs = _utils.createResourceDefs(resourceDefData_multipleHierarchies); + Map resources = _utils.createPolicyResourceMap(policyResourceMap_good); + List> additionalResources = new ArrayList<>(); + + when(_serviceDef.getName()).thenReturn(serviceName ); + when(_serviceDef.getUpdateTime()).thenReturn(now); + when(_serviceDef.getResources()).thenReturn(resourceDefs); + when(_policy.getResources()).thenReturn(resources); + when(_policy.getAdditionalResources()).thenReturn(additionalResources); + + Assert.assertTrue("valid resources and empty additionalResources", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); + + additionalResources.add(_utils.createPolicyResourceMap(policyResourceMap_good)); + Assert.assertTrue("valid resources and additionalResources[0]", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); + + additionalResources.add(_utils.createPolicyResourceMap(policyResourceMap_bad)); + Assert.assertFalse("valid resources and invalid additionalResources[1]", _validator.isValidResourceNames(_policy, _failures, _serviceDef)); + } + @Test public final void test_isValidServiceWithZone_happyPath() throws Exception{ boolean isAdmin = true; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java index b0c1085303..a667c9b0b1 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerServiceDefHelper.java @@ -31,6 +31,7 @@ import java.util.List; import java.util.Set; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; import org.apache.ranger.plugin.model.RangerPolicy; import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; @@ -38,7 +39,6 @@ import org.junit.Before; import org.junit.Test; -import com.google.common.collect.Lists; public class TestRangerServiceDefHelper { diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java index 6de590b0ed..3d5248ad60 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/model/validation/TestRangerValidator.java @@ -41,6 +41,7 @@ import org.apache.ranger.plugin.model.RangerServiceDef.RangerServiceConfigDef; import org.apache.ranger.plugin.model.validation.RangerValidator.Action; import org.apache.ranger.plugin.store.ServiceStore; +import org.apache.ranger.plugin.util.ServiceDefUtil; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -186,9 +187,9 @@ public final void test_getPoliciesForResourceSignature() throws Exception { String serviceName = "service-name"; boolean isPolicyEnabled = true; when(_store.getPoliciesByResourceSignature(serviceName, hexSignature, isPolicyEnabled)).thenReturn(null); - Assert.assertNull(_validator.getPoliciesForResourceSignature(serviceName, hexSignature)); + Assert.assertNotNull(_validator.getPoliciesForResourceSignature(serviceName, hexSignature)); when(_store.getPoliciesByResourceSignature(serviceName, hexSignature, isPolicyEnabled)).thenThrow(new Exception()); - Assert.assertNull(_validator.getPoliciesForResourceSignature(serviceName, hexSignature)); + Assert.assertNotNull(_validator.getPoliciesForResourceSignature(serviceName, hexSignature)); // what ever store returns should come back hexSignature = "anotherSignature"; @@ -258,6 +259,7 @@ public void test_getAccessTypes() { String[] names = new String[] { null, "", "a", " ", "b ", " ", " C", " D " }; accessTypeDefs.addAll(_utils.createAccessTypeDefs(names)); accessTypes = _validator.getAccessTypes(serviceDef); + accessTypes.removeAll(ServiceDefUtil.ACCESS_TYPE_MARKERS); Assert.assertEquals(4, accessTypes.size()); Assert.assertTrue(accessTypes.contains("a")); Assert.assertTrue(accessTypes.contains("b ")); diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestCacheMap.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestCacheMap.java index db8b8131ff..66d191b39a 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestCacheMap.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestCacheMap.java @@ -19,16 +19,16 @@ package org.apache.ranger.plugin.policyengine; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Set; public class TestCacheMap { - private static final Log LOG = LogFactory.getLog(TestCacheMap.class); + private static final Logger LOG = LoggerFactory.getLogger(TestCacheMap.class); private static CacheMap testCacheMap; private static int initialCapacity = 16; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPathResourceTrie.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPathResourceTrie.java new file mode 100644 index 0000000000..24e1a80cc8 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPathResourceTrie.java @@ -0,0 +1,202 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher; +import org.apache.ranger.plugin.resourcematcher.RangerResourceMatcher; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + + +public class TestPathResourceTrie { + private static final RangerResourceDef PATH_RESOURCE_DEF = getPathResourceDef(); + private static final RangerResourceEvaluator EVAL_ = getEvaluator("/"); + private static final RangerResourceEvaluator EVAL_nr = getEvaluator("/", false, false); + private static final RangerResourceEvaluator EVAL_HOME = getEvaluator("/home"); + private static final RangerResourceEvaluator EVAL_HOME_ = getEvaluator("/home/"); + private static final RangerResourceEvaluator EVAL_TMPnr = getEvaluator("/tmp", false, false); + private static final RangerResourceEvaluator EVAL_TMP_nr = getEvaluator("/tmp/", false, false); + private static final RangerResourceEvaluator EVAL_TMP_AB = getEvaluator("/tmp/ab"); + private static final RangerResourceEvaluator EVAL_TMP_A_B = getEvaluator("/tmp/a/b"); + private static final RangerResourceEvaluator EVAL_TMP_AC_D_E_F = getEvaluator("/tmp/ac/d/e/f"); + private static final RangerResourceEvaluator EVAL_TMPFILE = getEvaluator("/tmpfile"); + private static final RangerResourceEvaluator EVAL_TMPdTXT = getEvaluator("/tmp.txt"); + private static final RangerResourceEvaluator EVAL_TMPA_B = getEvaluator("/tmpa/b"); + + private static final List EVALUATORS = Arrays.asList(EVAL_, + EVAL_nr, + EVAL_HOME, + EVAL_HOME_, + EVAL_TMPnr, + EVAL_TMP_nr, + EVAL_TMP_AB, + EVAL_TMP_A_B, + EVAL_TMP_AC_D_E_F, + EVAL_TMPFILE, + EVAL_TMPdTXT, + EVAL_TMPA_B + ); + + private final RangerResourceTrie trie = new RangerResourceTrie<>(PATH_RESOURCE_DEF, EVALUATORS); + + @Test + public void testChildrenScope() { + final ResourceElementMatchingScope scope = ResourceElementMatchingScope.SELF_OR_CHILD; + + verifyEvaluators("/", scope, EVAL_, EVAL_nr, EVAL_HOME, EVAL_HOME_, EVAL_TMPnr, EVAL_TMP_nr, EVAL_TMPFILE, EVAL_TMPdTXT); + verifyEvaluators("/tmp", scope, EVAL_, EVAL_TMPnr, EVAL_TMP_nr, EVAL_TMP_AB); + verifyEvaluators("/tmp/", scope, EVAL_, EVAL_TMP_nr, EVAL_TMP_AB); + verifyEvaluators("/tmp/a", scope, EVAL_, EVAL_TMP_A_B); + verifyEvaluators("/tmp/ac", scope, EVAL_); + verifyEvaluators("/tmp/ac/d", scope, EVAL_); + verifyEvaluators("/tmp/ac/d/e", scope, EVAL_, EVAL_TMP_AC_D_E_F); + verifyEvaluators("/unmatched", scope, EVAL_); + verifyEvaluators("invalid: does-not-begin-with-sep", scope); + } + + @Test + public void testPrefixScope() { + final ResourceElementMatchingScope scope = ResourceElementMatchingScope.SELF_OR_PREFIX; + + verifyEvaluators("/", scope, EVAL_, EVAL_nr, EVAL_HOME, EVAL_HOME_, EVAL_TMPnr, EVAL_TMP_nr, EVAL_TMP_AB, EVAL_TMP_A_B, EVAL_TMP_AC_D_E_F, EVAL_TMPFILE, EVAL_TMPdTXT, EVAL_TMPA_B); + verifyEvaluators("/tmp", scope, EVAL_, EVAL_TMPnr, EVAL_TMP_nr, EVAL_TMP_AB, EVAL_TMP_A_B, EVAL_TMP_AC_D_E_F, EVAL_TMPFILE, EVAL_TMPdTXT, EVAL_TMPA_B); + verifyEvaluators("/tmp/", scope, EVAL_, EVAL_TMP_nr, EVAL_TMP_AB, EVAL_TMP_A_B, EVAL_TMP_AC_D_E_F); + verifyEvaluators("/tmp/a", scope, EVAL_, EVAL_TMP_AB, EVAL_TMP_A_B, EVAL_TMP_AC_D_E_F); + verifyEvaluators("/tmp/ac", scope, EVAL_, EVAL_TMP_AC_D_E_F); + verifyEvaluators("/tmp/ac/d", scope, EVAL_, EVAL_TMP_AC_D_E_F); + verifyEvaluators("/tmp/ac/d/e", scope, EVAL_, EVAL_TMP_AC_D_E_F); + verifyEvaluators("/unmatched", scope, EVAL_); + verifyEvaluators("invalid: does-not-begin-with-sep", scope); + } + + @Test + public void testSelfScope() { + final ResourceElementMatchingScope scope = ResourceElementMatchingScope.SELF; + + verifyEvaluators("/", scope, EVAL_, EVAL_nr); + verifyEvaluators("/tmp", scope, EVAL_, EVAL_TMPnr); + verifyEvaluators("/tmp/", scope, EVAL_, EVAL_TMP_nr); + verifyEvaluators("/tmp/a", scope, EVAL_); + verifyEvaluators("/tmp/ac", scope, EVAL_); + verifyEvaluators("/tmp/ac/d", scope, EVAL_); + verifyEvaluators("/tmp/ac/d/e", scope, EVAL_); + verifyEvaluators("/unmatched", scope, EVAL_); + verifyEvaluators("invalid: does-not-begin-with-sep", scope); + } + + private void verifyEvaluators(String resource, ResourceElementMatchingScope scope, RangerResourceEvaluator... evaluators) { + Set expected = evaluators.length == 0 ? null : new HashSet<>(Arrays.asList(evaluators)); + Set result = trie.getEvaluatorsForResource(resource, scope); + + assertEquals("incorrect evaluators for resource " + resource, expected, result); + } + + private static RangerResourceDef getPathResourceDef() { + RangerResourceDef ret = new RangerResourceDef(); + + ret.setItemId(1L); + ret.setName("path"); + ret.setType("path"); + ret.setLevel(10); + ret.setParent(""); + ret.setMatcher("org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher"); + ret.setMatcherOptions(new HashMap() {{ + put("wildCard", "true"); + put("ignoreCase", "true"); + put("pathSeparatorChar", "/"); + }}); + + return ret; + } + + private static RangerResourceEvaluator getEvaluator(String resource) { + return new TestPolicyResourceEvaluator(new RangerPolicyResource(resource, false, true)); + } + + private static RangerResourceEvaluator getEvaluator(String resource, boolean isExcludes, boolean isRecursive) { + return new TestPolicyResourceEvaluator(new RangerPolicyResource(resource, isExcludes, isRecursive)); + } + + private static class TestPolicyResourceEvaluator implements RangerResourceEvaluator { + private static long nextId = 1; + + private final long id; + private final RangerPolicyResource policyResource; + private final RangerResourceMatcher resourceMatcher; + + TestPolicyResourceEvaluator(RangerPolicyResource policyResource) { + this.id = nextId++; + this.policyResource = policyResource; + this.resourceMatcher = new RangerPathResourceMatcher(); + + resourceMatcher.setResourceDef(PATH_RESOURCE_DEF); + resourceMatcher.setPolicyResource(policyResource); + + resourceMatcher.init(); + } + + @Override + public long getId() { + return id; + } + + @Override + public RangerPolicyResourceMatcher getPolicyResourceMatcher() { + return null; + } + + @Override + public Map getPolicyResource() { + return Collections.singletonMap(PATH_RESOURCE_DEF.getName(), policyResource); + } + + @Override + public RangerResourceMatcher getResourceMatcher(String resourceName) { + return resourceMatcher; + } + + @Override + public boolean isAncestorOf(RangerResourceDef resourceDef) { + return false; + } + + @Override + public boolean isLeaf(String resourceName) { return true; } + + @Override + public String toString() { + return "id=" + id + ", resource=" + policyResource; + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyACLs.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyACLs.java index e1709ad949..9a69efcbac 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyACLs.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyACLs.java @@ -35,6 +35,9 @@ import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs.DataMaskResult; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs.RowFilterResult; import org.apache.ranger.plugin.util.ServicePolicies; import org.junit.After; import org.junit.AfterClass; @@ -45,6 +48,7 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; + public class TestPolicyACLs { private static Gson gsonBuilder; @@ -76,6 +80,27 @@ public void testResourceMatcher_default() throws Exception { runTestsFromResourceFiles(tests); } + @Test + public void testResourceACLs_dataMask() throws Exception { + String[] tests = {"/policyengine/test_aclprovider_mask_filter.json"}; + + runTestsFromResourceFiles(tests); + } + + @Test + public void testResourceACLs_hdfs() throws Exception { + String[] tests = {"/policyengine/test_aclprovider_hdfs.json"}; + + runTestsFromResourceFiles(tests); + } + + @Test + public void testResourceACLs_resource_hierarchy_tags() throws Exception { + String[] tests = {"/policyengine/test_aclprovider_resource_hierarchy_tags.json"}; + + runTestsFromResourceFiles(tests); + } + private void runTestsFromResourceFiles(String[] resourceNames) throws Exception { for(String resourceName : resourceNames) { InputStream inStream = this.getClass().getResourceAsStream(resourceName); @@ -91,8 +116,9 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio assertTrue("invalid input: " + testName, testCases != null && testCases.testCases != null); for(PolicyACLsTests.TestCase testCase : testCases.testCases) { + String serviceType = testCase.servicePolicies.getServiceDef().getName(); RangerPolicyEngineOptions policyEngineOptions = new RangerPolicyEngineOptions(); - RangerPluginContext pluginContext = new RangerPluginContext(new RangerPluginConfig("hive", null, "test-policy-acls", "cl1", "on-prem", policyEngineOptions)); + RangerPluginContext pluginContext = new RangerPluginContext(new RangerPluginConfig(serviceType, null, "test-policy-acls", "cl1", "on-prem", policyEngineOptions)); RangerPolicyEngine policyEngine = new RangerPolicyEngineImpl(testCase.servicePolicies, pluginContext, null); for(PolicyACLsTests.TestCase.OneTest oneTest : testCase.tests) { @@ -100,11 +126,15 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio continue; } RangerAccessRequestImpl request = new RangerAccessRequestImpl(oneTest.resource, RangerPolicyEngine.ANY_ACCESS, null, null, null); + + request.setResourceMatchingScope(oneTest.resourceMatchingScope); + RangerResourceACLs acls = policyEngine.getResourceACLs(request); - boolean userACLsMatched = true, groupACLsMatched = true, roleACLsMatched = true; + boolean userACLsMatched = true, groupACLsMatched = true, roleACLsMatched = true, rowFiltersMatched = true, dataMaskingMatched = true; if (MapUtils.isNotEmpty(acls.getUserACLs()) && MapUtils.isNotEmpty(oneTest.userPermissions)) { + assertEquals("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - userACLsMatched", oneTest.userPermissions.size(), acls.getUserACLs().size()); for (Map.Entry> entry : acls.getUserACLs().entrySet()) { @@ -142,7 +172,43 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio userACLsMatched = false; } + if (acls.getDataMasks().isEmpty()) { + dataMaskingMatched = (oneTest.dataMasks == null || oneTest.dataMasks.isEmpty()); + } else if (acls.getDataMasks().size() != (oneTest.dataMasks == null ? 0 : oneTest.dataMasks.size())) { + dataMaskingMatched = false; + } else { + for (int i = 0; i < acls.getDataMasks().size(); i++) { + DataMaskResult found = acls.getDataMasks().get(i); + DataMaskResult expected = oneTest.dataMasks.get(i); + + dataMaskingMatched = found.equals(expected); + + if (!dataMaskingMatched) { + break; + } + } + } + + if (acls.getRowFilters().isEmpty()) { + rowFiltersMatched = (oneTest.rowFilters == null || oneTest.rowFilters.isEmpty()); + } else if (acls.getRowFilters().size() != (oneTest.rowFilters == null ? 0 : oneTest.rowFilters.size())) { + rowFiltersMatched = false; + } else { + for (int i = 0; i < acls.getRowFilters().size(); i++) { + RowFilterResult found = acls.getRowFilters().get(i); + RowFilterResult expected = oneTest.rowFilters.get(i); + + rowFiltersMatched = found.equals(expected); + + if (!rowFiltersMatched) { + break; + } + } + } + if (MapUtils.isNotEmpty(acls.getGroupACLs()) && MapUtils.isNotEmpty(oneTest.groupPermissions)) { + assertEquals("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - groupACLsMatched", oneTest.groupPermissions.size(), acls.getGroupACLs().size()); + for (Map.Entry> entry : acls.getGroupACLs().entrySet()) { String groupName = entry.getKey(); @@ -180,6 +246,8 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio } if (MapUtils.isNotEmpty(acls.getRoleACLs()) && MapUtils.isNotEmpty(oneTest.rolePermissions)) { + assertEquals("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - roleACLsMatched", oneTest.rolePermissions.size(), acls.getRoleACLs().size()); + for (Map.Entry> entry : acls.getRoleACLs().entrySet()) { String roleName = entry.getKey(); @@ -215,7 +283,11 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio } else if (!(MapUtils.isEmpty(acls.getRoleACLs()) && MapUtils.isEmpty(oneTest.rolePermissions))) { roleACLsMatched = false; } - assertTrue("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name, userACLsMatched && groupACLsMatched && roleACLsMatched); + assertTrue("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - userACLsMatched", userACLsMatched); + assertTrue("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - groupACLsMatched", groupACLsMatched); + assertTrue("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - roleACLsMatched", roleACLsMatched); + assertTrue("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - rowFiltersMatched", rowFiltersMatched); + assertTrue("getResourceACLs() failed! " + testCase.name + ":" + oneTest.name + " - dataMaskingMatched", dataMaskingMatched); } } } @@ -230,10 +302,13 @@ class TestCase { class OneTest { String name; - RangerAccessResource resource; + RangerAccessResource resource; + ResourceMatchingScope resourceMatchingScope; Map> userPermissions; Map> groupPermissions; Map> rolePermissions; + List rowFilters; + List dataMasks; } } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java index cc16655bff..c892060221 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngine.java @@ -28,6 +28,7 @@ import com.google.gson.reflect.TypeToken; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.ranger.audit.provider.AuditHandler; import org.apache.ranger.audit.provider.AuditProviderFactory; @@ -45,12 +46,14 @@ import org.apache.ranger.plugin.model.validation.RangerValidityScheduleValidator; import org.apache.ranger.plugin.model.validation.ValidationFailureDetails; import org.apache.ranger.plugin.policyengine.TestPolicyEngine.PolicyEngineTestCase.TestData; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator.RangerPolicyResourceEvaluator; import org.apache.ranger.plugin.policyevaluator.RangerValidityScheduleEvaluator; -import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceEvaluator; +import org.apache.ranger.plugin.policyresourcematcher.RangerResourceEvaluator; import org.apache.ranger.plugin.service.RangerBasePlugin; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.apache.ranger.plugin.util.RangerRequestedResources; import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.RangerUserStore; import org.apache.ranger.plugin.util.ServicePolicies; import org.apache.ranger.plugin.util.ServiceTags; import org.junit.AfterClass; @@ -64,16 +67,7 @@ import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Properties; -import java.util.Set; -import java.util.TimeZone; +import java.util.*; import static org.junit.Assert.*; @@ -105,21 +99,11 @@ public static void setUpBeforeClass() throws Exception { } else { System.out.println("Audit properties file missing: " + AUDIT_PROPERTIES_FILE); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.url", "jdbc:mysql://node-1:3306/xasecure_audit"); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.user", "xalogger"); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.password", "xalogger"); - auditProperties.setProperty("xasecure.audit.jpa.javax.persistence.jdbc.driver", "com.mysql.jdbc.Driver"); - auditProperties.setProperty("xasecure.audit.is.enabled", "false"); // Set this to true to enable audit logging auditProperties.setProperty("xasecure.audit.log4j.is.enabled", "false"); auditProperties.setProperty("xasecure.audit.log4j.is.async", "false"); auditProperties.setProperty("xasecure.audit.log4j.async.max.queue.size", "100000"); auditProperties.setProperty("xasecure.audit.log4j.async.max.flush.interval.ms", "30000"); - auditProperties.setProperty("xasecure.audit.db.is.enabled", "false"); - auditProperties.setProperty("xasecure.audit.db.is.async", "false"); - auditProperties.setProperty("xasecure.audit.db.async.max.queue.size", "100000"); - auditProperties.setProperty("xasecure.audit.db.async.max.flush.interval.ms", "30000"); - auditProperties.setProperty("xasecure.audit.db.batch.size", "100"); } AuditProviderFactory factory = AuditProviderFactory.getInstance(); @@ -265,6 +249,20 @@ public void testPolicyEngine_hive_incremental_update() { runTestsFromResourceFiles(hiveTestResourceFiles); } + @Test + public void testPolicyEngine_hdfs_incremental_update() { + String[] hdfsTestResourceFiles = {"/policyengine/test_policyengine_hdfs_incremental_update.json"}; + + runTestsFromResourceFiles(hdfsTestResourceFiles); + } + + @Test + public void testPolicyEngine_hdfs_incremental_update_for_wildcard_evaluators() { + String[] hdfsTestResourceFiles = {"/policyengine/test_policyengine_hdfs_incremental_update_for_wildcard_evaluators.json"}; + + runTestsFromResourceFiles(hdfsTestResourceFiles); + } + @Test public void testPolicyEngine_hiveForTag() { String[] hiveTestResourceFiles = { "/policyengine/test_policyengine_tag_hive.json" }; @@ -293,6 +291,13 @@ public void testPolicyEngine_hbase_namespace() { runTestsFromResourceFiles(hbaseTestResourceFiles); } + @Test + public void testPolicyEngine_hbaseForTag_filebased() { + String[] hbaseTestResourceFiles = { "/policyengine/test_policyengine_tag_hbase.json" }; + + runTestsFromResourceFiles(hbaseTestResourceFiles); + } + @Test public void testPolicyEngine_conditions() { String[] conditionsTestResourceFiles = { "/policyengine/test_policyengine_conditions.json" }; @@ -349,6 +354,13 @@ public void testPolicyEngine_hiveMasking() { runTestsFromResourceFiles(resourceFiles); } + @Test + public void testPolicyEngine_hiveMaskingWithReqExpressions() { + String[] resourceFiles = {"/policyengine/test_policyengine_hive_mask_filter_with_req_expressions.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + @Test public void testPolicyEngine_hiveTagMasking() { String[] resourceFiles = {"/policyengine/test_policyengine_tag_hive_mask.json"}; @@ -423,6 +435,91 @@ public void testPolicyEngine_PolicyPriority() { runTestsFromResourceFiles(resourceFiles); } + @Test + public void testPolicyEngine_superUserAccess() { + String[] resourceFiles = {"/policyengine/test_policyengine_super_user_access.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testPolicyEngine_auditFilterHdfs() { + String[] resourceFiles = {"/policyengine/test_policyengine_audit_filter_hdfs.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testPolicyEngine_descendantTagsDeny() { + String[] resourceFiles = {"/policyengine/test_policyengine_descendant_tags_deny.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + + @Test + public void testPolicyEngine_auditFilterHive() { + String[] resourceFiles = {"/policyengine/test_policyengine_audit_filter_hive.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testPolicyEngine_aws() { + String[] awsTestResourceFiles = {"/policyengine/test_policyengine_aws.json"}; + + runTestsFromResourceFiles(awsTestResourceFiles); + } + + @Test + public void testPolicyEngine_resourceWithReqExpressions() { + String[] resourceFiles = {"/policyengine/test_policyengine_resource_with_req_expressions.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testPolicyEngin_policyWithAdditionalResources() { + String[] resourceFiles = {"/policyengine/test_policyengine_policy_with_additional_resources.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testPolicyEngin_markerAccessTypes() { + String[] resourceFiles = {"/policyengine/test_policyengine_marker_access_types.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testAnyResourceAccess_Kafka() throws Exception { + String[] resourceFiles = {"/policyengine/test_policyengine_kafka.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testAnyResourceAccess_S3() throws Exception { + String[] resourceFiles = {"/policyengine/test_policyengine_aws_s3.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testResourceHierarchyTags() throws Exception { + String[] resourceFiles = {"/policyengine/test_policyengine_resource_hierarchy_tags.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + + @Test + public void testMultipleAccessAuthorization() throws Exception { + String[] resourceFiles = {"/policyengine/test_policyengine_hdfs_multiple_accesses.json"}; + + runTestsFromResourceFiles(resourceFiles); + } + private void runTestsFromResourceFiles(String[] resourceNames) { for(String resourceName : resourceNames) { InputStream inStream = this.getClass().getResourceAsStream(resourceName); @@ -454,6 +551,7 @@ private void runTests(InputStreamReader reader, String testName) { tagPolicies.setServiceName(testCase.tagPolicyInfo.serviceName); tagPolicies.setServiceDef(testCase.tagPolicyInfo.serviceDef); tagPolicies.setPolicies(testCase.tagPolicyInfo.tagPolicies); + tagPolicies.setServiceConfig(testCase.tagPolicyInfo.serviceConfig); if (StringUtils.isNotBlank(testCase.auditMode)) { tagPolicies.setAuditMode(testCase.auditMode); @@ -461,8 +559,9 @@ private void runTests(InputStreamReader reader, String testName) { servicePolicies.setTagPolicies(tagPolicies); } - boolean useForwardedIPAddress = pluginContext.getConfig().getBoolean("ranger.plugin.hive.use.x-forwarded-for.ipaddress", false); - String trustedProxyAddressString = pluginContext.getConfig().get("ranger.plugin.hive.trusted.proxy.ipaddresses"); + RangerPluginConfig config = pluginContext.getConfig(); + boolean useForwardedIPAddress = config.getBoolean(config.getPropertyPrefix() + ".use.x-forwarded-for.ipaddress", false); + String trustedProxyAddressString = config.get(config.getPropertyPrefix() + ".trusted.proxy.ipaddresses"); String[] trustedProxyAddresses = StringUtils.split(trustedProxyAddressString, ';'); if (trustedProxyAddresses != null) { for (int i = 0; i < trustedProxyAddresses.length; i++) { @@ -519,44 +618,33 @@ private void runTests(InputStreamReader reader, String testName) { roles.setRangerRoles(rolesSet); - RangerPolicyEngineOptions policyEngineOptions = pluginContext.getConfig().getPolicyEngineOptions(); + RangerPolicyEngineOptions policyEngineOptions = config.getPolicyEngineOptions(); - policyEngineOptions.disableAccessEvaluationWithPolicyACLSummary = true; - - setPluginConfig(pluginContext.getConfig(), ".super.users", testCase.superUsers); - setPluginConfig(pluginContext.getConfig(), ".super.groups", testCase.superGroups); - setPluginConfig(pluginContext.getConfig(), ".audit.exclude.users", testCase.auditExcludedUsers); - setPluginConfig(pluginContext.getConfig(), ".audit.exclude.groups", testCase.auditExcludedGroups); - setPluginConfig(pluginContext.getConfig(), ".audit.exclude.roles", testCase.auditExcludedRoles); + setPluginConfig(config, ".super.users", testCase.superUsers); + setPluginConfig(config, ".super.groups", testCase.superGroups); + setPluginConfig(config, ".audit.exclude.users", testCase.auditExcludedUsers); + setPluginConfig(config, ".audit.exclude.groups", testCase.auditExcludedGroups); + setPluginConfig(config, ".audit.exclude.roles", testCase.auditExcludedRoles); // so that setSuperUsersAndGroups(), setAuditExcludedUsersGroupsRoles() will be called on the pluginConfig - new RangerBasePlugin(pluginContext.getConfig()); + new RangerBasePlugin(config); RangerPolicyEngineImpl policyEngine = new RangerPolicyEngineImpl(servicePolicies, pluginContext, roles); policyEngine.setUseForwardedIPAddress(useForwardedIPAddress); policyEngine.setTrustedProxyAddresses(trustedProxyAddresses); - policyEngineOptions.disableAccessEvaluationWithPolicyACLSummary = false; - - RangerPolicyEngineImpl policyEngineForEvaluatingWithACLs = new RangerPolicyEngineImpl(servicePolicies, pluginContext, roles); - - policyEngineForEvaluatingWithACLs.setUseForwardedIPAddress(useForwardedIPAddress); - policyEngineForEvaluatingWithACLs.setTrustedProxyAddresses(trustedProxyAddresses); - - runTestCaseTests(policyEngine, policyEngineForEvaluatingWithACLs, testCase.serviceDef, testName, testCase.tests); + runTestCaseTests(policyEngine, testCase.serviceDef, testName, testCase.tests); if (testCase.updatedPolicies != null) { servicePolicies.setPolicyDeltas(testCase.updatedPolicies.policyDeltas); servicePolicies.setSecurityZones(testCase.updatedPolicies.securityZones); RangerPolicyEngine updatedPolicyEngine = RangerPolicyEngineImpl.getPolicyEngine(policyEngine, servicePolicies); - RangerPolicyEngine updatedPolicyEngineForEvaluatingWithACLs = RangerPolicyEngineImpl.getPolicyEngine(policyEngineForEvaluatingWithACLs, servicePolicies); - runTestCaseTests(updatedPolicyEngine, updatedPolicyEngineForEvaluatingWithACLs, testCase.serviceDef, testName, testCase.updatedTests); + runTestCaseTests(updatedPolicyEngine, testCase.serviceDef, testName, testCase.updatedTests); } } - private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerPolicyEngine policyEngineForEvaluatingWithACLs, RangerServiceDef serviceDef, String testName, List tests) { - + private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerServiceDef serviceDef, String testName, List tests) { RangerAccessRequest request = null; for(TestData test : tests) { @@ -630,21 +718,26 @@ private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerPolicyEngin RangerAccessResultProcessor auditHandler = new RangerDefaultAuditHandler(); + if (MapUtils.isNotEmpty(test.userAttributes) || MapUtils.isNotEmpty(test.groupAttributes)) { + RangerUserStore userStore = new RangerUserStore(); + + userStore.setUserAttrMapping(test.userAttributes); + userStore.setGroupAttrMapping(test.groupAttributes); + + RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore); + } + if(test.result != null) { RangerAccessResult expected = test.result; RangerAccessResult result; result = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ACCESS, auditHandler); + policyEngine.evaluateAuditPolicies(result); + assertNotNull("result was null! - " + test.name, result); assertEquals("isAllowed mismatched! - " + test.name, expected.getIsAllowed(), result.getIsAllowed()); assertEquals("isAudited mismatched! - " + test.name, expected.getIsAudited(), result.getIsAudited()); - - result = policyEngineForEvaluatingWithACLs.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ACCESS, auditHandler); - - assertNotNull("result was null! - " + test.name, result); - assertEquals("isAllowed mismatched! - " + test.name, expected.getIsAllowed(), result.getIsAllowed()); - assertEquals("isAudited mismatched! - " + test.name, expected.getIsAudited(), result.getIsAudited()); } if(test.dataMaskResult != null) { @@ -653,20 +746,13 @@ private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerPolicyEngin result = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_DATAMASK, auditHandler); + policyEngine.evaluateAuditPolicies(result); + assertNotNull("result was null! - " + test.name, result); assertEquals("maskType mismatched! - " + test.name, expected.getMaskType(), result.getMaskType()); assertEquals("maskCondition mismatched! - " + test.name, expected.getMaskCondition(), result.getMaskCondition()); assertEquals("maskedValue mismatched! - " + test.name, expected.getMaskedValue(), result.getMaskedValue()); assertEquals("policyId mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); - - result = policyEngineForEvaluatingWithACLs.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_DATAMASK, auditHandler); - - assertNotNull("result was null! - " + test.name, result); - assertEquals("maskType mismatched! - " + test.name, expected.getMaskType(), result.getMaskType()); - assertEquals("maskCondition mismatched! - " + test.name, expected.getMaskCondition(), result.getMaskCondition()); - assertEquals("maskedValue mismatched! - " + test.name, expected.getMaskedValue(), result.getMaskedValue()); - assertEquals("policyId mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); - } if(test.rowFilterResult != null) { @@ -675,16 +761,11 @@ private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerPolicyEngin result = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ROWFILTER, auditHandler); + policyEngine.evaluateAuditPolicies(result); + assertNotNull("result was null! - " + test.name, result); assertEquals("filterExpr mismatched! - " + test.name, expected.getFilterExpr(), result.getFilterExpr()); assertEquals("policyId mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); - - result = policyEngineForEvaluatingWithACLs.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ROWFILTER, auditHandler); - - assertNotNull("result was null! - " + test.name, result); - assertEquals("filterExpr mismatched! - " + test.name, expected.getFilterExpr(), result.getFilterExpr()); - assertEquals("policyId mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); - } if(test.resourceAccessInfo != null) { @@ -733,11 +814,14 @@ class TestData { public RangerAccessResult dataMaskResult; public RangerAccessResult rowFilterResult; public RangerResourceAccessInfo resourceAccessInfo; + public Map> userAttributes; + public Map> groupAttributes; } class TagPolicyInfo { public String serviceName; public RangerServiceDef serviceDef; + public Map serviceConfig; public List tagPolicies; } } @@ -824,9 +908,9 @@ private void runValiditySchedulerTests(String resourceName) { } } - assertTrue(testCase.name, isValid == testCase.result.isValid); - assertTrue(testCase.name, isApplicable == testCase.result.isApplicable); - assertTrue(testCase.name + ", [" + validationFailures +"]", validationFailures.size() == testCase.result.validationFailureCount); + assertEquals(testCase.name + " - isValid (validationFailures: " + validationFailures + ")", testCase.result.isValid, isValid); + assertEquals(testCase.name + " - isApplicable (validationFailures: " + validationFailures + ")", testCase.result.isApplicable, isApplicable); + assertEquals(testCase.name + " - validationFailureCount (validationFailures: " + validationFailures +")", testCase.result.validationFailureCount, validationFailures.size()); } } TimeZone.setDefault(defaultTZ); @@ -842,6 +926,27 @@ public RangerAccessRequest deserialize(JsonElement jsonObj, Type type, if (ret.getAccessTime() == null) { ret.setAccessTime(new Date()); } + Map reqContext = ret.getContext(); + Object accessTypes = reqContext.get(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPES); + if (accessTypes != null) { + Collection accessTypesCollection = (Collection) accessTypes; + Set requestedAccesses = new TreeSet<>(accessTypesCollection); + ret.getContext().put(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPES, requestedAccesses); + } + + Object accessTypeGroups = reqContext.get(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS); + if (accessTypeGroups != null) { + Set> setOfAccessTypeGroups = new HashSet<>(); + + List listOfAccessTypeGroups = (List) accessTypeGroups; + for (Object accessTypeGroup : listOfAccessTypeGroups) { + List accesses = (List) accessTypeGroup; + Set setOfAccesses = new TreeSet<>(accesses); + setOfAccessTypeGroups.add(setOfAccesses); + } + + reqContext.put(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS, setOfAccessTypeGroups); + } return ret; } @@ -874,17 +979,7 @@ public static boolean compare(PolicyEngine me, PolicyEngine other) { } if (ret) { - ret = Objects.equals(me.getResourceZoneTrie().keySet(), other.getResourceZoneTrie().keySet()); - - if (ret) { - for (Map.Entry entry : me.getResourceZoneTrie().entrySet()) { - ret = compareSubtree(entry.getValue(), other.getResourceZoneTrie().get(entry.getKey())); - - if (!ret) { - break; - } - } - } + ret = Objects.equals(me.getZoneMatcher(), other.getZoneMatcher()); } if (ret) { @@ -913,13 +1008,13 @@ public static boolean compare(RangerPolicyRepository me, RangerPolicyRepository public static boolean compareTrie(final int policyType, RangerPolicyRepository me, RangerPolicyRepository other) { boolean ret; - Map myTrie = me.getTrie(policyType); - Map otherTrie = other.getTrie(policyType); + Map> myTrie = me.getTrie(policyType); + Map> otherTrie = other.getTrie(policyType); ret = myTrie.size() == otherTrie.size(); if (ret) { - for (Map.Entry entry : myTrie.entrySet()) { + for (Map.Entry> entry : myTrie.entrySet()) { RangerResourceTrie myResourceTrie = entry.getValue(); RangerResourceTrie otherResourceTrie = otherTrie.get(entry.getKey()); @@ -1042,28 +1137,36 @@ private static boolean compareLists(Set me, Set other) { ret = me.size() == other.size(); if (ret) { - List meAsList = new ArrayList<>(me); - List otherAsList = new ArrayList<>(other); - - List myIds = new ArrayList<>(); - List otherIds = new ArrayList<>(); - for (RangerPolicyResourceEvaluator evaluator : meAsList) { - myIds.add(evaluator.getId()); + List meAsList = new ArrayList<>(me); + List otherAsList = new ArrayList<>(other); + List myIds = new ArrayList<>(); + List otherIds = new ArrayList<>(); + + for (RangerResourceEvaluator evaluator : meAsList) { + if (evaluator instanceof RangerPolicyResourceEvaluator) { + myIds.add(((RangerPolicyResourceEvaluator) evaluator).getPolicyId()); + } else { + myIds.add(evaluator.getId()); + } } - for (RangerPolicyResourceEvaluator evaluator : otherAsList) { - otherIds.add(evaluator.getId()); + + for (RangerResourceEvaluator evaluator : otherAsList) { + if (evaluator instanceof RangerPolicyResourceEvaluator) { + otherIds.add(((RangerPolicyResourceEvaluator) evaluator).getPolicyId()); + } else { + otherIds.add(evaluator.getId()); + } } ret = compareLongLists(myIds, otherIds); } } + return ret; } private static boolean compareLongLists(List me, List other) { return me.size() == CollectionUtils.intersection(me, other).size(); } - - } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineComparison.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineComparison.java index e2049832ff..6f644cc3bf 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineComparison.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineComparison.java @@ -138,11 +138,15 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio myTagEnricher.setAppId("test-compare-my-tags"); myTagEnricher.setServiceDef(myServicePolicies.getServiceDef()); myTagEnricher.setServiceName(myServiceTags.getServiceName()); + myTagEnricher.init(); + myTagEnricher.setServiceTags(myServiceTags); otherTagEnricher.setAppId("test-compare-other-tags"); otherTagEnricher.setServiceDef(myServicePolicies.getServiceDef()); otherTagEnricher.setServiceName(otherServiceTags.getServiceName()); + otherTagEnricher.init(); + otherTagEnricher.setServiceTags(otherServiceTags); isTagsEqual = TestPolicyEngine.compare(myTagEnricher, otherTagEnricher) && TestPolicyEngine.compare(otherTagEnricher, myTagEnricher); diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineForDeltas.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineForDeltas.java new file mode 100644 index 0000000000..961fde2eb8 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestPolicyEngineForDeltas.java @@ -0,0 +1,520 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.audit.provider.AuditHandler; +import org.apache.ranger.audit.provider.AuditProviderFactory; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler; +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicyDelta; +import org.apache.ranger.plugin.model.RangerRole; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.TestPolicyEngineForDeltas.PolicyEngineTestCase.TestData; +import org.apache.ranger.plugin.service.RangerBasePlugin; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.RangerRequestedResources; +import org.apache.ranger.plugin.util.RangerRoles; +import org.apache.ranger.plugin.util.ServicePolicies; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import static org.junit.Assert.*; + +public class TestPolicyEngineForDeltas { + static RangerPluginContext pluginContext; + static Gson gsonBuilder; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + pluginContext = new RangerPluginContext(new RangerPluginConfig("hive", null, "hive", "cl1", "on-prem", null)); + + gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSSZ") + .setPrettyPrinting() + .registerTypeAdapter(RangerAccessRequest.class, new RangerAccessRequestDeserializer()) + .registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer()) + .create(); + + // For setting up auditProvider + Properties auditProperties = new Properties(); + + String AUDIT_PROPERTIES_FILE = "xasecure-audit.properties"; + + File propFile = new File(AUDIT_PROPERTIES_FILE); + + if (propFile.exists()) { + System.out.println("Loading Audit properties file" + AUDIT_PROPERTIES_FILE); + + auditProperties.load(new FileInputStream(propFile)); + } else { + System.out.println("Audit properties file missing: " + AUDIT_PROPERTIES_FILE); + + auditProperties.setProperty("xasecure.audit.is.enabled", "false"); // Set this to true to enable audit logging + auditProperties.setProperty("xasecure.audit.log4j.is.enabled", "false"); + auditProperties.setProperty("xasecure.audit.log4j.is.async", "false"); + auditProperties.setProperty("xasecure.audit.log4j.async.max.queue.size", "100000"); + auditProperties.setProperty("xasecure.audit.log4j.async.max.flush.interval.ms", "30000"); + } + + AuditProviderFactory factory = AuditProviderFactory.getInstance(); + factory.init(auditProperties, "hdfs"); // second parameter does not matter for v2 + + AuditHandler provider = factory.getAuditProvider(); + + System.out.println("provider=" + provider.toString()); + + File file = File.createTempFile("ranger-admin-test-site", ".xml"); + file.deleteOnExit(); + + FileOutputStream outStream = new FileOutputStream(file); + OutputStreamWriter writer = new OutputStreamWriter(outStream); + + /* + // For setting up TestTagProvider + + writer.write("\n" + + " \n" + + " ranger.plugin.tag.policy.rest.url\n" + + " http://os-def:6080\n" + + " \n" + + " \n" + + " ranger.externalurl\n" + + " http://os-def:6080\n" + + " \n" + + "\n"); + */ + + writer.write("\n" + + /* + // For setting up TestTagProvider + " \n" + + " ranger.plugin.tag.policy.rest.url\n" + + " http://os-def:6080\n" + + " \n" + + " \n" + + " ranger.externalurl\n" + + " http://os-def:6080\n" + + " \n" + + */ + // For setting up x-forwarded-for for Hive + " \n" + + " ranger.plugin.hive.use.x-forwarded-for.ipaddress\n" + + " true\n" + + " \n" + + " \n" + + " ranger.plugin.hive.trusted.proxy.ipaddresses\n" + + " 255.255.255.255; 128.101.101.101;128.101.101.99\n" + + " \n" + + " \n" + + " ranger.plugin.tag.attr.additional.date.formats\n" + + " abcd||xyz||yyyy/MM/dd'T'HH:mm:ss.SSS'Z'\n" + + " \n" + + " \n" + + " ranger.policyengine.trie.builder.thread.count\n" + + " 3\n" + + " \n" + + "\n"); + writer.close(); + + pluginContext.getConfig().addResource(new org.apache.hadoop.fs.Path(file.toURI())); + } + + @AfterClass + public static void tearDownAfterClass() { + } + + @Test + public void testPolicyEngine_hdfs_incremental_delete() { + String[] hdfsTestResourceFiles = {"/policyengine/test_policyengine_hdfs_incremental_delete.json"}; + + runTestsFromResourceFiles(hdfsTestResourceFiles); + } + + private void runTestsFromResourceFiles(String[] resourceNames) { + for(String resourceName : resourceNames) { + InputStream inStream = this.getClass().getResourceAsStream(resourceName); + InputStreamReader reader = new InputStreamReader(inStream); + + runTests(reader, resourceName); + } + } + + private void runTests(InputStreamReader reader, String testName) { + PolicyEngineTestCase testCase = gsonBuilder.fromJson(reader, PolicyEngineTestCase.class); + + assertTrue("invalid input: " + testName, testCase != null && testCase.serviceDef != null && testCase.policies != null && testCase.testsInfo != null && testCase.testsInfo.tests != null); + + ServicePolicies servicePolicies = new ServicePolicies(); + servicePolicies.setPolicyVersion(100L); + servicePolicies.setServiceName(testCase.serviceName); + servicePolicies.setServiceDef(testCase.serviceDef); + servicePolicies.setPolicies(testCase.policies); + servicePolicies.setSecurityZones(testCase.securityZones); + servicePolicies.setServiceConfig(testCase.serviceConfig); + + if (StringUtils.isNotBlank(testCase.auditMode)) { + servicePolicies.setAuditMode(testCase.auditMode); + } + + if (null != testCase.tagPolicyInfo) { + ServicePolicies.TagPolicies tagPolicies = new ServicePolicies.TagPolicies(); + tagPolicies.setServiceName(testCase.tagPolicyInfo.serviceName); + tagPolicies.setServiceDef(testCase.tagPolicyInfo.serviceDef); + tagPolicies.setPolicies(testCase.tagPolicyInfo.tagPolicies); + tagPolicies.setServiceConfig(testCase.tagPolicyInfo.serviceConfig); + + if (StringUtils.isNotBlank(testCase.auditMode)) { + tagPolicies.setAuditMode(testCase.auditMode); + } + servicePolicies.setTagPolicies(tagPolicies); + } + + RangerPluginConfig config = pluginContext.getConfig(); + boolean useForwardedIPAddress = config.getBoolean(config.getPropertyPrefix() + ".use.x-forwarded-for.ipaddress", false); + String trustedProxyAddressString = config.get(config.getPropertyPrefix() + ".trusted.proxy.ipaddresses"); + String[] trustedProxyAddresses = StringUtils.split(trustedProxyAddressString, ';'); + if (trustedProxyAddresses != null) { + for (int i = 0; i < trustedProxyAddresses.length; i++) { + trustedProxyAddresses[i] = trustedProxyAddresses[i].trim(); + } + } + + RangerRoles roles = new RangerRoles(); + roles.setServiceName(testCase.serviceName); + roles.setRoleVersion(-1L); + Set rolesSet = new HashSet<>(); + + Map> userRoleMapping = testCase.userRoles; + Map> groupRoleMapping = testCase.groupRoles; + Map> roleRoleMapping = testCase.roleRoles; + if (userRoleMapping != null) { + for (Map.Entry> userRole : userRoleMapping.entrySet()) { + String user = userRole.getKey(); + Set userRoles = userRole.getValue(); + RangerRole.RoleMember userRoleMember = new RangerRole.RoleMember(user, true); + List userRoleMembers = Arrays.asList(userRoleMember); + for (String usrRole : userRoles) { + RangerRole rangerUserRole = new RangerRole(usrRole, usrRole, null, userRoleMembers, null); + rolesSet.add(rangerUserRole); + } + } + } + + if (groupRoleMapping != null) { + for (Map.Entry> groupRole : groupRoleMapping.entrySet()) { + String group = groupRole.getKey(); + Set groupRoles = groupRole.getValue(); + RangerRole.RoleMember groupRoleMember = new RangerRole.RoleMember(group, true); + List groupRoleMembers = Arrays.asList(groupRoleMember); + for (String grpRole : groupRoles) { + RangerRole rangerGroupRole = new RangerRole(grpRole, grpRole, null, null, groupRoleMembers); + rolesSet.add(rangerGroupRole); + } + } + } + + if (roleRoleMapping != null) { + for (Map.Entry> roleRole : roleRoleMapping.entrySet()) { + String role = roleRole.getKey(); + Set roleRoles = roleRole.getValue(); + RangerRole.RoleMember roleRoleMember = new RangerRole.RoleMember(role, true); + List roleRoleMembers = Arrays.asList(roleRoleMember); + for (String rleRole : roleRoles) { + RangerRole rangerRoleRole = new RangerRole(rleRole, rleRole, null, null, null, roleRoleMembers); + rolesSet.add(rangerRoleRole); + } + } + } + + roles.setRangerRoles(rolesSet); + + RangerPolicyEngineOptions policyEngineOptions = config.getPolicyEngineOptions(); + + setPluginConfig(config, ".super.users", testCase.superUsers); + setPluginConfig(config, ".super.groups", testCase.superGroups); + setPluginConfig(config, ".audit.exclude.users", testCase.auditExcludedUsers); + setPluginConfig(config, ".audit.exclude.groups", testCase.auditExcludedGroups); + setPluginConfig(config, ".audit.exclude.roles", testCase.auditExcludedRoles); + + // so that setSuperUsersAndGroups(), setAuditExcludedUsersGroupsRoles() will be called on the pluginConfig + new RangerBasePlugin(config); + + RangerPolicyEngineImpl policyEngine = new RangerPolicyEngineImpl(servicePolicies, pluginContext, roles); + + policyEngine.setUseForwardedIPAddress(useForwardedIPAddress); + policyEngine.setTrustedProxyAddresses(trustedProxyAddresses); + + PolicyEngineTestCase.TestsInfo testsInfo = testCase.testsInfo; + do { + runTestCaseTests(policyEngine, testCase.serviceDef, testName, testsInfo.tests); + if (testsInfo.updatedPolicies != null && CollectionUtils.isNotEmpty(testsInfo.updatedPolicies.policyDeltas)) { + servicePolicies.setPolicyDeltas(testsInfo.updatedPolicies.policyDeltas); + servicePolicies.setPolicies(null); + if (MapUtils.isNotEmpty(testsInfo.updatedPolicies.securityZones)) { + servicePolicies.setSecurityZones(testsInfo.updatedPolicies.securityZones); + } + policyEngine = (RangerPolicyEngineImpl) RangerPolicyEngineImpl.getPolicyEngine(policyEngine, servicePolicies); + + testsInfo = null; + } else { + testsInfo = null; + } + + } while (testsInfo != null && testsInfo.tests != null); + + } + + private void runTestCaseTests(RangerPolicyEngine policyEngine, RangerServiceDef serviceDef, String testName, List tests) { + RangerAccessRequest request; + + for(TestData test : tests) { + request = test.request; + + if (request.getContext().containsKey(RangerAccessRequestUtil.KEY_CONTEXT_TAGS) || + request.getContext().containsKey(RangerAccessRequestUtil.KEY_CONTEXT_REQUESTED_RESOURCES)) { + // Create a new AccessRequest + RangerAccessRequestImpl newRequest = + new RangerAccessRequestImpl(request.getResource(), request.getAccessType(), + request.getUser(), request.getUserGroups(), null); + + newRequest.setClientType(request.getClientType()); + newRequest.setAccessTime(request.getAccessTime()); + newRequest.setAction(request.getAction()); + newRequest.setRemoteIPAddress(request.getRemoteIPAddress()); + newRequest.setForwardedAddresses(request.getForwardedAddresses()); + newRequest.setRequestData(request.getRequestData()); + newRequest.setSessionId(request.getSessionId()); + + Map context = request.getContext(); + String tagsJsonString = (String) context.get(RangerAccessRequestUtil.KEY_CONTEXT_TAGS); + context.remove(RangerAccessRequestUtil.KEY_CONTEXT_TAGS); + + if(!StringUtils.isEmpty(tagsJsonString)) { + try { + Type setType = new TypeToken>() { + }.getType(); + Set tags = gsonBuilder.fromJson(tagsJsonString, setType); + + context.put(RangerAccessRequestUtil.KEY_CONTEXT_TAGS, tags); + } catch (Exception e) { + System.err.println("TestPolicyEngineForDeltas.runTests(): error parsing TAGS JSON string in file " + testName + ", tagsJsonString=" + + tagsJsonString + ", exception=" + e); + } + } else if (request.getContext().containsKey(RangerAccessRequestUtil.KEY_CONTEXT_REQUESTED_RESOURCES)) { + String resourcesJsonString = (String) context.get(RangerAccessRequestUtil.KEY_CONTEXT_REQUESTED_RESOURCES); + context.remove(RangerAccessRequestUtil.KEY_CONTEXT_REQUESTED_RESOURCES); + if (!StringUtils.isEmpty(resourcesJsonString)) { + try { + /* + Reader stringReader = new StringReader(resourcesJsonString); + RangerRequestedResources resources = gsonBuilder.fromJson(stringReader, RangerRequestedResources.class); + */ + + Type myType = new TypeToken() { + }.getType(); + RangerRequestedResources resources = gsonBuilder.fromJson(resourcesJsonString, myType); + + context.put(RangerAccessRequestUtil.KEY_CONTEXT_REQUESTED_RESOURCES, resources); + } catch (Exception e) { + System.err.println("TestPolicyEngineForDeltas.runTests(): error parsing REQUESTED_RESOURCES string in file " + testName + ", resourcesJsonString=" + + resourcesJsonString + ", exception=" + e); + } + } + } + newRequest.setContext(context); + + // accessResource.ServiceDef is set here, so that we can skip call to policyEngine.preProcess() which + // sets the serviceDef in the resource AND calls enrichers. We dont want enrichers to be called when + // context already contains tags -- This may change when we want enrichers to enrich request in the + // presence of tags!!! + + // Safe cast + RangerAccessResourceImpl accessResource = (RangerAccessResourceImpl) request.getResource(); + accessResource.setServiceDef(serviceDef); + + request = newRequest; + + } + + RangerAccessResultProcessor auditHandler = new RangerDefaultAuditHandler(); + + if(test.result != null) { + RangerAccessResult expected = test.result; + RangerAccessResult result; + + result = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ACCESS, auditHandler); + + policyEngine.evaluateAuditPolicies(result); + + assertNotNull("result was null! - " + test.name, result); + assertEquals("isAllowed mismatched! - " + test.name, expected.getIsAllowed(), result.getIsAllowed()); + assertEquals("policy-id mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); + assertEquals("isAudited mismatched! - " + test.name, expected.getIsAudited(), result.getIsAudited() && result.getIsAuditedDetermined()); + } + + if(test.dataMaskResult != null) { + RangerAccessResult expected = test.dataMaskResult; + RangerAccessResult result; + + result = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_DATAMASK, auditHandler); + + policyEngine.evaluateAuditPolicies(result); + + assertNotNull("result was null! - " + test.name, result); + assertEquals("maskType mismatched! - " + test.name, expected.getMaskType(), result.getMaskType()); + assertEquals("maskCondition mismatched! - " + test.name, expected.getMaskCondition(), result.getMaskCondition()); + assertEquals("maskedValue mismatched! - " + test.name, expected.getMaskedValue(), result.getMaskedValue()); + assertEquals("policyId mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); + } + + if(test.rowFilterResult != null) { + RangerAccessResult expected = test.rowFilterResult; + RangerAccessResult result; + + result = policyEngine.evaluatePolicies(request, RangerPolicy.POLICY_TYPE_ROWFILTER, auditHandler); + + policyEngine.evaluateAuditPolicies(result); + + assertNotNull("result was null! - " + test.name, result); + assertEquals("filterExpr mismatched! - " + test.name, expected.getFilterExpr(), result.getFilterExpr()); + assertEquals("policyId mismatched! - " + test.name, expected.getPolicyId(), result.getPolicyId()); + } + + if(test.resourceAccessInfo != null) { + + RangerResourceAccessInfo expected = new RangerResourceAccessInfo(test.resourceAccessInfo); + RangerResourceAccessInfo result = policyEngine.getResourceAccessInfo(test.request); + + assertNotNull("result was null! - " + test.name, result); + assertEquals("allowedUsers mismatched! - " + test.name, expected.getAllowedUsers(), result.getAllowedUsers()); + assertEquals("allowedGroups mismatched! - " + test.name, expected.getAllowedGroups(), result.getAllowedGroups()); + assertEquals("deniedUsers mismatched! - " + test.name, expected.getDeniedUsers(), result.getDeniedUsers()); + assertEquals("deniedGroups mismatched! - " + test.name, expected.getDeniedGroups(), result.getDeniedGroups()); + } + } + + } + + private void setPluginConfig(RangerPluginConfig conf, String suffix, Set value) { + conf.set(conf.getPropertyPrefix() + suffix, CollectionUtils.isNotEmpty(value) ? StringUtils.join(value, ',') : ""); + } + + static class PolicyEngineTestCase { + public String serviceName; + public RangerServiceDef serviceDef; + public List policies; + public TagPolicyInfo tagPolicyInfo; + public Map securityZones; + public Map> userRoles; + public Map> groupRoles; + public Map> roleRoles; + public String auditMode; + public TestsInfo testsInfo; + //public List tests; + public Map serviceConfig; + //public UpdatedPolicies updatedPolicies; + //public List updatedTests; + public Set superUsers; + public Set superGroups; + public Set auditExcludedUsers; + public Set auditExcludedGroups; + public Set auditExcludedRoles; + + static class TestsInfo { + public List tests; + public UpdatedPolicies updatedPolicies; + public TestsInfo updatedTestsInfo; + + } + static class TestData { + public String name; + public RangerAccessRequest request; + public RangerAccessResult result; + public RangerAccessResult dataMaskResult; + public RangerAccessResult rowFilterResult; + public RangerResourceAccessInfo resourceAccessInfo; + } + + static class TagPolicyInfo { + public String serviceName; + public RangerServiceDef serviceDef; + public Map serviceConfig; + public List tagPolicies; + } + } + + static class UpdatedPolicies { + public Map securityZones; + public List policyDeltas; + } + + static class RangerAccessRequestDeserializer implements JsonDeserializer { + @Override + public RangerAccessRequest deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + RangerAccessRequestImpl ret = gsonBuilder.fromJson(jsonObj, RangerAccessRequestImpl.class); + + ret.setAccessType(ret.getAccessType()); // to force computation of isAccessTypeAny and isAccessTypeDelegatedAdmin + if (ret.getAccessTime() == null) { + ret.setAccessTime(new Date()); + } + + return ret; + } + } + + static class RangerResourceDeserializer implements JsonDeserializer { + @Override + public RangerAccessResource deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + return gsonBuilder.fromJson(jsonObj, RangerAccessResourceImpl.class); + } + } + +} + diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestProjectProvider.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestProjectProvider.java index ef99ecbfbe..4027fdde73 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestProjectProvider.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestProjectProvider.java @@ -20,9 +20,9 @@ package org.apache.ranger.plugin.policyengine; import org.apache.commons.lang.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.apache.ranger.plugin.contextenricher.RangerAbstractContextEnricher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Properties; @@ -53,7 +53,7 @@ @see Java Properties List */ public class TestProjectProvider extends RangerAbstractContextEnricher { - private static final Log LOG = LogFactory.getLog(TestProjectProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(TestProjectProvider.class); private String contextName = "PROJECT"; private Properties userProjectMap = null; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java index c07a7ea021..f45bc43f61 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerAuthContext.java @@ -19,6 +19,7 @@ package org.apache.ranger.plugin.policyengine; +import static org.apache.ranger.plugin.util.ServiceDefUtil.IMPLICIT_GDS_ENRICHER_NAME; import static org.junit.Assert.*; import java.io.InputStream; @@ -28,7 +29,9 @@ import org.apache.commons.collections.MapUtils; import org.apache.ranger.plugin.contextenricher.RangerContextEnricher; +import org.apache.ranger.plugin.contextenricher.RangerGdsEnricher; import org.apache.ranger.plugin.contextenricher.RangerTagEnricher; +import org.apache.ranger.plugin.policyengine.gds.GdsPolicyEngine; import org.apache.ranger.plugin.service.RangerAuthContext; import org.apache.ranger.plugin.service.RangerBasePlugin; import org.apache.ranger.plugin.util.ServicePolicies; @@ -93,7 +96,7 @@ private void runTests(InputStreamReader reader, String fileName) throws Exceptio RangerAuthContext ctx = plugin.getCurrentRangerAuthContext(); Map contextEnrichers = ctx.getRequestContextEnrichers(); - assertTrue(fileName + "-" + testName + " - Empty contextEnrichers", MapUtils.isNotEmpty(contextEnrichers) && contextEnrichers.size() == 2); + assertTrue(fileName + "-" + testName + " - Empty contextEnrichers", MapUtils.isNotEmpty(contextEnrichers) && contextEnrichers.size() == 3); for (Map.Entry entry : contextEnrichers.entrySet()) { RangerContextEnricher enricher = entry.getKey(); @@ -104,8 +107,10 @@ private void runTests(InputStreamReader reader, String fileName) throws Exceptio assertTrue(fileName + "-" + testName + " - Invalid contextEnricher", enricherData instanceof RangerContextEnricher); } else if (enricherName.equals("TagEnricher")) { assertTrue("- Invalid contextEnricher", (enricherData instanceof RangerTagEnricher || enricherData instanceof RangerTagEnricher.EnrichedServiceTags)); + } else if (enricherName.equals(IMPLICIT_GDS_ENRICHER_NAME) || enricher instanceof RangerGdsEnricher) { + assertTrue("- Invalid contextEnricher", (enricherData instanceof RangerGdsEnricher || enricherData instanceof GdsPolicyEngine)); } else { - assertTrue(fileName + "-" + testName + " - Unexpected type of contextEnricher", false); + fail(fileName + "-" + testName + " - Unexpected type of contextEnricher: " + enricher); } } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerSecurityZoneMatcher.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerSecurityZoneMatcher.java new file mode 100644 index 0000000000..1506df3b5c --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/TestRangerSecurityZoneMatcher.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.policyengine; + + +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.util.ServicePolicies.SecurityZoneInfo; +import org.junit.Test; + +import java.util.*; + +import static org.junit.Assert.*; + +public class TestRangerSecurityZoneMatcher { + final RangerPluginContext pluginContext = new RangerPluginContext(new RangerPluginConfig("hive", null, "hive", "cl1", "on-prem", null)); + final Map securityZones = createSecurityZones(); + final RangerServiceDef serviceDef = createServiceDef(); + + @Test + public void testZoneMatcher() { + RangerSecurityZoneMatcher zoneMatcher = new RangerSecurityZoneMatcher(securityZones, serviceDef, pluginContext); + + RangerAccessResource res; + Set zones; + + res = createResource("database", "db1", "table", "tbl1"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("z1"), zones); + + res = createResource("database", "db2", "table", "tbl1"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("z2"), zones); + + res = createResource("database", "db3", "table", "test_1"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("z3"), zones); + + res = createResource("database", "db3", "table", "test_2"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("z3"), zones); + + res = createResource("database", "db3", "table", "orders"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertNull(zones); + + res = createResource("database", "db3", "table", "user_1"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("z4"), zones); + + res = createResource("database", "db3", "table", "user_2"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("z4"), zones); + + res = createResource("database", "db3"); + zones = zoneMatcher.getZonesForResourceAndChildren(res); + assertEquals(createSet("", "z3", "z4"), zones); + } + + private Map createSecurityZones() { + HashMap> db1 = new HashMap>() {{ put("database", Arrays.asList("db1")); }}; + HashMap> db2 = new HashMap>() {{ put("database", Arrays.asList("db2")); }}; + HashMap> db3Test = new HashMap>() {{ put("database", Arrays.asList("db3")); put("table", Arrays.asList("test_*")); }}; + HashMap> db4User = new HashMap>() {{ put("database", Arrays.asList("db3")); put("table", Arrays.asList("user_*")); }}; + + SecurityZoneInfo z1 = new SecurityZoneInfo(); + SecurityZoneInfo z2 = new SecurityZoneInfo(); + SecurityZoneInfo z3 = new SecurityZoneInfo(); + SecurityZoneInfo z4 = new SecurityZoneInfo(); + + z1.setZoneName("z1"); + z1.setResources(Arrays.asList(db1)); + + z2.setZoneName("z2"); + z2.setResources(Arrays.asList(db2)); + + z3.setZoneName("z3"); + z3.setResources(Arrays.asList(db3Test)); + + z4.setZoneName("z4"); + z4.setResources(Arrays.asList(db4User)); + + Map ret = new HashMap<>(); + + ret.put(z1.getZoneName(), z1); + ret.put(z2.getZoneName(), z2); + ret.put(z3.getZoneName(), z3); + ret.put(z4.getZoneName(), z4); + + return ret; + } + + private RangerServiceDef createServiceDef() { + RangerServiceDef ret = new RangerServiceDef(); + + ret.setName("hive"); + ret.setResources(createResourceDefs()); + + return ret; + } + + private List createResourceDefs() { + List ret = new ArrayList<>(); + + ret.add(createResourceDef("database", null)); + ret.add(createResourceDef("table", "database")); + ret.add(createResourceDef("column", "table")); + + return ret; + } + + private RangerResourceDef createResourceDef(String name, String parent) { + RangerResourceDef ret = new RangerResourceDef(); + + ret.setName(name); + ret.setType("string"); + ret.setParent(parent); + + return ret; + } + + private RangerAccessResource createResource(String...args) { + RangerAccessResourceImpl ret = new RangerAccessResourceImpl(); + + for (int i = 1; i < args.length; i += 2) { + ret.setValue(args[i - 1], args[i]); + } + + return ret; + } + + private Set createSet(String...args) { + Set ret = new HashSet<>(); + + for (String arg : args) { + ret.add(arg); + } + + return ret; + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/gds/TestGdsPolicyEngine.java b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/gds/TestGdsPolicyEngine.java new file mode 100644 index 0000000000..7bfebbb99e --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/policyengine/gds/TestGdsPolicyEngine.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.ranger.plugin.policyengine.gds; + +import com.google.gson.*; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.validation.RangerServiceDefHelper; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.util.RangerAccessRequestUtil; +import org.apache.ranger.plugin.util.ServiceDefUtil; +import org.apache.ranger.plugin.util.ServiceGdsInfo; +import org.apache.ranger.plugin.util.ServicePolicies.SecurityZoneInfo; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.util.*; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TestGdsPolicyEngine { + static Gson gsonBuilder; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSSZ") + .setPrettyPrinting() + .registerTypeAdapter(RangerAccessRequest.class, new RangerAccessRequestDeserializer()) + .registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer()) + .create(); + } + + @Test + public void testGdsPolicyEngineHive() throws Exception { + runTestsFromResourceFile("/policyengine/gds/test_gds_policy_engine_hive.json"); + } + + private void runTestsFromResourceFile(String resourceFile) throws Exception { + InputStream inStream = this.getClass().getResourceAsStream(resourceFile); + InputStreamReader reader = new InputStreamReader(inStream); + + runTests(reader, resourceFile); + } + + private void runTests(Reader reader, String testName) { + GdsPolicyEngineTestCase testCase = gsonBuilder.fromJson(reader, GdsPolicyEngineTestCase.class); + + if (StringUtils.isNotBlank(testCase.gdsInfoFilename)) { + InputStream inStream = this.getClass().getResourceAsStream(testCase.gdsInfoFilename); + + testCase.gdsInfo = gsonBuilder.fromJson(new InputStreamReader(inStream), ServiceGdsInfo.class); + } + + assertTrue("invalid input: " + testName, testCase != null && testCase.gdsInfo != null && testCase.tests != null); + + testCase.serviceDef.setMarkerAccessTypes(ServiceDefUtil.getMarkerAccessTypes(testCase.serviceDef.getAccessTypes())); + + RangerPluginContext pluginContext = new RangerPluginContext(new RangerPluginConfig(testCase.serviceDef.getName(), null, "hive", "cl1", "on-prem", null)); + RangerSecurityZoneMatcher zoneMatcher = new RangerSecurityZoneMatcher(testCase.securityZones, testCase.serviceDef, pluginContext); + GdsPolicyEngine policyEngine = new GdsPolicyEngine(testCase.gdsInfo, new RangerServiceDefHelper(testCase.serviceDef, false), pluginContext); + + for (TestData test : testCase.tests) { + if (test.request != null) { + Set zoneNames = zoneMatcher.getZonesForResourceAndChildren(test.request.getResource()); + + RangerAccessRequestUtil.setResourceZoneNamesInContext(test.request, zoneNames); + + if (test.acls != null) { + RangerResourceACLs acls = policyEngine.getResourceACLs(test.request); + + assertEquals(test.name, test.acls, acls); + } else { + GdsAccessResult result = policyEngine.evaluate(test.request); + + assertEquals(test.name, test.result, result); + } + } else if (test.sharedWith != null) { + Set users = test.sharedWith.get("users"); + Set groups = test.sharedWith.get("groups"); + Set roles = test.sharedWith.get("roles"); + + if (test.datasets != null) { + Set datasets = policyEngine.getDatasetsSharedWith(users, groups, roles); + + assertEquals(test.name, test.datasets, datasets); + } + + if (test.projects != null) { + Set projects = policyEngine.getProjectsSharedWith(users, groups, roles); + + assertEquals(test.name, test.projects, projects); + } + } else if (test.resourceIds != null) { + Iterator iter; + + if (test.datasetId != null) { + iter = policyEngine.getDatasetResources(test.datasetId); + } else if (test.projectId != null) { + iter = policyEngine.getProjectResources(test.projectId); + } else if (test.dataShareId != null) { + iter = policyEngine.getDataShareResources(test.dataShareId); + } else if (test.projectIds != null || test.datasetIds != null || test.dataShareIds != null) { + iter = policyEngine.getResources(test.projectIds, test.datasetIds, test.dataShareIds); + } else { + iter = Collections.emptyIterator(); + } + + Set resourceIds = new HashSet<>(); + + iter.forEachRemaining(e -> resourceIds.add(e.getId())); + + assertEquals(test.name, test.resourceIds, resourceIds); + } + } + } + + static class GdsPolicyEngineTestCase { + public RangerServiceDef serviceDef; + public Map securityZones; + public ServiceGdsInfo gdsInfo; + public String gdsInfoFilename; + public List tests; + } + + static class TestData { + public String name; + public RangerAccessRequest request; + public GdsAccessResult result; + public RangerResourceACLs acls; + public Map> sharedWith; // principals + public Set datasets; + public Set projects; + public Long datasetId; + public Long projectId; + public Long dataShareId; + public List datasetIds; + public List projectIds; + public List dataShareIds; + public Set resourceIds; + } + + static class RangerAccessRequestDeserializer implements JsonDeserializer { + @Override + public RangerAccessRequest deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + RangerAccessRequestImpl ret = gsonBuilder.fromJson(jsonObj, RangerAccessRequestImpl.class); + + ret.setAccessType(ret.getAccessType()); // to force computation of isAccessTypeAny and isAccessTypeDelegatedAdmin + if (ret.getAccessTime() == null) { + ret.setAccessTime(new Date()); + } + Map reqContext = ret.getContext(); + Object accessTypes = reqContext.get(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPES); + if (accessTypes != null) { + Collection accessTypesCollection = (Collection) accessTypes; + Set requestedAccesses = new TreeSet<>(accessTypesCollection); + ret.getContext().put(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPES, requestedAccesses); + } + + Object accessTypeGroups = reqContext.get(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS); + if (accessTypeGroups != null) { + Set> setOfAccessTypeGroups = new HashSet<>(); + + List listOfAccessTypeGroups = (List) accessTypeGroups; + for (Object accessTypeGroup : listOfAccessTypeGroups) { + List accesses = (List) accessTypeGroup; + Set setOfAccesses = new TreeSet<>(accesses); + setOfAccessTypeGroups.add(setOfAccesses); + } + + reqContext.put(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS, setOfAccessTypeGroups); + } + + return ret; + } + } + + static class RangerResourceDeserializer implements JsonDeserializer { + @Override + public RangerAccessResource deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + return gsonBuilder.fromJson(jsonObj, RangerAccessResourceImpl.class); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java index e31437fc16..b6864183de 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerAbstractResourceMatcherTest.java @@ -19,6 +19,9 @@ package org.apache.ranger.plugin.resourcematcher; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType; import org.junit.Test; import java.util.Map; @@ -42,9 +45,16 @@ public void test_isAllPossibleValues() { static class AbstractMatcherWrapper extends RangerAbstractResourceMatcher { @Override - public boolean isMatch(Object resource, Map evalContext) { + public boolean isMatch(Object resource, ResourceElementMatchingScope matchingScope, Map evalContext) { fail("This method is not expected to be used by test!"); return false; } + + @Override + public ResourceElementMatchType getMatchType(Object resource, ResourceElementMatchingScope matchingScope, Map evalContext) { + fail("This method is not expected to be used by test!"); + return RangerAccessRequest.ResourceElementMatchType.NONE; + } + } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java index c635e0ded8..f588f3e2a4 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerDefaultResourceMatcherTest.java @@ -19,8 +19,11 @@ package org.apache.ranger.plugin.resourcematcher; -import com.google.common.collect.Lists; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.junit.Test; @@ -28,56 +31,152 @@ import java.util.Map; import static org.junit.Assert.*; +import static org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchType.*; public class RangerDefaultResourceMatcherTest { Object[][] data = { - // { resource, policy, excludes, result - { "*", "*", false, true, "user" }, // resource is all values - { "*", "*", true, false, "user" }, - { "*", "a*", false, false, "user" }, // but, policy is not match any - { "*", "a*", true, false, "user" }, // ==> compare with above: exclude flag has no effect here - { "a*", "a", false, false, "user" }, // resource has regex marker! - { "a*", "a", true, true, "user" }, - { "a", "a", false, true, "user" }, // exact match - { "a", "a", true, false, "user" }, - { "a1", "a*", false, true, "user" }, // trivial regex match - { "a1", "a*", true, false, "user" }, + // { resource, policy, excludes, matchType, result, user } + { "*", "*", false, SELF, true, "user" }, // resource is all values + { "*", "*", true, NONE, false, "user" }, + { "*", "a*", false, NONE, false, "user" }, // but, policy is not match any + { "*", "a*", true, NONE, false, "user" }, // ==> compare with above: exclude flag has no effect here + { "a*", "a", false, NONE, false, "user" }, // resource has regex marker! + { "a*", "a", true, SELF, true, "user" }, + { "a", "a", false, SELF, true, "user" }, // exact match + { "a", "a", true, NONE, false, "user" }, + { "a1", "a*", false, SELF, true, "user" }, // trivial regex match + { "a1", "a*", true, NONE, false, "user" }, + + // matchScope=SELF, excludes=false + { "*", "*", false, SELF, true, "user" }, // resource is all values + { "a*", "*", false, SELF, true, "user" }, // resource has regex, policy matches + { "a*", "a", false, NONE, false, "user" }, // resource has regex, policy does not match + { "*", "a*", false, NONE, false, "user" }, // resource has regex, policy does not match + { "a*", "a*", false, SELF, true, "user" }, // resource has regex, policy matches + { "a?", "a*", false, SELF, true, "user" }, // resource has regex, policy matches + { "a*b", "a*", false, SELF, true, "user" }, // resource has regex, policy matches + { "a?b", "a*", false, SELF, true, "user" }, // resource has regex, policy matches + { "a*b", "a*b", false, SELF, true, "user" }, // resource has regex, policy matches + { "a?b", "a?b", false, SELF, true, "user" }, // resource has regex, policy matches + { "a1b", "a1b", false, SELF, true, "user" }, // exact match + { "a1b", "a*", false, SELF, true, "user" }, // regex match - suffix + { "a1b", "*b", false, SELF, true, "user" }, // regex match - prefix + { "a1b", "*1*", false, SELF, true, "user" }, // regex match + { "a1b", "a?b", false, SELF, true, "user" }, // regex match - single char + { "a", "abc", false, NONE, false, "user" }, // policy has more than resource + { "ab", "abc", false, NONE, false, "user" }, // policy has more than resource + { "ab", "*c", false, NONE, false, "user" }, // policy has more than resource + { "*b", "a*bc", false, NONE, false, "user" }, // policy has more than resource + { "a*b", "a*bc", false, NONE, false, "user" }, // policy has more than resource + + // matchScope=SELF, excludes=true + { "*", "*", true, NONE, false, "user" }, + { "a*", "*", true, NONE, false, "user" }, + { "a*", "a", true, SELF, true, "user" }, + { "*", "a*", true, NONE, false, "user" }, // ==> compare with above: exclude flag has no effect here + { "a*", "a*", true, NONE, false, "user" }, + { "a?", "a*", true, NONE, false, "user" }, + { "a*b", "a*", true, NONE, false, "user" }, + { "a?b", "a*", true, NONE, false, "user" }, + { "a*b", "a*b", true, NONE, false, "user" }, + { "a?b", "a?b", true, NONE, false, "user" }, + { "a1b", "a1b", true, NONE, false, "user" }, + { "a1b", "a*", true, NONE, false, "user" }, + { "a1b", "*b", true, NONE, false, "user" }, + { "a1b", "*1*", true, NONE, false, "user" }, + { "a1b", "a?b", true, NONE, false, "user" }, + { "a", "abc", true, SELF, true, "user" }, + { "ab", "abc", true, SELF, true, "user" }, + { "ab", "*c", true, SELF, true, "user" }, + { "*b", "a*bc", true, SELF, true, "user" }, + { "a*b", "a*bc", true, SELF, true, "user" }, + }; + + Object[][] dataForPrefixMatch = { + // { resource, policy, excludes, matchType, result, user } + { "a", "abc", false, PREFIX, true, "user" }, + { "ab", "abc", false, PREFIX, true, "user" }, + { "ab", "*c", false, PREFIX, true, "user" }, + { "a", "a*c", false, PREFIX, true, "user" }, + { "ab", "a*c", false, PREFIX, true, "user" }, + { "abc", "a*c", false, SELF, true, "user" }, + { "abcd", "a*c", false, PREFIX, true, "user" }, + { "abcd", "a*c*d", false, SELF, true, "user" }, + { "abcd", "a*c*de", false, PREFIX, true, "user" }, + { "acbd", "ab*c", false, NONE, false, "user" }, + { "b", "ab*c", false, NONE, false, "user" }, + { "a", "ab*", false, PREFIX, true, "user" }, + { "b", "ab*", false, NONE, false, "user" }, }; @Test public void testIsMatch() throws Exception { + ResourceElementMatchingScope matchScope = ResourceElementMatchingScope.SELF; + for (Object[] row : data) { - String resource = (String)row[0]; - String policyValue = (String)row[1]; - boolean excludes = (boolean)row[2]; - boolean result = (boolean)row[3]; - String user = (String) row[4]; + String resource = (String)row[0]; + String policyValue = (String)row[1]; + boolean excludes = (boolean)row[2]; + ResourceElementMatchType matchType = (ResourceElementMatchType) row[3]; + boolean result = (boolean)row[4]; + String user = (String) row[5]; + Map evalContext = new HashMap<>(); - Map evalContext = new HashMap<>(); RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); MatcherWrapper matcher = new MatcherWrapper(policyValue, excludes); - assertEquals(getMessage(row), result, matcher.isMatch(resource, evalContext)); + + assertEquals(getMessage(row), matchType, matcher.getMatchType(resource, matchScope, evalContext)); + assertEquals(getMessage(row), result, matcher.isMatch(resource, matchScope, evalContext)); + } + } + + @Test + public void testIsPrefixMatch() { + ResourceElementMatchingScope matchScope = ResourceElementMatchingScope.SELF_OR_PREFIX; + + for (Object[] row : dataForPrefixMatch) { + String resource = (String)row[0]; + String policyValue = (String)row[1]; + boolean excludes = (boolean)row[2]; + ResourceElementMatchType matchType = (ResourceElementMatchType) row[3]; + boolean result = (boolean)row[4]; + String user = (String) row[5]; + Map evalContext = new HashMap<>(); + + RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); + + MatcherWrapper matcher = new MatcherWrapper(policyValue, excludes); + + assertEquals(getMessage(row), matchType, matcher.getMatchType(resource, matchScope, evalContext)); + assertEquals(getMessage(row), result, matcher.isMatch(resource, matchScope, evalContext)); } } String getMessage(Object[] row) { - return String.format("Resource=%s, Policy=%s, excludes=%s, result=%s", - (String)row[0], (String)row[1], (boolean)row[2], (boolean)row[3]); + return String.format("Resource=%s, Policy=%s, excludes=%s, matchScope=%s, matchType=%s, result=%s", + row[0], row[1], row[2], row[3], row[4], row[5]); } static class MatcherWrapper extends RangerDefaultResourceMatcher { MatcherWrapper(String policyValue, boolean exclude) { + RangerResourceDef resourceDef = new RangerResourceDef(); + Map matcherOptions = new HashMap<>(); + + matcherOptions.put(OPTION_WILD_CARD, Boolean.toString(policyValue.contains(WILDCARD_ASTERISK) || policyValue.contains(WILDCARD_QUESTION_MARK))); + matcherOptions.put(OPTION_IGNORE_CASE, Boolean.toString(false)); + + resourceDef.setMatcherOptions(matcherOptions); + + setResourceDef(resourceDef); + RangerPolicy.RangerPolicyResource policyResource = new RangerPolicy.RangerPolicyResource(); policyResource.setIsExcludes(exclude); policyResource.setValues(Lists.newArrayList(policyValue)); setPolicyResource(policyResource); - if (policyValue.contains(WILDCARD_ASTERISK)) { - this.optWildCard = true; - } - this.optIgnoreCase = false; + init(); } } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java index 5c497779d4..9083725743 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerPathResourceMatcherTest.java @@ -19,11 +19,14 @@ package org.apache.ranger.plugin.resourcematcher; -import com.google.common.collect.Lists; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -32,6 +35,35 @@ public class RangerPathResourceMatcherTest { Object[][] data = { + // resource policy wildcard recursive result user + { "/app/hive/test.db", "/", true, false, false, "user" }, + { "/app/hive/test.db", "/", true, true, true, "user" }, + { "/app/hive/test.db", "/*", true, false, true, "user" }, + { "/app/hbase/test.tbl", "/*", true, false, true, "user" }, + { "/app/hive/test.db", "/app", true, false, false, "user" }, + { "/app/hive/test.db", "/app/", true, false, false, "user" }, + { "/app/hive/test.db", "/app/", true, true, true, "user" }, + { "/app/hive/test.db", "/app/*", true, false, true, "user" }, + { "/app/hbase/test.tbl", "/app/*", true, false, true, "user" }, + { "/app/hive/test.db", "/app/hive/*", true, false, true, "user" }, + { "/app/hbase/test.tbl", "/app/hive/*", true, false, false, "user" }, + { "/app/hive/test.db", "/app/hive/test*", true, false, true, "user" }, + { "/app/hbase/test.tbl", "/app/hive/test*", true, false, false, "user" }, + { "/app/hive/test.db", "/app/hive/test.db", true, false, true, "user" }, + { "/app/hbase/test.tbl", "/app/hive/test.db", true, false, false, "user" }, + { "app/hive/*", "app/hive/*", false, false, true, "user" }, // simple string match + { "app/hive/test.db", "app/hive/*", false, false, false, "user" }, // simple string match + { "/app/", "/app/", true, true, true, "user" }, + { "/app/", "/app/", true, false, true, "user" }, + { "/app", "/app/", true, true, false, "user" }, + { "/app", "/app/", true, false, false, "user" }, + { "/app/", "/app/*", true, true, true, "user" }, + { "/app/", "/app/*", true, false, true, "user" }, + { "/app", "/app/*", true, true, false, "user" }, + { "/app", "/app/*", true, false, false, "user" }, + }; + + Object[][] dataForSelfOrChildScope = { // { resource, policy, optWildcard, recursive, result { "/app/hive/test.db", "/", true, false, false, "user" }, { "/app/hive/test.db", "/", true, true, true, "user" }, @@ -48,6 +80,71 @@ public class RangerPathResourceMatcherTest { { "/app/hbase/test.tbl", "/app/hive/test*", true, false, false, "user" }, { "/app/hive/test.db", "/app/hive/test.db", true, false, true, "user" }, { "/app/hbase/test.tbl", "/app/hive/test.db", true, false, false, "user" }, + { "/app/hbase/test.db", "/app/hbase", true, true, true, "user" }, + { "/app/hbase/test.db", "/app/hbase/test.db/test.tbl", true, true, true, "user" }, + { "/app/hbase/test.db/", "/app/hbase/test.db/test.tbl", true, true, true, "user" }, + { "/app/hbase/test.db", "/app/hbase/test.db/test.tbl/test.col", true, true, false, "user" }, + { "/app/hbase/test.db", "/app/h*/test.db/test.tbl", true, true, true, "user" }, + { "/app/hbase/test.db", "/app/hbase/test.db/test.tbl", true, false, true, "user" }, + { "/app/hbase/test.db/", "/app/hbase/test.db/test.tbl", true, false, true, "user" }, + { "/app/hbase/test.db/", "/app/hbase/test.db/test.tbl", true, false, true, "user" }, + { "/app/hbase/test.db", "/app/h*/test.db/test.tbl", true, false, true, "user" }, + { "/app/hbase/test.db", "*/hbase/test.db/test.tbl", true, false, true, "user" }, + { "/app/hbase/test.db", "/app/hbase/test.db/test.t*", true, false, true, "user" }, + { "/app/hbase/test.db", "/app/hbase/test.db/tmp/test.t*", true, false, false, "user" }, + }; + + Object[][] dataForSelfOrPrefixScope = { + // { resource, policy, optWildcard, recursive, result + { "/", "/app/hive/test.db", true, false, true, "user" }, + { "/app", "/app/hive/test.db", true, false, true, "user" }, + { "/app/", "/app/hive/test.db", true, false, true, "user" }, + { "/app/hive", "/app/hive/test.db", true, false, true, "user" }, + { "/app/hive/", "/app/hive/test.db", true, false, true, "user" }, + { "/app/hive/test.db", "/app/hive/test.db", true, false, true, "user" }, + { "/", "/app/*/test.db", true, false, true, "user" }, + { "/app", "/app/*/test.db", true, false, true, "user" }, + { "/app/", "/app/*/test.db", true, false, true, "user" }, + { "/app/hive", "/app/*/test.db", true, false, true, "user" }, + { "/app/hive/", "/app/*/test.db", true, false, true, "user" }, + { "/app/hive/test.db", "/app/*/test.db", true, false, true, "user" }, + { "/", "*/hive/test.db", true, false, true, "user" }, + { "/app", "*/hive/test.db", true, false, true, "user" }, + { "/app/", "*/hive/test.db", true, false, true, "user" }, + { "/app/hive", "*/hive/test.db", true, false, true, "user" }, + { "/app/hive/", "*/hive/test.db", true, false, true, "user" }, + { "/app/hive/test.db", "*/hive/test.db", true, false, true, "user" }, + { "/", "/*", true, false, true, "user" }, + { "/app", "/*", true, false, true, "user" }, + { "/app/", "/*", true, false, true, "user" }, + { "/app/hive", "/*", true, false, true, "user" }, + { "/app/hive/", "/*", true, false, true, "user" }, + { "/app/hive/test.db", "/*", true, false, true, "user" }, + + { "/", "/app/hive/test.db", true, true, true, "user" }, + { "/app", "/app/hive/test.db", true, true, true, "user" }, + { "/app/", "/app/hive/test.db", true, true, true, "user" }, + { "/app/hive", "/app/hive/test.db", true, true, true, "user" }, + { "/app/hive/", "/app/hive/test.db", true, true, true, "user" }, + { "/app/hive/test.db", "/app/hive/test.db", true, true, true, "user" }, + { "/", "/app/*/test.db", true, true, true, "user" }, + { "/app", "/app/*/test.db", true, true, true, "user" }, + { "/app/", "/app/*/test.db", true, true, true, "user" }, + { "/app/hive", "/app/*/test.db", true, true, true, "user" }, + { "/app/hive/", "/app/*/test.db", true, true, true, "user" }, + { "/app/hive/test.db", "/app/*/test.db", true, true, true, "user" }, + { "/", "*/hive/test.db", true, true, true, "user" }, + { "/app", "*/hive/test.db", true, true, true, "user" }, + { "/app/", "*/hive/test.db", true, true, true, "user" }, + { "/app/hive", "*/hive/test.db", true, true, true, "user" }, + { "/app/hive/", "*/hive/test.db", true, true, true, "user" }, + { "/app/hive/test.db", "*/hive/test.db", true, true, true, "user" }, + { "/", "/", true, true, true, "user" }, + { "/app", "/", true, true, true, "user" }, + { "/app/", "/", true, true, true, "user" }, + { "/app/hive", "/", true, true, true, "user" }, + { "/app/hive/", "/", true, true, true, "user" }, + { "/app/hive/test.db", "/", true, true, true, "user" }, }; @Test @@ -64,7 +161,45 @@ public void testIsMatch() throws Exception { RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive); - assertEquals(getMessage(row), result, matcher.isMatch(resource, evalContext)); + assertEquals(getMessage(row), result, matcher.isMatch(resource, ResourceElementMatchingScope.SELF, evalContext)); + } + } + + @Test + public void testIsMatchForSelfOrChildScope() throws Exception { + for (Object[] row : dataForSelfOrChildScope) { + String resource = (String)row[0]; + String policyValue = (String)row[1]; + boolean optWildcard = (boolean)row[2]; + boolean isRecursive = (boolean)row[3]; + boolean result = (boolean)row[4]; + String user = (String) row[5]; + + Map evalContext = new HashMap<>(); + RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); + + MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive); + assertEquals(getMessage(row), result, matcher.isMatch(resource, ResourceElementMatchingScope.SELF_OR_CHILD, evalContext)); + } + } + + @Test + public void testIsMatchForSelfOrPrefixScope() { + ResourceElementMatchingScope matchScope = ResourceElementMatchingScope.SELF_OR_PREFIX; + + for (Object[] row : dataForSelfOrPrefixScope) { + String resource = (String)row[0]; + String policyValue = (String)row[1]; + boolean optWildcard = (boolean)row[2]; + boolean isRecursive = (boolean)row[3]; + boolean result = (boolean)row[4]; + String user = (String) row[5]; + Map evalContext = new HashMap<>(); + + RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); + + MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive); + assertEquals(getMessage(row), result, matcher.isMatch(resource, matchScope, evalContext)); } } @@ -75,7 +210,12 @@ String getMessage(Object[] row) { static class MatcherWrapper extends RangerPathResourceMatcher { MatcherWrapper(String policyValue, boolean optWildcard, boolean isRecursive) { - super.optWildCard = optWildcard; + RangerResourceDef resourceDef = new RangerResourceDef(); + Map matcherOptions = Collections.singletonMap(OPTION_WILD_CARD, Boolean.toString(optWildcard)); + + resourceDef.setMatcherOptions(matcherOptions); + + setResourceDef(resourceDef); RangerPolicy.RangerPolicyResource policyResource = new RangerPolicy.RangerPolicyResource(); policyResource.setIsRecursive(isRecursive); @@ -83,7 +223,7 @@ static class MatcherWrapper extends RangerPathResourceMatcher { setPolicyResource(policyResource); init(); - } + } } } \ No newline at end of file diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java index 2762932d36..dd10a6b69b 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/RangerURLResourceMatcherTest.java @@ -19,11 +19,14 @@ package org.apache.ranger.plugin.resourcematcher; -import com.google.common.collect.Lists; +import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; import org.apache.ranger.plugin.util.RangerAccessRequestUtil; import org.junit.Test; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -36,7 +39,8 @@ public class RangerURLResourceMatcherTest { { "hdfs://hostname:8020/app/warehouse/data/emp.db", "hdfs://hostname:8020/app/warehouse/*", true, true, true, "user" }, { "hdfs://hostname:8020/app/warehouse/data/emp.db", "hdfs://hostname:8020/*", true, true, true, "user" }, { "hdfs://hostname:8020/app/warehouse/data/emp.db", "hdfs://hostname:8020/app/*", true, false, true, "user" }, - { "hdfs://hostname:8020/app/warehouse/data/emp.db", "hdfs://hostname:8020/app/*", false, false, true, "user" }, + { "hdfs://hostname:8020/app/warehouse/data/emp.db", "hdfs://hostname:8020/app/*", false, false, false, "user" }, // simple string match + { "hdfs://hostname:8020/app/*", "hdfs://hostname:8020/app/*", false, false, true, "user" }, // simple string match { "hdfs://hostname:8020/app/warehouse/data/emp.db", "hdfs://hostname:8020/app/", true, true, true, "user" }, { "s3a://app/warehouse/data/emp.db", "s3a://app/*", true, true, true, "user" }, { "adls:/app/warehouse/data/emp.db", "adls://app/*", true, true, false, "user" }, @@ -49,6 +53,60 @@ public class RangerURLResourceMatcherTest { { "://apps/warehouse/data/emp.db", "hdfs://app/*", true, true, false, "user" } }; + + Object[][] dataForSelfOrPrefixScope = { + // { resource, policy, optWildcard, recursive, result + { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/*/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020*/hive/test.db", true, false, true, "user" }, + { "hdfs://hostname:8020/", "hdfs://hostname:8020/*", true, false, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020/*", true, false, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/*", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/*", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/*", true, false, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/*", true, false, true, "user" }, + + { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/app/*/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020*/hive/test.db", true, true, true, "user" }, + { "hdfs://hostname:8020/", "hdfs://hostname:8020/", true, true, true, "user" }, + { "hdfs://hostname:8020/app", "hdfs://hostname:8020/", true, true, true, "user" }, + { "hdfs://hostname:8020/app/", "hdfs://hostname:8020/", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive", "hdfs://hostname:8020/", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/", "hdfs://hostname:8020/", true, true, true, "user" }, + { "hdfs://hostname:8020/app/hive/test.db", "hdfs://hostname:8020/", true, true, true, "user" }, + }; + @Test public void testIsMatch() throws Exception { for (Object[] row : data) { @@ -63,7 +121,26 @@ public void testIsMatch() throws Exception { RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive); - assertEquals(getMessage(row), result, matcher.isMatch(resource, evalContext)); + assertEquals(getMessage(row), result, matcher.isMatch(resource, ResourceElementMatchingScope.SELF, evalContext)); + } + } + + @Test + public void testIsPrefixMatch() { + for (Object[] row : dataForSelfOrPrefixScope) { + String resource = (String)row[0]; + String policyValue = (String)row[1]; + boolean optWildcard = (boolean)row[2]; + boolean isRecursive = (boolean)row[3]; + boolean result = (boolean)row[4]; + String user = (String) row[5]; + + Map evalContext = new HashMap<>(); + + RangerAccessRequestUtil.setCurrentUserInContext(evalContext, user); + + MatcherWrapper matcher = new MatcherWrapper(policyValue, optWildcard, isRecursive); + assertEquals(getMessage(row), result, matcher.isMatch(resource, ResourceElementMatchingScope.SELF_OR_PREFIX, evalContext)); } } @@ -74,7 +151,12 @@ String getMessage(Object[] row) { static class MatcherWrapper extends RangerURLResourceMatcher { MatcherWrapper(String policyValue, boolean optWildcard, boolean isRecursive) { - super.optWildCard = optWildcard; + RangerResourceDef resourceDef = new RangerResourceDef(); + Map matcherOptions = Collections.singletonMap(OPTION_WILD_CARD, Boolean.toString(optWildcard)); + + resourceDef.setMatcherOptions(matcherOptions); + + setResourceDef(resourceDef); RangerPolicy.RangerPolicyResource policyResource = new RangerPolicy.RangerPolicyResource(); policyResource.setIsRecursive(isRecursive); diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java index 8ea71057a4..2b432a10b0 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestDefaultPolicyResourceMatcher.java @@ -96,6 +96,13 @@ public void testDefaultPolicyResourceMatcher() throws Exception { runTestsFromResourceFiles(tests, null); } + @Test + public void testDefaultPolicyResourceMatcherQuoted() throws Exception { + String[] tests = {"/resourcematcher/test_defaultpolicyresourcematcher_quoted.json"}; + + runTestsFromResourceFiles(tests, null); + } + @Test public void testDefaultPolicyResourceMatcher_ResourceSpecific() throws Exception { String[] tests = { "/resourcematcher/test_defaultpolicyresourcematcher.json" }; diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestResourceMatcher.java b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestResourceMatcher.java index ea7bc01f25..ef8ee6c321 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestResourceMatcher.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/resourcematcher/TestResourceMatcher.java @@ -28,6 +28,7 @@ import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; import org.apache.ranger.plugin.model.RangerServiceDef.RangerResourceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceElementMatchingScope; import org.apache.ranger.plugin.resourcematcher.TestResourceMatcher.ResourceMatcherTestCases.TestCase; import org.apache.ranger.plugin.resourcematcher.TestResourceMatcher.ResourceMatcherTestCases.TestCase.OneTest; import org.junit.After; @@ -112,7 +113,7 @@ private void runTests(InputStreamReader reader, String testName) throws Exceptio } boolean expected = oneTest.result; - boolean result = matcher.isMatch(oneTest.input, oneTest.evalContext); + boolean result = matcher.isMatch(oneTest.input, ResourceElementMatchingScope.SELF, oneTest.evalContext); assertEquals("isMatch() failed! " + testCase.name + ":" + oneTest.name + ": input=" + oneTest.input, expected, result); } diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java new file mode 100644 index 0000000000..ff871a7065 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/service/TestRangerBasePlugin.java @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.service; + +import com.google.gson.*; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.policyengine.*; +import org.apache.ranger.plugin.util.*; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Type; +import java.util.*; + +import static org.junit.Assert.*; + +public class TestRangerBasePlugin { + static Gson gsonBuilder; + static RangerPolicyEngineOptions peOptions; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSSZ") + .setPrettyPrinting() + .registerTypeAdapter(RangerAccessRequest.class, new RangerAccessRequestDeserializer()) + .registerTypeAdapter(RangerAccessResource.class, new RangerResourceDeserializer()) + .create(); + + peOptions = new RangerPolicyEngineOptions(); + + peOptions.disablePolicyRefresher = true; + peOptions.disableTagRetriever = true; + peOptions.disableUserStoreRetriever = true; + peOptions.disableGdsInfoRetriever = true; + + } + + + @Test + public void testBasePluginHive() { + runTestsFromResourceFile("/plugin/test_base_plugin_hive.json"); + } + + private void runTestsFromResourceFile(String resourceFile) { + InputStream inStream = this.getClass().getResourceAsStream(resourceFile); + InputStreamReader reader = new InputStreamReader(inStream); + + runTests(reader, resourceFile); + } + + private void runTests(Reader reader, String testName) { + RangerBasePluginTestCase testCase = readTestCase(reader); + + assertNotNull("invalid input: " + testName, testCase); + assertNotNull("invalid input: " + testName, testCase.policies); + assertNotNull("invalid input: " + testName, testCase.tags); + assertNotNull("invalid input: " + testName, testCase.roles); + assertNotNull("invalid input: " + testName, testCase.userStore); + assertNotNull("invalid input: " + testName, testCase.gdsInfo); + assertNotNull("invalid input: " + testName, testCase.tests); + + RangerPluginConfig pluginConfig = new RangerPluginConfig(testCase.policies.getServiceDef().getName(), testCase.policies.getServiceName(), "hive", "cl1", "on-prem", peOptions); + RangerBasePlugin plugin = new RangerBasePlugin(pluginConfig, testCase.policies, testCase.tags, testCase.roles, testCase.userStore, testCase.gdsInfo); + + for (TestData test : testCase.tests) { + RangerAccessRequest request = test.request; + + if (test.result != null) { + RangerAccessResult result = plugin.isAccessAllowed(request); + + assertNotNull("result was null! - " + test.name, result); + assertEquals("isAllowed mismatched! - " + test.name, test.result.getIsAllowed(), result.getIsAllowed()); + assertEquals("isAccessDetermined mismatched! - " + test.name, test.result.getIsAccessDetermined(), result.getIsAccessDetermined()); + assertEquals("isAllowed mismatched! - " + test.name, test.result.getPolicyId(), result.getPolicyId()); + assertEquals("isAudited mismatched! - " + test.name, test.result.getIsAudited(), result.getIsAudited()); + assertEquals("isAuditedDetermined mismatched! - " + test.name, test.result.getIsAuditedDetermined(), result.getIsAuditedDetermined()); + } + + if (test.acls != null) { + RangerAccessRequest req = new RangerAccessRequestImpl(request.getResource(), RangerPolicyEngine.ANY_ACCESS, null, null, null); + RangerResourceACLs acls = plugin.getResourceACLs(req); + + assertEquals(test.name, test.acls, acls); + } + } + } + + private RangerBasePluginTestCase readTestCase(Reader reader) { + RangerBasePluginTestCase testCase = gsonBuilder.fromJson(reader, RangerBasePluginTestCase.class); + + if (StringUtils.isNotBlank(testCase.policiesFilename)) { + InputStream inStream = this.getClass().getResourceAsStream(testCase.policiesFilename); + + testCase.policies = gsonBuilder.fromJson(new InputStreamReader(inStream), ServicePolicies.class); + } + + if (StringUtils.isNotBlank(testCase.tagsFilename)) { + InputStream inStream = this.getClass().getResourceAsStream(testCase.tagsFilename); + + testCase.tags = gsonBuilder.fromJson(new InputStreamReader(inStream), ServiceTags.class); + } + + if (StringUtils.isNotBlank(testCase.rolesFilename)) { + InputStream inStream = this.getClass().getResourceAsStream(testCase.rolesFilename); + + testCase.roles = gsonBuilder.fromJson(new InputStreamReader(inStream), RangerRoles.class); + } + + if (StringUtils.isNotBlank(testCase.userStoreFilename)) { + InputStream inStream = this.getClass().getResourceAsStream(testCase.userStoreFilename); + + testCase.userStore = gsonBuilder.fromJson(new InputStreamReader(inStream), RangerUserStore.class); + } + + if (StringUtils.isNotBlank(testCase.gdsInfoFilename)) { + InputStream inStream = this.getClass().getResourceAsStream(testCase.gdsInfoFilename); + + testCase.gdsInfo = gsonBuilder.fromJson(new InputStreamReader(inStream), ServiceGdsInfo.class); + } + + if (testCase.policies != null && testCase.policies.getServiceDef() != null) { + testCase.policies.getServiceDef().setMarkerAccessTypes(ServiceDefUtil.getMarkerAccessTypes(testCase.policies.getServiceDef().getAccessTypes())); + } + + return testCase; + } + + static class RangerBasePluginTestCase { + public ServicePolicies policies; + public ServiceTags tags; + public RangerRoles roles; + public RangerUserStore userStore; + public ServiceGdsInfo gdsInfo; + public String policiesFilename; + public String tagsFilename; + public String rolesFilename; + public String userStoreFilename; + public String gdsInfoFilename; + public List tests; + } + + static class TestData { + public String name; + public RangerAccessRequest request; + public RangerAccessResult result; + public RangerResourceACLs acls; + } + + static class RangerAccessRequestDeserializer implements JsonDeserializer { + @Override + public RangerAccessRequest deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + RangerAccessRequestImpl ret = gsonBuilder.fromJson(jsonObj, RangerAccessRequestImpl.class); + + ret.setAccessType(ret.getAccessType()); // to force computation of isAccessTypeAny and isAccessTypeDelegatedAdmin + if (ret.getAccessTime() == null) { + ret.setAccessTime(new Date()); + } + Map reqContext = ret.getContext(); + Object accessTypes = reqContext.get(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPES); + if (accessTypes != null) { + Collection accessTypesCollection = (Collection) accessTypes; + Set requestedAccesses = new TreeSet<>(accessTypesCollection); + ret.getContext().put(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPES, requestedAccesses); + } + + Object accessTypeGroups = reqContext.get(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS); + if (accessTypeGroups != null) { + Set> setOfAccessTypeGroups = new HashSet<>(); + + List listOfAccessTypeGroups = (List) accessTypeGroups; + for (Object accessTypeGroup : listOfAccessTypeGroups) { + List accesses = (List) accessTypeGroup; + Set setOfAccesses = new TreeSet<>(accesses); + setOfAccessTypeGroups.add(setOfAccesses); + } + + reqContext.put(RangerAccessRequestUtil.KEY_CONTEXT_ALL_ACCESSTYPE_GROUPS, setOfAccessTypeGroups); + } + + return ret; + } + } + + static class RangerResourceDeserializer implements JsonDeserializer { + @Override + public RangerAccessResource deserialize(JsonElement jsonObj, Type type, + JsonDeserializationContext context) throws JsonParseException { + return gsonBuilder.fromJson(jsonObj, RangerAccessResourceImpl.class); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/JavaScriptEditsTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/JavaScriptEditsTest.java new file mode 100644 index 0000000000..f531290d3d --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/JavaScriptEditsTest.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class JavaScriptEditsTest { + + @Test + public void testExpressions() { + Map tests = new HashMap<>(); + + tests.put("[[TAG.value]].intersects([[USER[TAG._type]]])", "TAG.value.split(\",\").intersects(USER[TAG._type].split(\",\"))"); + tests.put("${{[[\"$USER.eventType\",'|']]}}.includes(jsonAttr.eventType)", "${{\"$USER.eventType\".split(\"|\")}}.includes(jsonAttr.eventType)"); + tests.put("TAG.value == 'email'", "TAG.value == 'email'"); // output same as input + tests.put("UGNAMES[0] == 'analyst'", "UGNAMES[0] == 'analyst'"); // output same as input + + for (Map.Entry test : tests.entrySet()) { + String input = test.getKey(); + String output = test.getValue(); + + assertEquals("input: " + input, output, JavaScriptEdits.replaceDoubleBrackets(input)); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/MacroProcessorTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/MacroProcessorTest.java new file mode 100644 index 0000000000..7573c7f3f3 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/MacroProcessorTest.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.*; + +public class MacroProcessorTest { + + @Test + public void testMacros() { + Map macros = new HashMap<>(); + + macros.put("USER", "getUser()"); + macros.put("USER_NAME", "getUserName()"); + macros.put("GROUPS", "getGroups()"); + macros.put("GET_GROUP_ATTR", "getGroupAttr"); + + MacroProcessor processor = new MacroProcessor(macros); + + Map testInputOutput = new HashMap<>(); + + testInputOutput.putAll(macros); + testInputOutput.put("USER != null", "getUser() != null"); + testInputOutput.put("USER_NAME=='testUser'", "getUserName()=='testUser'"); + testInputOutput.put("USER != null && USER_NAME=='testUser'", "getUser() != null && getUserName()=='testUser'"); + testInputOutput.put("GROUPS.length() > 1", "getGroups().length() > 1"); + testInputOutput.put("GET_GROUP_ATTR('group1', 'attr1')", "getGroupAttr('group1', 'attr1')"); + + for (Map.Entry inputOutput : testInputOutput.entrySet()) { + String input = inputOutput.getKey(); + String output = inputOutput.getValue(); + + assertEquals(output, processor.expandMacros(input));; + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/PasswordUtilsTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/PasswordUtilsTest.java index d0b1806457..c624ddbdac 100644 --- a/agents-common/src/test/java/org/apache/ranger/plugin/util/PasswordUtilsTest.java +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/PasswordUtilsTest.java @@ -24,9 +24,9 @@ import java.io.IOException; import java.security.NoSuchAlgorithmException; +import org.apache.hadoop.thirdparty.com.google.common.base.Joiner; import org.junit.Test; -import com.google.common.base.Joiner; public class PasswordUtilsTest { diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerCacheTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerCacheTest.java new file mode 100644 index 0000000000..f9bc426ac1 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerCacheTest.java @@ -0,0 +1,487 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.util.RangerCache.RefreshMode; +import org.apache.ranger.plugin.util.RangerCache.RefreshableValue; +import org.junit.Test; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.*; + +public class RangerCacheTest { + private static final int CACHE_THREAD_COUNT = 25; + private static final int VALUE_VALIDITY_DURATION_MS = 3 * 1000; + private static final int VALUE_REFRESH_TIMEOUT_MS = 10; + private static final int VALUE_INIT_TIMEOUT_MS = -1; // no timeout for init + private static final int VALUE_LOAD_TIME_TYPICAL_MAX_MS = 8; + private static final int VALUE_LOAD_TIME_FAIL_MAX_MS = 100; + private static final int VALUE_LOAD_TIME_LONG_MIN_MS = VALUE_VALIDITY_DURATION_MS / 2; + private static final int VALUE_LOAD_TIME_VERY_LONG_MIN_MS = VALUE_VALIDITY_DURATION_MS; + + private static final int USER_COUNT = 100; + private static final int CACHE_CLIENT_THREAD_COUNT = 20; + private static final int CACHE_LOOKUP_COUNT = 200; + private static final int CACHE_LOOKUP_INTERVAL_MS = 5; + + private static final boolean IS_DEBUG_ENABLED = false; + + /* + * Test cases: + * 1. successful initial load and refresh + * 2. failure in initial load + * 3. failure in refresh + * 4. long initial load - just above half the value validity period + * 5. long refresh - just above half the value validity period + * 6. very long initial load - above the value validity period + * 7. very long refresh - above the value validity period + */ + private static final String USERNAME_PREFIX_TYPICAL_LOAD = "typical_"; + private static final String USERNAME_PREFIX_FAILED_FIRST_INIT = "failedFirstInit_"; + private static final String USERNAME_PREFIX_FAILED_INIT = "failedInit_"; + private static final String USERNAME_PREFIX_FAILED_REFRESH = "failedRefresh_"; + private static final String USERNAME_PREFIX_REMOVED = "removed_"; + private static final String USERNAME_PREFIX_LONG_INIT = "longInit_"; + private static final String USERNAME_PREFIX_LONG_REFRESH = "longRefresh_"; + private static final String USERNAME_PREFIX_VERY_LONG_INIT = "veryLongInit_"; + private static final String USERNAME_PREFIX_VERY_LONG_REFRESH = "veryLongRefresh_"; + + private final Random random = new Random(); + + + @Test + public void testOnAccessRefreshCacheMultiThreadedGet() throws Throwable { + UserGroupCache cache = createCache(RefreshMode.ON_ACCESS); + + runMultiThreadedGet("testOnAccessRefreshCacheMultiThreadedGet", cache); + } + + @Test + public void testOnScheduleRefreshCacheMultiThreadedGet() throws Throwable { + UserGroupCache cache = createCache(RefreshMode.ON_SCHEDULE); + + runMultiThreadedGet("testOnScheduleRefreshCacheMultiThreadedGet", cache); + } + + @Test + public void testOnScheduleRefreshCacheRemoveKey() throws Exception { + UserGroupCache cache = createCache(RefreshMode.ON_SCHEDULE); + ThreadPoolExecutor executor = new ThreadPoolExecutor(CACHE_CLIENT_THREAD_COUNT, CACHE_CLIENT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); + List> futures = new ArrayList<>(); + + long startTimeMs = System.currentTimeMillis(); + + // submit tasks to access cache from multiple threads + for (String user : cache.stats.keySet()) { + Future future = executor.submit(new GetGroupsForUserFromCache(cache, user, 0)); + + futures.add(future); + } + + log(String.format("waiting for %s submitted tasks to complete", futures.size())); + for (Future future : futures) { + future.get(); + } + + log(String.format("all submitted tasks completed: timeTaken=%sms", (System.currentTimeMillis() - startTimeMs))); + + executor.shutdown(); + + for (String user : cache.stats.keySet()) { + cache.remove(user); + } + + assertEquals("cache should have no users", 0, cache.getKeys().size()); + + log(String.format("all entries in the cache are now removed: timeTaken=%sms", (System.currentTimeMillis() - startTimeMs))); + } + + private void runMultiThreadedGet(String testName, UserGroupCache cache) throws Throwable { + ThreadPoolExecutor executor = new ThreadPoolExecutor(CACHE_CLIENT_THREAD_COUNT, CACHE_CLIENT_THREAD_COUNT, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); + List> futures = new ArrayList<>(); + + long startTimeMs = System.currentTimeMillis(); + + for (int i = 0; i < CACHE_LOOKUP_COUNT; i++) { + for (String user : cache.stats.keySet()) { + Future future = executor.submit(new GetGroupsForUserFromCache(cache, user, i)); + + futures.add(future); + } + } + + log(String.format("waiting for %s submitted tasks to complete", futures.size())); + for (Future future : futures) { + future.get(); + } + + executor.shutdown(); + + printStats(testName, System.currentTimeMillis() - startTimeMs, cache); + } + + private UserGroupCache createCache(RefreshMode refreshMode) { + UserGroupCache ret = new UserGroupCache("ug", CACHE_THREAD_COUNT, refreshMode, VALUE_VALIDITY_DURATION_MS, VALUE_INIT_TIMEOUT_MS, VALUE_REFRESH_TIMEOUT_MS); + + // initialize cache with users + for (int i = 0; i < USER_COUNT; i++) { + ret.addUserStats(getUserName(i)); + } + + // prime the cache with empty entry for each user, to avoid the test from making excessive concurrent calls to enter into cache + for (String user : ret.stats.keySet()) { + ret.addIfAbsent(user); + } + + return ret; + } + + private String getUserName(int index) { + int percent = (index % USER_COUNT) * 100 / USER_COUNT; + final String ret; + + if (percent < 88) { + ret = USERNAME_PREFIX_TYPICAL_LOAD; + } else if (percent < 89) { + ret = USERNAME_PREFIX_FAILED_FIRST_INIT; + } else if (percent < 90) { + ret = USERNAME_PREFIX_FAILED_INIT; + } else if (percent < 91) { + ret = USERNAME_PREFIX_FAILED_REFRESH; + } else if (percent < 92) { + ret = USERNAME_PREFIX_REMOVED; + } else if (percent < 94) { + ret = USERNAME_PREFIX_LONG_INIT; + } else if (percent < 96) { + ret = USERNAME_PREFIX_LONG_REFRESH; + } else if (percent < 98) { + ret = USERNAME_PREFIX_VERY_LONG_INIT; + } else { + ret = USERNAME_PREFIX_VERY_LONG_REFRESH; + } + + return String.format("%s%04d", ret, index); + } + + private void log(String msg) { + System.out.println(new Date() + " [" + Thread.currentThread().getName() + "] " + msg); + } + + private void testLoadWait(UserGroupCache.UserStats userStats, RefreshableValue> currVal) throws Exception { + boolean fail = false; + long sleepTimeMs; + String userName = userStats.userName; + + if (currVal == null) { // initial load + if (userName.startsWith(USERNAME_PREFIX_LONG_INIT)) { + sleepTimeMs = VALUE_LOAD_TIME_LONG_MIN_MS + random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + } else if (userName.startsWith(USERNAME_PREFIX_VERY_LONG_INIT)) { + sleepTimeMs = VALUE_LOAD_TIME_VERY_LONG_MIN_MS + random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + } else if (userName.startsWith(USERNAME_PREFIX_FAILED_FIRST_INIT)) { + sleepTimeMs = random.nextInt(VALUE_LOAD_TIME_FAIL_MAX_MS); + + fail = userStats.load.count.get() == 0; + } else if (userName.startsWith(USERNAME_PREFIX_FAILED_INIT)) { + sleepTimeMs = random.nextInt(VALUE_LOAD_TIME_FAIL_MAX_MS); + + fail = true; + } else { + sleepTimeMs = random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + } + } else { // refresh + if (userName.startsWith(USERNAME_PREFIX_LONG_REFRESH)) { + sleepTimeMs = VALUE_LOAD_TIME_LONG_MIN_MS + random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + } else if (userName.startsWith(USERNAME_PREFIX_VERY_LONG_REFRESH)) { + sleepTimeMs = VALUE_LOAD_TIME_VERY_LONG_MIN_MS + random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + } else if (userName.startsWith(USERNAME_PREFIX_FAILED_REFRESH)) { + sleepTimeMs = random.nextInt(VALUE_LOAD_TIME_FAIL_MAX_MS); + + fail = true; + } else if (userName.startsWith(USERNAME_PREFIX_REMOVED)) { + sleepTimeMs = random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + + fail = true; + } else { + sleepTimeMs = random.nextInt(VALUE_LOAD_TIME_TYPICAL_MAX_MS); + } + } + + sleep(sleepTimeMs); + + if (fail) { + if (userName.startsWith(USERNAME_PREFIX_REMOVED)) { + throw new RangerCache.KeyNotFoundException(userName + ": user not found"); + } else { + throw new Exception("failed to retrieve value"); + } + } + } + + private void sleep(long timeoutMs) { + try { + Thread.sleep(timeoutMs); + } catch (InterruptedException excp) { + // ignore + } + } + + /* Sample output: + * + testOnAccessRefreshCacheMultiThreadedGet(): timeTaken=7489ms + cache: loaderThreads=25, refreshMode=ON_ACCESS, valueValidityMs=3000, valueInitTimeoutMs=-1, valueRefreshTimeoutMs=10 + test: cacheKeyCount=100, cacheClientThreads=20, lookupCount=200, lookupIntervalMs=5 + userPrefix=failedFirstInit_ userCount=1 loadCount=4 getCount=200 avgLoadTime=19.750 avgGetTime=0.520 + userPrefix=failedInit_ userCount=1 loadCount=99 getCount=99 avgLoadTime=57.283 avgGetTime=57.566 + userPrefix=failedRefresh_ userCount=1 loadCount=3 getCount=200 avgLoadTime=21.667 avgGetTime=0.165 + userPrefix=longInit_ userCount=2 loadCount=4 getCount=322 avgLoadTime=756.000 avgGetTime=9.416 + userPrefix=longRefresh_ userCount=2 loadCount=4 getCount=400 avgLoadTime=756.500 avgGetTime=2.885 + userPrefix=removed_ userCount=1 loadCount=5 getCount=200 avgLoadTime=5.400 avgGetTime=0.205 + userPrefix=typical_ userCount=88 loadCount=264 getCount=17600 avgLoadTime=4.405 avgGetTime=0.147 + userPrefix=veryLongInit_ userCount=2 loadCount=4 getCount=236 avgLoadTime=1507.250 avgGetTime=25.691 + userPrefix=veryLongRefresh_ userCount=2 loadCount=4 getCount=400 avgLoadTime=1506.500 avgGetTime=5.260 + + ****** Detailed stats for each user ****** + failedFirstInit_0088: lastValue([group-1, group-2, group-3]), load(count: 4, totalTime: 79, minTime: 0, maxTime: 65, avgTime: 19.750), get(count: 200, totalTime: 104, minTime: 0, maxTime: 65, avgTime: 0.520) + failedInit_0089: lastValue(null), load(count: 99, totalTime: 5671, minTime: 0, maxTime: 110, avgTime: 57.283), get(count: 99, totalTime: 5699, minTime: 0, maxTime: 110, avgTime: 57.566) + failedRefresh_0090: lastValue([group-1]), load(count: 3, totalTime: 65, minTime: 6, maxTime: 33, avgTime: 21.667), get(count: 200, totalTime: 33, minTime: 0, maxTime: 12, avgTime: 0.165) + longInit_0092: lastValue([group-1, group-2]), load(count: 2, totalTime: 1513, minTime: 3, maxTime: 1510, avgTime: 756.500), get(count: 161, totalTime: 1513, minTime: 0, maxTime: 1510, avgTime: 9.398) + longInit_0093: lastValue([group-1, group-2]), load(count: 2, totalTime: 1511, minTime: 3, maxTime: 1508, avgTime: 755.500), get(count: 161, totalTime: 1519, minTime: 0, maxTime: 1508, avgTime: 9.435) + longRefresh_0094: lastValue([group-1, group-2]), load(count: 2, totalTime: 1513, minTime: 3, maxTime: 1510, avgTime: 756.500), get(count: 200, totalTime: 585, minTime: 0, maxTime: 39, avgTime: 2.925) + longRefresh_0095: lastValue([group-1, group-2]), load(count: 2, totalTime: 1513, minTime: 4, maxTime: 1509, avgTime: 756.500), get(count: 200, totalTime: 569, minTime: 0, maxTime: 38, avgTime: 2.845) + removed_0091: lastValue([group-1]), load(count: 5, totalTime: 27, minTime: 3, maxTime: 8, avgTime: 5.400), get(count: 200, totalTime: 41, minTime: 0, maxTime: 9, avgTime: 0.205) + typical_0000: lastValue([group-1, group-2, group-3]), load(count: 3, totalTime: 17, minTime: 2, maxTime: 8, avgTime: 5.667), get(count: 200, totalTime: 19, minTime: 0, maxTime: 10, avgTime: 0.095) + ... + typical_0087: lastValue([group-1, group-2, group-3]), load(count: 3, totalTime: 10, minTime: 1, maxTime: 6, avgTime: 3.333), get(count: 200, totalTime: 13, minTime: 0, maxTime: 7, avgTime: 0.065) + veryLongInit_0096: lastValue([group-1, group-2]), load(count: 2, totalTime: 3011, minTime: 5, maxTime: 3006, avgTime: 1505.500), get(count: 118, totalTime: 3032, minTime: 0, maxTime: 3015, avgTime: 25.695) + veryLongInit_0097: lastValue([group-1, group-2]), load(count: 2, totalTime: 3018, minTime: 13, maxTime: 3005, avgTime: 1509.000), get(count: 118, totalTime: 3031, minTime: 0, maxTime: 3014, avgTime: 25.686) + veryLongRefresh_0098: lastValue([group-1, group-2]), load(count: 2, totalTime: 3012, minTime: 6, maxTime: 3006, avgTime: 1506.000), get(count: 200, totalTime: 1050, minTime: 0, maxTime: 21, avgTime: 5.250) + veryLongRefresh_0099: lastValue([group-1, group-2]), load(count: 2, totalTime: 3014, minTime: 0, maxTime: 3014, avgTime: 1507.000), get(count: 200, totalTime: 1054, minTime: 0, maxTime: 19, avgTime: 5.270) + * + */ + private void printStats(String testName, long timeTakenMs, UserGroupCache cache) { + log(String.format("%s(): timeTaken=%sms", testName, timeTakenMs)); + log(String.format(" cache: loaderThreads=%s, refreshMode=%s, valueValidityMs=%s, valueInitTimeoutMs=%s, valueRefreshTimeoutMs=%s", cache.getLoaderThreadsCount(), cache.getRefreshMode(), cache.getValueValidityPeriodMs(), cache.getValueInitLoadTimeoutMs(), cache.getValueRefreshLoadTimeoutMs())); + log(String.format(" test: cacheKeyCount=%s, cacheClientThreads=%s, lookupCount=%s, lookupIntervalMs=%s", cache.stats.size(), CACHE_CLIENT_THREAD_COUNT, CACHE_LOOKUP_COUNT, CACHE_LOOKUP_INTERVAL_MS)); + + printStats(cache.stats, USERNAME_PREFIX_FAILED_FIRST_INIT); + printStats(cache.stats, USERNAME_PREFIX_FAILED_INIT); + printStats(cache.stats, USERNAME_PREFIX_FAILED_REFRESH); + printStats(cache.stats, USERNAME_PREFIX_LONG_INIT); + printStats(cache.stats, USERNAME_PREFIX_LONG_REFRESH); + printStats(cache.stats, USERNAME_PREFIX_REMOVED); + printStats(cache.stats, USERNAME_PREFIX_TYPICAL_LOAD); + printStats(cache.stats, USERNAME_PREFIX_VERY_LONG_INIT); + printStats(cache.stats, USERNAME_PREFIX_VERY_LONG_REFRESH); + + log(""); + log("****** Detailed stats for each user ******"); + + // print stats for all users, in a predictable order + List userNames = new ArrayList<>(cache.stats.keySet()); + + Collections.sort(userNames); + + for (String userName : userNames) { + log(String.format("%s", cache.stats.get(userName))); + } + } + + private void printStats(Map stats, String userNamePrefix) { + long userCount = 0, loadCount = 0, getCount = 0, totalLoadTime = 0, totalGetTime = 0; + + for (Map.Entry entry : stats.entrySet()) { + String userName = entry.getKey(); + + if (!userName.startsWith(userNamePrefix)) { + continue; + } + + UserGroupCache.UserStats userStats = entry.getValue(); + + userCount++; + loadCount += userStats.load.count.get(); + getCount += userStats.get.count.get(); + totalLoadTime += userStats.load.totalTime.get(); + totalGetTime += userStats.get.totalTime.get(); + } + + log(String.format(" userPrefix=%-16s userCount=%-4s loadCount=%-5s getCount=%-7s avgLoadTime=%-9.3f avgGetTime=%-6.3f", userNamePrefix, userCount, loadCount, getCount, (totalLoadTime / (float)loadCount), (totalGetTime / (float)getCount))); + } + + // multiple instances of this class are used by the test to simulate simultaneous access to cache to obtain groups for users + private class GetGroupsForUserFromCache implements Runnable { + private final UserGroupCache cache; + private final String userName; + private final int lookupCount; + + public GetGroupsForUserFromCache(UserGroupCache cache, String userName, int lookupCount) { + this.cache = cache; + this.userName = userName; + this.lookupCount = lookupCount; + } + + @Override + public void run() { + UserGroupCache.UserStats userStats = cache.getUserStats(userName); + + // test threads can be blocked by values that take a long time to initialize + // avoid this by restricting such values to have only one pending call until they are initialized + if (!cache.isLoaded(userName) && userStats.inProgressCount.get() > 0) { + if (userName.startsWith(USERNAME_PREFIX_FAILED_INIT) || userName.startsWith(USERNAME_PREFIX_LONG_INIT) || userName.startsWith(USERNAME_PREFIX_VERY_LONG_INIT)) { + if (IS_DEBUG_ENABLED) { + log(String.format("[%s] [lookupCount=%s] get(%s): aborted, as initial loading is already in progress for this user", Thread.currentThread().getName(), lookupCount, userName)); + } + + return; + } + } + + userStats.inProgressCount.getAndIncrement(); + + long startTime = System.currentTimeMillis(); + List userGroups = cache.get(userName); + long timeTaken = System.currentTimeMillis() - startTime; + + userStats.inProgressCount.getAndDecrement(); + + userStats.get.record(timeTaken); + + if (userName.startsWith(USERNAME_PREFIX_FAILED_INIT)) { + assertNull("userGroups should be null for user=" + userName + ", lookupCount=" + lookupCount, userGroups); + } else if (userName.startsWith(USERNAME_PREFIX_FAILED_FIRST_INIT)) { + if (lookupCount == 0) { + assertNull("userGroups should be null after first lookup for user=" + userName + ", lookupCount=" + lookupCount, userGroups); + } else { + assertNotNull("userGroups should be null only after first lookup for user=" + userName + ", lookupCount=" + lookupCount, userGroups); + } + } else { + assertNotNull("userGroups should not be null for user=" + userName + ", lookupCount=" + lookupCount, userGroups); + } + + userStats.lastValue = userGroups; + + if (IS_DEBUG_ENABLED) { + log(String.format("[%s] [lookupCount=%s] get(%s): timeTaken=%s, userGroups=%s", Thread.currentThread().getName(), lookupCount, userName, timeTaken, userGroups)); + } + + sleep(CACHE_LOOKUP_INTERVAL_MS); + } + } + + private class UserGroupCache extends RangerCache> { + private final Map stats = new HashMap<>(); + + public UserGroupCache(String name, int loaderThreadsCount, RefreshMode refreshMode, long valueValidityPeriodMs, long valueInitLoadTimeoutMs, long valueRefreshLoadTimeoutMs) { + super(name, null, loaderThreadsCount, refreshMode, valueValidityPeriodMs, valueInitLoadTimeoutMs, valueRefreshLoadTimeoutMs); + + setLoader(new UserGroupLoader()); + } + + public void addUserStats(String userName) { + stats.put(userName, new UserStats(userName)); + } + + public UserStats getUserStats(String userName) { + return stats.get(userName); + } + + // + // this class implements value-loader interface used by the cache to populate and refresh the cache + // load() method simulates loading of groups for the given user + // + private class UserGroupLoader extends ValueLoader> { + public UserGroupLoader() { + } + + @Override + public RefreshableValue> load(String userName, RefreshableValue> currVal, Object context) throws Exception { + long startTimeMs = System.currentTimeMillis(); + + UserStats userStats = stats.get(userName); + + try { + testLoadWait(userStats, currVal); // simulate various load conditions, depending on the userName + + // simply append 'group-#' to current value, where # is the number of groups including this one + final List value = currVal != null && currVal.getValue() != null ? new ArrayList<>(currVal.getValue()) : new ArrayList<>(); + + value.add("group-" + (value.size() + 1)); + + return new RefreshableValue<>(value); + } finally { + userStats.load.record(System.currentTimeMillis() - startTimeMs); + } + } + } + + private class UserStats { + final String userName; + final TimedCounter get = new TimedCounter(); + final TimedCounter load = new TimedCounter(); + final AtomicLong inProgressCount = new AtomicLong(); + List lastValue; + + public UserStats(String userName) { + this.userName = userName; + } + + @Override + public String toString() { + return userName + ": lastValue(" + lastValue + "), load(" + load + "), get(" + get + ")"; + } + } + } + + private static class TimedCounter { + final AtomicLong count = new AtomicLong(); + final AtomicLong totalTime = new AtomicLong(); + final AtomicLong minTime = new AtomicLong(Long.MAX_VALUE); + final AtomicLong maxTime = new AtomicLong(); + + public void record(long timeTaken) { + count.getAndIncrement(); + totalTime.addAndGet(timeTaken); + + long minTimeTaken = minTime.get(); + long maxTimeTaken = maxTime.get(); + + if (timeTaken < minTimeTaken) { + minTime.compareAndSet(minTimeTaken, timeTaken); + } + + if (timeTaken > maxTimeTaken) { + maxTime.compareAndSet(maxTimeTaken, timeTaken); + } + } + + @Override + public String toString() { + return "count: " + count.get() + ", totalTime: " + totalTime.get() + ", minTime: " + minTime.get() + ", maxTime: " + maxTime.get() + ", avgTime: " + (getAvgTimeMs()); + } + + private String getAvgTimeMs() { + long totalTime = this.totalTime.get(); + long count = this.count.get(); + + return String.format("%.3f", (count != 0 ? (totalTime / (double)count) : -1)); + } + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java new file mode 100644 index 0000000000..e0e593be25 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerRequestExprResolverTest.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.contextenricher.RangerTagForEval; +import org.apache.ranger.plugin.model.RangerTag; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResource; +import org.apache.ranger.plugin.policyresourcematcher.RangerPolicyResourceMatcher; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class RangerRequestExprResolverTest { + @Test + public void testRequestAttributes() { + RangerAccessRequest request = createRequest(Arrays.asList("PII", "PCI")); + + Map exprValue = new HashMap<>(); + + exprValue.put("s3://mybucket/users/${{USER._name}}/${{USER.state}}/test.txt", "s3://mybucket/users/test-user/CA/test.txt"); + exprValue.put("state == '${{USER.state}}' AND dept == '${{UGA.sVal.dept}}'", "state == 'CA' AND dept == 'ENGG'"); + exprValue.put("state == '${{USER.state}}' AND group IN (${{GET_UG_NAMES_Q()}})", "state == 'CA' AND group IN ('test-group1','test-group2')"); + exprValue.put("attr1 == '${{TAG.attr1}}'", "attr1 == 'PII_value'"); + + exprValue.put("${{USER._name}}", "test-user"); + exprValue.put("${{USER['state']}}", "CA"); + exprValue.put("${{USER.state}}", "CA"); + + exprValue.put("${{UGNAMES.indexOf('test-group1') != -1}}", "true"); + exprValue.put("${{UGNAMES.indexOf('test-group2') != -1}}", "true"); + exprValue.put("${{UGNAMES.indexOf('test-group3') == -1}}", "true"); + + exprValue.put("${{UG['test-group1'].dept}}", "ENGG"); + exprValue.put("${{UG['test-group1'].site}}", "10"); + exprValue.put("${{UG['test-group2'].dept}}", "PROD"); + exprValue.put("${{UG['test-group2'].site}}", "20"); + exprValue.put("${{UG['test-group3']}}", ""); + exprValue.put("${{UG['test-group1'].notExists}}", ""); + + exprValue.put("${{URNAMES.indexOf('test-role1') != -1}}", "true"); + exprValue.put("${{URNAMES.indexOf('test-role2') != -1}}", "true"); + exprValue.put("${{URNAMES.indexOf('test-role3') == -1}}", "true"); + + exprValue.put("${{UGA.sVal.dept}}", "ENGG"); + exprValue.put("${{UGA.sVal.site}}", "10"); + exprValue.put("${{UGA.sVal.notExists}}", ""); + exprValue.put("${{J(UGA.mVal.dept)}}", "[\"ENGG\",\"PROD\"]"); + exprValue.put("${{J(UGA.mVal.site)}}", "[\"10\",\"20\"]"); + exprValue.put("${{J(UGA.mVal.notExists)}}", ""); + exprValue.put("${{UGA.mVal['dept'].indexOf('ENGG') != -1}}", "true"); + exprValue.put("${{UGA.mVal['dept'].indexOf('PROD') != -1}}", "true"); + exprValue.put("${{UGA.mVal['dept'].indexOf('EXEC') == -1}}", "true"); + + exprValue.put("${{REQ.accessType}}", "select"); + exprValue.put("${{REQ.action}}", "query"); + + exprValue.put("${{RES._ownerUser}}", "testUser"); + exprValue.put("${{RES.database}}", "db1"); + exprValue.put("${{RES.table}}", "tbl1"); + exprValue.put("${{RES.column}}", "col1"); + + exprValue.put("${{TAG._type}}", "PII"); + exprValue.put("${{TAG.attr1}}", "PII_value"); + exprValue.put("${{Object.keys(TAGS).length}}", "2"); + exprValue.put("${{TAGS.PCI._type}}", "PCI"); + exprValue.put("${{TAGS.PCI.attr1}}", "PCI_value"); + exprValue.put("${{TAGS.PII._type}}", "PII"); + exprValue.put("${{TAGS.PII.attr1}}", "PII_value"); + + exprValue.put("${{TAGNAMES.length}}", "2"); + exprValue.put("${{TAGNAMES.indexOf('PII') != -1}}", "true"); + exprValue.put("${{TAGNAMES.indexOf('PCI') != -1}}", "true"); + exprValue.put("${{var s=USER['state'];}}state == '${{s}}'", "state == 'CA'"); + + for (Map.Entry entry : exprValue.entrySet()) { + String expr = entry.getKey(); + String value = entry.getValue(); + RangerRequestExprResolver resolver = new RangerRequestExprResolver(expr, null); + String resolvedVal = resolver.resolveExpressions(request); + + Assert.assertEquals(expr, value, resolvedVal); + } + } + + + RangerAccessRequest createRequest(List resourceTags) { + RangerAccessResource resource = mock(RangerAccessResource.class); + + Map resourceMap = new HashMap<>(); + + resourceMap.put("database", "db1"); + resourceMap.put("table", "tbl1"); + resourceMap.put("column", "col1"); + + when(resource.getAsString()).thenReturn("db1/tbl1/col1"); + when(resource.getOwnerUser()).thenReturn("testUser"); + when(resource.getAsMap()).thenReturn(resourceMap); + when(resource.getReadOnlyCopy()).thenReturn(resource); + + RangerAccessRequestImpl request = new RangerAccessRequestImpl(); + + request.setResource(resource); + request.setResourceMatchingScope(RangerAccessRequest.ResourceMatchingScope.SELF); + request.setAccessType("select"); + request.setAction("query"); + request.setUser("test-user"); + request.setUserGroups(new HashSet<>(Arrays.asList("test-group1", "test-group2"))); + request.setUserRoles(new HashSet<>(Arrays.asList("test-role1", "test-role2"))); + + RangerAccessRequestUtil.setCurrentResourceInContext(request.getContext(), resource); + + if (resourceTags != null) { + Set rangerTagForEvals = new HashSet<>(); + RangerTagForEval currentTag = null; + + for (String resourceTag : resourceTags) { + RangerTag tag = new RangerTag(UUID.randomUUID().toString(), resourceTag, Collections.singletonMap("attr1", resourceTag + "_value"), null, null, null); + RangerTagForEval tagForEval = new RangerTagForEval(tag, RangerPolicyResourceMatcher.MatchType.SELF); + + rangerTagForEvals.add(tagForEval); + + if (currentTag == null) { + currentTag = tagForEval; + } + } + + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), rangerTagForEvals); + RangerAccessRequestUtil.setCurrentTagInContext(request.getContext(), currentTag); + } else { + RangerAccessRequestUtil.setRequestTagsInContext(request.getContext(), null); + } + + RangerUserStore userStore = mock(RangerUserStore.class); + + RangerAccessRequestUtil.setRequestUserStoreInContext(request.getContext(), userStore); + + Map> userAttrMapping = Collections.singletonMap("test-user", Collections.singletonMap("state", "CA")); + Map> groupAttrMapping = new HashMap<>(); + + groupAttrMapping.put("test-group1", new HashMap() {{ put("dept", "ENGG"); put("site", "10"); }}); + groupAttrMapping.put("test-group2", new HashMap() {{ put("dept", "PROD"); put("site", "20"); }}); + + when(userStore.getUserAttrMapping()).thenReturn(userAttrMapping); + when(userStore.getGroupAttrMapping()).thenReturn(groupAttrMapping); + + return request; + } + +} \ No newline at end of file diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerTimeRangeCheckerTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerTimeRangeCheckerTest.java new file mode 100644 index 0000000000..7065180ef1 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/RangerTimeRangeCheckerTest.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.ranger.plugin.util; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Calendar; +import java.util.TimeZone; + +public class RangerTimeRangeCheckerTest { + private static final String[] TIME_ZONES = { null, "GMT", "PDT" }; + + @Test + public void testAfterDate() { + String baseTime = "2023/06/05"; + int year = 2023, month = 6, day = 5, hour = 0, min = 0, sec = 0; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(baseTime, null, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // baseTime (should be *after*) + long time = getTime(year, month, day, hour, min, sec, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec, tz), checker.isInRange(time)); + + // baseTime + 1 second + time = getTime(year, month, day, hour, min, sec + 1, tz); + Assert.assertTrue(toDateString(year, month, day, hour, min, sec + 1, tz), checker.isInRange(time)); + + // baseTime - 1 second + time = getTime(year, month, day, hour, min, sec - 1, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testAfterDateHHMM() { + String baseTime = "2023/06/05 5:5"; + int year = 2023, month = 6, day = 5, hour = 5, min = 5, sec = 0; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(baseTime, null, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // baseTime (should be *after*) + long time = getTime(year, month, day, hour, min, sec, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec, tz), checker.isInRange(time)); + + // baseTime + 1 second + time = getTime(year, month, day, hour, min, sec + 1, tz); + Assert.assertTrue(toDateString(year, month, day, hour, min, sec + 1, tz), checker.isInRange(time)); + + // baseTime - 1 second + time = getTime(year, month, day, hour, min, sec - 1, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testAfterDateHHMMss() { + String baseTime = "2023/06/05 5:5:5"; + int year = 2023, month = 6, day = 5, hour = 5, min = 5, sec = 5; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(baseTime, null, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // baseTime (should be *after*) + long time = getTime(year, month, day, hour, min, sec, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec, tz), checker.isInRange(time)); + + // baseTime + 1 second + time = getTime(year, month, day, hour, min, sec + 1, tz); + Assert.assertTrue(toDateString(year, month, day, hour, min, sec + 1, tz), checker.isInRange(time)); + + // baseTime - 1 second + time = getTime(year, month, day, hour, min, sec - 1, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testBeforeDate() { + String baseTime = "2023/07/05"; + int year = 2023, month = 7, day = 5, hour = 0, min = 0, sec = 0; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(null, baseTime, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // baseTime (should be *before*) + long time = getTime(year, month, day, hour, min, sec, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec, tz), checker.isInRange(time)); + + // baseTime + 1 second + time = getTime(year, month, day, hour, min, sec + 1, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec + 1, tz), checker.isInRange(time)); + + // baseTime - 1 second + time = getTime(year, month, day, hour, min, sec - 1, tz); + Assert.assertTrue(toDateString(year, month, day, hour, min, sec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testBeforeDateHHMM() { + String baseTime = "2023/07/05 5:5"; + int year = 2023, month = 7, day = 5, hour = 5, min = 5, sec = 0; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(null, baseTime, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // baseTime (should be *before*) + long time = getTime(year, month, day, hour, min, sec, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec, tz), checker.isInRange(time)); + + // baseTime + 1 second + time = getTime(year, month, day, hour, min, sec + 1, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec + 1, tz), checker.isInRange(time)); + + // baseTime - 1 second + time = getTime(year, month, day, hour, min, sec - 1, tz); + Assert.assertTrue(toDateString(year, month, day, hour, min, sec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testBeforeDateHHMMss() { + String baseTime = "2023/07/05 5:5:5"; + int year = 2023, month = 7, day = 5, hour = 5, min = 5, sec = 5; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(null, baseTime, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // baseTime (should be *before*) + long time = getTime(year, month, day, hour, min, sec, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec, tz), checker.isInRange(time)); + + // baseTime + 1 second + time = getTime(year, month, day, hour, min, sec + 1, tz); + Assert.assertFalse(toDateString(year, month, day, hour, min, sec + 1, tz), checker.isInRange(time)); + + // baseTime - 1 second + time = getTime(year, month, day, hour, min, sec - 1, tz); + Assert.assertTrue(toDateString(year, month, day, hour, min, sec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testBetweenDate() { + String fromTime = "2023/06/05"; + String toTIme = "2023/07/05"; + int fromYear = 2023, fromMonth = 6, fromDay = 5, fromHour = 0, fromMin = 0, fromSec = 0; + int toYear = 2023, toMonth = 7, toDay = 5, toHour = 0, toMin = 0, toSec = 0; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(fromTime, toTIme, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // fromTime (should be *on or after*) + long time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec, tz); + Assert.assertTrue(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec, tz), checker.isInRange(time)); + + // fromTime + 1 second + time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec + 1, tz); + Assert.assertTrue(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec + 1, tz), checker.isInRange(time)); + + // fromTime - 1 second + time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec - 1, tz); + Assert.assertFalse(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec - 1, tz), checker.isInRange(time)); + + // toTime (should be *before*) + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec, tz); + Assert.assertFalse(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec, tz), checker.isInRange(time)); + + // toTime + 1 second + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec + 1, tz); + Assert.assertFalse(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec + 1, tz), checker.isInRange(time)); + + // toTime - 1 second + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec - 1, tz); + Assert.assertTrue(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testBetweenDateHHMM() { + String fromTime = "2023/06/05 5:5"; + String toTIme = "2023/07/05 5:5"; + int fromYear = 2023, fromMonth = 6, fromDay = 5, fromHour = 5, fromMin = 5, fromSec = 0; + int toYear = 2023, toMonth = 7, toDay = 5, toHour = 5, toMin = 5, toSec = 0; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(fromTime, toTIme, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // fromTime (should be *on or after*) + long time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec, tz); + Assert.assertTrue(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec, tz), checker.isInRange(time)); + + // fromTime + 1 second + time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec + 1, tz); + Assert.assertTrue(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec + 1, tz), checker.isInRange(time)); + + // fromTime - 1 second + time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec - 1, tz); + Assert.assertFalse(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec - 1, tz), checker.isInRange(time)); + + // toTime (should be *before*) + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec, tz); + Assert.assertFalse(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec, tz), checker.isInRange(time)); + + // toTime + 1 second + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec + 1, tz); + Assert.assertFalse(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec + 1, tz), checker.isInRange(time)); + + // toTime - 1 second + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec - 1, tz); + Assert.assertTrue(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec - 1, tz), checker.isInRange(time)); + } + } + + @Test + public void testBetweenDateHHMMss() { + String fromTime = "2023/06/05 5:5:5"; + String toTIme = "2023/07/05 5:5:5"; + int fromYear = 2023, fromMonth = 6, fromDay = 5, fromHour = 5, fromMin = 5, fromSec = 5; + int toYear = 2023, toMonth = 7, toDay = 5, toHour = 5, toMin = 5, toSec = 5; + + for (String timeZone : TIME_ZONES) { + RangerTimeRangeChecker checker = new RangerTimeRangeChecker(fromTime, toTIme, timeZone); + TimeZone tz = timeZone == null ? TimeZone.getDefault() : TimeZone.getTimeZone(timeZone); + + // fromTime (should be *on or after*) + long time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec, tz); + Assert.assertTrue(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec, tz), checker.isInRange(time)); + + // fromTime + 1 second + time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec + 1, tz); + Assert.assertTrue(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec + 1, tz), checker.isInRange(time)); + + // fromTime - 1 second + time = getTime(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec - 1, tz); + Assert.assertFalse(toDateString(fromYear, fromMonth, fromDay, fromHour, fromMin, fromSec - 1, tz), checker.isInRange(time)); + + // toTime (should be *before*) + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec, tz); + Assert.assertFalse(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec, tz), checker.isInRange(time)); + + // toTime + 1 second + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec + 1, tz); + Assert.assertFalse(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec + 1, tz), checker.isInRange(time)); + + // toTime - 1 second + time = getTime(toYear, toMonth, toDay, toHour, toMin, toSec - 1, tz); + Assert.assertTrue(toDateString(toYear, toMonth, toDay, toHour, toMin, toSec - 1, tz), checker.isInRange(time)); + } + } + + private long getTime(int year, int month, int day, int hour, int minute, int sec, TimeZone tz) { + Calendar cal = new Calendar.Builder().setDate(year, month - 1, day).setTimeOfDay(hour, minute, sec).setTimeZone(tz).build(); + + return cal.getTime().getTime(); + } + + private static String toDateString(int year, int month, int day, int hour, int minute, int sec, TimeZone tz) { + Calendar cal = new Calendar.Builder().setDate(year, month - 1, day).setTimeOfDay(hour, minute, sec).setTimeZone(tz).build(); + + return cal.getTime().toString(); + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/ServiceDefUtilTest.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/ServiceDefUtilTest.java new file mode 100644 index 0000000000..54aefbed85 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/ServiceDefUtilTest.java @@ -0,0 +1,530 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.commons.lang.StringUtils; +import org.apache.ranger.plugin.contextenricher.RangerAdminUserStoreRetriever; +import org.apache.ranger.plugin.model.RangerPolicy; +import org.apache.ranger.plugin.model.RangerPolicy.RangerDataMaskPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemCondition; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemDataMaskInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyItemRowFilterInfo; +import org.apache.ranger.plugin.model.RangerPolicy.RangerRowFilterPolicyItem; +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerPolicyDelta; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef; +import org.apache.ranger.plugin.model.RangerServiceDef.RangerAccessTypeDef.AccessTypeCategory; +import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; +import org.apache.ranger.plugin.util.ServicePolicies.SecurityZoneInfo; +import org.junit.BeforeClass; +import org.junit.Test; + + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.apache.ranger.plugin.util.ServiceDefUtil.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +public class ServiceDefUtilTest { + private static final String REF_USER = "USER.dept"; + private static final String REF_UG = "UG['test-group1'].dept"; + private static final String REF_UGA = "UGA.sVal['dept']"; + private static final String REF_GET_UG_ATTR_CSV = "GET_UG_ATTR_CSV('dept')"; + private static final String REF_GET_UG_ATTR_Q_CSV = "GET_UG_ATTR_Q_CSV('dept')"; + private static final String REF_UG_ATTR_NAMES_CSV = "UG_ATTR_NAMES_CSV"; + private static final String REF_UG_ATTR_NAMES_Q_CSV = "UG_ATTR_NAMES_Q_CSV"; + private static final String REF_USER_ATTR_NAMES_CSV = "USER_ATTR_NAMES_CSV"; + private static final String REF_USER_ATTR_NAMES_Q_CSV = "USER_ATTR_NAMES_Q_CSV"; + private static final String REF_GET_UG_ATTR_CSV_F = "ctx.ugAttrCsv('dept')"; + private static final String REF_GET_UG_ATTR_Q_CSV_F = "ctx.ugAttrCsvQ('dept')"; + private static final String REF_UG_ATTR_NAMES_CSV_F = "ctx.ugAttrNamesCsv()"; + private static final String REF_UG_ATTR_NAMES_Q_CSV_F = "ctx.ugAttrNamesCsvQ()"; + private static final String REF_USER_ATTR_NAMES_CSV_F = "ctx.userAttrNamesCsv()"; + private static final String REF_USER_ATTR_NAMES_Q_CSV_F = "ctx.userAttrNamesCsvQ()"; + + private static final String[] UGA_ATTR_EXPRESSIONS = new String[] { + REF_USER, REF_UG, REF_UGA, + REF_GET_UG_ATTR_CSV, REF_GET_UG_ATTR_Q_CSV, + REF_UG_ATTR_NAMES_CSV, REF_UG_ATTR_NAMES_Q_CSV, + REF_USER_ATTR_NAMES_CSV, REF_USER_ATTR_NAMES_Q_CSV, + REF_GET_UG_ATTR_CSV_F, REF_GET_UG_ATTR_Q_CSV_F, + REF_UG_ATTR_NAMES_CSV_F, REF_UG_ATTR_NAMES_Q_CSV_F, + REF_USER_ATTR_NAMES_CSV_F, REF_USER_ATTR_NAMES_Q_CSV_F + }; + + static Gson gsonBuilder; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + gsonBuilder = new GsonBuilder().setDateFormat("yyyyMMdd-HH:mm:ss.SSSZ").setPrettyPrinting().create(); + } + + @Test + public void testNoUserGroupAttrRef() { + ServicePolicies svcPolicies = getServicePolicies(); + RangerPolicy policy = getPolicy(svcPolicies); + + svcPolicies.getPolicies().add(policy); + assertFalse("policy doesn't have any reference to user/group attribute", ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + policy.setResource("database", new RangerPolicyResource("/departments/USER.dept/")); // expressions must be within ${{}} + assertFalse("policy doesn't have any reference to user/group attribute", ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + policy.getRowFilterPolicyItems().get(0).getRowFilterInfo().setFilterExpr("dept in USER.dept"); // expressions must be within ${{}} + assertFalse("policy doesn't have any reference to user/group attribute", ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + } + + @Test + public void testResourceUserGroupAttrRef() { + for (String attrExpr : UGA_ATTR_EXPRESSIONS) { + String resource = "test_" + "${{" + attrExpr + "}}"; + ServicePolicies svcPolicies = getServicePolicies(); + RangerPolicy policy = getPolicy(svcPolicies); + + policy.setResource("database", new RangerPolicyResource(resource)); + + svcPolicies.getPolicies().add(policy); + assertTrue("policy resource refers to user/group attribute: " + resource, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("policy-delta resource refers to user/group attribute: " + resource, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicyDeltas().clear(); + svcPolicies.getSecurityZones().put("zone1", getSecurityZoneInfo("zone1")); + svcPolicies.getSecurityZones().get("zone1").getPolicies().add(policy); + assertTrue("zone-policy resource refers to user/group attribute: " + resource, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicies().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("zone-policy-delta resource refers to user/group attribute: " + resource, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + } + } + + @Test + public void testPolicyConditionUserGroupAttrRef() { + for (String attrExpr : UGA_ATTR_EXPRESSIONS) { + String condExpr = attrExpr + " != null"; + ServicePolicies svcPolicies = getServicePolicies(); + RangerPolicy policy = getPolicy(svcPolicies); + + policy.addCondition(new RangerPolicyItemCondition("expr", Collections.singletonList(condExpr))); + + svcPolicies.getPolicies().add(policy); + assertTrue("policy condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("policy-delta condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicyDeltas().clear(); + svcPolicies.getSecurityZones().put("zone1", getSecurityZoneInfo("zone1")); + svcPolicies.getSecurityZones().get("zone1").getPolicies().add(policy); + assertTrue("zone-policy condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicies().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("zone-policy-delta condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getPolicyDeltas().clear(); + svcPolicies.getSecurityZones().clear(); + svcPolicies.getTagPolicies().getPolicies().add(policy); + assertTrue("tag-policy condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + } + } + + @Test + public void testPolicyItemConditionUserGroupRef() { + int i = 0; + + for (String attrExpr : UGA_ATTR_EXPRESSIONS) { + String condExpr = attrExpr + " != null"; + ServicePolicies svcPolicies = getServicePolicies(); + RangerPolicy policy = getPolicy(svcPolicies); + + final List policyItems; + + switch (i % 6) { + case 0: + policyItems = policy.getPolicyItems(); + break; + + case 1: + policyItems = policy.getDenyPolicyItems(); + break; + + case 2: + policyItems = policy.getAllowExceptions(); + break; + + case 3: + policyItems = policy.getDenyExceptions(); + break; + + case 4: + policyItems = policy.getRowFilterPolicyItems(); + break; + + case 5: + policyItems = policy.getDataMaskPolicyItems(); + break; + + default: + policyItems = policy.getPolicyItems(); + break; + } + + policyItems.get(0).addCondition(new RangerPolicyItemCondition("expr", Collections.singletonList(condExpr))); + + svcPolicies.getPolicies().add(policy); + assertTrue("policyItem condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("policy-delta-item condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicyDeltas().clear(); + svcPolicies.getSecurityZones().put("zone1", getSecurityZoneInfo("zone1")); + svcPolicies.getSecurityZones().get("zone1").getPolicies().add(policy); + assertTrue("zone-policy-item condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicies().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("zone-policy-delta-item condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getTagPolicies().getPolicies().add(policy); + + assertTrue("tag-policyItem condition refers to user/group attribute: " + condExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + i++; + } + } + + @Test + public void testPolicyItemRowFilterExprUserGroupRef() { + for (String attrExpr : UGA_ATTR_EXPRESSIONS) { + String filterExpr = "${{" + attrExpr + "}}"; + ServicePolicies svcPolicies = getServicePolicies(); + RangerPolicy policy = getPolicy(svcPolicies); + + policy.getRowFilterPolicyItems().get(0).setRowFilterInfo(new RangerPolicyItemRowFilterInfo("dept in (" + filterExpr + ")")); + + svcPolicies.getPolicies().add(policy); + assertTrue("policy row-filter refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("policy-delta row-filter refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicyDeltas().clear(); + svcPolicies.getSecurityZones().put("zone1", getSecurityZoneInfo("zone1")); + svcPolicies.getSecurityZones().get("zone1").getPolicies().add(policy); + assertTrue("zone-policy row-filter refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicies().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("zone-policy-delta row-filter refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + } + } + + @Test + public void testNormalizeAccessTypeDefs() throws Exception { + try (InputStream inStream = this.getClass().getResourceAsStream("/test_servicedef-normalize.json")) { + InputStreamReader reader = new InputStreamReader(inStream); + ServicePolicies policies = gsonBuilder.fromJson(reader, ServicePolicies.class); + + RangerAccessTypeDef serviceMarkerAll = getAccessType(policies.getServiceDef().getMarkerAccessTypes(), ACCESS_TYPE_MARKER_ALL); + RangerAccessTypeDef tagMarkerAll = getAccessType(policies.getTagPolicies().getServiceDef().getMarkerAccessTypes(), ACCESS_TYPE_MARKER_ALL); + + assertNotEquals("accessType count", policies.getServiceDef().getAccessTypes().size(), policies.getTagPolicies().getServiceDef().getAccessTypes().size()); + assertNotEquals("impliedGrants: _ALL", new HashSet<>(serviceMarkerAll.getImpliedGrants()), new HashSet<>(tagMarkerAll.getImpliedGrants())); + assertNotEquals("dataMask.accessType count", policies.getServiceDef().getDataMaskDef().getAccessTypes().size(), policies.getTagPolicies().getServiceDef().getDataMaskDef().getAccessTypes().size()); + assertNotEquals("rowFilter.accessType count", policies.getServiceDef().getRowFilterDef().getAccessTypes().size(), policies.getTagPolicies().getServiceDef().getRowFilterDef().getAccessTypes().size()); + + ServiceDefUtil.normalizeAccessTypeDefs(policies.getTagPolicies().getServiceDef(), policies.getServiceDef().getName()); + + serviceMarkerAll = getAccessType(policies.getServiceDef().getMarkerAccessTypes(), ACCESS_TYPE_MARKER_ALL); + tagMarkerAll = getAccessType(policies.getTagPolicies().getServiceDef().getMarkerAccessTypes(), ACCESS_TYPE_MARKER_ALL); + + assertEquals("accessType count", policies.getServiceDef().getAccessTypes().size(), policies.getTagPolicies().getServiceDef().getAccessTypes().size()); + assertEquals("impliedGrants: _ALL", new HashSet<>(serviceMarkerAll.getImpliedGrants()), new HashSet<>(tagMarkerAll.getImpliedGrants())); + assertEquals("dataMask.accessType count", policies.getServiceDef().getDataMaskDef().getAccessTypes().size(), policies.getTagPolicies().getServiceDef().getDataMaskDef().getAccessTypes().size()); + assertEquals("rowFilter.accessType count", 0, policies.getTagPolicies().getServiceDef().getRowFilterDef().getAccessTypes().size()); + } + } + + private RangerAccessTypeDef getAccessType(List accessTypeDefs, String accessType) { + RangerAccessTypeDef ret = null; + + if (accessTypeDefs != null) { + for (RangerAccessTypeDef accessTypeDef : accessTypeDefs) { + if (StringUtils.equals(accessTypeDef.getName(), accessType)) { + ret = accessTypeDef; + + break; + } + } + } + + return ret; + } + + @Test + public void testAccessTypeMarkers() { + RangerAccessTypeDef create = new RangerAccessTypeDef(1L, "create", "create", null, null, AccessTypeCategory.CREATE); + RangerAccessTypeDef select = new RangerAccessTypeDef(2L, "select", "select", null, null, AccessTypeCategory.READ); + RangerAccessTypeDef update = new RangerAccessTypeDef(3L, "update", "update", null, null, AccessTypeCategory.UPDATE); + RangerAccessTypeDef delete = new RangerAccessTypeDef(4L, "delete", "delete", null, null, AccessTypeCategory.DELETE); + RangerAccessTypeDef manage = new RangerAccessTypeDef(5L, "manage", "manage", null, null, AccessTypeCategory.MANAGE); + RangerAccessTypeDef read = new RangerAccessTypeDef(6L, "read", "read", null, null, AccessTypeCategory.READ); + RangerAccessTypeDef write = new RangerAccessTypeDef(7L, "write", "write", null, null, AccessTypeCategory.UPDATE); + RangerAccessTypeDef execute = new RangerAccessTypeDef(8L, "execute", "execute", null, null, null); + Set allNames = toSet(create.getName(), select.getName(), update.getName(), delete.getName(), manage.getName(), read.getName(), write.getName(), execute.getName()); + + // 6 marker access-types should be populated with impliedGrants + List accessTypeDefs = Arrays.asList(create, select, update, delete, manage, read, write, execute); + List markerTypeDefs = ServiceDefUtil.getMarkerAccessTypes(accessTypeDefs); + assertEquals("markerTypeDefs count", 6, markerTypeDefs.size()); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_CREATE, toSet(create.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_CREATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_READ, toSet(select.getName(), read.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_READ)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_UPDATE, toSet(update.getName(), write.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_UPDATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_DELETE, toSet(delete.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_DELETE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_MANAGE, toSet(manage.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_MANAGE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_ALL, allNames, getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_ALL)); + + // 2 marker access-types should be populated with impliedGrants: _CREATE, _ALL + accessTypeDefs = new ArrayList<>(Collections.singleton(create)); + markerTypeDefs = ServiceDefUtil.getMarkerAccessTypes(accessTypeDefs); + assertEquals("markerTypeDefs count", 6, markerTypeDefs.size()); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_CREATE, toSet(create.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_CREATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_READ, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_READ)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_UPDATE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_UPDATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_DELETE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_DELETE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_MANAGE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_MANAGE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_ALL, toSet(create.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_ALL)); + + // 2 marker access-types should be populated with impliedGrants: _READ, _ALL + accessTypeDefs = new ArrayList<>(Arrays.asList(select, read)); + markerTypeDefs = ServiceDefUtil.getMarkerAccessTypes(accessTypeDefs); + assertEquals("markerTypeDefs count", 6, markerTypeDefs.size()); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_CREATE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_CREATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_READ, toSet(select.getName(), read.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_READ)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_UPDATE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_UPDATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_DELETE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_DELETE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_MANAGE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_MANAGE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_ALL, toSet(select.getName(), read.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_ALL)); + + // accessTypes with no category should be added to _ALL + accessTypeDefs = new ArrayList<>(Collections.singleton(execute)); + markerTypeDefs = ServiceDefUtil.getMarkerAccessTypes(accessTypeDefs); + assertEquals("markerTypeDefs count", 6, markerTypeDefs.size()); // 1 marker access-types should be added: _ALL + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_CREATE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_CREATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_READ, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_READ)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_UPDATE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_UPDATE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_DELETE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_DELETE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_MANAGE, Collections.emptySet(), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_MANAGE)); + assertEquals("impliedGrants in " + ACCESS_TYPE_MARKER_ALL, toSet(execute.getName()), getImpliedGrants(markerTypeDefs, ACCESS_TYPE_MARKER_ALL)); + } + + private Set getImpliedGrants(List accessTypeDefs, String accessType) { + Set ret = null; + + if (accessTypeDefs != null) { + for (RangerAccessTypeDef accessTypeDef : accessTypeDefs) { + if (StringUtils.equals(accessTypeDef.getName(), accessType)) { + ret = new HashSet<>(accessTypeDef.getImpliedGrants()); + + break; + } + } + } + + return ret; + } + + private Set toSet(String...values) { + Set ret = new HashSet<>(); + + if (values != null) { + for (String value : values) { + ret.add(value); + } + } + + return ret; + } + public void testPolicyItemDataMaskExprUserGroupRef() { + for (String attrExpr : UGA_ATTR_EXPRESSIONS) { + String filterExpr = "${{" + attrExpr + "}}"; + ServicePolicies svcPolicies = getServicePolicies(); + RangerPolicy policy = getPolicy(svcPolicies); + + policy.getDataMaskPolicyItems().get(0).setDataMaskInfo(new RangerPolicyItemDataMaskInfo("CUSTOM", "", "CASE WHEN dept in (" + filterExpr + ")THEN {col} ELSE '0' END")); + + svcPolicies.getPolicies().add(policy); + assertTrue("policy data-mask refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicies().clear(); + svcPolicies.getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("policy-delta data-mask refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getPolicyDeltas().clear(); + svcPolicies.getSecurityZones().put("zone1", getSecurityZoneInfo("zone1")); + svcPolicies.getSecurityZones().get("zone1").getPolicies().add(policy); + assertTrue("zone-policy data-mask refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + + svcPolicies.getServiceDef().getContextEnrichers().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicies().clear(); + svcPolicies.getSecurityZones().get("zone1").getPolicyDeltas().add(new RangerPolicyDelta(1L, RangerPolicyDelta.CHANGE_TYPE_POLICY_CREATE, 1L, policy)); + assertTrue("zone-policy-delta data-mask refers to user/group attribute: " + filterExpr, ServiceDefUtil.addUserStoreEnricherIfNeeded(svcPolicies, RangerAdminUserStoreRetriever.class.getCanonicalName(), "60000")); + } + } + + private ServicePolicies getServicePolicies() { + ServicePolicies ret = new ServicePolicies(); + + ret.setServiceName("dev_hive"); + ret.setServiceId(1L); + ret.setPolicyVersion(1L); + ret.setServiceDef(getServiceDef("hive")); + ret.setPolicies(new ArrayList<>()); + ret.setPolicyDeltas(new ArrayList<>()); + ret.setSecurityZones(new HashMap<>()); + + ret.setTagPolicies(new ServicePolicies.TagPolicies()); + ret.getTagPolicies().setServiceDef(getServiceDef("tag")); + ret.getTagPolicies().setPolicies(new ArrayList<>()); + + return ret; + } + + private RangerServiceDef getServiceDef(String serviceType) { + RangerServiceDef ret = null; + + try { + RangerServiceDef serviceDef = EmbeddedServiceDefsUtil.instance().getEmbeddedServiceDef(serviceType); + + if (serviceDef != null) { // make a copy + ret = new RangerServiceDef(); + + ret.updateFrom(serviceDef); + } + } catch (Exception excp) { + // ignore + } + + return ret; + } + + private RangerPolicy getPolicy(ServicePolicies svcPolicies) { + RangerPolicy ret = new RangerPolicy(); + + ret.setConditions(new ArrayList<>()); + + ret.setService(svcPolicies.getServiceName()); + ret.setServiceType(svcPolicies.getServiceDef().getName()); + ret.setResource("database", new RangerPolicyResource("testdb")); + ret.addCondition(new RangerPolicyItemCondition("expr", Collections.singletonList("TAG.attr1 == 'value1'"))); + ret.addPolicyItem(getPolicyItem()); + ret.addAllowException(getPolicyItem()); + ret.addDenyPolicyItem(getPolicyItem()); + ret.addDenyException(getPolicyItem()); + ret.addDataMaskPolicyItem(getDataMaskPolicyItem()); + ret.addRowFilterPolicyItem(getRowFilterPolicyItem()); + + return ret; + } + + private RangerPolicyItem getPolicyItem() { + RangerPolicyItem ret = new RangerPolicyItem(); + + ret.addUser("testUser"); + ret.addGroup("testGroup"); + ret.addRole("testRole"); + ret.addCondition(new RangerPolicyItemCondition("expr", Collections.singletonList("TAG.attr1 == 'value1'"))); + + return ret; + } + + private RangerDataMaskPolicyItem getDataMaskPolicyItem() { + RangerDataMaskPolicyItem ret = new RangerDataMaskPolicyItem(); + + ret.addUser("testUser"); + ret.addGroup("testGroup"); + ret.addRole("testRole"); + ret.addCondition(new RangerPolicyItemCondition("expr", Collections.singletonList("TAG.attr1 == 'value1'"))); + ret.setDataMaskInfo(new RangerPolicyItemDataMaskInfo("MASK_NULL", null, null)); + + return ret; + } + + private RangerRowFilterPolicyItem getRowFilterPolicyItem() { + RangerRowFilterPolicyItem ret = new RangerRowFilterPolicyItem(); + + ret.addUser("testUser"); + ret.addGroup("testGroup"); + ret.addRole("testRole"); + ret.addCondition(new RangerPolicyItemCondition("expr", Collections.singletonList("TAG.attr1 == 'value1'"))); + ret.setRowFilterInfo(new RangerPolicyItemRowFilterInfo("dept in ('dept1','dept2')")); + + return ret; + } + + private SecurityZoneInfo getSecurityZoneInfo(String zoneName) { + SecurityZoneInfo ret = new SecurityZoneInfo(); + + ret.setZoneName(zoneName); + ret.setPolicies(new ArrayList<>()); + ret.setPolicyDeltas(new ArrayList<>()); + + return ret; + } +} diff --git a/agents-common/src/test/java/org/apache/ranger/plugin/util/TestServiceTags.java b/agents-common/src/test/java/org/apache/ranger/plugin/util/TestServiceTags.java new file mode 100644 index 0000000000..c41d4e1964 --- /dev/null +++ b/agents-common/src/test/java/org/apache/ranger/plugin/util/TestServiceTags.java @@ -0,0 +1,144 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource; +import org.apache.ranger.plugin.model.RangerServiceResource; +import org.apache.ranger.plugin.model.RangerTag; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; + +public class TestServiceTags { + private static final RangerServiceResource[] RESOURCES = { + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db1"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db2"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db3"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db4"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db5"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db6"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db7"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db8"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db9"))), + new RangerServiceResource("dev_hive", Collections.singletonMap("database", new RangerPolicyResource("db10"))) + }; + + @Test + public void testDedupEmpty() { + ServiceTags svcTags = new ServiceTags(); + + assertEquals(0, svcTags.dedupTags()); + } + + @Test + public void testDedup_NoDupTagsNoAttr() { + RangerTag[] tags = { + new RangerTag("PII", Collections.emptyMap()), + new RangerTag("PHI", Collections.emptyMap()), + }; + + ServiceTags svcTags = createServiceTags(tags, RESOURCES); + + assertEquals(0, svcTags.dedupTags()); + } + + @Test + public void testDedup_NoDupTagsWithAttr() { + RangerTag[] tags = { + new RangerTag("PII", Collections.emptyMap()), + new RangerTag("PII", Collections.singletonMap("type", "email")), + }; + + ServiceTags svcTags = createServiceTags(tags, RESOURCES); + + assertEquals(0, svcTags.dedupTags()); + } + + @Test + public void testDedup_DupTagsNoAttr() { + RangerTag[] tags = { + new RangerTag("PII", Collections.emptyMap()), + new RangerTag("PII", Collections.emptyMap()), + }; + + ServiceTags svcTags = createServiceTags(tags, RESOURCES); + + assertEquals(1, svcTags.dedupTags()); + } + + @Test + public void testDedup_DupTagsWithAttr() { + RangerTag[] tags = { + new RangerTag("PII", Collections.singletonMap("type", "email")), + new RangerTag("PII", Collections.singletonMap("type", "email")), + }; + + ServiceTags svcTags = createServiceTags(tags, RESOURCES); + + assertEquals(1, svcTags.dedupTags()); + } + + @Test + public void testDedup_DupTagsWithAttr_MultipleCalls() { + RangerTag[] tags = { + new RangerTag("PII", Collections.singletonMap("type", "email")), + new RangerTag("PII", Collections.singletonMap("type", "email")), + new RangerTag("PCI", Collections.emptyMap()), + new RangerTag("PCI", Collections.emptyMap()) + }; + + ServiceTags svcTags = createServiceTags(tags, RESOURCES); + + assertEquals(2, svcTags.dedupTags()); + assertEquals(0, svcTags.dedupTags()); + } + + + private ServiceTags createServiceTags(RangerTag[] tags, RangerServiceResource[] resources) { + ServiceTags ret = new ServiceTags(); + + // add tags + for (int i = 0; i < tags.length; i++) { + RangerTag tag = tags[i]; + + tag.setId(i + 1L); + ret.getTags().put(tag.getId(), tag); + } + + // add resources + for (int i = 0; i < resources.length; i++) { + RangerServiceResource resource = resources[i]; + + resource.setId(i + 1L); + ret.getServiceResources().add(resource); + } + + // set resourceToTagIds + ret.getServiceResources().forEach(resource -> { + List tagIds = new ArrayList<>(ret.getTags().keySet()); // add all available tags + + ret.getResourceToTagIds().put(resource.getId(), tagIds); + }); + + return ret; + } +} diff --git a/agents-common/src/test/resources/log4j.xml b/agents-common/src/test/resources/log4j.xml deleted file mode 100644 index 714d463904..0000000000 --- a/agents-common/src/test/resources/log4j.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/agents-common/src/test/resources/logback.xml b/agents-common/src/test/resources/logback.xml new file mode 100644 index 0000000000..7e59c78cd3 --- /dev/null +++ b/agents-common/src/test/resources/logback.xml @@ -0,0 +1,43 @@ + + + + + + System.out + + %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n + + + INFO + + + + + System.out + + %d [%t] - %m%n + + + INFO + + + + + + + diff --git a/agents-common/src/test/resources/plugin/hive_gds_info.json b/agents-common/src/test/resources/plugin/hive_gds_info.json new file mode 100644 index 0000000000..fd2d9ff7ec --- /dev/null +++ b/agents-common/src/test/resources/plugin/hive_gds_info.json @@ -0,0 +1,191 @@ +{ + "serviceName": "dev_hive", + "datasets": [ + { "id": 1, "name": "dataset-1", + "policies": [ + { "id": 2001, "name": "dataset-1", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["1"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "ds-user", "ds1-user" ], "groups": []} + ] + } + ] + }, + { "id": 2, "name": "dataset-2", + "policies": [ + { "id": 2002, "name": "dataset-2", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["2"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "ds-user", "ds2-user" ], "groups": []} + ] + } + ] + }, + { "id": 3, "name": "dataset-3", + "policies": [ + { "id": 2003, "name": "dataset-3", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["3"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "ds-user", "ds3-user" ], "groups": []} + ] + } + ] + }, + { "id": 4, "name": "dataset-4", + "policies": [ + { "id": 2004, "name": "dataset-4", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["4"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "ds-user", "ds4-user" ], "groups": []} + ] + } + ] + }, + { "id": 5, "name": "dataset-5", "validitySchedule": { "startTime": "2023/01/01 00:00:00", "endTime": "2024/01/01 00:00:00" }, + "policies": [ + { "id": 2005, "name": "dataset-5", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["5"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "ds-user", "ds5-user" ], "groups": []} + ] + } + ] + }, + { "id": 6, "name": "dataset-6", "validitySchedule": { "startTime": "2024/01/01 00:00:00", "endTime": "2064/01/01 00:00:00" }, + "policies": [ + { "id": 2006, "name": "dataset-6", "isEnabled": true, "isAuditEnabled": true, + "resources": { "dataset-id": { "values": ["6"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "ds-user", "ds6-user" ], "groups": []} + ] + } + ] + } + ], + "projects": [ + { "id": 1, "name": "project-1", + "policies": [ + { "id": 3001, "name": "project-1", "isEnabled": true, "isAuditEnabled": true, + "resources": { "project-id": { "values": ["1"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "proj-user", "proj1-user" ], "groups": []} + ] + } + ] + }, + { "id": 2, "name": "project-2", + "policies": [ + { "id": 3002, "name": "project-2", "isEnabled": true, "isAuditEnabled": true, + "resources": { "project-id": { "values": ["2"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "proj-user", "proj2-user" ], "groups": []} + ] + } + ] + }, + { "id": 3, "name": "project-3", "validitySchedule": { "startTime": "2023/01/01 00:00:00", "endTime": "2024/01/01 00:00:00" }, + "policies": [ + { "id": 3003, "name": "project-3", "isEnabled": true, "isAuditEnabled": true, + "resources": { "project-id": { "values": ["3"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "proj-user", "proj3-user" ], "groups": []} + ] + } + ] + }, + { "id": 4, "name": "project-4", "validitySchedule": { "startTime": "2024/01/01 00:00:00", "endTime": "2064/01/01 00:00:00" }, + "policies": [ + { "id": 3004, "name": "project-4", "isEnabled": true, "isAuditEnabled": true, + "resources": { "project-id": { "values": ["4"] } }, + "policyItems":[ + { "accesses":[ { "type": "_ALL", "isAllowed": true } ], "users": [ "proj-user", "proj4-user" ], "groups": []} + ] + } + ] + } + ], + "dataShares": [ + { "id": 1, "name": "hive-sales-2023", "zoneName": "sales", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 2, "name": "hive-finance-2023", "zoneName": "finance", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 3, "name": "hive-shipping-2023", "zoneName": "shipping", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 4, "name": "hive-new-customers-2023", "zoneName": "", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 5, "name": "hive-facilities", "zoneName": "", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] }, + { "id": 6, "name": "hive-eu-customers", "zoneName": "", "conditionExpr": "", "defaultAccessTypes": [ "_READ" ], "defaultTagMasks": [ ] } + ], + "dshids": [ + { "dataShareId": 1, "datasetId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 2, "datasetId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 2, "datasetId": 2, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 3, "datasetId": 2, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 4, "datasetId": 3, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 5, "datasetId": 4, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 6, "datasetId": 5, "status": "ACTIVE", "validitySchedule": null }, + { "dataShareId": 6, "datasetId": 6, "status": "ACTIVE", "validitySchedule": null } + ], + "dips": [ + { "datasetId": 1, "projectId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "datasetId": 2, "projectId": 1, "status": "ACTIVE", "validitySchedule": null }, + { "datasetId": 3, "projectId": 2, "status": "ACTIVE", "validitySchedule": null }, + { "datasetId": 5, "projectId": 3, "status": "ACTIVE", "validitySchedule": null }, + { "datasetId": 6, "projectId": 4, "status": "ACTIVE", "validitySchedule": null } + ], + "resources": [ + { + "id": 11, "dataShareId": 1, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "sales" ] }, "table": { "values": [ "prospects" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 12, "dataShareId": 1, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "sales" ] }, "table": { "values": [ "orders" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 21, "dataShareId": 2, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "finance" ] }, "table": { "values": [ "invoices" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 22, "dataShareId": 2, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "finance" ] }, "table": { "values": [ "payments" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 31, "dataShareId": 3, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "shipping" ] }, "table": { "values": [ "shipments" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": { } + }, + { + "id": 41, "dataShareId": 4, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "customers" ] }, "table": { "values": [ "contact_info" ] } }, "rowFilter": { "filterExpr": "created_time >= '2023-01-01' and created_time < '2024-01-01'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": null + }, + { + "id": 51, "dataShareId": 5, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "operations" ] }, "table": { "values": [ "facilities" ] } }, "rowFilter": null, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": null + }, + { + "id": 61, "dataShareId": 6, "conditionExpr": "", "accessTypes": [ "select" ], + "resource": { "database": { "values": [ "customers" ] }, "table": { "values": [ "contact_info" ] } }, "rowFilter": { "filterExpr": "country = 'US'" }, + "subResourceType": "column", "subResource": { "values": [ "*" ] }, "subResourceMasks": null + } + ], + "gdsServiceDef": { + "name": "gds", + "id": 3, + "resources": [ + { "name": "dataset-id", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": false, "ignoreCase": true }, "label": "Dataset ID", "description": "Dataset ID" }, + { "name": "project-id", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": false, "ignoreCase": true }, "label": "Project ID", "description": "Project ID" } + ], + "accessTypes":[ + { "itemId": 1, "name": "_CREATE", "label": "_CREATE" }, + { "itemId": 2, "name": "_READ", "label": "_READ" }, + { "itemId": 3, "name": "_UPDATE", "label": "_UPDATE" }, + { "itemId": 4, "name": "_DELETE", "label": "_DELETE" }, + { "itemId": 5, "name": "_MANAGE", "label": "_MANAGE" }, + { "itemId": 6, "name": "_ALL", "label": "_ALL" } + ] + }, + "gdsVersion": 1 +} \ No newline at end of file diff --git a/agents-common/src/test/resources/plugin/hive_policies.json b/agents-common/src/test/resources/plugin/hive_policies.json new file mode 100644 index 0000000000..7eef385f5b --- /dev/null +++ b/agents-common/src/test/resources/plugin/hive_policies.json @@ -0,0 +1,161 @@ +{ + "serviceDef":{ + "name": "hive", + "id": 3, + "resources": [ + { "name": "database", "level":1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "url", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "URL", "description": "URL", "recursiveSupported": true }, + { "name": "hiveservice", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "HiveService", "description": "HiveService" }, + { "name": "table", "level":2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "udf", "level":2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive UDF", "description": "Hive UDF" }, + { "name": "column", "level":3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes":[ + { "name": "select", "label": "Select", "category": "READ" }, + { "name": "update", "label": "Update", "category": "UPDATE" }, + { "name": "create", "label": "Create", "category": "CREATE" }, + { "name": "drop", "label": "Drop", "category": "DELETE" }, + { "name": "alter", "label": "Alter", "category": "CREATE" }, + { "name": "index", "label": "Index", "category": "MANAGE" }, + { "name": "lock", "label": "Lock", "category": "MANAGE" }, + { "name": "read", "label": "Read", "category": "READ" }, + { "name": "write", "label": "Write", "category": "UPDATE" }, + { "name": "repladmin", "label": "ReplAdmin", "category": "MANAGE" }, + { "name": "serviceadmin", "label": "ServiceAdmin", "category": "MANAGE" }, + { "name": "all", "label": "All", + "impliedGrants": [ "select", "update", "create", "drop", "alter", "index", "lock", "read", "write", "repladmin", "serviceadmin" ] + } + ] + }, + "securityZones": { + "sales": { + "zoneName": "sales", + "resources": [ + { "database": [ "sales" ] } + ], + "policies": [ + { "id": 100, "name": "table: sales.prospects", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "sales" ] }, "table": { "values": [ "prospects" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + }, + { "id": 101, "name": "table: sales.orders", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "sales" ] }, "table": { "values": [ "orders" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + } + ] + }, + "finance": { + "zoneName": "finance", + "resources": [ + { "database": [ "finance" ] } + ], + "policies": [ + { "id": 110, "name": "table: finance.invoices", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "finance" ] }, "table": { "values": [ "invoices" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + }, + { "id": 111, "name": "table: finance.payments", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "finance" ] }, "table": { "values": [ "payments" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + } + ] + }, + "shipping": { + "zoneName": "shipping", + "resources": [ + { "database": [ "shipping" ] } + ], + "policies": [ + { "id": 121, "name": "table: shipping.shipments", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "shipping" ] }, "table": { "values": [ "shipments" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + } + ] + } + }, + "policies":[ + { "id": 131, "name": "table: customers.contact_info", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "customers" ] }, "table": { "values": [ "contact_info" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + }, + { "id": 141, "name": "table: operations.facilities", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "operations" ] }, "table": { "values": [ "facilities" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "res-user" ], "delegateAdmin": false } + ] + } + ], + "tagPolicies": { + "serviceName": "dev_tag", + "serviceDef": { + "name": "tag", + "id": 1, + "resources": [ + { "name": "tag", "level":1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "TAGe", "description": "TAG" } + ], + "accessTypes":[ + { "name": "hive:select", "label": "Select", "category": "READ" }, + { "name": "hive:update", "label": "Update", "category": "UPDATE" }, + { "name": "hive:create", "label": "Create", "category": "CREATE" }, + { "name": "hive:drop", "label": "Drop", "category": "DELETE" }, + { "name": "hive:alter", "label": "Alter", "category": "CREATE" }, + { "name": "hive:index", "label": "Index", "category": "MANAGE" }, + { "name": "hive:lock", "label": "Lock", "category": "MANAGE" }, + { "name": "hive:read", "label": "Read", "category": "READ" }, + { "name": "hive:write", "label": "Write", "category": "UPDATE" }, + { "name": "hive:repladmin", "label": "ReplAdmin", "category": "MANAGE" }, + { "name": "hive:serviceadmin", "label": "ServiceAdmin", "category": "MANAGE" }, + { "name": "hive:all", "label": "All", + "impliedGrants": [ "hive:select", "hive:update", "hive:create", "hive:drop", "hive:alter", "hive:index", "hive:lock", "hive:read", "hive:write", "hive:repladmin", "hive:serviceadmin" ] + } + ], + "contextEnrichers": [ + { "itemId": 1, "name": "TagEnricher", "enricher": "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", "enricherOptions" : { "tagRetrieverClassName": "org.apache.ranger.plugin.contextenricher.RangerAdminTagRetriever", "tagRefresherPollingInterval": 60000 }} + ] + }, + "policies": [ + { "id": 200, "name": "tag: SALES", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "SALES" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "tag-user" ], "delegateAdmin": false } + ] + }, + { "id": 201, "name": "tag: FINANCE", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "FINANCE" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "tag-user" ], "delegateAdmin": false } + ] + }, + { "id": 202, "name": "tag: SHIPPING", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "SHIPPING" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "tag-user" ], "delegateAdmin": false } + ] + }, + { "id": 203, "name": "tag: CUSTOMER", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "CUSTOMER" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "tag-user" ], "delegateAdmin": false } + ] + }, + { "id": 204, "name": "tag: OPERATIONS", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "OPERATIONS" ] } }, + "policyItems":[ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "tag-user" ], "delegateAdmin": false } + ] + } + ] + } +} diff --git a/agents-common/src/test/resources/plugin/hive_roles.json b/agents-common/src/test/resources/plugin/hive_roles.json new file mode 100644 index 0000000000..c7c83e8caa --- /dev/null +++ b/agents-common/src/test/resources/plugin/hive_roles.json @@ -0,0 +1,15 @@ +{ + "serviceName": "dev_hive", + "rangerRoles": [ + { + "name": "role-1", + "users": [ + { "name": "user-1", "isAdmin": false }, + { "name": "user-2", "usAdmin": false } + ], + "groups": [ ], + "roles": [ ] + } + ], + "roleVersion": 1 +} \ No newline at end of file diff --git a/agents-common/src/test/resources/plugin/hive_tags.json b/agents-common/src/test/resources/plugin/hive_tags.json new file mode 100644 index 0000000000..38eec4a313 --- /dev/null +++ b/agents-common/src/test/resources/plugin/hive_tags.json @@ -0,0 +1,37 @@ +{ + "op": "add_or_update", + "tagModel": "resource_private", + "serviceName": "dev_hive", + "tagDefinitions": { + "1": { "name": "SALES" }, + "2": { "name": "FINANCE" }, + "3": { "name": "SHIPPING" }, + "4": { "name": "CUSTOMER" }, + "5": { "name": "OPERATIONS"} + }, + "tags": { + "1": { "id": 1, "type": "SALES" }, + "2": { "id": 2, "type": "FINANCE" }, + "3": { "id": 3, "type": "SHIPPING" }, + "4": { "id": 4, "type": "CUSTOMER" }, + "5": { "id": 5, "type": "OPERATIONS" } + }, + "serviceResources": [ + { "id": 1, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "sales" ] }, "table": { "values": [ "prospects" ] } } }, + { "id": 2, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "sales" ] }, "table": { "values": [ "orders" ] } } }, + { "id": 3, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "finance" ] }, "table": { "values": [ "invoices" ] } } }, + { "id": 4, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "finance" ] }, "table": { "values": [ "payments" ] } } }, + { "id": 5, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "shipping" ] }, "table": { "values": [ "shipments" ] } } }, + { "id": 6, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "customers" ] }, "table": { "values": [ "contact_info" ] } } }, + { "id": 7, "serviceName": "dev_hive", "resourceElements": { "database": { "values": [ "operations" ] }, "table": { "values": [ "facilities" ] } } } + ], + "resourceToTagIds": { + "1": [ 1 ], + "2": [ 1 ], + "3": [ 2 ], + "4": [ 2 ], + "5": [ 3 ], + "6": [ 4 ], + "7": [ 5 ] + } +} diff --git a/agents-common/src/test/resources/plugin/hive_user_store.json b/agents-common/src/test/resources/plugin/hive_user_store.json new file mode 100644 index 0000000000..8b4bfe88ff --- /dev/null +++ b/agents-common/src/test/resources/plugin/hive_user_store.json @@ -0,0 +1,8 @@ +{ + "userAttrMapping": { }, + "groupAttrMapping": { }, + "userGroupMapping": { }, + "userCloudIdMapping": { }, + "groupCloudIdMapping": { }, + "userStoreVersion": 1 +} diff --git a/agents-common/src/test/resources/plugin/test_base_plugin_hive.json b/agents-common/src/test/resources/plugin/test_base_plugin_hive.json new file mode 100644 index 0000000000..b38551c91b --- /dev/null +++ b/agents-common/src/test/resources/plugin/test_base_plugin_hive.json @@ -0,0 +1,377 @@ +{ + "policiesFilename": "/plugin/hive_policies.json", + "tagsFilename": "/plugin/hive_tags.json", + "rolesFilename": "/plugin/hive_roles.json", + "userStoreFilename": "/plugin/hive_user_store.json", + "gdsInfoFilename": "/plugin/hive_gds_info.json", + "tests": [ + { + "name": "table: sales.prospects, user: res-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "res-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 100 } + }, + { + "name": "table: sales.prospects, user: tag-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "tag-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 200 } + }, + { + "name": "table: sales.prospects, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2001 } + }, + { + "name": "table: sales.prospects, user: ds1-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "ds1-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2001 } + }, + { + "name": "table: sales.prospects, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3001 } + }, + { + "name": "table: sales.prospects, user: proj1-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "proj1-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3001 } + }, + { + "name": "table: sales.prospects, user: no-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "no-user", "userGroups": [] + }, + "result": { "isAllowed": false, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": -1 } + }, + + { + "name": "table: finance.invoices, user: res-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "res-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 110 } + }, + { + "name": "table: finance.invoices, user: tag-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "tag-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 201 } + }, + { + "name": "table: finance.invoices, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2001 } + }, + { + "name": "table: finance.invoices, user: ds1-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "ds1-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2001 } + }, + { + "name": "table: finance.invoices, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3001 } + }, + { + "name": "table: finance.invoices, user: proj1-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "proj1-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3001 } + }, + { + "name": "table: finance.invoices, user: no-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "no-user", "userGroups": [] + }, + "result": { "isAllowed": false, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": -1 } + }, + + { + "name": "table: shipping.shipments, user: res-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "res-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 121 } + }, + { + "name": "table: shipping.shipments, user: tag-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "tag-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 202 } + }, + { + "name": "table: shipping.shipments, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2002 } + }, + { + "name": "table: shipping.shipments, user: ds2-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "ds2-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2002 } + }, + { + "name": "table: shipping.shipments, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3001 } + }, + { + "name": "table: shipping.shipments, user: proj1-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "proj1-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3001 } + }, + { + "name": "table: shipping.shipments, user: no-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "no-user", "userGroups": [] + }, + "result": { "isAllowed": false, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": -1 } + }, + + + { + "name": "table: customers.contact_info, user: res-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "res-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 131 } + }, + { + "name": "table: customers.contact_info, user: tag-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "tag-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 203 } + }, + { + "name": "table: customers.contact_info, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2003 } + }, + { + "name": "table: customers.contact_info, user: ds3-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "ds3-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2003 } + }, + { + "name": "table: customers.contact_info, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3002 } + }, + { + "name": "table: customers.contact_info, user: proj2-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "proj2-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 3002 } + }, + { + "name": "table: customers.contact_info, user: no-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "no-user", "userGroups": [] + }, + "result": { "isAllowed": false, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": -1 } + }, + + { + "name": "table: operations.facilities, user: res-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "res-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 141 } + }, + { + "name": "table: operations.facilities, user: tag-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "tag-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 204 } + }, + { + "name": "table: operations.facilities, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2004 } + }, + { + "name": "table: operations.facilities, user: ds4-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "ds4-user", "userGroups": [] + }, + "result": { "isAllowed": true, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": 2004 } + }, + { + "name": "table: operations.facilities, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "isAllowed": false, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": -1 } + }, + { + "name": "table: operations.facilities, user: no-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "no-user", "userGroups": [] + }, + "result": { "isAllowed": false, "isAccessDetermined": "true", "isAudited": true, "isAuditedDetermined": true, "policyId": -1 } + }, + + { + "name": "ACLs: table: sales.prospects", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } } + }, + "acls": { + "userACLs": { + "res-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 100 } } }, + "tag-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 200 } } }, + "ds-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2001 } } }, + "ds1-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2001 } } }, + "proj-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3001 } } }, + "proj1-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3001 } } } + }, + "datasets": [ "dataset-1" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: table: finance.invoices", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } } + }, + "acls": { + "userACLs": { + "res-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 100 } } }, + "tag-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 200 } } }, + "ds-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2001 } } }, + "ds1-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2001 } } }, + "ds2-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2002 } } }, + "proj-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3001 } } }, + "proj1-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3001 } } } + }, + "datasets": [ "dataset-1", "dataset-2" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: table: shipping.shipments", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } } + }, + "acls": { + "userACLs": { + "res-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 100 } } }, + "tag-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 200 } } }, + "ds-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2002 } } }, + "ds2-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2002 } } }, + "proj-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3001 } } }, + "proj1-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3001 } } } + }, + "datasets": [ "dataset-2" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: table: customers.contact_info", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } } + }, + "acls": { + "userACLs": { + "res-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 131 } } }, + "tag-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 203 } } }, + "ds-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2003 } } }, + "ds3-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2003 } } }, + "ds6-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2006 } } }, + "proj-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3002 } } }, + "proj2-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3002 } } }, + "proj4-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 3004 } } } + }, + "datasets": [ "dataset-3", "dataset-6" ], + "projects": [ "project-2", "project-4" ] + } + }, + { + "name": "ACLs: table: operations.facilities", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } } + }, + "acls": { + "userACLs": { + "res-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 141 } } }, + "tag-user": { "select": { "result": 1, "isFinal": true, "policy": { "id": 204 } } }, + "ds-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2004 } } }, + "ds4-user": { "select": { "result": 1, "isFinal": false, "policy": { "id": 2004 } } } + }, + "datasets": [ "dataset-4" ] + } + } + ] +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/ACLResourceTags.json b/agents-common/src/test/resources/policyengine/ACLResourceTags.json index 711190c9e1..1fa7e89d9d 100644 --- a/agents-common/src/test/resources/policyengine/ACLResourceTags.json +++ b/agents-common/src/test/resources/policyengine/ACLResourceTags.json @@ -32,6 +32,12 @@ "attributeDefs": [ { "name": "activation_date", "type": "datetime" } ], "id": 5, "guid": "tagdefinition-restricted-final-guid" + }, + "6": { + "name": "DATA_QUALITY", + "attributeDefs": [ { "name": "score", "type": "float" } ], + "id": 5, + "guid": "tagdefinition-data_quality-guid" } }, "tags": { @@ -70,6 +76,18 @@ "attributes": { "expiry": "2026/06/15" }, "id": 6, "guid": "tag-pii-final-6-guid" + }, + "7": { + "type": "DATA_QUALITY", + "attributes": { "score": "0.8" }, + "id": 7, + "guid": "tag-data_quality-7-guid" + }, + "8": { + "type": "RESTRICTED", + "validityPeriods": [{"startTime": "2018/01/12 14:32:00", "endTime": "2030/01/12 14:32:00"}], + "id": 8, + "guid": "tag-restricted-8-guid" } }, "serviceResources": [ @@ -188,7 +206,27 @@ }, "id": 12, "guid": "finance.sales.invoice_id-guid" - } + }, + { + "serviceName": "cl1_hive", + "resourceElements": { + "database": { "values": [ "employee" ] }, + "table": { "values": [ "personal" ] }, + "column": { "values": [ "mrn" ] } + }, + "id": 13, + "guid": "employee.personal.mrn-guid" + }, + { + "serviceName": "cl1_hive", + "resourceElements": { + "database": { "values": [ "finance" ] }, + "table": { "values": [ "forecast" ] }, + "column": { "values": [ "revenue" ] } + }, + "id": 14, + "guid": "finance.forecast.revenue-guid" + } ], "resourceToTagIds": { "1": [ 1 ], @@ -201,7 +239,9 @@ "9": [ 5 ], "10": [ 6 ], "11": [ 6 ], - "12": [ 5 ] + "12": [ 5 ], + "13": [ 7 ], + "14": [ 8 ] } } diff --git a/agents-common/src/test/resources/policyengine/aws_s3_tags.json b/agents-common/src/test/resources/policyengine/aws_s3_tags.json new file mode 100644 index 0000000000..be598893ca --- /dev/null +++ b/agents-common/src/test/resources/policyengine/aws_s3_tags.json @@ -0,0 +1,33 @@ +{ + "op": "add_or_update", + "serviceName": "dev_aws-s3", + "tagDefinitions": { + "1": { "id": 1, "name": "PII" }, + "2": { "id": 2, "name": "PII-VENDOR" } + }, + "tags": { + "1": { "id": 1, "type": "PII" }, + "2": { "id": 2, "type": "PII-VENDOR" } + }, + "serviceResources": [ + { + "id": 1, "serviceName": "dev_aws-s3", + "resourceElements": { + "bucketname": { "values": [ "mycompany" ] }, + "objectpath": { "values": [ "/pii" ], "isRecursive": true } + } + }, + { + "id": 2, "serviceName": "dev_aws-s3", + "resourceElements": { + "bucketname": { "values": [ "mycompany" ] }, + "objectpath": { "values": [ "/vendor/pii" ], "isRecursive": true } + } + } + ], + "resourceToTagIds": { + "1": [ 1 ], + "2": [ 2 ] + } +} + diff --git a/agents-common/src/test/resources/policyengine/gds/test_gds_policy_engine_hive.json b/agents-common/src/test/resources/policyengine/gds/test_gds_policy_engine_hive.json new file mode 100644 index 0000000000..969c184f7e --- /dev/null +++ b/agents-common/src/test/resources/policyengine/gds/test_gds_policy_engine_hive.json @@ -0,0 +1,627 @@ +{ + "serviceDef":{ + "name": "hive", + "id": 3, + "resources": [ + { "name": "database", "level":1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "url", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "URL", "description": "URL", "recursiveSupported": true }, + { "name": "hiveservice", "level":1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "HiveService", "description": "HiveService" }, + { "name": "table", "level":2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "udf", "level":2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive UDF", "description": "Hive UDF" }, + { "name": "column", "level":3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions":{ "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes":[ + { "name": "select", "label": "Select", "category": "READ" }, + { "name": "update", "label": "Update", "category": "UPDATE" }, + { "name": "create", "label": "Create", "category": "CREATE" }, + { "name": "drop", "label": "Drop", "category": "DELETE" }, + { "name": "alter", "label": "Alter", "category": "CREATE" }, + { "name": "index", "label": "Index", "category": "MANAGE" }, + { "name": "lock", "label": "Lock", "category": "MANAGE" }, + { "name": "read", "label": "Read", "category": "READ" }, + { "name": "write", "label": "Write", "category": "UPDATE" }, + { "name": "repladmin", "label": "ReplAdmin", "category": "MANAGE" }, + { "name": "serviceadmin", "label": "ServiceAdmin", "category": "MANAGE" }, + { "name": "all", "label": "All", + "impliedGrants": [ "select", "update", "create", "drop", "alter", "index", "lock", "read", "write", "repladmin", "serviceadmin" ] + } + ] + }, + "securityZones": { + "sales": { "zoneName": "sales", "resources": [ { "database": [ "sales" ] } ] }, + "finance": { "zoneName": "finance", "resources": [ { "database": [ "finance" ] } ] }, + "shipping": { "zoneName": "shipping", "resources": [ { "database": [ "shipping" ] } ] } + }, + "gdsInfoFilename": "/plugin/hive_gds_info.json", + "tests": [ + { + "name": "table: sales.prospects, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: sales.orders, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "orders" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "database: sales, user: ds-user, access: _any", + "request": { + "resource": { "elements": { "database": "sales" } }, + "accessType": "", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: finance.invoices, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1", "dataset-2" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: finance.invoices, user: ds1-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "ds1-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: finance.invoices, user: ds2-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "ds2-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-2" ], "isAllowed": true, "isAudited": true, "policyId": 2002 } + }, + { + "name": "table: finance.payments, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "payments" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1", "dataset-2" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "database: finance, user: ds-user, access: _any", + "request": { + "resource": { "elements": { "database": "finance" } }, + "accessType": "", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-1", "dataset-2" ], "isAllowed": true, "isAudited": true, "policyId": 2001 } + }, + { + "name": "table: shipping.shipments, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-2" ], "isAllowed": true, "isAudited": true, "policyId": 2002 } + }, + { + "name": "database: shipping, user: ds-user, access: _any", + "request": { + "resource": { "elements": { "database": "shipping" } }, + "accessType": "", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-2" ], "projects": [ "project-1" ], "allowedByDatasets": [ "dataset-2" ], "isAllowed": true, "isAudited": true, "policyId": 2002 } + }, + { + "name": "table: customers.contact_info, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByDatasets": [ "dataset-3", "dataset-6" ], "isAllowed": true, "isAudited": true, "policyId": 2003 } + }, + { + "name": "table: customers.contact_info, user: ds3-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "ds3-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByDatasets": [ "dataset-3" ], "isAllowed": true, "isAudited": true, "policyId": 2003 } + }, + { + "name": "table: customers.contact_info, user: ds6-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "ds6-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByDatasets": [ "dataset-6" ], "isAllowed": true, "isAudited": true, "policyId": 2006 } + }, + { + "name": "database: customers, user: ds-user, access: _any", + "request": { + "resource": { "elements": { "database": "customers" } }, + "accessType": "", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByDatasets": [ "dataset-3", "dataset-6" ], "isAllowed": true, "isAudited": true, "policyId": 2003 } + }, + { + "name": "table: operations.facilities, user: ds-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-4" ], "projects": null, "allowedByDatasets": [ "dataset-4" ], "isAllowed": true, "isAudited": true, "policyId": 2004 } + }, + { + "name": "database: operations, user: ds-user, access: _any", + "request": { + "resource": { "elements": { "database": "operations" } }, + "accessType": "", "user": "ds-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-4" ], "projects": null, "allowedByDatasets": [ "dataset-4" ], "isAllowed": true, "isAudited": true, "policyId": 2004 } + }, + + + { + "name": "table: sales.prospects, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "allowedByProjects": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: sales.orders, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "orders" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "allowedByProjects": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: finance.invoices, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByProjects": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: finance.payments, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "payments" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "allowedByProjects": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: shipping.shipments, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-2" ], "projects": [ "project-1" ], "allowedByProjects": [ "project-1" ], "isAllowed": true, "isAudited": true, "policyId": 3001 } + }, + { + "name": "table: customers.contact_info, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByProjects": [ "project-2", "project-4" ], "isAllowed": true, "isAudited": true, "policyId": 3002 } + }, + { + "name": "table: customers.contact_info, user: proj2-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "proj2-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByProjects": [ "project-2" ], "isAllowed": true, "isAudited": true, "policyId": 3002 } + }, + { + "name": "table: customers.contact_info, user: proj4-user, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "proj4-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "allowedByProjects": [ "project-4" ], "isAllowed": true, "isAudited": true, "policyId": 3004 } + }, + { + "name": "table: operations.facilities, user: proj-user, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "proj-user", "userGroups": [] + }, + "result": { "datasets": [ "dataset-4" ], "projects": null, "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + + + { + "name": "table: sales.prospects, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "prospects" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: sales.orders, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "sales", "table": "orders" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1" ], "projects": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: finance.invoices, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "invoices" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: finance.payments, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "finance", "table": "payments" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-1", "dataset-2" ], "projects": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: shipping.shipments, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "shipping", "table": "shipments" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-2" ], "projects": [ "project-1" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: customers.contact_info, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "customers", "table": "contact_info" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-3", "dataset-6" ], "projects": [ "project-2", "project-4" ], "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + { + "name": "table: operations.facilities, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-4" ], "projects": null, "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + + + { + "name": "table: operations.facilities, user: scott, access: select", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "select", "user": "scott", "userGroups": [] + }, + "result": { "datasets": [ "dataset-4" ], "projects": null, "isAllowed": false, "isAudited": true, "policyId": -1 } + }, + + { + "name": "table: operations.facilities, user: ds-user, access: update", + "request": { + "resource": { "elements": { "database": "operations", "table": "facilities" } }, + "accessType": "update", "user": "ds-user", "userGroups": [] + }, + "result": null + }, + + { + "name": "ACLs: database: sales", + "request": { "resource": { "elements": { "database": "sales" } } }, + "acls": { } + }, + { + "name": "ACLs: table: sales.prospects", + "request": { "resource": { "elements": { "database": "sales", "table": "prospects" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds1-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj1-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-1" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: table: sales.orders", + "request": { "resource": { "elements": { "database": "sales", "table": "orders" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds1-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj1-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-1" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: table: sales.non_existent_table", + "request": { "resource": { "elements": { "database": "sales", "table": "non_existent_table" } } }, + "acls": { } + }, + { + "name": "ACLs: column: sales.orders.created_time", + "request": { "resource": { "elements": { "database": "sales", "table": "orders", "column": "created_time" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds1-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj1-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-1" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: database: finance", + "request": { "resource": { "elements": { "database": "finance" } } }, + "acls": { } + }, + { + "name": "ACLs: table: finance.invoices", + "request": { "resource": { "elements": { "database": "finance", "table": "invoices" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds1-user": { "select": { "result": 1, "isFinal": true } }, + "ds2-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj1-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-1", "dataset-2" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: table: finance.payments", + "request": { "resource": { "elements": { "database": "finance", "table": "payments" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds1-user": { "select": { "result": 1, "isFinal": true } }, + "ds2-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj1-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-1", "dataset-2" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: database: shipping", + "request": { "resource": { "elements": { "database": "shipping" } } }, + "acls": { } + }, + { + "name": "ACLs: table: shipping.shipments", + "request": { "resource": { "elements": { "database": "shipping", "table": "shipments" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds2-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj1-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-2" ], + "projects": [ "project-1" ] + } + }, + { + "name": "ACLs: database: customers", + "request": { "resource": { "elements": { "database": "customers" } } }, + "acls": { } + }, + { + "name": "ACLs: table: customers.contact_info", + "request": { "resource": { "elements": { "database": "customers", "table": "contact_info" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds3-user": { "select": { "result": 1, "isFinal": true } }, + "ds6-user": { "select": { "result": 1, "isFinal": true } }, + "proj-user": { "select": { "result": 1, "isFinal": true } }, + "proj2-user": { "select": { "result": 1, "isFinal": true } }, + "proj4-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-3", "dataset-6" ], + "projects": [ "project-2", "project-4" ] + } + }, + { + "name": "ACLs: database: operations", + "request": { "resource": { "elements": { "database": "operations" } } }, + "acls": { } + }, + { + "name": "ACLs: table: operations.facilities", + "request": { "resource": { "elements": { "database": "operations", "table": "facilities" } } }, + "acls": { + "userACLs": { + "ds-user": { "select": { "result": 1, "isFinal": true } }, + "ds4-user": { "select": { "result": 1, "isFinal": true } } + }, + "datasets": [ "dataset-4" ] + } + }, + + { + "name": "Datasets for principals: users[ ds-user ]", + "sharedWith": { "users": [ "ds-user" ] }, + "datasets": [ 1, 2, 3, 4, 5, 6 ] + }, + { + "name": "Datasets for principals: users[ ds1-user ]", + "sharedWith": { "users": [ "ds1-user" ] }, + "datasets": [ 1 ] + }, + { + "name": "Datasets for principals: users[ ds2-user ]", + "sharedWith": { "users": [ "ds2-user" ] }, + "datasets": [ 2 ] + }, + { + "name": "Datasets for principals: users[ ds3-user ]", + "sharedWith": { "users": [ "ds3-user" ] }, + "datasets": [ 3 ] + }, + { + "name": "Datasets for principals: users[ ds4-user ]", + "sharedWith": { "users": [ "ds4-user" ] }, + "datasets": [ 4 ] + }, + { + "name": "Datasets for principals: users[ ds5-user ]", + "sharedWith": { "users": [ "ds5-user" ] }, + "datasets": [ 5 ] + }, + { + "name": "Datasets for principals: users[ ds6-user ]", + "sharedWith": { "users": [ "ds6-user" ] }, + "datasets": [ 6 ] + }, + { + "name": "Datasets for principals: users[ ds1-user, ds4-user ]", + "sharedWith": { "users": [ "ds1-user","ds4-user" ] }, + "datasets": [ 1, 4 ] + }, + { + "name": "Projects for principals: users=[ proj-user ]", + "principals:": { "users": [ "proj-user" ] }, + "projects": [ 1, 2, 3, 4 ] + }, + { + "name": "Projects for principals: users[ proj1-user ]", + "sharedWith": { "users": [ "proj1-user" ] }, + "projects": [ 1 ] + }, + { + "name": "Projects for principals: users[ proj2-user ]", + "sharedWith": { "users": [ "proj2-user" ] }, + "projects": [ 2 ] + }, + { + "name": "Projects for principals: users[ proj3-user ]", + "sharedWith": { "users": [ "proj3-user" ] }, + "projects": [ 3 ] + }, + { + "name": "Projects for principals: users[ proj4-user ]", + "sharedWith": { "users": [ "proj4-user" ] }, + "projects": [ 4 ] + }, + { + "name": "Projects for principals: users[ proj1-user, proj2-user ]", + "sharedWith": { "users": [ "proj1-user", "proj2-user" ] }, + "projects": [ 1, 2 ] + }, + + { + "name": "Resources for Dataset: id=1", + "datasetId": 1, + "resourceIds": [ 11, 12, 21, 22 ] + }, + { + "name": "Resources for Dataset: id=2", + "datasetId": 2, + "resourceIds": [ 21, 22, 31 ] + }, + { + "name": "Resources for Dataset: id=3", + "datasetId": 3, + "resourceIds": [ 41 ] + }, + { + "name": "Resources for Dataset: id=4", + "datasetId": 4, + "resourceIds": [ 51 ] + }, + { + "name": "Resources for Dataset: id=1234 (non_existent_dataset)", + "datasetId": 1234, + "resourceIds": [ ] + }, + + { + "name": "Resources for Project: id=1", + "projectId": 1, + "resourceIds": [ 11, 12, 21, 22, 31 ] + }, + { + "name": "Resources for Project: id=2", + "projectId": 2, + "resourceIds": [ 41 ] + }, + { + "name": "Resources for Project: id=1234 (non_existent_project)", + "projectId": 1234, + "resourceIds": [ ] + }, + + { + "name": "Resources for DataShare: id=1", + "dataShareId": 1, + "resourceIds": [ 11, 12 ] + }, + { + "name": "Resources for DataShare: id=2", + "dataShareId": 2, + "resourceIds": [ 21, 22 ] + }, + { + "name": "Resources for DataShare: id=3", + "dataShareId": 3, + "resourceIds": [ 31 ] + }, + { + "name": "Resources for DataShare: id=4", + "dataShareId": 4, + "resourceIds": [ 41 ] + }, + { + "name": "Resources for DataShare: id=5", + "dataShareId": 5, + "resourceIds": [ 51 ] + }, + { + "name": "Resources for DataShare: id=1234 (non_existent_data_share)", + "dataShareId": 1234, + "resourceIds": [ ] + }, + { + "name": "Resources for projects(1, 2)", + "projectIds": [ 1, 2 ], + "resourceIds": [ 11, 12, 21, 22, 31, 41 ] + }, + { + "name": "Resources for datasets(1, 2)", + "datasetIds": [ 1, 2 ], + "resourceIds": [ 11, 12, 21, 22, 31 ] + }, + { + "name": "Resources for datasets(3, 4)", + "datasetIds": [ 3, 4 ], + "resourceIds": [ 41, 51 ] + }, + { + "name": "Resources for projects(1, 2), datasets(1, 2, 3, 4), dataShares(1, 2, 3, 4, 5)", + "projectIds": [ 1, 2 ], + "datasetIds": [ 1, 2, 3, 4 ], + "dataShareIds": [ 1, 2, 3, 4, 5 ], + "resourceIds": [ 11, 12, 21, 22, 31, 41, 51 ] + } + ] +} diff --git a/agents-common/src/test/resources/policyengine/hbaseTags.json b/agents-common/src/test/resources/policyengine/hbaseTags.json new file mode 100644 index 0000000000..a6762f11d9 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/hbaseTags.json @@ -0,0 +1,74 @@ +{ + "op":"add_or_update", + "tagModel":"resource_private", + "serviceName": "hbase_tag", + "tagDefinitions": { + "1": { + "name": "COLUMN_TAG", + "id": 1, + "guid": "tagdefinition-column-guid" + }, + "2": { + "name": "COLUMN_FAMILY_TAG", + "id": 2, + "guid": "tagdefinition-column-family-guid" + }, + "3": { + "name": "TABLE_TAG", + "id": 3, + "guid": "tagdefinition-table-guid" + } + }, + "tags": { + "1": { + "type": "COLUMN_TAG", + "id": 1, + "guid": "tag-column-guid" + }, + "2": { + "type": "COLUMN_FAMILY_TAG", + "id": 2, + "guid": "tag-column-family-guid" + }, + "3": { + "type": "TABLE_TAG", + "id": 3, + "guid": "tag-table-guid" + } + }, + "serviceResources": [ + { + "serviceName": "hbasedev", + "resourceElements": { + "table": { "values": [ "finance" ] }, + "column-family": { "values": [ "professional" ] }, + "column": { "values": [ "ssn" ] } + }, + "id": 1, + "guid": "finance.professional.ssn-guid" + }, + { + "serviceName": "hbasedev", + "resourceElements": { + "table": { "values": [ "finance" ] }, + "column-family": { "values": [ "personal" ] } + }, + "id": 2, + "guid": "finance.personal-guid" + }, + { + "serviceName": "hbasedev", + "resourceElements": { + "table": { "values": [ "finance" ] } + }, + "id": 3, + "guid": "finance-guid" + } + ], + "resourceToTagIds": { + "1": [ 1 ], + "2": [ 2 ], + "3": [ 3 ] + } +} + diff --git a/agents-common/src/test/resources/policyengine/plugin/test_plugin_capability.json b/agents-common/src/test/resources/policyengine/plugin/test_plugin_capability.json index a9f741b58a..135126cdc8 100644 --- a/agents-common/src/test/resources/policyengine/plugin/test_plugin_capability.json +++ b/agents-common/src/test/resources/policyengine/plugin/test_plugin_capability.json @@ -21,14 +21,14 @@ { "name": "Using all existing capabilities", "myCapabilities": [], - "otherCapabilities": ["RANGER_PLUGIN_CAPABILITY_TAG_POLICIES", "RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER","RANGER_PLUGIN_CAPABILITY_MASKING_AND_ROW_FILTERING", "RANGER_PLUGIN_CAPABILITY_MACROS", "RANGER_PLUGIN_CAPABILITY_AUDIT_MODE", "RANGER_PLUGIN_CAPABILITY_RESOURCE_IS_VALID_LEAF", "RANGER_PLUGIN_CAPABILITY_VALIDITY_PERIOD", "RANGER_PLUGIN_CAPABILITY_POLICY_PRIORITY","RANGER_PLUGIN_CAPABILITY_SECURITY_ZONE","RANGER_PLUGIN_CAPABILITY_POLICY_LEVEL_CONDITION", "RANGER_PLUGIN_CAPABILITY_DENY_ALL_ELSE_POLICY","RANGER_PLUGIN_CAPABILITY_ROLE"], + "otherCapabilities": ["RANGER_PLUGIN_CAPABILITY_TAG_POLICIES","RANGER_PLUGIN_CAPABILITY_MASKING_AND_ROW_FILTERING", "RANGER_PLUGIN_CAPABILITY_MACROS", "RANGER_PLUGIN_CAPABILITY_AUDIT_MODE", "RANGER_PLUGIN_CAPABILITY_RESOURCE_IS_VALID_LEAF", "RANGER_PLUGIN_CAPABILITY_VALIDITY_PERIOD", "RANGER_PLUGIN_CAPABILITY_POLICY_PRIORITY","RANGER_PLUGIN_CAPABILITY_SECURITY_ZONE","RANGER_PLUGIN_CAPABILITY_POLICY_LEVEL_CONDITION", "RANGER_PLUGIN_CAPABILITY_DENY_ALL_ELSE_POLICY","RANGER_PLUGIN_CAPABILITY_ROLE", "RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER", "RANGER_PLUGIN_CAPABILITY_AUDIT_EXCLUDED_USERS", "RANGER_PLUGIN_CAPABILITY_CHAINED_PLUGINS", "RANGER_PLUGIN_CAPABILITY_SUPERUSER_PERMISSIONS", "RANGER_PLUGIN_CAPABILITY_USERSTORE_DOWNLOAD", "RANGER_PLUGIN_CAPABILITY_AUDIT_POLICY", "RANGER_PLUGIN_CAPABILITY_UGT_ATTRIBUTES_IN_POLICY", "RANGER_PLUGIN_CAPABILITY_ADDITIONAL_RESOURCES_IN_POLICY", "RANGER_PLUGIN_CAPABILITY_GDS_POLICIES"], "difference": [] }, { "name": "Using all existing capabilities, other has fewer", "myCapabilities": [], "otherCapabilities": ["RANGER_PLUGIN_CAPABILITY_ROLE_DOWNLOAD_TIMER","RANGER_PLUGIN_CAPABILITY_MASKING_AND_ROW_FILTERING", "RANGER_PLUGIN_CAPABILITY_MACROS", "RANGER_PLUGIN_CAPABILITY_AUDIT_MODE", "RANGER_PLUGIN_CAPABILITY_RESOURCE_IS_VALID_LEAF", "RANGER_PLUGIN_CAPABILITY_VALIDITY_PERIOD", "RANGER_PLUGIN_CAPABILITY_POLICY_PRIORITY","RANGER_PLUGIN_CAPABILITY_SECURITY_ZONE","RANGER_PLUGIN_CAPABILITY_POLICY_LEVEL_CONDITION", "RANGER_PLUGIN_CAPABILITY_DENY_ALL_ELSE_POLICY","RANGER_PLUGIN_CAPABILITY_ROLE"], - "difference": ["RANGER_PLUGIN_CAPABILITY_TAG_POLICIES"] + "difference": ["RANGER_PLUGIN_CAPABILITY_TAG_POLICIES", "RANGER_PLUGIN_CAPABILITY_AUDIT_EXCLUDED_USERS", "RANGER_PLUGIN_CAPABILITY_CHAINED_PLUGINS", "RANGER_PLUGIN_CAPABILITY_SUPERUSER_PERMISSIONS", "RANGER_PLUGIN_CAPABILITY_USERSTORE_DOWNLOAD", "RANGER_PLUGIN_CAPABILITY_AUDIT_POLICY", "RANGER_PLUGIN_CAPABILITY_UGT_ATTRIBUTES_IN_POLICY", "RANGER_PLUGIN_CAPABILITY_ADDITIONAL_RESOURCES_IN_POLICY","RANGER_PLUGIN_CAPABILITY_GDS_POLICIES"] } ] } \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/resource_hierarchy_tags.json b/agents-common/src/test/resources/policyengine/resource_hierarchy_tags.json new file mode 100644 index 0000000000..268bdf95e4 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/resource_hierarchy_tags.json @@ -0,0 +1,42 @@ +{ + "op": "add_or_update", + "tagModel": "resource_private", + "serviceName": "cl1_hive", + "tagDefinitions": { + "1": { "id": 1, "guid": "tag-def-1", "name": "SENSITIVE", "attributeDefs": [ { "name": "level", "type": "string" } ] }, + "2": { "id": 2, "guid": "tag-def-2", "name": "ORDER" }, + "3": { "id": 3, "guid": "tag-def-3", "name": "CUSTOMER" }, + "4": { "id": 4, "guid": "tag-def-4", "name": "ADDRESS" } + }, + "tags": { + "1": { "id": 1, "guid": "tag-1", "type": "SENSITIVE", "attributes": { "level": "normal" } }, + "2": { "id": 2, "guid": "tag-2", "type": "SENSITIVE", "attributes": { "level": "high" } }, + "3": { "id": 3, "guid": "tag-3", "type": "SENSITIVE", "attributes": { "level": "top" } }, + "4": { "id": 4, "guid": "tag-4", "type": "SENSITIVE", "attributes": { "level": "top" } }, + "5": { "id": 5, "guid": "tag-5", "type": "SENSITIVE", "attributes": { "level": "top" } }, + "6": { "id": 6, "guid": "tag-6", "type": "ORDER" }, + "7": { "id": 7, "guid": "tag-7", "type": "CUSTOMER" }, + "8": { "id": 8, "guid": "tag-8", "type": "ADDRESS" } + }, + "serviceResources": [ + { "id": 1, "guid": "resource-1", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl1" ] } } }, + { "id": 2, "guid": "resource-2", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl1" ] }, "column": { "values": [ "SSN" ] } } }, + { "id": 3, "guid": "resource-3", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl1" ] }, "column": { "values": [ "Age" ] } } }, + { "id": 4, "guid": "resource-4", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl1" ] }, "column": { "values": [ "Name" ] } } }, + { "id": 5, "guid": "resource-5", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "db2" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } } }, + { "id": 6, "guid": "resource-6", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "order" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } } }, + { "id": 7, "guid": "resource-7", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "order" ] }, "table": { "values": [ "customer" ] } } }, + { "id": 8, "guid": "resource-8", "serviceName": "cl1_hive", "resourceElements": { "database": { "values": [ "order" ] }, "table": { "values": [ "customer" ] }, "column": { "values": [ "address" ] } } } + ], + "resourceToTagIds": { + "1": [ 1 ], + "2": [ 2 ], + "3": [ 3 ], + "4": [ 4 ], + "5": [ 5 ], + "6": [ 6 ], + "7": [ 7 ], + "8": [ 8 ] + } +} + diff --git a/agents-common/src/test/resources/policyengine/test_aclprovider_default.json b/agents-common/src/test/resources/policyengine/test_aclprovider_default.json index 37f4f3c2d5..5434fd28c9 100644 --- a/agents-common/src/test/resources/policyengine/test_aclprovider_default.json +++ b/agents-common/src/test/resources/policyengine/test_aclprovider_default.json @@ -394,6 +394,30 @@ "users": ["user1", "user2"] } ] + }, + { "id": 21, "name": "db=user_{USER}, table=*, column=*", "isEnabled": true, "isAuditEnabled": true, "isDenyAllElse": false, + "resources": { "database": { "values": [ "user_{USER}*" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "select" }, { "type": "update" } ], + "groups": [ "public" ] + } + ] + }, + { "id": 22, "name": "db=dept_${{USER.dept}}, table=*, column=*", "isEnabled": true, "isAuditEnabled": true, "isDenyAllElse": false, + "resources": { "database": { "values": [ "dept_${{USER.dept}}" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "select" } ], + "groups": [ "public", "engg" ] + } + ] + }, + { "id": 23, "name": "db=dept_engg, table=*, column=*", "isEnabled": true, "isAuditEnabled": true, "isDenyAllElse": false, + "resources": { "database": { "values": [ "dept_engg" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "select" } ], + "groups": [ "engg" ] + } + ] } ], "tagPolicies": { @@ -557,6 +581,14 @@ }, "tests": [ + { "name": "{USER} macro in database name", + "resource": { "elements": { "database": "user_madhan", "table": "test_tbl1" } }, + "groupPermissions": { "public": { "select": { "result": 2, "isFinal": true }, "update": { "result": 2, "isFinal": true } } } + }, + { "name": "${{USER.dept}} macro in database name", + "resource": { "elements": { "database": "dept_engg", "table": "test_tbl1" } }, + "groupPermissions": { "public": { "select": { "result": 2, "isFinal": true } }, "engg": { "select": { "result": 1, "isFinal": true } } } + }, { "name": "denyAllElse-test", "resource": {"elements":{"database":"denyAllElse", "table":"table-1", "column": "column-1" }}, @@ -594,6 +626,7 @@ { "name": "conditional-tag-only-test-descendant", "resource": {"elements":{"database":"finance", "table":"sales"}}, + "resourceMatchingScope": "SELF_OR_DESCENDANTS", "userPermissions": {"hive":{"select":{"result":-1, "isFinal":true},"create":{"result":1, "isFinal":true}, "drop":{"result":-1, "isFinal":true}}, "admin":{"select":{"result":-1, "isFinal":true}} }, "groupPermissions": {"public": {"index":{"result":2, "isFinal":true}}} }, @@ -606,14 +639,14 @@ { "name": "public-allow-test", "resource": {"elements":{"database":"finance", "table":"accounts", "column": "status" }}, - "userPermissions": {"john":{"select":{"result":2, "isFinal":true}, "update":{"result":2, "isFinal":true}}, "jane":{"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "mary":{"update":{"result":-1, "isFinal":true}}}, - "groupPermissions": {"public": {"select":{"result":2, "isFinal":true}}, "accounting": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "admin": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "interns":{"update":{"result":-1, "isFinal":true}}, "housekeeping":{"select":{"result":-1, "isFinal":true}}} + "userPermissions": {"john":{"select":{"result":2, "isFinal":true}, "update":{"result":2, "isFinal":true}}, "jane":{"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}}, + "groupPermissions": {"public": {"select":{"result":2, "isFinal":true}}, "accounting": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "admin": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "housekeeping":{"select":{"result":-1, "isFinal":true}}} }, { "name": "public-allow-test-next", "resource": {"elements":{"database":"finance", "table":"accounts", "column": "amount" }}, - "userPermissions": {"john":{"select":{"result":2, "isFinal":true}, "update":{"result":2, "isFinal":true}}, "jane":{"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "mary":{"update":{"result":-1, "isFinal":true}}}, - "groupPermissions": {"public": {"select":{"result":2, "isFinal":true}}, "accounting": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "admin": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "interns":{"update":{"result":-1, "isFinal":true}}, "housekeeping":{"drop":{"result":-1, "isFinal":true}}} + "userPermissions": {"john":{"select":{"result":2, "isFinal":true}, "update":{"result":2, "isFinal":true}}, "jane":{"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}}, + "groupPermissions": {"public": {"select":{"result":2, "isFinal":true}}, "accounting": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "admin": {"select":{"result":2, "isFinal":true},"update":{"result":2, "isFinal":true}}, "housekeeping":{"drop":{"result":-1, "isFinal":true}}} }, { "name": "conditions-in-exceptions-test", diff --git a/agents-common/src/test/resources/policyengine/test_aclprovider_hdfs.json b/agents-common/src/test/resources/policyengine/test_aclprovider_hdfs.json new file mode 100644 index 0000000000..9526763a2f --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_aclprovider_hdfs.json @@ -0,0 +1,131 @@ +{ + "testCases": [ + { + "name": "Test-ACL-Provider-for-HDFS", + + "servicePolicies": { + "serviceName": "hivedev", + "serviceDef": { + "name": "hdfs", + "id": 1, + "resources": [ + { + "name": "path", + "type": "path", + "level": 1, + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "label": "Resource Path", + "description": "HDFS file or directory path" + } + ], + "accessTypes": [ + { + "name": "read", + "label": "Read" + }, + { + "name": "write", + "label": "Write" + }, + { + "name": "execute", + "label": "Execute" + } + ], + "contextEnrichers": [ + { + "itemId": 1, + "name": "GeolocationEnricher", + "enricher": "org.apache.ranger.plugin.contextenricher.RangerFileBasedGeolocationProvider", + "enricherOptions": { + "FilePath": "/etc/ranger/geo/geo.txt", + "ForceRead": "false", + "IPInDotFormat": "true", + "geolocation.meta.prefix": "TEST_" + } + } + ], + "policyConditions": [ + { + "itemId": 1, + "name": "ScriptConditionEvaluator", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions": { + "engineName": "JavaScript" + }, + "label": "Script", + "description": "Script to execute" + } + ] + }, + "policies": [ + { + "id": 1, "name": "audit-all-access under /finance/restricted/", + "resources": { + "path": {"values": ["/finance/restricted/"], "isRecursive": true} + }, + "policyItems": [ + {"accesses": [], "users": [], "groups": [ "public"]} + ] + }, + { + "id": 2, "name": "allow-read-to-all under /public/", + "resources": { + "path": {"values": [ "/public/*" ], "isRecursive": true} + }, + "policyItems": [ + {"accesses": [{"type": "read", "isAllowed": true},{"type": "execute", "isAllowed": true}], "users": [], "groups": ["public"]} + ] + }, + { + "id": 3, "name": "allow-read-to-finance under /finance/restricted", + "resources": { + "path": {"values": [ "/finance/restricted" ], "isRecursive": true} + }, + "policyItems": [ + {"accesses": [{"type": "read", "isAllowed": true}], "users": [], "groups": ["finance"]} + ] + }, + { + "id": 4, "name": "allow-read-to-finance under /finance/limited", + "resources": { + "path": {"values": [ "/finance/limited"], "isRecursive": true} + }, + "policyItems": [ + {"accesses": [{"type": "read", "isAllowed": true}], "users": [], "groups": ["stewards"]} + ] + } + ] + }, + "tests": [ + { + "name": "test-finance-restricted", + "resource": {"elements":{"path":"/finance/restricted"}}, + "userPermissions": {}, + "groupPermissions": {"finance": {"read": {"result": 1, "isFinal": true}}}, + "rolePermissions": {} + }, + { + "name": "test-finance-limited", + "resource": {"elements":{"path":"/finance/limited"}}, + "userPermissions": {}, + "groupPermissions": {"stewards": {"read": {"result": 1, "isFinal": true}}}, + "rolePermissions": {} + }, + { + "name": "test-anything-under-public", + "resource": {"elements":{"path":"/public/anything"}}, + "userPermissions": {}, + "groupPermissions": {"public": {"read": {"result": 1, "isFinal": true}, "execute": {"result": 1, "isFinal": true}}}, + "rolePermissions": {} + } + ] + } + ] +} diff --git a/agents-common/src/test/resources/policyengine/test_aclprovider_mask_filter.json b/agents-common/src/test/resources/policyengine/test_aclprovider_mask_filter.json new file mode 100644 index 0000000000..c8f7acf5e2 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_aclprovider_mask_filter.json @@ -0,0 +1,413 @@ +{ + "testCases": [ + { + "name": "Test-ACL-Provider-DataMask", + + "servicePolicies": { + "serviceName": "hivedev", + "serviceDef": { + "name": "hive", "id": 3, + "resources": [ + { "name": "database", "level": 1, "mandatory": true, "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Hive Database", "description": "Hive Database" + }, + { + "name": "table", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Hive Table", "description": "Hive Table" + }, + { + "name": "udf", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Hive UDF", "description": "Hive UDF" + }, + { + "name": "column", "level": 3, "parent": "table", "mandatory": true, "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": true }, + "label": "Hive Column", "description": "Hive Column" + } + ], + "accessTypes": [ + { "name": "select", "label": "Select" }, + { "name": "update", "label": "Update" }, + { "name": "create", "label": "Create" }, + { "name": "drop", "label": "Drop" }, + { "name": "alter", "label": "Alter" }, + { "name": "index", "label": "Index" }, + { "name": "lock", "label": "Lock" }, + { "name": "all", "label": "All" } + ], + "policyConditions": [ + { "itemId": 1, "name": "expression", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions": { "engineName": "JavaScript", "ui.isMultiline": "true" }, + "label": "Enter boolean expression", "description": "Boolean expression" + } + ], + "dataMaskDef": { + "maskTypes": [ + { + "itemId": 1, + "name": "MASK", + "label": "Mask", + "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'" + }, + { + "itemId": 2, + "name": "SHUFFLE", + "label": "Shuffle", + "description": "Randomly shuffle the contents" + }, + { + "itemId": 3, + "name": "MASH_HASH", + "label": "Hash", + "description": "Hash value of the contents" + }, + { + "itemId": 4, + "name": "MASH_NONE", + "label": "No masking", + "description": "Unmasked value of the contents" + }, + { + "itemId": 10, + "name": "NULL", + "label": "NULL", + "description": "Replace with NULL" + } + + ], + "accessTypes":[ + {"name":"select","label":"Select"} + ], + "resources":[ + {"name":"database","matcherOptions":{"wildCard":false}}, + {"name":"table","matcherOptions":{"wildCard":false}}, + {"name":"column","matcherOptions":{"wildCard":false}} + ] + }, + "rowFilterDef": { + "accessTypes":[ + {"name":"select","label":"Select"} + ], + "resources":[ + {"name":"database","matcherOptions":{"wildCard":false}}, + {"name":"table","matcherOptions":{"wildCard":false}} + ] + } + }, + "policies": [ + {"id":101,"name":"01: db=employee, table=personal, column=ssn: mask","isEnabled":true,"isAuditEnabled":true,"policyType":1, + "resources":{"database":{"values":["employee"]},"table":{"values":["personal"]},"column":{"values":["ssn"]}}, + "dataMaskPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"SHUFFLE"} + } + ] + }, + {"id":1011,"name":"02: db=employee, table=personal, column=ssn,dummy: mask","isEnabled":true,"isAuditEnabled":true,"policyType":1, + "resources":{"database":{"values":["employee"]},"table":{"values":["personal"]},"column":{"values":["ssn", "dummy"]}}, + "dataMaskPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"HASH"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"} + } + ] + }, + {"id":102,"name":"db=hr, table=employee, column=date_of_birth: mask","isEnabled":true,"isAuditEnabled":true,"policyType":1, + "resources":{"database":{"values":["hr"]},"table":{"values":["employee"]},"column":{"values":["date_of_birth"]}}, + "dataMaskPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"SHUFFLE"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user3"],"groups":[],"conditions":[{"type": "expression", "values": ["test"]}],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"LAST_4"} + } + ] + }, + {"id":103,"name":"db=hr, table=employee, column=project: conditional-mask: validity-schedule","isEnabled":true,"isAuditEnabled":true,"policyType":1, + "resources":{"database":{"values":["hr"]},"table":{"values":["employee"]},"column":{"values":["project"]}}, + "validitySchedules": [{"startTime": "2018/01/12 14:32:00", "endTime": "2020/01/12 14:32:00"}], + "dataMaskPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"HASH"} + } + ] + }, + { "id": 104, "name": "db=test_db, table=dept_${{USER.dept}}, column=col1: unmasked for users in the department", + "isEnabled": true, "isAuditEnabled": true, "policyPriority": 1, "policyType": 1, + "resources": { "database": { "values": [ "test_db" ] }, "table": { "values": [ "dept_${{USER.dept}}" ] }, "column": { "values": [ "col1" ] } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "{USER}" ], "groups": [], "delegateAdmin": false, "dataMaskInfo": { "dataMaskType": "MASK_NONE" } } + ] + }, + { "id": 105, "name": "db=test_db, table=dept_hr, column=col1: mask hash for all users", + "isEnabled": true, "isAuditEnabled": true, "policyPriority": 0, "policyType": 1, + "resources": { "database": { "values": [ "test_db" ] }, "table": { "values": [ "dept_hr" ] }, "column": { "values": [ "col1" ] } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [], "groups": [ "public" ], "delegateAdmin": false, "dataMaskInfo": { "dataMaskType": "MASK_HASH" } } + ] + }, + {"id":201,"name":"db=employee, table=personal: row-filter","isEnabled":true,"isAuditEnabled":true,"policyType":2, + "resources":{"database":{"values":["employee"]},"table":{"values":["personal"]}}, + "rowFilterPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"location='US'"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"location='CA'"} + } + ] + }, + {"id":202,"name":"db=hr, table=employee: row-filter","isEnabled":true,"isAuditEnabled":true,"policyType":2, + "resources":{"database":{"values":["hr"]},"table":{"values":["employee"]}}, + "rowFilterPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"dept='production'"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"dept='purchase'"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user3"],"groups":[],"conditions":[{"type": "expression", "values": ["test"]}],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"location='GR'"} + } + ] + }, + {"id":203,"name":"db=hr, table=employee2: conditional-row-filter: validity-schedule","isEnabled":true,"isAuditEnabled":true,"policyType":2, + "resources":{"database":{"values":["hr"]},"table":{"values":["employee2"]}}, + "validitySchedules": [{"startTime": "2018/01/12 14:32:00", "endTime": "2020/01/12 14:32:00"}], + "rowFilterPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"dept='production'"} + }, + {"accesses":[{"type":"select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "rowFilterInfo": {"filterExpr":"dept='purchase'"} + } + ] + }, + { "id": 204, "name": "db=test_db, table=dept_${{USER.dept}}: no filter for users in the department", + "isEnabled": true, "isAuditEnabled": true, "policyPriority": 1, "policyType": 2, + "resources": { "database": { "values": [ "test_db" ] }, "table": { "values": [ "dept_${{USER.dept}}" ] } }, + "rowFilterPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "{USER}" ], "groups": [], "delegateAdmin": false, "rowFilterInfo": { "filterExpr": "1 = 1" } } + ] + }, + { "id": 205, "name": "db=test_db, table=dept_hr: row-filter", + "isEnabled": true, "isAuditEnabled": true, "policyPriority": 0, "policyType": 2, + "resources": { "database": { "values": [ "test_db" ] }, "table": { "values": [ "dept_hr" ] } }, + "rowFilterPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [], "groups": [ "public" ], "delegateAdmin": false, "rowFilterInfo": { "filterExpr": "dept != 'hr'" } } + ] + } + ], + "tagPolicies": { + "serviceName": "tagdev", + "serviceDef": { + "name": "tag", "id": 100, + "resources": [ + { "itemId": 1, "name": "tag", "type": "string", "level": 1, "parent": "", "mandatory": true, + "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { "wildCard": true, "ignoreCase": false }, + "label": "TAG", "description": "TAG" + } + ], + "accessTypes": [ + { "itemId": 1, "name": "hive:select", "label": "hive:select" }, + { "itemId": 2, "name": "hive:update", "label": "hive:update" }, + { "itemId": 3, "name": "hive:create", "label": "hive:create" }, + { "itemId": 4, "name": "hive:drop", "label": "hive:drop" }, + { "itemId": 5, "name": "hive:alter", "label": "hive:alter" }, + { "itemId": 6, "name": "hive:index", "label": "hive:index" }, + { "itemId": 7, "name": "hive:lock", "label": "hive:lock" }, + { "itemId": 8, "name": "hive:all", "label": "hive:all", + "impliedGrants": [ "hive:select", "hive:update", "hive:create", "hive:drop", "hive:alter", "hive:index", "hive:lock" ] } + ], + "dataMaskDef": { + "resources":[ + {"name":"tag"} + ] + }, + "contextEnrichers": [ + { "itemId": 1, "name": "TagEnricher", + "enricher": "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", + "enricherOptions": { + "tagRetrieverClassName": "org.apache.ranger.plugin.contextenricher.RangerFileBasedTagRetriever", + "tagRefresherPollingInterval": 60000, + "serviceTagsFileName": "/policyengine/ACLResourceTags.json" + } + } + ], + "policyConditions": [ + { "itemId": 1, "name": "expression", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions": { "engineName": "JavaScript", "ui.isMultiline": "true" }, + "label": "Enter boolean expression", "description": "Boolean expression" + }, + { + "itemId": 2, "name": "enforce-expiry", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptTemplateConditionEvaluator", + "evaluatorOptions": { "scriptTemplate": "ctx.isAccessedAfter('expiry_date');" }, + "label": "Deny access after expiry_date?", "description": "Deny access after expiry_date? (yes/no)" + }, + { + "itemId": 3, "name": "ip-range", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerIpMatcher", "evaluatorOptions": { }, + "label": "IP Address Range", "description": "IP Address Range" + } + ] + }, + "policies": [ + { "id": 101, "name": "RESTRICTED", "isEnabled": true, "isAuditEnabled": true,"policyType":1, + "resources": { + "tag": { "values": [ "RESTRICTED" ], "isRecursive": false } + }, + "dataMaskPolicyItems":[ + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"} + }, + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"HASH"} + } + ] + }, + { "id": 102, "name": "DATA_QUALITY", "isEnabled": true, "isAuditEnabled": true,"policyType":1, + "resources": { + "tag": { "values": [ "DATA_QUALITY" ], "isRecursive": false } + }, + "dataMaskPolicyItems":[ + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"}, "conditions": [{ "type": "expression", "values": [ "tag.score > 0.6" ] }] + }, + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"HASH"}, "conditions": [{ "type": "expression", "values": [ "tag.score > 0.6" ] }] + } + ] + }, + { "id": 103, "name": "RESTRICTED-FINAL: conditional mask - validity schedule", "isEnabled": true, "isAuditEnabled": true,"policyType":1, + "resources": { + "tag": { "values": [ "RESTRICTED-FINAL" ], "isRecursive": false } + }, + "validitySchedules": [{"startTime": "2018/01/12 14:32:00", "endTime": "2020/01/12 14:32:00"}], + "dataMaskPolicyItems":[ + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"MASK"} + }, + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false, + "dataMaskInfo": {"dataMaskType":"HASH"} + } + ] + } + ] + } + }, + "tests": [ + {"name":"mask: employee.personal.ssn", + "resource":{"elements":{"database":"employee", "table":"personal", "column":"ssn"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"SHUFFLE"}}, + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"HASH"}}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}} + ] + }, + {"name":"mask: hr.employee.date_of_birth", + "resource":{"elements":{"database":"hr", "table":"employee", "column":"date_of_birth"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"SHUFFLE"}}, + {"users":["user3"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"LAST_4"}, "isConditional": true} + ] + }, + {"name":"mask: hr.employee.project - conditional: validity-schedule", + "resource":{"elements":{"database":"hr", "table":"employee", "column":"project"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}, "isConditional": true}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"HASH"}, "isConditional": true} + ] + }, + {"name":"mask: employee.personal.city - tag-based: RESTRICTED", + "resource":{"elements":{"database":"employee", "table":"personal", "column":"city"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"HASH"}} + ] + }, + {"name":"mask: employee.personal.mrn - tag-based: DATA_QUALITY; conditional", + "resource":{"elements":{"database":"employee", "table":"personal", "column":"mrn"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}, "isConditional": true}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"HASH"}, "isConditional": true} + ] + }, + {"name":"mask: employee.personal.address - tag-based: RESTRICTED-FINAL; conditional: validity-schedule", + "resource":{"elements":{"database":"employee", "table":"personal", "column":"address"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}, "isConditional": true}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"HASH"}, "isConditional": true} + ] + }, + {"name":"mask: finance.forecast.revenue - tag-based: RESTRICTED; conditional: tag-validity-period", + "resource":{"elements":{"database":"finance", "table":"forecast", "column":"revenue"}}, + "dataMasks": [ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"MASK"}, "isConditional": true}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "maskInfo":{"dataMaskType":"HASH"}, "isConditional": true} + ] + }, + { "name": "mask: test_db.dept_hr.col1: conditional", + "resource": { "elements": { "database": "test_db", "table":"dept_hr", "column":"col1" } }, + "dataMasks": [ + { "users": [ ], "groups": [ "public" ], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_NONE" }, "isConditional": true }, + { "users": [ ], "groups": [ "public" ], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_HASH" }, "isConditional": false } + ] + }, + {"name":"row-filter: employee.personal", + "resource":{"elements":{"database":"employee", "table":"personal"}}, + "rowFilters":[ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"location='US'"}}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"location='CA'"}} + ] + }, + {"name":"row-filter: hr.employee", + "resource":{"elements":{"database":"hr", "table":"employee"}}, + "rowFilters":[ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"dept='production'"}}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"dept='purchase'"}}, + {"users":["user3"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"location='GR'"}, "isConditional": true} + ] + }, + {"name":"row-filter: hr.employee2 - conditional: validity-schedule", + "resource":{"elements":{"database":"hr", "table":"employee2"}}, + "rowFilters":[ + {"users":["user1"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"dept='production'"}, "isConditional": true}, + {"users":["user2"], "groups":[], "roles":[], "accessTypes":["select"], "filterInfo":{"filterExpr":"dept='purchase'"}, "isConditional": true} + ] + }, + { "name": "row-filter: test_db.dept_hr: conditional", + "resource": { "elements": { "database": "test_db", "table":"dept_hr" } }, + "rowFilters": [ + { "users": [], "groups": [ "public" ], "roles": [], "accessTypes": [ "select" ], "filterInfo": { "filterExpr": "1 = 1" }, "isConditional": true }, + { "users": [], "groups": [ "public" ], "roles": [], "accessTypes": [ "select" ], "filterInfo": { "filterExpr": "dept != 'hr'" }, "isConditional": false } + ] + } + ] + } + ] +} diff --git a/agents-common/src/test/resources/policyengine/test_aclprovider_resource_hierarchy_tags.json b/agents-common/src/test/resources/policyengine/test_aclprovider_resource_hierarchy_tags.json new file mode 100644 index 0000000000..206a6563d0 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_aclprovider_resource_hierarchy_tags.json @@ -0,0 +1,224 @@ +{ + "testCases": [ + { + "name": "Test multiple tag instances for resource hierarchy", + + "servicePolicies": { + "serviceName": "hivedev", + "serviceDef": { + "name": "hive", "id": 3, + "resources": [ + { "name": "database", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "table", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "column", "level": 3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes": [ + { "name": "select", "label": "Select" }, + { "name": "update", "label": "Update" }, + { "name": "create", "label": "Create" }, + { "name": "drop", "label": "Drop" }, + { "name": "alter", "label": "Alter" }, + { "name": "index", "label": "Index" }, + { "name": "lock", "label": "Lock" }, + { "name": "all", "label": "All" } + ], + "policyConditions": [ + { "itemId": 1, "name": "expression", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", "evaluatorOptions": { "engineName": "JavaScript", "ui.isMultiline": "true" }, "label": "Enter boolean expression", "description": "Boolean expression" } + ], + "dataMaskDef": { + "maskTypes": [ + { "itemId": 1, "name": "MASK", "label": "Mask", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'" }, + { "itemId": 2, "name": "SHUFFLE", "label": "Shuffle", "description": "Randomly shuffle the contents" }, + { "itemId": 3, "name": "MASK_HASH", "label": "Hash", "description": "Hash value of the contents" }, + { "itemId": 4, "name": "MASK_NONE", "label": "No masking", "description": "Unmasked value of the contents" }, + { "itemId": 10, "name": "NULL", "label": "NULL", "description": "Replace with NULL" } + ], + "accessTypes":[ + { "name": "select", "label": "Select" } + ], + "resources":[ + { "name": "database", "matcherOptions": { "wildCard": false } }, + { "name": "table", "matcherOptions": { "wildCard": false } }, + { "name": "column", "matcherOptions": { "wildCard": false } } + ] + }, + "rowFilterDef": { + "accessTypes":[ + { "name": "select", "label": "Select"} + ], + "resources":[ + { "name": "database", "matcherOptions": { "wildCard": false } }, + { "name": "table", "matcherOptions": { "wildCard": false } } + ] + } + }, + "policies": [ + ], + "tagPolicies": { + "serviceName": "tagdev", + "serviceDef": { + "name": "tag", "id": 100, + "resources": [ + { "itemId": 1, "name": "tag", "type": "string", "level": 1, "parent": "", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "TAG", "description": "TAG" } + ], + "accessTypes": [ + { "itemId": 1, "name": "hive:select", "label": "hive:select" }, + { "itemId": 2, "name": "hive:update", "label": "hive:update" }, + { "itemId": 3, "name": "hive:create", "label": "hive:create" }, + { "itemId": 4, "name": "hive:drop", "label": "hive:drop" }, + { "itemId": 5, "name": "hive:alter", "label": "hive:alter" }, + { "itemId": 6, "name": "hive:index", "label": "hive:index" }, + { "itemId": 7, "name": "hive:lock", "label": "hive:lock" }, + { "itemId": 8, "name": "hive:all", "label": "hive:all", + "impliedGrants": [ "hive:select", "hive:update", "hive:create", "hive:drop", "hive:alter", "hive:index", "hive:lock" ] } + ], + "dataMaskDef": { + "resources":[ + { "name": "tag" } + ] + }, + "contextEnrichers": [ + { "itemId": 1, "name": "TagEnricher", "enricher": "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", "enricherOptions": { "tagRetrieverClassName": "org.apache.ranger.plugin.contextenricher.RangerFileBasedTagRetriever", "tagRefresherPollingInterval": 60000, "serviceTagsFileName": "/policyengine/resource_hierarchy_tags.json" } } + ], + "policyConditions": [ + { "itemId": 1, "name": "expression", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", "evaluatorOptions": { "engineName": "JavaScript", "ui.isMultiline": "true" }, "label": "Enter boolean expression", "description": "Boolean expression" }, + { "itemId": 2, "name": "enforce-expiry", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptTemplateConditionEvaluator", "evaluatorOptions": { "scriptTemplate": "ctx.isAccessedAfter('expiry_date');" }, "label": "Deny access after expiry_date?", "description": "Deny access after expiry_date? (yes/no)" }, + { "itemId": 3, "name": "ip-range", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerIpMatcher", "evaluatorOptions": { }, "label": "IP Address Range", "description": "IP Address Range" } + ] + }, + "policies": [ + { "id": 1, "name": "1: access: SENSITIVE", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:select", "isAllowed": true}], "users": [ "test-user"] } + ] + }, + { "id": 2, "name": "2: access: ORDER", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "ORDER" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:create", "isAllowed": true}], "users": [ "dba"] } + ] + }, + { "id": 3, "name": "2: access: CUSTOMER", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "CUSTOMER" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:select", "isAllowed": true}], "users": [ "test-user"] } + ] + }, + { "id": 4, "name": "3: access: ADDRESS", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "ADDRESS" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:select", "isAllowed": true}], "users": [ "test-user"] } + ] + }, + { "id": 101, "name": "101: mask: SENSITIVE(level=normal)", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "conditions": [ { "type": "expression", "values": [ "TAG.level == 'normal'" ] } ], + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "SHUFFLE"}} + ] + }, + { "id": 102, "name": "102: mask: SENSITIVE(level=high)", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "conditions": [ { "type": "expression", "values": [ "TAG.level == 'high'" ] } ], + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK"}} + ] + }, + { "id": 103, "name": "103: mask: SENSITIVE(level=top)", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "conditions": [ { "type": "expression", "values": [ "TAG.level == 'top'" ] } ], + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK_HASH"}} + ] + }, + { "id": 104, "name": "104: mask: CUSTOMER", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "CUSTOMER" ], "isRecursive": false } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK_NONE"}} + ] + }, + { "id": 105, "name": "105: mask: ADDRESS", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "ADDRESS" ], "isRecursive": false } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK_HASH"}} + ] + } + ] + } + }, + "tests": [ + { "name": "table: db1.tbl1", + "resource": { "elements": { "database": "db1", "table": "tbl1" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } } + }, + { "name": "column: db1.tbl1.SSN", + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "SSN" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } }, + "dataMasks": [ + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "SHUFFLE" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_HASH" }, "isConditional": true } + ] + }, + { "name": "column: db1.tbl1.Age", + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "Age" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } }, + "dataMasks": [ + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "SHUFFLE" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_HASH" }, "isConditional": true } + ] + }, + { "name": "column: db1.tbl1.Name", + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "Name" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } }, + "dataMasks": [ + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "SHUFFLE" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_HASH" }, "isConditional": true } + ] + }, + { "name": "database: db2", + "resource": { "elements": { "database": "db2" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } } + }, + { "name": "table: db2.tbl1", + "resource": { "elements": { "database": "db2", "table": "tbl1" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } } + }, + { "name": "column: db2.tbl1.Name", + "resource": { "elements": { "database": "db2", "table": "tbl1", "column": "Name" } }, + "userPermissions": { "test-user": { "select": { "result": 1, "isFinal": true } } }, + "dataMasks": [ + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "SHUFFLE" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK" }, "isConditional": true }, + {"users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_HASH" }, "isConditional": true } + ] + }, + { "name": "database: order", + "resource": { "elements": { "database": "order" } }, + "userPermissions": { "dba": { "create": { "result": 1, "isFinal": true } } } + }, + { "name": "table: order.customer", + "resource": { "elements": { "database": "order", "table": "customer" } }, + "userPermissions": { + "test-user": { "select": { "result": 1, "isFinal": true } }, + "dba": { "create": { "result": 1, "isFinal": true } } + } + }, + { "name": "column: order.customer.address", + "resource": { "elements": { "database": "order", "table": "customer", "column": "address" } }, + "userPermissions": { + "test-user": { "select": { "result": 1, "isFinal": true } }, + "dba": { "create": { "result": 1, "isFinal": true } } + }, + "dataMasks": [ + { "users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_NONE" }, "isConditional": false }, + { "users": [ "test-user" ], "groups": [], "roles": [], "accessTypes": [ "select" ], "maskInfo": { "dataMaskType": "MASK_HASH" }, "isConditional": false } + ] + } + ] + } + ] +} diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_atlas.json b/agents-common/src/test/resources/policyengine/test_policyengine_atlas.json index 8275df4fe4..f9d513f755 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_atlas.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_atlas.json @@ -7,7 +7,7 @@ "resources":[ {"name":"entity-type","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Entity Type","description":"Entity Type"}, {"name":"entity-classification","level":2,"parent":"entity-type","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Entity Classification","description":"Entity Classification"}, - {"name":"entity","level":2,"parent":"entity-classification","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Entity ID","description":"Entity ID"} + {"name":"entity","level":3,"parent":"entity-classification","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Entity ID","description":"Entity ID"} ], "accessTypes":[ {"name":"entity-read","label":"Read Entity"}, @@ -48,12 +48,19 @@ {"accesses":[{"type":"entity-read", "isAllowed":true}],"users":[],"groups":["email-admins"],"delegateAdmin":false} ] } + , + {"id":3,"name":"policy for atlas resource matching","isEnabled":true,"isAuditEnabled":true, + "resources":{"entity-type":{"values":["table1"]},"entity-classification":{"values":["classification1"]},"entity":{"values":["entity1"]}}, + "policyItems":[ + {"accesses":[{"type":"entity-read", "isAllowed":true}],"users":[],"groups":["group1"],"delegateAdmin":false} + ] + } ], "tests":[ {"name":"DataSet read by a data-steward", "request":{ - "resource":{"elements":{"entity-type":"DataSet", "entity-classification":[]}, "entity":"default@cl1"}, + "resource":{"elements":{"entity-type":"DataSet", "entity-classification":[], "entity":"default@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["data-stewards"] }, "result":{"isAudited":true,"isAllowed":true,"policyId":1} @@ -61,7 +68,7 @@ , {"name":"DataSet read by a hive-admin", "request":{ - "resource":{"elements":{"entity-type":"DataSet", "entity-classification":""}, "entity":"default@cl1"}, + "resource":{"elements":{"entity-type":"DataSet", "entity-classification":"", "entity":"default@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["hive-admins"] }, "result":{"isAudited":true,"isAllowed":false,"policyId":-1} @@ -69,7 +76,7 @@ , {"name":"hive_table read by a data-steward", "request":{ - "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":""}, "entity":"default.testtable@cl1"}, + "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":"", "entity":"default.testtable@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["data-stewards"] }, "result":{"isAudited":true,"isAllowed":true,"policyId":1} @@ -77,7 +84,7 @@ , {"name":"hive_table read by a hive-admin", "request":{ - "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":""}, "entity":"default.testtable@cl1"}, + "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":"", "entity":"default.testtable@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["hive-admins"] }, "result":{"isAudited":true,"isAllowed":true,"policyId":2} @@ -85,7 +92,7 @@ , {"name":"PII hive_table read by a privacy-officer", "request":{ - "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII"]}, "entity":"default.testtable@cl1"}, + "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII"], "entity":"default.testtable@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["privacy-officers"] }, "result":{"isAudited":true,"isAllowed":true,"policyId":10} @@ -93,7 +100,7 @@ , {"name":"PII hive_table read by a email-admin", "request":{ - "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII"]}, "entity":"default.testtable@cl1"}, + "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII"], "entity":"default.testtable@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["email-admins"] }, "result":{"isAudited":true,"isAllowed":false,"policyId":-1} @@ -101,7 +108,7 @@ , {"name":"EMAIL_PII hive_table read by a privacy-officer", "request":{ - "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII", "EMAIL_PII"]}, "entity":"default.testtable@cl1"}, + "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII", "EMAIL_PII"], "entity":"default.testtable@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["privacy-officers"] }, "result":{"isAudited":true,"isAllowed":true,"policyId":10} @@ -109,11 +116,19 @@ , {"name":"EMAIL_PII hive_table read by a email-admin", "request":{ - "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII", "EMAIL_PII"]}, "entity":"default.testtable@cl1"}, + "resource":{"elements":{"entity-type":["hive_table", "DataSet"], "entity-classification":["PII", "EMAIL_PII"], "entity":"default.testtable@cl1"}}, "accessType":"entity-read","user":"user1","userGroups":["email-admins"] }, "result":{"isAudited":true,"isAllowed":true,"policyId":20} } + , + {"name":"Resource matching for atlas", + "request":{ + "resource":{"elements":{"entity-type":["table1"], "entity-classification":["classification1"], "entity":"entity1"}}, + "accessType":"entity-read","user":"user1","userGroups":["group1"] + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":3} + } ] } diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_audit_filter_hdfs.json b/agents-common/src/test/resources/policyengine/test_policyengine_audit_filter_hdfs.json new file mode 100644 index 0000000000..153b39b65c --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_audit_filter_hdfs.json @@ -0,0 +1,120 @@ +{ + "serviceName":"hdfsdev", + + "serviceDef":{ "name":"hdfs", "id":1, + "resources":[ + {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"} + ], + + "accessTypes":[ + {"name":"read","label":"Read"}, + {"name":"write","label":"Write"}, + {"name":"execute","label":"Execute"} + ] + }, + + "serviceConfig": { + "ranger.plugin.audit.filters": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'users': ['unaudited-user1'], 'groups': ['unaudited-group1'], 'roles': ['unaudited-role1'], 'isAudited': false}, {'actions': [ 'listStatus', 'getfileinfo' ], 'accessTypes':['execute'], 'isAudited': false}, {'resources':{'path':{'values':['/audited'], 'isRecursive': true}},'isAudited': true}, {'resources':{'path':{'values':['/unaudited'], 'isRecursive': true}},'isAudited': false} ]" + }, + + "policies":[ + { "id":10,"name":"/audited/","isEnabled":true,"isAuditEnabled":true, + "isDenyAllElse": true, + "resources":{"path":{"values":["/audited"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"write","isAllowed":true},{"type":"read","isAllowed":true},{"type":"execute","isAllowed":true}],"users":["allowed-user1", "unaudited-user1"], "groups": ["unaudited-group1"], "roles": ["unaudited-role1"] } + ] + }, + { "id":11,"name":"/unaudited/","isEnabled":true,"isAuditEnabled":true, + "isDenyAllElse": true, + "resources":{"path":{"values":["/unaudited"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"write","isAllowed":true},{"type":"read","isAllowed":true},{"type":"execute","isAllowed":true}],"users":["allowed-user1"]} + ] + } + ], + + "tagPolicyInfo": { + "serviceName":"tagdev", + + "serviceDef": { "name": "tag", "id": 100, + "resources": [ + { "name": "tag", "type": "string", "level": 1, "mandatory": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": false, "ignoreCase": false }, "label": "TAG", "description": "TAG" } + ], + "accessTypes": [ + { "name": "hdfs:read", "label": "hdfs:Read" }, + { "name": "hdfs:write", "label": "hdfs:Write" }, + { "name": "hdfs:execute", "label": "hdfs:Execute" } + ] + }, + + "serviceConfig": { + "ranger.plugin.audit.filters": "[ {'resources':{'tag':{'values':['AUDIT_ALL_ACCESS']}},'isAudited': true}, {'resources':{'tag':{'values':['NO_AUDIT_ACCESS']}},'isAudited': false} ]" + }, + + "tagPolicies":[ + { "id":101, "name":"PII", "isEnabled":true, "isAuditEnabled":true, + "resources":{"tag":{"values":["PII"]}}, + "policyItems":[ + { "accesses":[{"type":"hdfs:read", "isAllowed":true},{"type":"hdfs:write", "isAllowed":true}], "users":["allowed-user1", "unaudited-user1"], "groups":["unaudited-group1"], "roles":["unaudited-role1"]} + ] + } + ] + }, + + "tests":[ + { "name":"audit denied access: resource=/audited/test.txt, accessType=read, action=null, user=denied-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"read", "user":"denied-user1" }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + { "name":"audit denied access: resource=/unaudited/test.txt, accessType=read, action=null, user=denied-user1", + "request":{ "resource":{"elements":{"path":"/unaudited/test.txt"}}, "accessType":"read","user":"denied-user1" }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + { "name":"audit read access to /audited: resource=/audited/test.txt, accessType=read, action=null, user=allowed-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"read","user":"allowed-user1" }, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + }, + { "name":"audit write access to /audited: resource=/audited/test.txt, accessType=write, action=null, user=allowed-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"write","user":"allowed-user1" }, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + }, + { "name":"no audit for access to /unaudited: resource=/unaudited/test.txt, accessType=read, action=null, user=allowed-user1", + "request":{ "resource":{"elements":{"path":"/unaudited/test.txt"}}, "accessType":"read","user":"allowed-user1" }, + "result":{"isAudited":false,"isAllowed":true,"policyId":11} + }, + { "name":"no audit for listStatus action: resource=/audited/test.txt', accessType=read, action=listStatus, user=allowed-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"read","action":"listStatus","user":"allowed-user1" }, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + }, + { "name":"no audit for getfileinfo action: resource=/audited/test.txt, accessType=read, action=getfileinfo, user=allowed-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"read","action":"getfileinfo","user":"allowed-user1" }, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + }, + { "name":"no audit for execute access: resource=/audited/test.txt, accessType=execute, action=null, user=allowed-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"execute","user":"allowed-user1" }, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + }, + { "name":"no audit for unaudited-user1 user: resource=/audited/test.txt, accessType=execute, action=null, user=unaudited-user1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"write","user":"unaudited-user1" }, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + }, + { "name":"no audit for unaudited-group1 group: resource=/audited/test.txt, accessType=execute, action=null, group=unaudited-group1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"write","user":"nonexisting-user1", "userGroups":["unaudited-group1"] }, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + }, + { "name":"no audit for unaudited-role1 role: resource=/audited/test.txt, accessType=execute, action=null, role=unaudited-role1", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"write","user":"nonexisting-user1", "userGroups":["nonexisting-group1"], "userRoles":["unaudited-role1"] }, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + }, + { "name":"audit access to AUDIT_ALL_ACCESS tagged: resource=/unaudited/test.txt, accessType=write, action=null, user=allowed-user1, tags=AUDIT_ALL_ACCESS", + "request":{ "resource":{"elements":{"path":"/unaudited/test.txt"}}, "accessType":"write", "user":"allowed-user1", "context":{"TAGS": "[{'type':'AUDIT_ALL_ACCESS'}]"}}, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + }, + { "name":"no audit access to NO_AUDIT_ACCESS tagged: resource=/audited/test.txt, accessType=write, action=null, user=allowed-user1, tags=NO_AUDIT_ACCESS", + "request":{ "resource":{"elements":{"path":"/audited/test.txt"}}, "accessType":"write", "user":"allowed-user1", "context":{"TAGS": "[{'type':'NO_AUDIT_ACCESS'}]"}}, + "result":{"isAudited":false,"isAllowed":true,"policyId":10} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_audit_filter_hive.json b/agents-common/src/test/resources/policyengine/test_policyengine_audit_filter_hive.json new file mode 100644 index 0000000000..3225f157e9 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_audit_filter_hive.json @@ -0,0 +1,248 @@ +{ + "serviceName":"hivedev", + + "serviceDef":{ + "name":"hive", + "id":3, + "resources":[ + {"name":"database","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Database","description":"Hive Database"}, + {"name":"global","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Global","description":"Global"}, + {"name":"url","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"URL","description":"URL"}, + {"name":"hiveservice","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Service","description":"Hive Service"}, + {"name":"table","level":2,"parent":"database","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Table","description":"Hive Table"}, + {"name":"udf","level":2,"parent":"database","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive UDF","description":"Hive UDF"}, + {"name":"column","level":3,"parent":"table","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Column","description":"Hive Column"} + ], + "accessTypes":[ + {"name":"select","label":"Select"}, + {"name":"update","label":"Update"}, + {"name":"create","label":"Create"}, + {"name":"drop","label":"Drop"}, + {"name":"alter","label":"Alter"}, + {"name":"index","label":"Index"}, + {"name":"lock","label":"Lock"}, + {"name":"all","label":"All", + "impliedGrants": [ + "select", + "update", + "create", + "drop", + "alter", + "index", + "lock" + ] + } + ] + }, + + "serviceConfig": { + "ranger.plugin.audit.filters": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'resources':{'database':{'values':['temp']},'table':{'values':['tempdata']},'column':{'values':['*']}},'isAudited' : false}, {'resources':{'database':{'values':['sys']},'table':{'values':['dump']}},'users':['user2'],'isAudited': false }, {'actions':['METADATA OPERATION'], 'isAudited': false}, {'users':['superuser1'],'groups':['supergroup1'], 'isAudited': false} ]" + }, + +"policies": [ + {"id":1,"name":"db=*:table=*,column=*","isEnabled":true,"isAuditEnabled":true,"policyType":0, + "resources":{"database":{"values":["*"]},"table":{"values":["*"]},"column":{"values":["*"]}}, + "policyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["hive", "user1", "user2", "superuser1"],"groups":["supergroup1"],"delegateAdmin":false} + ] + } + ], + "tagPolicyInfo": { + + "serviceName":"tagdev", + "serviceDef": { + "name": "tag", + "id": 100, + "resources": [ + { + "itemId": 1, + "name": "tag", + "type": "string", + "level": 1, + "parent": "", + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": false + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "TAG", + "description": "TAG" + } + ], + "accessTypes": [ + { + "itemId": 1, + "name": "hive:select", + "label": "hive:select" + }, + { + "itemId": 2, + "name": "hive:update", + "label": "hive:update" + }, + { + "itemId": 3, + "name": "hive:create", + "label": "hive:create" + } + , + { + "itemId": 4, + "name": "hive:grant", + "label": "hive:grant" + } + , + { + "itemId": 5, + "name": "hive:drop", + "label": "hive:drop" + } + , + { + "itemId": 6, + "name": "hive:alter", + "label": "hive:alter" + }, + { + "itemId": 7, + "name": "hive:index", + "label": "hive:index" + }, + { + "itemId": 8, + "name": "hive:lock", + "label": "hive:lock" + }, + { + "itemId": 9, + "name": "hive:all", + "label": "hive:all", + "impliedGrants": + [ + "hive:select", + "hive:update", + "hive:create", + "hive:grant", + "hive:drop", + "hive:alter", + "hive:index", + "hive:lock" + ] + } + ], + "contextEnrichers": [], + "policyConditions": [] + }, + + "serviceConfig": { + "ranger.plugin.audit.filters": "[ {'resources':{'tag':{'values':['NO_AUDIT']}},'isAudited': false}, {'resources':{'tag':{'values':['SYS_DATA']}},'users':['user1'],'isAudited': false}, {'resources':{'tag':{'values':['HIPPA']}},'users':['user1'], 'isAudited': true} ]" + }, + + "tagPolicies":[ + {"id":1001,"name":"DEFAULT","isEnabled":true,"isAuditEnabled":true, + "resources":{"tag":{"values":["DEFAULT"],"isRecursive":false}}, + "policyItems":[{"accesses":[{"type":"hive:select","isAllowed":true}],"users":["hive", "user1"],"groups":[],"delegateAdmin":false}] + }, + {"id":1002,"name":"HIPPA","isEnabled":true,"isAuditEnabled":true, + "resources":{"tag":{"values":["HIPPA"],"isRecursive":false}}, + "policyItems":[{"accesses":[{"type":"hive:select","isAllowed":true}],"users":["hive", "user1","user2"],"groups":[],"delegateAdmin":false}] + } + ] + }, + + "tests": [ + {"name":"cmd -> select * from temp.tempdata, discard access audit for user1 to table -> temp.tempdata", + "request":{ + "resource":{"elements":{"database":"temp","table":"tempdata", "column": "*"}}, + "accessType":"select","user":"user1","userGroups":["user1"],"requestData":"select * from temp.tempdata" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + {"name":"cmd -> use temp, discard access audit for user1 to database -> temp" , + "request":{ + "resource":{"elements":{"database":"temp"}, + "accessType":"_any","user":"user1","userGroups":["user1"],"requestData":"use temp" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + } + }, + {"name":"cmd -> select * from sys.dump, discard access audit for user2 to table -> sys.dump", + "request":{ + "resource":{"elements":{"database":"sys","table":"dump"}}, + "accessType":"select","user":"user2","userGroups":[],"requestData":"select * from sys.dump" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + {"name":"cmd -> select * from hr.emp, discard access audit for superuser1 for access to table -> hr.emp", + "request":{ + "resource":{"elements":{"database":"hr"}}, + "accessType":"select","user":"superuser1","userGroups":[],"requestData":"select * from hr.emp" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + {"name":"cmd -> select * from hr.emp, discard access audit for supergroup1 to table -> hr.emp", + "request":{ + "resource":{"elements":{"database":"hr","table":"emp"}}, + "accessType":"select","user":"","userGroups":["supergroup1"],"requestData":"select * from hr.emp" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + {"name":"cmd -> select * from hr.emp;', Audit access for user4 to table hr.emp", + "request":{ + "resource":{"elements":{"database":"hr","table":"emp"}}, + "accessType":"select","user":"user4","userGroups":[],"requestData":"select * from hr.emp" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"cmd -> select * from sys.scheduled_queries, discard access audit for hive operation = METADATA OPERATION", + "request":{ + "resource":{"elements":{"database":"sys","table":"scheduled_queries"}}, + "accessType":"select","user":"hive","userGroups":[],"requestData":"select * from sys.scheduled_queries","action": "METADATA OPERATION" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + + {"name":"cmd -> select name from medical.data, Audit access of resources with TAG = HIPPA", + "request":{ + "resource":{"elements":{"database":"medical", "table":"data", "column":"name"}}, + "accessType":"select","user":"user2","userGroups":[],"requestData":"select name from medical.data for user1", + "context": {"TAGS": "[{\"type\":\"HIPPA\"}]"} + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + }, + + {"name":"cmd -> select name from temp.data, discard access audit of resources with TAG = NO_AUDIT", + "request":{ + "resource":{"elements":{"database":"temp", "table":"data", "column":"name"}}, + "accessType":"select","user":"user1","userGroups":[],"requestData":"select name from temp.data for user1", + "context": {"TAGS": "[{\"type\":\"NO_AUDIT\"}]"} + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + + {"name":"cmd -> select name from sysdb.data, discard access audit of resources with TAG = SYS_DATA", + "request":{ + "resource":{"elements":{"database":"sysdb", "table":"data", "column":"name"}}, + "accessType":"select","user":"user1","userGroups":[],"requestData":"select name from sysdb.data for user1", + "context": {"TAGS": "[{\"type\":\"SYS_DATA\"}]"} + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + }, + + {"name":"cmd -> use sysdb, discard access audit of resource with TAG = SYS_DATA", + "request":{ + "resource":{"elements":{"database":"sysdb"}}, + "accessType":"_any","user":"user1","userGroups":[],"requestData":"use sysdb", + "context": {"TAGS": "[{\"type\":\"SYS_DATA\"}]"} + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + } + ] +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_aws.json b/agents-common/src/test/resources/policyengine/test_policyengine_aws.json new file mode 100644 index 0000000000..56e082ad8c --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_aws.json @@ -0,0 +1,228 @@ +{ + "serviceName":"awsdev", + + "serviceDef":{ + "name":"hdfs", + "id":1, + "resources":[ + {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"} + ], + "accessTypes":[ + {"name":"read","label":"Read"}, + {"name":"write","label":"Write"}, + {"name":"execute","label":"Execute"} + ], + "contextEnrichers": [], + "policyConditions": [] + }, + + "policies":[ + {"id":10,"name":"allow-all-to-user1 /","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":20,"name":"allow-all-to-user1 /home/","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/home/"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":30,"name":"allow-all-to-user1 /tmpa/b","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmpa/b"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":40,"name":"allow-all-to-user1 /tmp/ab","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmp/ab"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":50,"name":"allow-all-to-user1 /tmp/a/b","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmp/a/b"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":60,"name":"allow-all-to-user1 /tmp/ac/d/e/f","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmp/ac/d/e/f"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":70,"name":"allow-all-to-user1 /tmpfile","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmpfile"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":80,"name":"allow-all-to-user1 /tmp.txt","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmp.txt"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + }, + + {"id":100,"name":"allow-read-to-/tmp/{USER}}", "isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmp/{USER}"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":["{USER}"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":200,"name":"allow-all-to-/tmp/{USER}/subdir}","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/tmp/{USER}/subdir"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["{USER}"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":300,"name":"allow-read-to-/user/dir}","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/user/dir"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":["scott"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":400,"name":"allow-all-to-/user/dir/subdir}","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/user/dir/subdir"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["scott"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":500,"name":"allow-read-to-/user/{USER}/a*}", "isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/user/{USER}/*"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":["{USER}"],"groups":[],"delegateAdmin":false} + ] + } + ], + + "tests":[ + {"name":"ALLOW 'write /tmp/scott' for u=scott for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/tmp/scott"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"write","user":"scott","userGroups":[],"requestData":"write /tmp/scott" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 200} + }, + {"name":"DENY 'ANY /tmp/scott' for u=joe for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/tmp/scott"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"joe","userGroups":[],"requestData":"ANY /tmp/scott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"ALLOW 'ANY /tmp/scott' for u=scott for scope SELF", + "request":{ + "resource":{"elements":{"path":"/tmp/scott"}}, + "accessType":"","user":"scott","userGroups":[],"requestData":"ANY /tmp/scott" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 100} + }, + {"name":"DENY 'ANY /tmp/scott' for u=joe for scope SELF", + "request":{ + "resource":{"elements":{"path":"/tmp/scott"}}, + "accessType":"","user":"joe","userGroups":[],"requestData":"ANY /tmp/scott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"ALLOW 'write /user/dir' for u=scott for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/user/dir"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"write","user":"scott","userGroups":[],"requestData":"write /user/dir" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 400} + }, + {"name":"ALLOW 'ANY /user/dir' for u=scott for scope SELF", + "request":{ + "resource":{"elements":{"path":"/user/dir"}}, + "accessType":"","user":"scott","userGroups":[],"requestData":"ANY /user/dir" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 300} + }, + {"name":"DENY 'ANY /user/dir' for u=joe for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/user/dir"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"joe","userGroups":[],"requestData":"ANY /user/dir" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId": -1} + }, + {"name":"ALLOW 'read /user/scott' for u=scott for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/user/scott"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"read","user":"scott","userGroups":[],"requestData":"read /tmp/scott" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 500} + }, + {"name":"DENY 'read /user/scott' for u=scott for scope SELF", + "request":{ + "resource":{"elements":{"path":"/user/scott"}}, + "accessType":"read","user":"scott","userGroups":[],"requestData":"read /tmp/scott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"ALLOW 'ANY /' for u=user1", + "request":{ + "resource":{"elements":{"path":"/"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":20} + } + , + {"name":"ALLOW 'ANY /tmp' for u=user1", + "request":{ + "resource":{"elements":{"path":"/tmp"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /tmp" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":40} + } + , + {"name":"ALLOW 'ANY /tmp/' for u=user1", + "request":{ + "resource":{"elements":{"path":"/tmp/"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /tmp/" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":40} + } + , + {"name":"ALLOW 'ANY /tmp/a' for u=user1", + "request":{ + "resource":{"elements":{"path":"/tmp/a"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /tmp/a" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":50} + } + , + {"name":"DENY 'ANY /tmp/ac' for u=user1", + "request":{ + "resource":{"elements":{"path":"/tmp/ac"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /tmp/ac" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY 'ANY /tmp/ac/d' for u=user1", + "request":{ + "resource":{"elements":{"path":"/tmp/ac/d"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /tmp/ac/d" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + } + , + {"name":"ALLOW 'ANY /tmp/ac/d/e' for u=user1", + "request":{ + "resource":{"elements":{"path":"/tmp/ac/d/e"}}, "resourceElementMatchingScopes": { "path": "SELF_OR_CHILD" }, + "accessType":"","user":"user1","userGroups":[],"requestData":"ANY /tmp/ac/d/e" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + } + ] +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_aws_s3.json b/agents-common/src/test/resources/policyengine/test_policyengine_aws_s3.json new file mode 100644 index 0000000000..e2373164c7 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_aws_s3.json @@ -0,0 +1,211 @@ +{ + "serviceName": "dev_aws-s3", + + "serviceDef": { + "name": "aws-s3", "id": 1, + "resources": [ + { "name": "bucketname", "parent":"", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "Bucket Name", "description": "Bucket Name" }, + { "name": "objectpath", "parent":"bucketname", "level": 2, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "Object Path", "description": "Object Path" } + ], + "accessTypes": [ + { "name": "read", "label": "Read" }, + { "name": "write", "label": "Write" }, + { "name": "delete", "label": "Delete" } + ] + }, + + "policies": [ + { + "id": 10, "name": "s3://mycompany/public", "isEnabled": true, "isAuditEnabled": true, + "resources": { + "bucketname": { "values": [ "mycompany" ] }, + "objectpath": { "values": [ "/public" ], "isRecursive": true } }, + "policyItems": [ + { "accesses": [ { "type": "read", "isAllowed": true } ], "groups": [ "public" ] } + ] + }, + { + "id": 20, "name": "s3://mycompany/home/{USER}", "isEnabled": true, "isAuditEnabled": true, + "resources": { + "bucketname": { "values": [ "mycompany" ] }, + "objectpath": { "values": [ "/home/{USER}" ], "isRecursive": true } }, + "policyItems": [ + { "accesses": [ { "type": "read", "isAllowed": true }, { "type": "write", "isAllowed": true }, { "type": "delete", "isAllowed": true } ], "users": [ "{USER}" ] } + ] + }, + { + "id": 30, "name": "s3://mycompany/dept/hr", "isEnabled": true, "isAuditEnabled": true, + "resources": { + "bucketname": { "values": [ "mycompany" ] }, + "objectpath": { "values": [ "/dept/hr" ], "isRecursive": true } }, + "policyItems": [ + { "accesses": [ { "type": "read", "isAllowed": true }, { "type": "write", "isAllowed": true }, { "type": "delete", "isAllowed": true } ], "groups": [ "hr-admins" ] }, + { "accesses": [ { "type": "read", "isAllowed": true } ], "groups": [ "hr-users" ] } + ] + } + ], + "tagPolicyInfo": { + "serviceName":"tagdev", + "serviceDef": { + "name": "tag", "id": 100, + "resources": [ + { "name": "tag", "type": "string", "level": 1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "TAG", "description": "TAG" + } + ], + "accessTypes": [ + { "name": "aws-s3:read", "label": "aws-s3:read" }, + { "name": "aws-s3:write", "label": "aws-s3:write" }, + { "name": "aws-s3:delete", "label": "aws-s3:delete" } + ], + "contextEnrichers": [ + { + "name" : "TagEnricher", + "enricher" : "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", + "enricherOptions" : { + "tagRetrieverClassName": "org.apache.ranger.plugin.contextenricher.RangerFileBasedTagRetriever", + "tagRefresherPollingInterval": 60000, + "serviceTagsFileName": "/policyengine/aws_s3_tags.json"} + } + ] + }, + "tagPolicies": [ + { + "id": 1001, "name": "PII", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "PII" ] } }, + "policyItems": [ + { "accesses": [ { "type": "aws-s3:read", "isAllowed": true } ], "users": [ "user3" ] } + ] + }, + { + "id": 1002, "name": "PII-VENDOR", "isEnabled": true, "isAuditEnabled": true, + "resources": { "tag": { "values": [ "PII-VENDOR" ] } }, + "policyItems": [ + { "accesses": [ { "type": "aws-s3:read", "isAllowed": true } ], "users": [ "user4" ] } + ] + } + ] + }, + "tests": [ + { + "name": "Verify user1 has read access to some object under s3://mycompany", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 10 } + }, + { + "name": "Verify user1 has read access to some object under s3://mycompany/public", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/public/" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 10 } + }, + { + "name": "Verify user1 has no write access to any object under s3://mycompany/public", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/public/" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "write", "user": "user1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { + "name": "Verify user1 has write access to some object under s3://mycompany/home", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/home" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "write", "user": "user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 20 } + }, + { + "name": "Verify user1 has no write access to any object under s3://mycompany/dept", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/dept" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "write", "user": "user1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { + "name": "Verify user3 has read access to some objects under s3://mycompany/", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user3" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1001 } + }, + { + "name": "Verify user3 has write access to some objects under s3://mycompany/", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "write", "user": "user3" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 20 } + }, + { + "name": "Verify user3 has read access to some objects under s3://mycompany/pii", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/pii" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user3" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1001 } + }, + { + "name": "Verify user3 has no write access to any object under s3://mycompany/pii", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/pii" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "write", "user": "user3" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { + "name": "Verify user3 has no read access to any object under s3://mycompany/vendor/pii", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/vendor/pii" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user3" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { + "name": "Verify user4 has read access to some objects under s3://mycompany/vendor", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/vendor" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user4" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1002 } + }, + { + "name": "Verify user4 has read access to some objects under s3://mycompany/vendor/pii", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/vendor/pii" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user4" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1002 } + }, + { + "name": "Verify user4 has read access to some objects under s3://mycompany/vendor/pii/test", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/vendor/pii/test" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user4" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1002 } + }, + { + "name": "Verify user4 has no read access to any object under s3://mycompany/pii", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/pii" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "read", "user": "user4" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { + "name": "Verify user4 has no write access to any object under s3://mycompany/pii", + "request": { + "resource": { "elements": { "bucketname": "mycompany", "objectpath": "/pii" } }, "resourceElementMatchingScopes": { "objectpath": "SELF_OR_PREFIX" }, + "accessType": "write", "user": "user4" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags.json b/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags.json index a2ec460e6c..7fa78ffbb6 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags.json @@ -156,7 +156,7 @@ {"id":2,"name":"PII_TAG_POLICY","isEnabled":true,"isAuditEnabled":true, "resources":{"tag":{"values":["PII"],"isRecursive":false}}, "policyItems":[ - {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["hive", "user1"],"groups":[],"delegateAdmin":false} + {"accesses":[{"type":"hive:select","isAllowed":true}, {"type":"hive:update","isAllowed":true}],"users":["hive", "user1"],"groups":[],"delegateAdmin":false} , {"accesses":[{"type":"hive:all","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false} ], @@ -170,7 +170,7 @@ {"id":3,"name":"EXPIRES_ON_TAG_POLICY","isEnabled":true,"isAuditEnabled":true, "resources":{"tag":{"values":["EXPIRES_ON"],"isRecursive":false}}, "policyItems":[ - {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user", "user1"],"groups":[],"delegateAdmin":false} + {"accesses":[{"type":"hive:select","isAllowed":true}, {"type":"hive:update","isAllowed":true}],"users":["user", "user1"],"groups":[],"delegateAdmin":false} ], "denyPolicyItems":[ {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["user"],"groups":[],"delegateAdmin":false} @@ -248,7 +248,7 @@ } , { - "name":"ALLOW 'use default;' for hive", + "name":"DENY 'use default;' for hive", "request":{ "resource":{"elements":{"database":"default"}}, "accessType":"","user":"hive","userGroups":[],"requestData":"'use default;' for hive" @@ -284,7 +284,7 @@ } , { - "name":"ALLOW 'show databases;' for hive", + "name":"DENY 'show databases;' for hive", "request":{ "resource":{"elements":{}}, "accessType":"","user":"hive","userGroups":[],"requestData":"'show databases;' for hive" diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags_deny.json b/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags_deny.json new file mode 100644 index 0000000000..33270ededf --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_descendant_tags_deny.json @@ -0,0 +1,107 @@ +{ + "serviceName":"hivedev", + + "serviceDef":{ + "name":"hive", + "id":3, + "resources":[ + {"name":"database","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Database","description":"Hive Database"}, + {"name":"table","level":2,"parent":"database","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Table","description":"Hive Table"}, + {"name":"udf","level":2,"parent":"database","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive UDF","description":"Hive UDF"}, + {"name":"column","level":3,"parent":"table","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Column","description":"Hive Column"} + ], + "accessTypes":[ + {"name":"select","label":"Select"}, + {"name":"update","label":"Update"}, + {"name":"create","label":"Create"}, + {"name":"grant","label":"Grant"}, + {"name":"drop","label":"Drop"}, + {"name":"alter","label":"Alter"}, + {"name":"index","label":"Index"}, + {"name":"lock","label":"Lock"}, + {"name":"all","label":"All", "impliedGrants": [ "select", "update", "create", "grant", "drop", "alter", "index", "lock" ] } + ] + }, + + "policies":[ + {"id":101,"name":"db=*, table=*, column=*","isEnabled":true,"isAuditEnabled":true, + "resources":{"database":{"values":["*"]},"table":{"values":["*"]},"column":{"values":["*"]}}, + "policyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1", "user2"],"groups":["public"],"delegateAdmin":false} + ] + } + ], + "tagPolicyInfo": { + "serviceName":"tagdev", + "serviceDef": { + "name": "tag", + "id": 100, + "resources": [ + { "itemId": 1, "name": "tag", "type": "string", "level": 1, "parent": "", "mandatory": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "TAG", "description": "TAG" } + ], + "accessTypes": [ + { "itemId": 1, "name": "hive:select", "label": "hive:select" }, + { "itemId": 2, "name": "hive:update", "label": "hive:update" }, + { "itemId": 3, "name": "hive:create", "label": "hive:create" } , + { "itemId": 4, "name": "hive:grant", "label": "hive:grant" } , + { "itemId": 5, "name": "hive:drop", "label": "hive:drop" } , + { "itemId": 6, "name": "hive:alter", "label": "hive:alter" }, + { "itemId": 7, "name": "hive:index", "label": "hive:index" }, + { "itemId": 8, "name": "hive:lock", "label": "hive:lock" }, + { "itemId": 9, "name": "hive:all", "label": "hive:all", "impliedGrants": [ "hive:select", "hive:update", "hive:create", "hive:grant", "hive:drop", "hive:alter", "hive:index", "hive:lock" ] } + ], + "contextEnrichers": [ + { + "itemId": 1, + "name" : "TagEnricher", + "enricher" : "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", + "enricherOptions" : {"tagRetrieverClassName":"org.apache.ranger.plugin.contextenricher.RangerFileBasedTagRetriever", "tagRefresherPollingInterval":60000, "serviceTagsFileName":"/policyengine/descendant_tags.json"} + } + ] + }, + "tagPolicies":[ + {"id":1,"name":"RESTRICTED","isEnabled":true,"isAuditEnabled":true, + "resources":{"tag":{"values":["RESTRICTED"],"isRecursive":false}}, + "isDenyAllElse": false, + "policyItems":[ + { "accesses":[{"type":"hive:select","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false } + ], + "denyPolicyItems":[ + { "accesses":[{"type":"hive:select","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false } + ] + } + ] + }, + + "tests":[ + {"name":"ALLOW 'select name from finance.tax_2016;' for user1", + "request":{ + "resource":{"elements":{"database":"finance", "table":"tax_2016", "column":"name"}}, + "accessType":"select","user":"user1","userGroups":[],"requestData":"select name from finance.tax_2016;' for user1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":1} + }, + {"name":"DENY 'select name from finance.tax_2016;' for user2", + "request":{ + "resource":{"elements":{"database":"finance", "table":"tax_2016", "column":"name"}}, + "accessType":"select","user":"user2","userGroups":[],"requestData":"select name from finance.tax_2016;' for user2" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":1} + }, + {"name":"ALLOW 'show databases' for user1", + "request":{ + "resource":{"elements":{}}, + "accessType":"_any","user":"user1","userGroups":[],"requestData":"show databases;' for user1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":1} + }, + {"name":"ALLOW 'use finance' for user2", + "request":{ + "resource":{"elements":{"database": "finance"}}, + "accessType":"_any","user":"user2","userGroups":[],"requestData":"use finance;' for user2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":101} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_geo.json b/agents-common/src/test/resources/policyengine/test_policyengine_geo.json index 4249996b85..3a038761d8 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_geo.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_geo.json @@ -66,7 +66,7 @@ {"accesses":[{"type":"read","isAllowed":true}],"users":[],"groups":["finance"],"delegateAdmin":false, "conditions":[{ "type":"ScriptConditionEvaluator", - "values":["var country_code_format_long = ctx.getRequestContextAttribute('LOCATION_FORMAT_LONG_COUNTRY_CODE'); var country_code_format_dot = ctx.getRequestContextAttribute('LOCATION_FORMAT_DOT_COUNTRY_CODE');ctx.result = (!!country_code_format_long && !!country_code_format_dot && (country_code_format_long == country_code_format_dot));"] + "values":["var country_code_format_long = ctx.getRequestContextAttribute('LOCATION_FORMAT_LONG_COUNTRY_CODE'); var country_code_format_dot = ctx.getRequestContextAttribute('LOCATION_FORMAT_DOT_COUNTRY_CODE');ctx.result = (country_code_format_long != null && country_code_format_dot != null && (country_code_format_long == country_code_format_dot));"] }]} ] } diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs.json b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs.json index 976cd25320..f06ca16778 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs.json @@ -47,6 +47,19 @@ {"accesses":[{"type":"read","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false} ] } + , + {"id":11,"name":"allow-read-to-public /test/forbidden/","isEnabled":true,"isAuditEnabled":true, + "isDenyAllElse": false, + "resources":{"path":{"values":["/test/forbidden/"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false} + ] + } + , + {"id":12,"name":"deny-any-to-everybody /test/forbidden/","isEnabled":true,"isAuditEnabled":true, + "isDenyAllElse": true, + "resources":{"path":{"values":["/test/forbidden/"],"isRecursive":true}} + } , {"id":20,"name":"allow-read-to-user2 /test/restricted/","isEnabled":true,"isAuditEnabled":true, "resources":{"path":{"values":["/test/restricted/"],"isRecursive":true}}, @@ -121,6 +134,15 @@ }, "result":{"isAudited":true,"isAllowed":true,"policyId":10} } + , + {"name":"DENY 'read /test/forbidden/sales.db' for u=user1", + "request":{ + "resource":{"elements":{"path":"/test/forbidden/sales.db"}}, + "accessType":"","user":"user1","userGroups":[],"requestData":"read /test/forbidden/sales.db", + "remoteIPAddress":"255.255.255.255" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":12} + } , {"name":"DENY 'read /test/restricted/sales.db' for u=user3", "request":{ diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_delete.json b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_delete.json new file mode 100644 index 0000000000..c74af3e37b --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_delete.json @@ -0,0 +1,635 @@ +{ + "serviceName":"hdfsdev", + + "serviceDef":{ + "name":"hdfs", + "id":1, + "resources":[ + {"name":"path","type":"path","level":10,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":false},"label":"Resource Path","description":"HDFS file or directory path"} + ], + "accessTypes":[ + {"itemId": 1, "name":"read","label":"Read"}, + {"itemId": 2, "name":"write","label":"Write"}, + {"itemId": 3, "name":"execute","label":"Execute"} + ], + "contextEnrichers": [], + "policyConditions": [] + }, + + "policies":[ + {"id":1,"name":"policy_for_audits","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/*"],"isRecursive":true}}, + "policyItems":[] + } + , + {"id":10,"name":"user1_/test?_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test?"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + ], + + "testsInfo" : { + "tests":[ + {"name":"ALLOW test_01A_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + } + , + {"name":"DENY test_01B_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/test11"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/test11" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY test_01C_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 10, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":20,"name":"user1_/test?","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test?"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_02A_wildcard_true_recursive_true", + "request":{ + "resource":{"elements":{"path":"/test1/test11"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/test11" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":20} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 20, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":30,"name":"user1_/test?a?_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test?a?"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_03A_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1a1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1a1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":30} + } + , + {"name":"DENY test_03B_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test2A2"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test2A2" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY test_03C_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1a12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1a12" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY test_03D_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test12a1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12a1" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY test_03E_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test12a12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12a12" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY test_03F_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1a"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1a" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 30, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":40,"name":"user1_/test?a?_recursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test?a?"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_04A_wildcard_true_recursive_true", + "request":{ + "resource":{"elements":{"path":"/test1a1/test11a1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1a1/test11a1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":40} + } + ] + , + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 40, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":50,"name":"user1_/test??_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test??"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_05A_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":50} + }, + {"name":"ALLOW test_05B_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"ALLOW test_05C_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1a1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1a1" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 50, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":60,"name":"user1_/test???_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test???"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"DENY test_06A_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"ALLOW test_06B_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":60} + }, + {"name":"ALLOW test_06C_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test123"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test123" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":60} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 60, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":70,"name":"user1_/test1/?_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test1/?"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"DENY test_07A_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"DENY test_07B_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/test11"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/test11" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"ALLOW test_07C_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/a"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/a" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":70} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 70, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": {"serviceType": "hdfs", "policyType": 0, "version": 1, + "id":80,"name":"user1_/test*_/test_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test*", "/test"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_08A_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":80} + } + , + {"name":"ALLOW test_08B_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":80} + } + , + {"name":"ALLOW test_08C_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/1/2"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1/2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":80} + } + , + {"name":"ALLOW test_08D_wildcard_true_recursive_false", + "request":{ + "resource":{"elements":{"path":"/test1/1/2/a.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1/2/a.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":80} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 80, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": { "serviceType": "hdfs", "policyType": 0, "version": 1, + "id":90,"name":"user1_/test*1*_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test*1*"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_09A_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":90} + }, + {"name":"ALLOW test_09B_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/1"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":90} + }, + {"name":"ALLOW test_09C_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/1/2"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1/2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":90} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 90, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": { "serviceType": "hdfs", "policyType": 0, "version": 1, + "id":100,"name":"user1_/test*1*2_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test*1*2"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_10A_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test12" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":100} + }, + {"name":"DENY test_10B_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test123"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test123" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"ALLOW test_10C_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test12a12"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /testa12" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":100} + }, + {"name":"ALLOW test_10D_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test2/1/2"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test2/1/2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":100} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 100, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": { "serviceType": "hdfs", "policyType": 0, "version": 1, + "id":110,"name":"user1_/test1/b*y.txt_/test2_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test1/b*y.txt", "/test2"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_11A_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/boy.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/boy.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":110} + }, + {"name":"ALLOW test_11B_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/b1/a2/any.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/b1/a1/any.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":110} + }, + {"name":"ALLOW test_11C_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/by.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/by.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":110} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 110, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + , + {"changeType": 0, + "policy": { "serviceType": "hdfs", "policyType": 0, "version": 1, + "id":120,"name":"user1_/test1/*/a.txt_/test2_notrecursive","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test1/*/a.txt", "/test2"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["hrt_21"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"ALLOW test_12A_wildcard_true_recursive_false_target_dir ", + "request":{ + "resource":{"elements":{"path":"/test2"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":120} + }, + {"name":"DENY test_12A_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/a.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/a.txt" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"ALLOW test_12B_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/1/2/a.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1/2/a.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":120} + }, + {"name":"DENY test_12C_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test1/1/2/ba.txt"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test1/1/2/ba.txt" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 120, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + ] + }, + "updatedTestsInfo" : { + "tests": [ + {"name":"DENY test_13_wildcard_true_recursive_false ", + "request":{ + "resource":{"elements":{"path":"/test2"}}, + "accessType":"write","user":"hrt_21","userGroups":[],"requestData":"write /test2" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 2, + "policy": { + "id": 1, + "version": 1, + "policyType": 0, + "serviceType": "hdfs" + } + } + ] + } + } + } + } + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_update.json b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_update.json new file mode 100644 index 0000000000..267242559e --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_update.json @@ -0,0 +1,83 @@ +{ + "serviceName":"hdfsdev", + + "serviceDef":{ + "name":"hdfs", + "id":1, + "resources":[ + {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"} + ], + "accessTypes":[ + {"name":"read","label":"Read"}, + {"name":"write","label":"Write"}, + {"name":"execute","label":"Execute"} + ], + "contextEnrichers": [], + "policyConditions": [] + }, + + "policies":[ + {"id":10,"name":"allow-read-to-user1 /a/b*","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/a/b*"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":20,"name":"allow-read-to-user1 /a/bc*","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/a/bc*"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + ], + + "tests":[ + {"name":"ALLOW 'read /a/bcd' for u=user1", + "request":{ + "resource":{"elements":{"path":"/a/bcd"}}, + "accessType":"read","user":"user1","userGroups":[],"requestData":"read /a/bcd" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + } + , + {"name":"ALLOW 'read /a/bd' for u=user1", + "request":{ + "resource":{"elements":{"path":"/a/bd"}}, + "accessType":"read","user":"user1","userGroups":[],"requestData":"read /a/bd" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":10} + } + ], + "updatedPolicies": { + "policyDeltas": [ + { + "changeType": 1, + "policy": { + "id": 10, "version": 2, "name": "path=/a/b", "isEnabled": true, "isAuditEnabled": true, "serviceType": "hdfs", "policyType": 0, + "resources":{"path":{"values":["/a/b"],"isRecursive":false}}, + "policyItems": [ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + } + } + ] + }, + "updatedTests": [ + {"name":"ALLOW 'read /a/bcd' for u=user1", + "request":{ + "resource":{"elements":{"path":"/a/bcd"}}, + "accessType":"read","user":"user1","userGroups":[],"requestData":"read /a/bcd" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":20} + } + , + {"name":"DENY 'read /a/bd' for u=user1", + "request":{ + "resource":{"elements":{"path":"/a/bd"}}, + "accessType":"read","user":"user1","userGroups":[],"requestData":"read /a/bd" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + } + ] +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_update_for_wildcard_evaluators.json b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_update_for_wildcard_evaluators.json new file mode 100644 index 0000000000..f607917a8f --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_incremental_update_for_wildcard_evaluators.json @@ -0,0 +1,125 @@ +{ + "serviceName": "hdfsdev", + "serviceDef": { + "name": "hdfs", + "id": 1, + "resources": [ + { + "name": "path", + "type": "path", + "level": 1, + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true + }, + "label": "Resource Path", + "description": "HDFS file or directory path" + } + ], + "accessTypes": [ + { + "name": "read", + "label": "Read" + }, + { + "name": "write", + "label": "Write" + }, + { + "name": "execute", + "label": "Execute" + } + ], + "contextEnrichers": [ + { + "itemId": 1, + "name": "GeolocationEnricher", + "enricher": "org.apache.ranger.plugin.contextenricher.RangerFileBasedGeolocationProvider", + "enricherOptions": { + "FilePath": "/etc/ranger/geo/geo.txt", + "ForceRead": "false", + "IPInDotFormat": "true", + "geolocation.meta.prefix": "TEST_" + } + } + ], + "policyConditions": [ + { + "itemId": 1, + "name": "ScriptConditionEvaluator", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions": { + "engineName": "JavaScript" + }, + "label": "Script", + "description": "Script to execute" + } + ] + }, + "policies": [ + { + "id": 162, + "name": "read to mybu-analyst", + "isEnabled": true, "isAuditEnabled": true, + "resources": {"path": {"values": ["/mybu/analyst"], "isRecursive": true}}, + "policyItems": [ + {"accesses": [{"type": "execute", "isAllowed": true}, {"type": "read", "isAllowed": true}], "users": ["superman"]} + ] + }, + { + "id": 163, + "name": "write to mybu-admin", + "isEnabled": true, "isAuditEnabled": true, + "resources": {"path": {"values": ["/mybu/analyst", "/mybu/admin"], "isRecursive": true}}, + "policyItems": [ + {"accesses": [{"type": "write", "isAllowed": true}, {"type": "execute", "isAllowed": true}], "users": ["superman"]} + ] + } + ], + "tests": [ + { + "name": "ALLOW 'read /mybu/analyst' for u=superman", + "request": {"resource": {"elements": {"path": "/mybu/analyst"}}, + "accessType": "read", + "user": "superman", "userGroups": [], "requestData": "read /mybu/analyst for u=superman" + }, + "result": {"isAudited": true, "isAllowed": true, "policyId": 162} + }, + { + "name": "ALLOW 'write /mybu/analyst' for u=superman", + "request": {"resource": {"elements": {"path": "/mybu/analyst"}}, + "accessType": "write", + "user": "superman", "userGroups": [], "requestData": "write /mybu/analyst for u=superman" + }, + "result": {"isAudited": true, "isAllowed": true, "policyId": 163} + } + ], + "updatedPolicies": { + "policyDeltas": [ + {"changeType": 1, + "policy": {"version": 1, "serviceType":"hdfs", "policyType": 0, + "id": 163, + "name": "write to mybu-analyst", + "isEnabled": true, "isAuditEnabled": true, + "resources": {"path": {"values": ["/mybu/analyst", "/mybu/admin","/mybu/test"], "isRecursive": true}}, + "policyItems": [ + {"accesses": [{"type": "write", "isAllowed": true}, {"type": "execute", "isAllowed": true}], "users": ["superman"]} + ] + } + } + ] + }, + "updatedTests": [ + { + "name": "ALLOW 'read /mybu/analyst' for u=superman", + "request": {"resource": {"elements": {"path": "/mybu/analyst"}}, + "accessType": "read", + "user": "superman", "userGroups": [], "requestData": "read /mybu/analyst for u=superman" + }, + "result": {"isAudited": true, "isAllowed": true, "policyId": 162} + } + ] +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_multiple_accesses.json b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_multiple_accesses.json new file mode 100644 index 0000000000..1701f354f7 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hdfs_multiple_accesses.json @@ -0,0 +1,147 @@ +{ + "serviceName":"hdfsdev", + + "serviceDef":{ + "name":"hdfs", + "id":1, + "resources":[ + {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"} + ], + "accessTypes":[ + {"name":"read","label":"Read"}, + {"name":"write","label":"Write"}, + {"name":"execute","label":"Execute"} + ], + "contextEnrichers": + [ + { + "itemId":1, + "name" : "GeolocationEnricher", + "enricher" : "org.apache.ranger.plugin.contextenricher.RangerFileBasedGeolocationProvider", + "enricherOptions" : { + "FilePath":"/etc/ranger/geo/geo.txt", "ForceRead":"false", "IPInDotFormat":"true" + ,"geolocation.meta.prefix": "TEST_" + } + } + ], + "policyConditions": [ + { + "itemId":1, + "name":"ScriptConditionEvaluator", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions" : {"engineName":"JavaScript"}, + "label":"Script", + "description": "Script to execute" + } + ] + }, + + "policies":[ + {"id":1,"name":"audit-all-access under /public","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/public"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[],"users":[],"groups":["public"],"delegateAdmin":false} + ] + } + , + {"id":2,"name":"allow-execute-to-all under /public/","isEnabled":true,"isAuditEnabled":false, + "resources":{"path":{"values":["/public/*"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false} + ] + } + , + {"id":3,"name":"allow-read-to-finance under /public/finance","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/public/finance"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":["finance"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":4,"name":"deny-all-to-finance under /public/finance to user guest","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/public/finance"],"isRecursive":true}}, + "denyPolicyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":["guest"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":5,"name":"allow-read-to-finance under /public/finance to user guest","isEnabled":true,"isAuditEnabled":true, "policyPriority": 1, + "resources":{"path":{"values":["/public/finance"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":["guest"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":6,"name":"allow-execute-to-finance under /public/finance to user guest","isEnabled":true,"isAuditEnabled":true, "policyPriority": 1, + "resources":{"path":{"values":["/public/finance"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"execute","isAllowed":true}],"users":["guest"],"groups":[],"delegateAdmin":false} + ] + } + ], + + "tests":[ + {"name":"ALLOW 'read_execute /public/finance' for user guest", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"read","user":"guest","userGroups":[],"requestData":"read_execute /public/finance", + "context": {"ALLACCESSTYPEGROUPS": [ ["read"], ["execute"] ], "ALLACCESSTYPES": [ "read", "execute"]} + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":6} + }, + {"name":"ALLOW 'read /public/finance' for user guest", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"read","user":"guest","userGroups":[],"requestData":"read /public/finance" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":5} + }, + {"name":"ALLOW 'execute /public/finance' for user guest", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"execute","user":"guest","userGroups":[],"requestData":"execute /public/finance" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":6} + }, + {"name":"DENY 'write /public/finance' for user guest", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"write","user":"guest","userGroups":[],"requestData":"write /public/finance" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":4} + }, + {"name":"DENY 'write_execute /public/finance' for user guest", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"write","user":"guest","userGroups":[],"requestData":"write_execute /public/finance", + "context": {"ALLACCESSTYPEGROUPS": [ ["write"], ["execute"] ], "ALLACCESSTYPES": [ "write", "execute"]} + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":4} + }, + {"name":"ALLOW 'read_execute /public/finance' for user finance", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"read","user":"finance","userGroups":[],"requestData":"read_execute /public/finance", + "context": {"ALLACCESSTYPEGROUPS": [ ["read"], ["execute"] ], "ALLACCESSTYPES": [ "read", "execute"]} + + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":3} + } + , + {"name":"DENY 'read_execute /public/finance' for user hr", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"read","user":"hr","userGroups":[],"requestData":"read_execute /public/finance", + "context": {"ALLACCESSTYPEGROUPS": [ ["read"], ["execute"] ], "ALLACCESSTYPES": [ "read", "execute"]} + + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"ALLOW 'execute /public/finance' for user hr", + "request":{ + "resource":{"elements":{"path":"/public/finance"}}, + "accessType":"execute","user":"hr","userGroups":[],"requestData":"execute /public/finance" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":2} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hive.json b/agents-common/src/test/resources/policyengine/test_policyengine_hive.json index ba5a53c30b..f064e163aa 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_hive.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hive.json @@ -6,7 +6,7 @@ "id":3, "resources":[ {"name":"database","level":1,"mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Database","description":"Hive Database"}, - {"name":"url","level":1,"mandatory":true,"lookupSupported":false,"recursiveSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"URL","description":"URL"}, + {"name":"url","level":1,"mandatory":true,"lookupSupported":false,"recursiveSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"URL","description":"URL"}, {"name":"hiveservice","level":1,"mandatory":true,"lookupSupported":false,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"HiveService","description":"HiveService"}, {"name":"table","level":2,"parent":"database","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive Table","description":"Hive Table"}, {"name":"udf","level":2,"parent":"database","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Hive UDF","description":"Hive UDF"}, @@ -99,10 +99,114 @@ "policyItems":[ {"accesses":[{"type":"create","isAllowed":true}],"users":["user1","user2"],"groups":["group1","group2"],"delegateAdmin":false} ] + }, + {"id":8,"name":"db=dummy; table=*; column=*","isEnabled":true,"isAuditEnabled":true, + "resources":{"database":{"values":["dummy"]},"table":{"values":["*"]},"column":{"values":["*"]}}, + "policyItems":[ + {"accesses":[{"type":"create","isAllowed":true},{"type":"update","isAllowed":true},{"type":"drop","isAllowed":true}],"users":["user1","user2"],"groups":[],"delegateAdmin":false} + ], + "allowExceptions":[ + {"accesses":[{"type":"create","isAllowed":true}, {"type":"update","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false}, + {"accesses":[{"type":"create","isAllowed":true}, {"type":"update","isAllowed":true},{"type":"drop","isAllowed":true}],"users":["user2"],"groups":[],"delegateAdmin":false} + ] + } + , + {"id":9,"name":"db=db1","isEnabled":true,"isAuditEnabled":true, + "resources":{"database":{"values":["db1"]}}, + "isDenyAllElse": true, + "policyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["user1","user3","user4"],"groups":["group1","group2"],"delegateAdmin":false} + ] + }, + {"id":201,"name":"url=http://qe-s3-bucket-mst/test_abcd/abcd/ URL-access-policy","isEnabled":true,"isAuditEnabled":true, + "resources":{"url":{"values": ["http://qe-s3-bucket-mst/test_abcd/abcd/"], "isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true},{"type":"write","isAllowed":true}],"users":["user1"],"groups":[],"delegateAdmin":false} + ] + }, + {"id":1001,"name":"db=org; table=employee; column=*","isEnabled":true,"isAuditEnabled":true, + "resources":{"database":{"values":["org"]},"table":{"values":["employee"]},"column":{"values":["*"], "isExcludes":false}}, + "policyItems":[ + {"accesses":[{"type":"select","isAllowed":true}, {"type":"create","isAllowed":true}, {"type":"read","isAllowed":true}],"users":["john"],"groups":[],"delegateAdmin":false} + ] } ], "tests":[ + {"name":"DENY 'create or write for org;' for john", + "request":{ + "resource":{"elements":{"database":"org"}}, + "accessType":"create","user":"john","userGroups":[],"requestData":"create org", + "context": {"ISANYACCESS":true, "ACCESSTYPES": [ "create", "write" ]} + }, + "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY 'create and write for org;' for john", + "request":{ + "resource":{"elements":{"database":"org"}}, + "accessType":"create","user":"john","userGroups":[],"requestData":"create org", + "context": {"ISANYACCESS":false, "ACCESSTYPES": [ "create", "write" ]} + }, + "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + } + , + {"name":"ALLOW 'any' for org;' for john", + "request":{ + "resource":{"elements":{"database":"org"}}, + "accessType":"","user":"john","userGroups":[],"requestData":"'any' access for org" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":1001} + } + , + {"name":"ALLOW 'read http://qe-s3-bucket-mst/test_abcd/abcd;' for user1", + "request":{ + "resource":{"elements":{"url":["http://qe-s3-bucket-mst/test_abcd/abcd", "http://qe-s3-bucket-mst/test_abcd/abcd/"]}}, + "accessType":"read","user":"user1","userGroups":["users"],"requestData":"read http://qe-s3-bucket-mst/test_abcd/abcd for user1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":201} + } + , + {"name":"ALLOW '_any access to no-database' for user5: match when request has less levels than policy", + "request":{ + "resource":{"elements":{}}, + "accessType":"","user":"user5","userGroups":["users"],"requestData":"show databases" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":200} + } + , + {"name":"ALLOW '_any access to db1' for user5: match when request has less levels than policy", + "request":{ + "resource":{"elements":{"database":"db1"}}, + "accessType":"","user":"user5","userGroups":["users"],"requestData":"use db1" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":9} + } + , + {"name":"DENY '_any access to db1' for user5: match when request has less levels than policy", + "request":{ + "resource":{"elements":{"database":"db1"}}, + "accessType":"","user":"user5","userGroups":["users"],"requestData":"use db1" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":9} + } + , + {"name":"ALLOW 'any dummy/*/*;' for user1", + "request":{ + "resource":{"elements":{"database":"dummy", "table": "dummy", "column": "dummy"}}, + "accessType":"","user":"user1","userGroups":["users"],"requestData":"any dummy/dummy/dummy for user1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":8} + } + , + {"name":"DENY 'any dummy/*/*;' for user2", + "request":{ + "resource":{"elements":{"database":"dummy", "table": "dummy", "column": "dummy"}}, + "accessType":"","user":"user2","userGroups":["users"],"requestData":"any dummy/dummy/dummy for user2" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , {"name":"ALLOW 'read s3a://qe-s3-bucket-mst/demo;' for user1", "request":{ "resource":{"elements":{"url":"s3a://qe-s3-bucket-mst/demo"}}, @@ -119,12 +223,12 @@ "result":{"isAudited":true,"isAllowed":true,"policyId":200} } , - {"name":"DENY 'read s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent;' for user1", + {"name":"ALLOW 'read s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent;' for user1", "request":{ "resource":{"elements":{"url":"s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent"}}, "accessType":"read","user":"user1","userGroups":["users"],"requestData":"read s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent for user1" }, - "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + "result":{"isAudited":true,"isAllowed":true,"policyId":200} } , {"name":"DENY 'select tmp_1 from db1.tmp ;' for user1", @@ -384,7 +488,7 @@ "result":{"isAudited":true,"isAllowed":true,"policyId":3} } , - {"name":"ALLOW '_any access to db1/table1' for user1: match when request has same levels as policy", + {"name":"ALLOW '_any access to db1/tbl1' for user1: match when request has same levels as policy", "request":{ "resource":{"elements":{"database":"db1", "table":"tbl1"}}, "accessType":"","user":"user1","userGroups":["users"],"requestData":"describe db1.tbl1" diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_hive_mask_filter_with_req_expressions.json b/agents-common/src/test/resources/policyengine/test_policyengine_hive_mask_filter_with_req_expressions.json new file mode 100644 index 0000000000..5a4637008d --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_hive_mask_filter_with_req_expressions.json @@ -0,0 +1,109 @@ +{ + "serviceName": "hivedev", + + "serviceDef": { + "name": "hive", "id": 3, + "resources": [ + { "name": "database", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "table", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "udf", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive UDF", "description": "Hive UDF" }, + { "name": "column", "level": 3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes": [ + { "name": "select", "label": "Select" }, + { "name": "update", "label": "Update" }, + { "name": "create", "label": "Create" }, + { "name": "drop", "label": "Drop" }, + { "name": "alter", "label": "Alter" }, + { "name": "index", "label": "Index" }, + { "name": "lock", "label": "Lock" }, + { "name": "all", "label": "All", "impliedGrants": [ "select", "update", "create", "drop", "alter", "index", "lock" ] } + ], + "dataMaskDef": { + "maskTypes": [ + { "itemId": 1, "name": "MASK", "label": "Mask", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'" }, + { "itemId": 2, "name": "SHUFFLE", "label": "Shuffle", "description": "Randomly shuffle the contents" }, + { "itemId": 10, "name": "NULL", "label": "NULL", "description": "Replace with NULL" }, + { "itemId": 11, "name": "CUSTOM", "label": "CUSTOM", "description": "Custom expression" } + ], + "accessTypes": [ + { "name": "select", "label": "Select" } + ], + "resources": [ + { "name": "database", "matcherOptions": { "wildCard":false } }, + { "name": "table", "matcherOptions": { "wildCard":false } }, + { "name": "column", "matcherOptions": { "wildCard":false } } + ] + }, + "rowFilterDef": { + "accessTypes": [ + { "name": "select", "label": "Select" } + ], + "resources": [ + { "name": "database", "matcherOptions": { "wildCard":false } }, + { "name": "table", "matcherOptions": { "wildCard":false } } + ] + } + }, + + "policies": [ + { "id": 1, "name": "db=*: audit-all-access", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "*" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "all", "isAllowed": true } ], "users": [ "hive", "user1", "user2" ], "groups": [ "public" ], "delegateAdmin":false } + ] + }, + { "id": 101, "name": "db=employee, table=personal, column=ssn: mask ssn column", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "database": { "values": [ "employee" ] }, "table": { "values": [ "personal" ] }, "column": { "values": [ "ssn" ] } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "groups": [ "public" ], "delegateAdmin":false, + "dataMaskInfo": { "dataMaskType": "CUSTOM", "valueExpr": "CASE WHEN dept = '${{USER.dept}}' THEN {col} ELSE mask_show_last_n({col}, 4, 'x', 'x', 'x', -1, '1') END" } + } + ] + }, + { "id": 201, "name": "db=employee, table=personal", "isEnabled": true, "isAuditEnabled": true, "policyType": 2, + "resources": { "database": { "values": [ "employee" ] }, "table": { "values": [ "personal" ] } }, + "rowFilterPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "groups": [ "public" ], "delegateAdmin":false, + "rowFilterInfo": { "filterExpr": "dept='${{USER.dept}}'" } + } + ] + } + ], + + "tests": [ + { "name": "'select ssn from employee.personal;' for user1 - maskType=CUSTOM", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal", "column": "ssn" } }, + "accessType": "select", "user": "user1", "userGroups": [], "requestData": "select ssn from employee.personal;' for user1" + }, + "userAttributes": { "user1": { "dept": "engg" }, "user2": { "dept": "r&d" } }, + "dataMaskResult": { "additionalInfo": { "maskType": "CUSTOM", "maskCondition":null, "maskedValue": "CASE WHEN dept = 'engg' THEN {col} ELSE mask_show_last_n({col}, 4, 'x', 'x', 'x', -1, '1') END"}, "policyId": 101} + }, + { "name": "'select ssn from employee.personal;' for user2 - maskType=CUSTOM", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal", "column": "ssn" } }, + "accessType": "select", "user": "user2", "userGroups": [], "requestData": "select ssn from employee.personal;' for user2" + }, + "userAttributes": { "user1": { "dept": "engg" }, "user2": { "dept": "r&d" } }, + "dataMaskResult": { "additionalInfo": { "maskType": "CUSTOM", "maskCondition":null, "maskedValue": "CASE WHEN dept = 'r&d' THEN {col} ELSE mask_show_last_n({col}, 4, 'x', 'x', 'x', -1, '1') END"}, "policyId": 101} + }, + { "name": "'select ssn from employee.personal;' for user1 - filterExpr=dept='engg'", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal" } }, + "accessType": "select", "user": "user1", "userGroups": [], "requestData": "select ssn from employee.personal;' for user1" + }, + "userAttributes": { "user1": { "dept": "engg" }, "user2": { "dept": "r&d" } }, + "rowFilterResult": { "additionalInfo": { "filterExpr": "dept='engg'" }, "policyId": 201} + }, + { "name": "'select ssn from employee.personal;' for user2 - filterExpr=dept='r&d'", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal" } }, + "accessType": "select", "user": "user2", "userGroups": [], "requestData": "select ssn from employee.personal;' for user2" + }, + "userAttributes": { "user1": { "dept": "engg" }, "user2": { "dept": "r&d" } }, + "rowFilterResult": { "additionalInfo": { "filterExpr": "dept='r&d'" }, "policyId": 201} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_kafka.json b/agents-common/src/test/resources/policyengine/test_policyengine_kafka.json new file mode 100644 index 0000000000..89ab9c0b5e --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_kafka.json @@ -0,0 +1,157 @@ +{ + "serviceName": "kafkadev", + + "serviceDef": { + "name": "kafka", "id": 9, + "resources": [ + { "name": "topic", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Topic", "description": "Topic", "accessTypeRestrictions": [ "create", "delete", "configure", "alter", "alter_configs", "describe", "describe_configs", "consume", "publish" ] }, + { "name": "transactionalid", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Transactional Id", "description": "Transactional Id", "accessTypeRestrictions": [ "describe", "publish" ] }, + { "name": "cluster", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Cluster", "description": "Cluster", "accessTypeRestrictions": [ "create", "configure", "alter", "alter_configs", "describe", "describe_configs", "kafka_admin", "idempotent_write", "cluster_action" ] }, + { "name": "delegationtoken", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Delegation Token", "description": "Delegation Token", "accessTypeRestrictions": [ "describe" ] }, + { "name": "consumergroup", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Consumer Group", "description": "Consumer Group", "accessTypeRestrictions": [ "consume", "describe", "delete" ] } + ], + "accessTypes": [ + { "name": "publish", "label": "Publish", "impliedGrants": [ "describe" ] }, + { "name": "consume", "label": "Consume", "impliedGrants": [ "describe" ] }, + { "name": "configure", "label": "Configure", "impliedGrants": [ "describe" ] }, + { "name": "describe", "label": "Describe" }, + { "name": "kafka_admin", "label": "Kafka Admin", "impliedGrants": [ "publish", "consume", "configure", "describe", "create", "delete", "describe_configs", "alter_configs", "alter", "idempotent_write", "cluster_action" ] }, + { "name": "create", "label": "Create" }, + { "name": "delete", "label": "Delete", "impliedGrants": [ "describe" ] }, + { "name": "idempotent_write", "label": "Idempotent Write" }, + { "name": "describe_configs", "label": "Describe Configs" }, + { "name": "alter_configs", "label": "Alter Configs" }, + { "name": "cluster_action", "label": "Cluster Action" }, + { "name": "alter", "label": "alter" } + ] + }, + + "policies": [ + { + "id": 10,"name": "10: test-topic publish/consume allowed","isEnabled": true,"isAuditEnabled": true, + "resources": { "topic": {"values": [ "test-topic-1" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "publish", "isAllowed": true },{"type": "consume", "isAllowed": true }], "users": [ "user1" ], "groups": [] } + ] + }, + { + "id": 101,"name": "101: test-topic publish/consume allowed","isEnabled": true,"isAuditEnabled": true, + "resources": { "topic": {"values": [ "test-topic-2" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "publish", "isAllowed": true },{"type": "consume", "isAllowed": true }], "users": [ "user2" ], "groups": [] } + ] + }, + { + "id": 102,"name": "102: test-topic publish/consume allowed","isEnabled": true,"isAuditEnabled": true, + "resources": {"topic": {"values": [ "test-topic-1","test-topic-4" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "consume", "isAllowed": true }], "users": [ "user3" ], "groups": [] } + ] + }, + { + "id": 103,"name": "103: test-topic publish/consume allowed","isEnabled": true,"isAuditEnabled": true, + "resources": { "topic": {"values": [ "test-topic-1","test-topic-5" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "consume", "isAllowed": true }], "users": [ "user4", "user5" ], "groups": [] } + ] + }, + { + "id": 104,"name": "104: test-topic publish/consume allowed","isEnabled": true,"isAuditEnabled": true, + "resources": { "topic": {"values": [ "test-topic-7" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "publish", "isAllowed": true },{"type": "consume", "isAllowed": true }], "users": [ "user6" ], "groups": [] } + ] + }, + { + "id": 20,"name": "02: transation id","isEnabled": true,"isAuditEnabled": true,"policyType": 1, + "resources": { "delegationtoken": {"values": [ "tid-1" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "publish", "isAllowed": true }], "users": [ "user1" ], "groups": [] } + ] + }, + { + "id": 30,"name": "create/alter access for user1 on cluster-1","isEnabled": true,"isAuditEnabled": true, + "resources": { "cluster": {"values": [ "cluseter-1" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "create", "isAllowed": true },{"type": "alter", "isAllowed": true }], "users": [ "user1" ], "groups": [] } + ] + }, + { + "id": 301,"name": "Alter access for user31 on cluster-2","isEnabled": true,"isAuditEnabled": true, + "resources": { "cluster": {"values": [ "cluster-2" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "alter", "isAllowed": true }], "users": [ "user31" ], "groups": [] } + ] + }, + { + "id": 302,"name": "Alter access for user32 on cluster-1","isEnabled": true,"isAuditEnabled": true, + "resources": { "cluster": {"values": [ "cluseter-1" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "alter", "isAllowed": true }], "users": [ "user32" ], "groups": [] } + ] + }, + { + "id": 40,"name": "delegationtoken","isEnabled": true,"isAuditEnabled": true, + "resources": { "delegationtoken": {"values": [ "dt-1" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "describe", "isAllowed": true }], "users": [ "user1" ], "groups": [] } + ] + }, + { + "id": 50,"name": "consumergroup-1 access","isEnabled": true,"isAuditEnabled": true, + "resources": { "consumergroup": {"values": [ "consumergroup-1" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "consume", "isAllowed": true }], "users": [ "user1" ], "groups": [] } + ] + }, + { + "id": 52,"name": "consumergroup-2 access for user4","isEnabled": true,"isAuditEnabled": true, + "resources": { "consumergroup": {"values": [ "consumergroup-2" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "consume", "isAllowed": true }], "users": [ "user4" ], "groups": [] } + ] + }, + { + "id": 110,"name": "110: test-topic-1/2 consume allowed for user2","isEnabled": true,"isAuditEnabled": true, + "resources": { "topic": {"values": [ "test-topic-1", "test-topic-2" ], "isRecursive": true } }, + "policyItems": [ + {"accesses": [{"type": "consume", "isAllowed": true }], "users": [ "user2" ], "groups": [] } + ] + }, + { + "id": 111, "name": "110: new-topic-1/2 allowed for user2", "isEnabled": true, "isAuditEnabled": true, + "resources": { "topic": { "values": [ "new-topic-1", "new-topic-2" ], "isRecursive": true } }, + "policyItems": [ + { "accesses": [ { "type": "consume", "isAllowed": true } ], "users": [ "user2" ], "groups": [] } + ] + } + ], + "tests": [ + { + "name": "Any topic Consume access for user3", + "request": { + "resource": { "elements": { "topic": "" } }, "resourceElementMatchingScopes": { "topic": "SELF_OR_PREFIX" }, + "accessType": "consume", "user": "user3", "userGroups": [] + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 102 } + }, + { + "name": "Any consumergroup consume access for user1", + "request": { + "resource": { "elements": { "consumergroup": "" } }, "resourceElementMatchingScopes": { "consumergroup": "SELF_OR_PREFIX" }, + "accessType": "consume", "user": "user1", "userGroups": [] + }, + "result": { "isAudited": true, "isAllowed": true," policyId": 50 } + }, + + { + "name": "Any consumergroup consume access for user3", + "request": { + "resource": { "elements": { "consumergroup": "" } }, "resourceElementMatchingScopes": { "consumergroup": "SELF_OR_PREFIX" }, + "accessType": "consume", "user": "user3", "userGroups": [] + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_marker_access_types.json b/agents-common/src/test/resources/policyengine/test_policyengine_marker_access_types.json new file mode 100644 index 0000000000..59b164a974 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_marker_access_types.json @@ -0,0 +1,435 @@ +{ + "serviceName": "hivedev", + + "serviceDef": { + "name": "hive", "id": 3, + "resources": [ + { "name": "database", "level": 1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "url", "level": 1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "URL", "description": "URL", "recursiveSupported": true }, + { "name": "hiveservice", "level": 1, "parent": "", "mandatory": true, "lookupSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "HiveService", "description": "HiveService" }, + { "name": "table", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "udf", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive UDF", "description": "Hive UDF" }, + { "name": "column", "level": 3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes": [ + { "name": "select", "label": "Select", "category": "READ" }, + { "name": "update", "label": "Update" , "category": "UPDATE" }, + { "name": "create", "label": "Create", "category": "CREATE" }, + { "name": "drop", "label": "Drop", "category": "DELETE" }, + { "name": "alter", "label": "Alter", "category": "CREATE" }, + { "name": "index", "label": "Index", "category": "MANAGE" }, + { "name": "lock", "label": "Lock", "category": "MANAGE" }, + { "name": "read", "label": "Read", "category": "READ" }, + { "name": "write", "label": "Write", "category": "UPDATE" }, + { "name": "repladmin", "label": "ReplAdmin", "category": "MANAGE" }, + { "name": "serviceadmin", "label": "ServiceAdmin", "category": "MANAGE" } + ], + "markerAccessTypes": [ + { "name": "_CREATE", "label": "_CREATE", "impliedGrants": [ "create", "alter" ] }, + { "name": "_READ", "label": "_READ", "impliedGrants": [ "select", "read" ] }, + { "name": "_UPDATE", "label": "_UPDATE", "impliedGrants": [ "update", "write" ] }, + { "name": "_DELETE", "label": "_DELETE", "impliedGrants": [ "drop", "alter" ] }, + { "name": "_MANAGE", "label": "_MANAGE", "impliedGrants": [ "index", "lock", "repladmin", "serviceadmin" ] }, + { "name": "_ALL", "label": "_ALL", "impliedGrants": [ "select", "update", "create", "drop", "alter", "index", "lock", "read", "write", "repladmin", "serviceadmin" ] } + ] + }, + + "policies": [ + { "id": 1, "name": "db=default: audit-all-access", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "default" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } } + }, + { "id": 2, "name": "db=default; table=test*; column=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "default" ] }, "table": { "values": [ "test*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false }, + { "accesses": [ { "type": "_CREATE", "isAllowed": true }, { "type": "_DELETE", "isAllowed": true } ], "users": [ "admin" ], "groups": [ "admin" ], "delegateAdmin": true } + ] + }, + { "id": 3, "name": "db=db1; table=tbl*; column=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false } + ] + }, + { "id": 4, "name": "db=db1; table=tmp; column=tmp*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "db1" ] }, "table": { "values": [ "tmp" ] }, "column": { "values": [ "tmp*" ], "isExcludes": true } }, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false } + ] + }, + { "id": 5, "name": "hiveservice=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "hiveservice": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "_MANAGE", "isAllowed": true } ], "users": [ "user1" ], "groups": [], "delegateAdmin": false } + ] + }, + { "id": 6, "name": "db=demo1,demo2", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "demo1", "demo2" ] } }, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false } + ] + }, + { "id": 7, "name": "db=demo1; table=demo1_tbl1,demo1_tbl2; column=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "demo1" ] }, "table": { "values": [ "demo1_tbl1", "demo1_tbl2" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "_CREATE", "isAllowed": true } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false } + ] + }, + { "id": 8, "name": "db=dummy; table=*; column=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "dummy" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } }, + "policyItems": [ + { "accesses": [ { "type": "_CREATE", "isAllowed": true }, { "type": "_UPDATE", "isAllowed": true }, { "type": "_DELETE", "isAllowed": true } ], "users": [ "user1", "user2" ], "groups": [], "delegateAdmin": false } + ], + "allowExceptions": [ + { "accesses": [ { "type": "_CREATE", "isAllowed": true }, { "type": "_UPDATE", "isAllowed": true } ], "users": [ "user1" ], "groups": [], "delegateAdmin": false }, + { "accesses": [ { "type": "_CREATE", "isAllowed": true }, { "type": "_UPDATE", "isAllowed": true }, { "type": "_DELETE", "isAllowed": true } ], "users": [ "user2" ], "groups": [], "delegateAdmin": false } + ] + }, + { "id": 9, "name": "db=db1", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "db1" ] } }, + "isDenyAllElse": true, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true } ], "users": [ "user1", "user3", "user4" ], "groups": [ "group1", "group2" ], "delegateAdmin": false } + ] + }, + { "id": 200, "name": "url=s3a://qe-s3-bucket-mst/test_abcd/abcd; s3a://qe-s3-bucket-mst/demo/*: URL-access-policy", "isEnabled": true, "isAuditEnabled": true, + "resources": { "url": { "values": [ "s3a://qe-s3-bucket-mst/test_abcd/abcd", "s3a://qe-s3-bucket-mst/demo" ], "isRecursive": true } }, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true }, { "type": "_UPDATE", "isAllowed": true } ], "users": [], "groups": [ "public" ], "delegateAdmin": false } + ] + }, + { "id": 201, "name": "url=http://qe-s3-bucket-mst/test_abcd/abcd/ URL-access-policy", "isEnabled": true, "isAuditEnabled": true, + "resources": { "url": { "values": [ "http://qe-s3-bucket-mst/test_abcd/abcd/" ], "isRecursive": true } }, + "policyItems": [ + { "accesses": [ { "type": "_READ", "isAllowed": true }, { "type": "_UPDATE", "isAllowed": true } ], "users": [ "user1" ], "groups": [], "delegateAdmin": false } + ] + } + ], + + "tests": [ + { "name": "ALLOW 'read http://qe-s3-bucket-mst/test_abcd/abcd;' for user1", + "request": { + "resource": { "elements": { "url": [ "http://qe-s3-bucket-mst/test_abcd/abcd", "http://qe-s3-bucket-mst/test_abcd/abcd/" ] } }, + "accessType": "read", "user": "user1", "userGroups": [ "users" ], "requestData": "read http://qe-s3-bucket-mst/test_abcd/abcd for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 201 } + }, + { "name": "ALLOW '_any access to no-database' for user5: match when request has less levels than policy", + "request": { + "resource": { "elements": {} }, + "accessType": "", "user": "user5", "userGroups": [ "users" ], "requestData": "show databases" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 200 } + }, + { "name": "ALLOW '_any access to db1' for user5: match when request has less levels than policy", + "request": { + "resource": { "elements": { "database": "db1" } }, + "accessType": "", "user": "user5", "userGroups": [ "users" ], "requestData": "use db1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": 9 } + }, + { "name": "DENY '_any access to db1' for user5: match when request has less levels than policy", + "request": { + "resource": { "elements": { "database": "db1" } }, + "accessType": "", "user": "user5", "userGroups": [ "users" ], "requestData": "use db1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": 9 } + }, + { "name": "ALLOW 'any dummy/*/*;' for user1", + "request": { + "resource": { "elements": { "database": "dummy", "table": "dummy", "column": "dummy" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "any dummy/dummy/dummy for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 8 } + }, + { "name": "DENY 'any dummy/*/*;' for user2", + "request": { + "resource": { "elements": { "database": "dummy", "table": "dummy", "column": "dummy" } }, + "accessType": "", "user": "user2", "userGroups": [ "users" ], "requestData": "any dummy/dummy/dummy for user2" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'read s3a://qe-s3-bucket-mst/demo;' for user1", + "request": { + "resource": { "elements": { "url": "s3a://qe-s3-bucket-mst/demo" } }, + "accessType": "read", "user": "user1", "userGroups": [ "users" ], "requestData": "read s3a://qe-s3-bucket-mst/demo for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 200 } + }, + { "name": "ALLOW 'read s3a://qe-s3-bucket-mst/test_abcd/abcd;' for user1", + "request": { + "resource": { "elements": { "url": "s3a://qe-s3-bucket-mst/test_abcd/abcd" } }, + "accessType": "read", "user": "user1", "userGroups": [ "users" ], "requestData": "read s3a://qe-s3-bucket-mst/test_abcd/abcd for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 200 } + }, + { "name": "ALLOW 'read s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent;' for user1", + "request": { + "resource": { "elements": { "url": "s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent" } }, + "accessType": "read", "user": "user1", "userGroups": [ "users" ], "requestData": "read s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 200 } + }, + { "name": "ALLOW 'write s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent;' for user1", + "request": { + "resource": { "elements": { "url": "s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent" } }, + "accessType": "write", "user": "user1", "userGroups": [ "users" ], "requestData": "write s3a://qe-s3-bucket-mst/test_abcd/abcd/non-existent for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 200 } + }, + { "name": "DENY 'select tmp_1 from db1.tmp ;' for user1", + "request": { + "resource": { "elements": { "database": "db1", "table": "tmp", "column": "tmp_1" } }, + "accessType": "select", "user": "user1", "userGroups": [ "users" ], "requestData": "select tmp_1 from db1.tmp for user1" + , "remoteIPAddress": "1.1.1.1", "forwardedAddresses": [ "127.0.0.1", "10.10.10.10" ] + }, + "result": { "isAudited": false, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'select abc_1 from db1.tmp ;' for user1", + "request": { + "resource": { "elements": { "database": "db1", "table": "tmp", "column": "abc_1" } }, + "accessType": "select", "user": "user1", "userGroups": [ "users" ], "requestData": "select abc_1 from db1.tmp for user1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 4 } + }, + { "name": "ALLOW 'use default;' for user1", + "request": { + "resource": { "elements": { "database": "default" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "use default" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW 'use default;' for user2", + "request": { + "resource": { "elements": { "database": "default" } }, + "accessType": "", "user": "user2", "userGroups": [ "users" ], "requestData": "use default" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY 'use default;' to user3", + "request": { + "resource": { "elements": { "database": "default" } }, + "accessType": "", "user": "user3", "userGroups": [ "users" ], "requestData": "use default" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'use default;' to group1", + "request": { + "resource": { "elements": { "database": "default" } }, + "accessType": "", "user": "user3", "userGroups": [ "users", "group1" ], "requestData": "use default" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW 'use default;' to group2", + "request": { + "resource": { "elements": { "database": "default" } }, + "accessType": "", "user": "user3", "userGroups": [ "users", "group2" ], "requestData": "use default" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY 'use default;' to user3/group3", + "request": { + "resource": { "elements": { "database": "default" } }, + "accessType": "", "user": "user3", "userGroups": [ "users", "group3" ], "requestData": "use default" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'use finance;' to user3/group3", + "request": { + "resource": { "elements": { "database": "finance" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "use finance" + }, + "result": { "isAudited": false, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'select col1 from default.testtable;' to user1", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable", "column": "col1" } }, + "accessType": "select", "user": "user1", "userGroups": [ "users" ], "requestData": "select col1 from default.testtable" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW 'select col1 from default.testtable;' to user2", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable", "column": "col1" } }, + "accessType": "select", "user": "user2", "userGroups": [ "users" ], "requestData": "select col1 from default.testtable" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY 'select col1 from default.testtable;' to user3", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable", "column": "col1" } }, + "accessType": "select", "user": "user3", "userGroups": [ "users" ], "requestData": "select col1 from default.testtable" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'select col1 from default.testtable;' to group1", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable", "column": "col1" } }, + "accessType": "select", "user": "user3", "userGroups": [ "users", "group1" ], "requestData": "select col1 from default.testtable" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW 'select col1 from default.testtable;' to group2", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable", "column": "col1" } }, + "accessType": "select", "user": "user3", "userGroups": [ "users", "group2" ], "requestData": "select col1 from default.testtable" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY 'select col1 from default.testtable;' to user3/group3", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable", "column": "col1" } }, + "accessType": "select", "user": "user3", "userGroups": [ "users", "group3" ], "requestData": "select col1 from default.testtable" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'select col1 from default.table1;' to user1", + "request": { + "resource": { "elements": { "database": "default", "table": "table1", "column": "col1" } }, + "accessType": "select", "user": "user1", "userGroups": [ "users" ], "requestData": "select col1 from default.table1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'create table default.testtable1;' to user1", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "create", "user": "user1", "userGroups": [ "users" ], "requestData": "create table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'create table default.testtable1;' to user1/group1", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "create", "user": "user1", "userGroups": [ "users", "group1" ], "requestData": "create table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'create table default.testtable1;' to admin", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "create", "user": "admin", "userGroups": [ "users" ], "requestData": "create table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW 'create table default.testtable1;' to user1/admin", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "create", "user": "user1", "userGroups": [ "users", "admin" ], "requestData": "create table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY 'drop table default.testtable1;' to user1", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "drop", "user": "user1", "userGroups": [ "users" ], "requestData": "drop table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'drop table default.testtable1;' to user1/group1", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "drop", "user": "user1", "userGroups": [ "users", "group1" ], "requestData": "drop table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW 'drop table default.testtable1;' to admin", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "drop", "user": "admin", "userGroups": [ "users" ], "requestData": "drop table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW 'drop table default.testtable1;' to user1/admin", + "request": { + "resource": { "elements": { "database": "default", "table": "testtable1" } }, + "accessType": "drop", "user": "user1", "userGroups": [ "users", "admin" ], "requestData": "drop table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY 'create table default.table1;' to user1", + "request": { + "resource": { "elements": { "database": "default", "table": "table1" } }, + "accessType": "create", "user": "user1", "userGroups": [ "users" ], "requestData": "create table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'create table default.table1;' to user1/admin", + "request": { + "resource": { "elements": { "database": "default", "table": "table1" } }, + "accessType": "create", "user": "user1", "userGroups": [ "users", "admin" ], "requestData": "create table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'drop table default.table1;' to user1", + "request": { + "resource": { "elements": { "database": "default", "table": "table1" } }, + "accessType": "drop", "user": "user1", "userGroups": [ "users" ], "requestData": "drop table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'drop table default.table1;' to user1/admin", + "request": { + "resource": { "elements": { "database": "default", "table": "table1" } }, + "accessType": "drop", "user": "user1", "userGroups": [ "users", "admin" ], "requestData": "drop table default.testtable1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY 'select col1 from default.table1;' to user3", + "request": { + "resource": { "elements": { "database": "default", "table": "table1", "column": "col1" } }, + "accessType": "select", "user": "user3", "userGroups": [ "users" ], "requestData": "select col1 from default.table1" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY '_any access to db1/table1' for user1: table-level mismatch", + "request": { + "resource": { "elements": { "database": "db1", "table": "table1" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "show columns in table1 from db1;" + }, + "result": { "isAudited": false, "isAllowed": false, "policyId": -1 } + }, + { "name": "DENY '_any access to db1/_/col1' for user1: table not specified but column was specified", + "request": { + "resource": { "elements": { "database": "db1", "column": "col1" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "fictional use case when request specified a lower level resource by skipping intermediate resource" + }, + "result": { "isAudited": false, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW '_any access to db1' for user1: match when request has less levels than policy", + "request": { + "resource": { "elements": { "database": "db1" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "use db1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 3 } + }, + { "name": "ALLOW '_any access to db1/tbl1' for user1: match when request has same levels as policy", + "request": { + "resource": { "elements": { "database": "db1", "table": "tbl1" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "describe db1.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 3 } + }, + { "name": "ALLOW '_any access to db1/tbl1/col1' for user1: match when request has more specific levels than policy", + "request": { + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "col1" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "fictional case: request for any match today happens only at a higher levels" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 3 } + }, + { "name": "ALLOW 'kill_query' for user1 on any cluster", + "request": { + "resource": { "elements": { "hiveservice": "testcluster" } }, + "accessType": "serviceadmin", "user": "user1", "userGroups": [ "users" ], "requestData": "kill query 'dummyqueryid'" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 5 } + }, + { "name": "ALLOW '_any access to demo1/demo_tbl1' for user1: show table test", + "request": { + "resource": { "elements": { "database": "demo1", "table": "demo1_tbl1" } }, + "accessType": "", "user": "user1", "userGroups": [ "users" ], "requestData": "show tables" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 7 } + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_policy_with_additional_resources.json b/agents-common/src/test/resources/policyengine/test_policyengine_policy_with_additional_resources.json new file mode 100644 index 0000000000..71a9c507a3 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_policy_with_additional_resources.json @@ -0,0 +1,84 @@ +{ + "serviceName": "hivedev", + + "serviceDef": { + "name": "hive", "id": 3, + "resources": [ + { "name": "database", "level": 1, "parent": "", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "table", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "udf", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive UDF", "description": "Hive UDF" }, + { "name": "column", "level": 3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes": [ + { "name": "select", "label": "Select" }, + { "name": "update", "label": "Update" }, + { "name": "create", "label": "Create" }, + { "name": "drop", "label": "Drop" }, + { "name": "alter", "label": "Alter" }, + { "name": "index", "label": "Index" }, + { "name": "lock", "label": "Lock" }, + { "name": "all", "label": "All" } + ], + "options": { + "enableDenyAndExceptionsInPolicies": "true" + } + }, + + "policies": [ + { "id": 1, "name": "db=default: audit-all-access", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "default" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } } + }, + { "id": 2, "name": "db=default; table=test*; column=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "default" ] }, "table": { "values": [ "test*" ] }, "column": { "values": [ "*" ] } }, + "additionalResources": [ + { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl*" ] }, "column": { "values": [ "*" ] } } + ], + "policyItems": [ + { "accesses": [ { "type": "select" } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false }, + { "accesses": [ { "type": "create" }, { "type": "drop" } ], "users": [ "admin" ], "groups": [ "admin" ], "delegateAdmin": true } + ] + }, + { "id": 3, "name": "db=default; table=test2; column=*", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "default" ] }, "table": { "values": [ "test2" ] }, "column": { "values": [ "*" ] } }, + "additionalResources": [ + { "database": { "values": [ "db1" ] }, "table": { "values": [ "tbl2" ] }, "column": { "values": [ "*" ] } } + ], + "denyPolicyItems":[ + { "accesses": [ { "type": "select" } ], "users": [ "user1", "user2" ], "groups": [ "group1", "group2" ], "delegateAdmin": false }, + { "accesses": [ { "type": "create" }, { "type": "drop" } ], "users": [ "admin" ], "groups": [ "admin" ], "delegateAdmin": true } + ] + } + ], + + "tests":[ + { "name": "ALLOW use default; for user1", + "request": { "resource": { "elements": { "database": "default"} }, "accessType":"", "user":"user1","userGroups":[],"requestData":"use default; user=user1" }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "ALLOW select * from default.test1; for user1", + "request": { "resource": { "elements": { "database": "default", "table": "test1" } }, "accessType": "select", "user": "user1", "userGroups":[], "requestData":"select * from default.test1; user=user1" }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2} + }, + { "name": "DENY create table default.test1; for user1", + "request": { "resource": { "elements": { "database": "default", "table": "test1" } }, "accessType": "create", "user": "user1", "userGroups":[], "requestData": "create table from default.test1; user=user1" }, + "result": { "isAudited": true, "isAllowed": false, "policyId": -1 } + }, + { "name": "ALLOW create table default.test1; for admin", + "request": { "resource": { "elements": { "database": "default", "table": "test1" } }, "accessType": "create", "user": "admin", "userGroups":[], "requestData": "create table from default.test1; user=admin" }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "DENY select * from default.test2; for user1", + "request": { "resource": { "elements": { "database": "default", "table": "test2" } }, "accessType": "select", "user": "user1", "userGroups": [], "requestData": "select * from default.test2; user=user1" }, + "result": { "isAudited": true, "isAllowed": false, "policyId": 3 } + }, + { "name": "DENY create default.test2; for admin", + "request": { "resource": { "elements": { "database": "default", "table": "test2" } }, "accessType":"create", "user": "admin", "userGroups":[], "requestData":"create default.test2; user=admin" }, + "result": { "isAudited": true, "isAllowed": false, "policyId": 3 } + }, + { "name": "ALLOW use db1; for user1", + "request": { "resource": { "elements": { "database":"db1" } }, "accessType":"", "user": "user1", "userGroups":[], "requestData":"use db1; user=user1" }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_resource_hierarchy_tags.json b/agents-common/src/test/resources/policyengine/test_policyengine_resource_hierarchy_tags.json new file mode 100644 index 0000000000..2380dadd13 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_resource_hierarchy_tags.json @@ -0,0 +1,239 @@ +{ + "serviceName": "hivedev", + "serviceDef": { + "name": "hive", "id": 3, + "resources": [ + { "name": "database", "level": 1, "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Database", "description": "Hive Database" }, + { "name": "table", "level": 2, "parent": "database", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Table", "description": "Hive Table" }, + { "name": "column", "level": 3, "parent": "table", "mandatory": true, "lookupSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": true }, "label": "Hive Column", "description": "Hive Column" } + ], + "accessTypes": [ + { "name": "select", "label": "Select" }, + { "name": "update", "label": "Update" }, + { "name": "create", "label": "Create" }, + { "name": "drop", "label": "Drop" }, + { "name": "alter", "label": "Alter" }, + { "name": "index", "label": "Index" }, + { "name": "lock", "label": "Lock" }, + { "name": "all", "label": "All" } + ], + "policyConditions": [ + { "itemId": 1, "name": "expression", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", "evaluatorOptions": { "engineName": "JavaScript", "ui.isMultiline": "true" }, "label": "Enter boolean expression", "description": "Boolean expression" } + ], + "dataMaskDef": { + "maskTypes": [ + { "itemId": 1, "name": "MASK", "label": "Mask", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'" }, + { "itemId": 2, "name": "SHUFFLE", "label": "Shuffle", "description": "Randomly shuffle the contents" }, + { "itemId": 3, "name": "MASK_HASH", "label": "Hash", "description": "Hash value of the contents" }, + { "itemId": 4, "name": "MASK_NONE", "label": "No masking", "description": "Unmasked value of the contents" }, + { "itemId": 10, "name": "NULL", "label": "NULL", "description": "Replace with NULL" } + ], + "accessTypes":[ + { "name": "select", "label": "Select" } + ], + "resources":[ + { "name": "database", "matcherOptions": { "wildCard": false } }, + { "name": "table", "matcherOptions": { "wildCard": false } }, + { "name": "column", "matcherOptions": { "wildCard": false } } + ] + }, + "rowFilterDef": { + "accessTypes":[ + { "name": "select", "label": "Select"} + ], + "resources":[ + { "name": "database", "matcherOptions": { "wildCard": false } }, + { "name": "table", "matcherOptions": { "wildCard": false } } + ] + } + }, + "policies": [ + ], + "tagPolicyInfo": { + "serviceName": "tagdev", + "serviceDef": { + "name": "tag", "id": 100, + "resources": [ + { "itemId": 1, "name": "tag", "type": "string", "level": 1, "parent": "", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": true, "ignoreCase": false }, "label": "TAG", "description": "TAG" } + ], + "accessTypes": [ + { "itemId": 1, "name": "hive:select", "label": "hive:select" }, + { "itemId": 2, "name": "hive:update", "label": "hive:update" }, + { "itemId": 3, "name": "hive:create", "label": "hive:create" }, + { "itemId": 4, "name": "hive:drop", "label": "hive:drop" }, + { "itemId": 5, "name": "hive:alter", "label": "hive:alter" }, + { "itemId": 6, "name": "hive:index", "label": "hive:index" }, + { "itemId": 7, "name": "hive:lock", "label": "hive:lock" }, + { "itemId": 8, "name": "hive:all", "label": "hive:all", + "impliedGrants": [ "hive:select", "hive:update", "hive:create", "hive:drop", "hive:alter", "hive:index", "hive:lock" ] } + ], + "dataMaskDef": { + "resources":[ + { "name": "tag" } + ] + }, + "contextEnrichers": [ + { "itemId": 1, "name": "TagEnricher", "enricher": "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", "enricherOptions": { "tagRetrieverClassName": "org.apache.ranger.plugin.contextenricher.RangerFileBasedTagRetriever", "tagRefresherPollingInterval": 60000, "serviceTagsFileName": "/policyengine/resource_hierarchy_tags.json" } } + ], + "policyConditions": [ + { "itemId": 1, "name": "expression", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", "evaluatorOptions": { "engineName": "JavaScript", "ui.isMultiline": "true" }, "label": "Enter boolean expression", "description": "Boolean expression" }, + { "itemId": 2, "name": "enforce-expiry", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptTemplateConditionEvaluator", "evaluatorOptions": { "scriptTemplate": "ctx.isAccessedAfter('expiry_date');" }, "label": "Deny access after expiry_date?", "description": "Deny access after expiry_date? (yes/no)" }, + { "itemId": 3, "name": "ip-range", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerIpMatcher", "evaluatorOptions": { }, "label": "IP Address Range", "description": "IP Address Range" } + ] + }, + "tagPolicies": [ + { "id": 1, "name": "1: access: SENSITIVE", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:select", "isAllowed": true}], "users": [ "test-user"] } + ] + }, + { "id": 2, "name": "2: access: ORDER", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "ORDER" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:create", "isAllowed": true}], "users": [ "dba"] } + ] + }, + { "id": 3, "name": "3: access: CUSTOMER", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "CUSTOMER" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:select", "isAllowed": true}], "users": [ "test-user"] } + ] + }, + { "id": 4, "name": "4: access: ADDRESS", "isEnabled": true, "isAuditEnabled": true, "policyType": 0, + "resources": { "tag": { "values": [ "ADDRESS" ], "isRecursive": false } }, + "policyItems": [ + {"accesses": [{"type": "hive:select", "isAllowed": true}], "users": [ "test-user"] } + ] + }, + { "id": 101, "name": "101: mask: SENSITIVE(level=normal)", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "conditions": [ { "type": "expression", "values": [ "TAG.level == 'normal'" ] } ], + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "SHUFFLE"}} + ] + }, + { "id": 102, "name": "102: mask: SENSITIVE(level=high)", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "conditions": [ { "type": "expression", "values": [ "TAG.level == 'high'" ] } ], + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK"}} + ] + }, + { "id": 103, "name": "103: mask: SENSITIVE(level=top)", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "SENSITIVE" ], "isRecursive": false } }, + "conditions": [ { "type": "expression", "values": [ "TAG.level == 'top'" ] } ], + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK_HASH"}} + ] + }, + { "id": 104, "name": "104: mask: CUSTOMER", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "CUSTOMER" ], "isRecursive": false } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK_NONE"}} + ] + }, + { "id": 105, "name": "105: mask: ADDRESS", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, + "resources": { "tag": { "values": [ "ADDRESS" ], "isRecursive": false } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "hive:select", "isAllowed": true } ], "users": [ "test-user"], "dataMaskInfo": { "dataMaskType": "MASK_HASH"}} + ] + } + ] + }, + "tests": [ + { "name": "table: db1.tbl1, user=test-user", + "request": { + "resource": { "elements": { "database": "db1", "table": "tbl1" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select * from db1.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "SHUFFLE", "maskCondition": null, "maskValue": null }, "policyId": 101 } + }, + { "name": "column: db1.tbl1.SSN, user=test-user", + "request": { + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "SSN" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select SSN from db1.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK", "maskCondition": null, "maskValue": null }, "policyId": 102 } + }, + { "name": "column: db1.tbl1.Age, user=test-user", + "request": { + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "Age" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select Age from db1.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_HASH", "maskCondition": null, "maskValue": null }, "policyId": 103 } + }, + { "name": "column: db1.tbl1.Name, user=test-user", + "request": { + "resource": { "elements": { "database": "db1", "table": "tbl1", "column": "Name" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select Name from db1.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_HASH", "maskCondition": null, "maskValue": null }, "policyId": 103 } + }, + { "name": "database: db2, user=test-user", + "request": { + "resource": { "elements": { "database": "db2" } }, + "accessType": "_any", "user": "test-user", "userGroups": [], "requestData": "use db2" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_HASH", "maskCondition": null, "maskValue": null }, "policyId": 103 } + }, + { "name": "table: db2.tbl1, user=test-user", + "request": { + "resource": { "elements": { "database": "db2", "table": "tbl1" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select * from db2.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_HASH", "maskCondition": null, "maskValue": null }, "policyId": 103 } + }, + { "name": "column: db2.tbl1.Name, user=test-user", + "request": { + "resource": { "elements": { "database": "db2", "table": "tbl1", "column": "Name" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select Name from db2.tbl1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 1 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_HASH", "maskCondition": null, "maskValue": null }, "policyId": 103 } + }, + { "name": "database: order", + "request": { + "resource": { "elements": { "database": "order" } }, + "accessType": "create", "user": "dba", "userGroups": [], "requestData": "create table order.t1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "table: order.customer", + "request": { + "resource": { "elements": { "database": "order", "table": "customer" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select * from order.customer" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 3 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_NONE", "maskCondition": null, "maskValue": null }, "policyId": 104 } + }, + { "name": "table: order.customer", + "request": { + "resource": { "elements": { "database": "order", "table": "customer" } }, + "accessType": "create", "user": "dba", "userGroups": [], "requestData": "create table order.t1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + }, + { "name": "column: order.customer.address", + "request": { + "resource": { "elements": { "database": "order", "table": "customer", "column": "address" } }, + "accessType": "select", "user": "test-user", "userGroups": [], "requestData": "select address from order.customer" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 4 }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK_HASH", "maskCondition": null, "maskValue": null }, "policyId": 105 } + }, + { "name": "column: order.customer.address", + "request": { + "resource": { "elements": { "database": "order", "table": "customer", "column": "address" } }, + "accessType": "create", "user": "dba", "userGroups": [], "requestData": "create table order.t1" + }, + "result": { "isAudited": true, "isAllowed": true, "policyId": 2 } + } + ] + } diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_resource_with_req_expressions.json b/agents-common/src/test/resources/policyengine/test_policyengine_resource_with_req_expressions.json new file mode 100644 index 0000000000..95e0c7c22b --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_resource_with_req_expressions.json @@ -0,0 +1,113 @@ +{ + "serviceName":"hdfsdev", + + "serviceDef":{ + "name":"hdfs", + "id":1, + "resources":[ + {"name":"path","type":"path","level":1,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"Resource Path","description":"HDFS file or directory path"}, + {"name":"path2","type":"path","level":1,"mandatory":true,"lookupSupported":true,"recursiveSupported": true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true, "replaceReqExpressions":false},"label":"Resource Path","description":"HDFS file or directory path"} + ], + "accessTypes":[ + {"name":"read","label":"Read"}, + {"name":"write","label":"Write"}, + {"name":"execute","label":"Execute"} + ], + "contextEnrichers": [], + "policyConditions": [] + }, + + "policies":[ + {"id":10,"name":"allow-all-to-user /home/${{USER._name}}","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/home/${{USER._name}}"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false} + ] + }, + {"id":20,"name":"allow-all-to-user1 /home/${{USER._name}}","isEnabled":true,"isAuditEnabled":true, + "resources":{"path2":{"values":["/home/${{USER._name}}"],"isRecursive":false}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false} + ] + }, + {"id":30,"name":"allow-all-to-user /home/${{USER._name}}/{USER}.data","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/home/${{USER._name}}/{USER}.data"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false} + ] + }, + {"id":40,"name":"allow-all-to-user /test/${{USER.testAttr1}}","isEnabled":true,"isAuditEnabled":true, + "resources":{"path":{"values":["/test/${{USER.testAttr1}}"],"isRecursive":true}}, + "policyItems":[ + {"accesses":[{"type":"read","isAllowed":true}, {"type":"write","isAllowed":true}, {"type":"execute","isAllowed":true}],"users":[],"groups":["public"],"delegateAdmin":false} + ] + } + ], + + "tests":[ + {"name":"ALLOW 'write path2=/home/${{USER._name}}' for u=scott; no evaluation of request-expression", + "request":{ + "resource":{"elements":{"path2":"/home/${{USER._name}}"}}, "resourceMatchingScope": "SELF_OR_CHILD", + "accessType":"write","user":"scott","userGroups":[],"requestData":"write path2=/home/${{USER._name}}" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 20} + }, + {"name":"DENY 'write path2=/home/scott' for u=scott; no evaluation of request-expression", + "request":{ + "resource":{"elements":{"path2":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD", + "accessType":"write","user":"scott","userGroups":[],"requestData":"write path2=/home/scott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"ALLOW 'write /home/scott' for u=scott for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD", + "accessType":"write","user":"scott","userGroups":[],"requestData":"write /home/scott" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 10} + }, + {"name":"DENY 'ANY /home/scott' for u=joe for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD", + "accessType":"","user":"joe","userGroups":[],"requestData":"ANY /home/scott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"DENY 'ANY /home/scott' for u=scot for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/home/scott"}}, "resourceMatchingScope": "SELF_OR_CHILD", + "accessType":"","user":"scot","userGroups":[],"requestData":"ANY /home/scott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"DENY 'ANY /home/sscott' for u=scott for scope SELF_OR_CHILD", + "request":{ + "resource":{"elements":{"path":"/home/sscott"}}, "resourceMatchingScope": "SELF_OR_CHILD", + "accessType":"","user":"scott","userGroups":[],"requestData":"ANY /home/sscott" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId": -1} + }, + {"name":"ALLOW 'write /home/scott/scott.data/test.txt' for u=scott", + "request":{ + "resource":{"elements":{"path":"/home/scott/scott.data/test.txt"}}, + "accessType":"write","user":"scott","userGroups":[],"requestData":"write /home/scott/scott.data/test.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 30} + }, + {"name":"ALLOW 'write /home/scott/scott.data/test.txt' for u=scott", + "request":{ + "resource":{"elements":{"path":"/home/scott/scott.data/test.txt"}}, + "accessType":"write","user":"scott","userGroups":[],"requestData":"write /home/scott/scott.data/test.txt" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 30} + }, + {"name":"ALLOW 'write /test/x_scott_y/test.txt' for u=scott", + "request":{ + "resource":{"elements":{"path":"/test/x_scott_y/test.txt"}}, + "accessType":"write","user":"scott","userGroups":[],"requestData":"write /test/x_scott_y/test.txt" + }, + "userAttributes": { "scott": { "testAttr1": "x_{USER}_y" } }, + "result":{"isAudited":true,"isAllowed":true,"policyId": 40} + } + ] +} \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_super_user_access.json b/agents-common/src/test/resources/policyengine/test_policyengine_super_user_access.json new file mode 100644 index 0000000000..cb5ca16024 --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_super_user_access.json @@ -0,0 +1,161 @@ +{ + "serviceName":"hivedev", + + "serviceDef": { + "id":3, + "name": "hive", + "implClass": "org.apache.ranger.services.hive.RangerServiceHive", + "label": "Hive Server2", + "description": "Hive Server2", + "guid": "3e1afb5a-184a-4e82-9d9c-87a5cacc243c", + + "resources": [ + {"itemId": 1, "name": "database", "type": "string", "level": 10, "parent": "", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":true }, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Hive Database", "description": "Hive Database", "isValidLeaf": true}, + {"itemId": 2, "name": "table", "type": "string", "level": 20, "parent": "database", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":true }, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Hive Table", "description": "Hive Table", "isValidLeaf": true}, + {"itemId": 3, "name": "udf", "type": "string", "level": 20, "parent": "database", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":true }, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Hive UDF", "description": "Hive UDF", "isValidLeaf": true}, + {"itemId": 4, "name": "column", "type": "string", "level": 30, "parent": "table", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":true }, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Hive Column", "description": "Hive Column", "isValidLeaf": true}, + {"itemId": 5, "name": "url", "type": "string", "level": 10, "parent": "", "mandatory": true, "lookupSupported": false, "recursiveSupported": true, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerPathResourceMatcher", "matcherOptions": { "wildCard":true, "ignoreCase":false }, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "URL", "description": "URL", "isValidLeaf": true} + ], + + "accessTypes": [ + {"itemId": 1, "name": "select", "label": "select"}, + {"itemId": 2, "name": "update", "label": "update"}, + {"itemId": 3, "name": "create", "label": "Create"}, + {"itemId": 4, "name": "drop", "label": "Drop"}, + {"itemId": 5, "name": "alter", "label": "Alter"}, + {"itemId": 6, "name": "index", "label": "Index"}, + {"itemId": 7, "name": "lock", "label": "Lock"}, + {"itemId": 8, "name": "all", "label": "All", + "impliedGrants": ["select", "update", "create", "drop", "alter", "index", "lock", "read", "write"]}, + {"itemId": 9, "name": "read", "label": "Read"}, + {"itemId": 10, "name": "write", "label": "Write"} + ], + + "configs": [ + {"itemId": 1, "name": "username", "type": "string", "mandatory": true, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Username"}, + {"itemId": 2, "name": "password", "type": "password", "mandatory": true, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Password"}, + {"itemId": 3, "name": "jdbc.driverClassName", "type": "string", "mandatory": true, "validationRegEx":"", "validationMessage": "", "uiHint":"", "defaultValue": "org.apache.hive.jdbc.HiveDriver"}, + {"itemId": 4, "name": "jdbc.url", "type": "string", "mandatory": true, "defaultValue": "", "validationRegEx":"", "validationMessage": "", "uiHint":""}, + {"itemId": 5, "name": "commonNameForCertificate", "type": "string", "mandatory": false, "validationRegEx":"", "validationMessage": "", "uiHint":"", "label": "Common Name for Certificate"} + ], + + "enums": [ + ], + + "contextEnrichers": [ + ], + + "policyConditions": [ + ] + }, + + "serviceConfig": { + "ranger.plugin.super.users": "svc-cfg-su1, svc-cfg-su2", + "ranger.plugin.super.groups": "svc-cfg-sg1, svc-cfg-sg2" + }, + + "policies":[ + {"id":1,"name":"database=db-*,table=*,column=* - audit-all-access","isEnabled":true,"isAuditEnabled":true, + "resources":{"database":{"values":["db-*"]},"table":{"values":["*"]},"column":{"values":["*"]}}, + "policyItems":[ + ], + "denyPolicyItems":[ + {"accesses":[{"type":"create","isAllowed":true}],"users":["hive1","hive2"],"groups":["hadoop","hive"],"delegateAdmin":false} + ] + } + ], + + "superUsers": [ "su1", "su2" ], + "superGroups": [ "sg1", "sg2" ], + + "tests":[ + {"name":"ALLOW '_super_user' for su1", + "request":{ + "resource":{"elements":{"database":"db-1"}}, + "accessType":"_super_user","user":"su1","userGroups":[""],"requestData":"_super_user for su1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for su2", + "request":{ + "resource":{"elements":{"database":"db-2"}}, + "accessType":"_super_user","user":"su2","userGroups":[""],"requestData":"_super_user for su2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for svc-cfg-su1", + "request":{ + "resource":{"elements":{"database":"db-1"}}, + "accessType":"_super_user","user":"svc-cfg-su1","userGroups":[""],"requestData":"_super_user for svc-cfg-su1" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for svc-cfg-su2", + "request":{ + "resource":{"elements":{"database":"db-2"}}, + "accessType":"_super_user","user":"svc-cfg-su2","userGroups":[""],"requestData":"_super_user for svc-cfg-su2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for user1 (in sg1)", + "request":{ + "resource":{"elements":{"database":"db-1", "table":"tbl-1"}}, + "accessType":"_super_user","user":"user1","userGroups":["sg1"],"requestData":"_super_user for user1 (in sg1)" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user;' for user2 (in sg2)", + "request":{ + "resource":{"elements":{"database":"db-1", "table":"tbl-2"}}, + "accessType":"_super_user","user":"user2","userGroups":["sg2"],"requestData":"_super_user for user2 (in sg2)" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for user1 (in svc-cfg-sg1)", + "request":{ + "resource":{"elements":{"database":"db-1", "table":"tbl-1"}}, + "accessType":"_super_user","user":"user1","userGroups":["svc-cfg-sg1"],"requestData":"_super_user for user1 (in svc-cfg-sg1)" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for user2 (in svc-cfg-sg2)", + "request":{ + "resource":{"elements":{"database":"db-1", "table":"tbl-2"}}, + "accessType":"_super_user","user":"user2","userGroups":["svc-cfg-sg2"],"requestData":"_super_user for user2 (in svc-cfg-sg2)" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":-1} + } + , + {"name":"ALLOW '_super_user' for su1; no audit", + "request":{ + "resource":{"elements":{"database":"testdb"}}, + "accessType":"_super_user","user":"su1","userGroups":[""],"requestData":"_super_user for su1" + }, + "result":{"isAudited":false,"isAllowed":true,"policyId":-1} + } + , + {"name":"DENY '_super_user' for user3", + "request":{ + "resource":{"elements":{"database":"db-1", "table":"tbl-3"}}, + "accessType":"_super_user","user":"user3","userGroups":["users"],"requestData":"_super_user for user3" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + } + , + {"name":"DENY '_super_user' for user3; no audit", + "request":{ + "resource":{"elements":{"database":"testdb"}}, + "accessType":"_super_user","user":"user3","userGroups":["users"],"requestData":"_super_user for user3" + }, + "result":{"isAudited":false,"isAllowed":false,"policyId":-1} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hbase.json b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hbase.json new file mode 100644 index 0000000000..c09ad1b3ff --- /dev/null +++ b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hbase.json @@ -0,0 +1,179 @@ +{ + "serviceName":"hbasedev", + + "serviceDef":{ + "name":"hbase", + "id":2, + "resources":[ + {"name":"table","level":1,"parent":"","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"HBase Table","description":"HBase Table"}, + {"name":"column-family","level":2,"parent":"table","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"HBase Column-Family","description":"HBase Column-Family"}, + {"name":"column","level":3,"parent":"column-family","mandatory":true,"lookupSupported":true,"matcher":"org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher","matcherOptions":{"wildCard":true, "ignoreCase":true},"label":"HBase Column","description":"HBase Column"} + ], + "accessTypes":[ + {"name":"read","label":"Read"}, + {"name":"write","label":"Write"}, + {"name":"create","label":"Create"}, + {"name":"admin","label":"Admin","impliedGrants":["read","write","create"]} + ] + }, + + "policies":[ + {"id":1,"name":"table=finance; column-family=*, column=*: audit-all-access","isEnabled":true,"isAuditEnabled":true, + "resources":{"table":{"values":["finance"]},"column-family":{"values":["*"]},"column":{"values":["*"]}} + } + , + {"id":2,"name":"table=finance; column-family=personal; column=*","isEnabled":true,"isAuditEnabled":true, + "resources":{"table":{"values":["finance"]},"column-family":{"values":["personal"]},"column": {"values": ["*"]}}, + "denyPolicyItems":[ + {"accesses":[{"type":"read","isAllowed":true}],"users":["hrt_12"],"groups":[],"delegateAdmin":false} + ] + } + ], + "tagPolicyInfo": { + + "serviceName":"tagdev", + "serviceDef": { + "name": "tag", + "id": 100, + "resources": [ + { + "itemId": 1, + "name": "tag", + "type": "string", + "level": 1, + "parent": "", + "mandatory": true, + "lookupSupported": true, + "recursiveSupported": false, + "excludesSupported": false, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": false + }, + "validationRegEx": "", + "validationMessage": "", + "uiHint": "", + "label": "TAG", + "description": "TAG" + } + ], + "accessTypes": [ + { + "itemId": 1, + "name": "hbase:read", + "label": "hbase:read" + }, + { + "itemId": 2, + "name": "hbase:write", + "label": "hbase:write" + }, + { + "itemId": 3, + "name": "hbase:create", + "label": "hbase:create" + } + , + { + "itemId": 4, + "name": "hbase:admin", + "label": "hbase:admin", + "impliedGrants": + [ + "hbase:read", + "hbase:write", + "hbase:create" + ] + }, + { + "itemId": 5, + "name": "hbase:all", + "label": "hbase:all", + "impliedGrants": + [ + "hbase:read", + "hbase:write", + "hbase:create", + "hbase:admin" + ] + } + ], + "contextEnrichers": [ + { + "itemId": 1, + "name" : "TagEnricher", + "enricher" : "org.apache.ranger.plugin.contextenricher.RangerTagEnricher", + "enricherOptions" : {"tagRetrieverClassName":"org.apache.ranger.plugin.contextenricher.RangerFileBasedTagRetriever", "tagRefresherPollingInterval":60000, "serviceTagsFileName":"/policyengine/hbaseTags.json"} + } + ], + "policyConditions": [ + { + "itemId":1, + "name":"expression", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator", + "evaluatorOptions" : {"engineName":"JavaScript", "ui.isMultiline":"true"}, + "label":"Enter boolean expression", + "description": "Boolean expression" + }, + { + "itemId":2, + "name":"enforce-expiry", + "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptTemplateConditionEvaluator", + "evaluatorOptions" : { "scriptTemplate":"ctx.isAccessedAfter('expiry_date');" }, + "label":"Deny access after expiry_date?", + "description": "Deny access after expiry_date? (yes/no)" + } + ] + }, + "tagPolicies":[ + {"id":100,"name":"COLUMN_POLICY","isEnabled":true,"isAuditEnabled":true, + "resources":{"tag":{"values":["COLUMN_TAG"],"isRecursive":false}}, + "policyItems":[ + { + "accesses":[{"type":"hbase:read","isAllowed":true}],"users":["hrt_12"],"groups":[],"delegateAdmin":false + } + ] + } + ] + }, + + "tests":[ + {"name":"DENY 'scan finance.professional;' for hrt_12", + "request":{ + "resource":{"elements":{"table":"finance", "column-family":"professional"}}, + "accessType":"read","user":"hrt_12","userGroups":[],"requestData":"scan finance.professional; for hrt_12" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + {"name":"ALLOW 'scan finance.professional; with resourceMatchingScope=SELF_OR_DESCENDANTS' for hrt_12", + "request":{ + "resource":{"elements":{"table":"finance", "column-family":"professional"}}, "resourceMatchingScope": "SELF_OR_DESCENDANTS", + "accessType":"read","user":"hrt_12","userGroups":[],"requestData":"scan finance.professional; with resourceMatchingScope=SELF_OR_DESCENDANTS for hrt_12" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":100} + }, + {"name":"ALLOW 'scan finance.professional.ssn;' for hrt_12", + "request":{ + "resource":{"elements":{"table":"finance", "column-family":"professional", "column":"ssn"}}, + "accessType":"read","user":"hrt_12","userGroups":[],"requestData":"scan finance.professional.ssn; for hrt_12" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":100} + }, + {"name":"DENY 'scan finance.personal;' for hrt_12", + "request":{ + "resource":{"elements":{"table":"finance", "column-family":"personal"}}, + "accessType":"read","user":"hrt_12","userGroups":[],"requestData":"scan finance.personal; for hrt_12" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":2} + }, + {"name":"DENY 'scan finance.personal;' with resourceMatchingScope=SELF_OR_DESCENDANTS for hrt_12", + "request":{ + "resource":{"elements":{"table":"finance", "column-family":"personal"}}, "resourceMatchingScope": "SELF_OR_DESCENDANTS", + "accessType":"read","user":"hrt_12","userGroups":[],"requestData":"scan finance.personal; for hrt_12 with with resourceMatchingScope=SELF_OR_DESCENDANTS" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":2} + } + ] +} + diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive.json b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive.json index 79417a0cbb..81feced15f 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive.json @@ -49,6 +49,12 @@ "policyItems":[ {"accesses":[{"type":"all","isAllowed":true}],"users":["hive", "user1", "user2"],"groups":["public"],"delegateAdmin":false} ] + }, + {"id":103,"name":"db=default, table=table2: audit-all-access","isEnabled":true,"isAuditEnabled":true, + "resources":{"database":{"values":["default"]},"table":{"values":["table2"]},"column":{"values":["*"]}}, + "denyPolicyItems":[ + {"accesses":[{"type":"select","isAllowed":true}],"users":["denieduser"],"groups":[],"delegateAdmin":false} + ] } ], "tagPolicyInfo": { @@ -222,6 +228,14 @@ }, "tests":[ + {"name":"DENY 'desc default.table2;' for denieduser", + "request":{ + "resource":{"elements":{"database":"default", "table":"table2"}}, + "accessType":"","user":"denieduser","userGroups":[],"requestData":"desc default.table2;' for denieduser", + "context": {"TAGS":"[{\"type\":\"PII-FINAL\", \"attributes\":{\"expiry\":\"2026/06/15\"}}]"} + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":103} + }, {"name":"DENY 'select ssn from employee.personal;' for testuser using EXPIRES_ON tag with DESCENDANT match", "request":{ "resource":{"elements":{"database":"employee", "table":"personal", "column":"ssn"}}, @@ -322,13 +336,21 @@ }, "result":{"isAudited":true,"isAllowed":false,"policyId":3} }, + {"name":"DENY 'desc default.table1;' for testuser", + "request":{ + "resource":{"elements":{"database":"default", "table":"table1"}}, + "accessType":"","user":"testuser","userGroups":[],"requestData":"desc default.table1;' for testuser", + "context": {"TAGS":"[{\"type\":\"PII-FINAL\", \"attributes\":{\"expiry\":\"2026/06/15\"}}]"} + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, {"name":"ALLOW 'use default;' for hive", "request":{ "resource":{"elements":{"database":"default"}}, "accessType":"","user":"hive","userGroups":[],"requestData":"use default", "context": {"TAGS":"[{\"type\":\"PII-FINAL\", \"attributes\":{\"expiry\":\"2026/06/15\"}}]"} }, - "result":{"isAudited":true,"isAllowed":false,"policyId":3} + "result":{"isAudited":true,"isAllowed":true,"policyId":101} }, {"name":"DENY 'use default;' for user1", "request":{ diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_filebased.json b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_filebased.json index 73fe540c7a..b3ca12ed65 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_filebased.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_filebased.json @@ -201,7 +201,7 @@ ] , "denyExceptions":[ - {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["hive", "user1"],"groups":[],"delegateAdmin":false, + {"accesses":[{"type":"hive:select","isAllowed":true}],"users":["hive", "user1", "{OWNER}"],"groups":[],"delegateAdmin":false, "conditions":[{ "type":"expression", "values":["if ( ctx.isAccessedBefore('activation_date') ) ctx.result = true;"] @@ -225,6 +225,14 @@ }, "tests":[ + {"name":"DENY 'select from employee.personal;' for user1 using EXPIRES_ON tag", + "request":{ + "resource":{"elements":{"database":"employee", "table":"personal"}}, "resourceMatchingScope": "SELF_OR_DESCENDANTS", + "accessType":"select","user":"user1","userGroups":[],"requestData":"select from employee.personal;' for user1" + + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":101} + }, {"name":"ALLOW 'select ssn from employee.personal;' for user1 using EXPIRES_ON tag", "request":{ "resource":{"elements":{"database":"employee", "table":"personal", "column":"ssn"}}, @@ -269,6 +277,20 @@ }, "result":{"isAudited":true,"isAllowed":false,"policyId":4} }, + {"name":"ALLOW 'select address from employee.personal;' for user2, the {OWNER}, using RESTRICTED-FINAL tag", + "request":{ + "resource":{"elements":{"database":"employee", "table":"personal", "column":"address"}, "ownerUser": "user2"}, + "accessType":"select","user":"user2","userGroups":[],"requestData":"select address from employee.personal;' for user2" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":101} + }, + {"name":"DENY 'select address from employee.personal;' for user3, owner=user2, using RESTRICTED-FINAL tag", + "request":{ + "resource":{"elements":{"database":"employee", "table":"personal", "column":"address"}, "ownerUser": "user2"}, + "accessType":"select","user":"user3","userGroups":[],"requestData":"select address from employee.personal;' for user2" + }, + "result":{"isAudited":true,"isAllowed":false,"policyId":4} + }, {"name":"ALLOW 'select name from employee.personal;' for user1 - no tag", "request":{ "resource":{"elements":{"database":"employee", "table":"personal", "column":"name"}}, @@ -283,12 +305,12 @@ }, "result":{"isAudited":true,"isAllowed":true,"policyId":2} }, - {"name":"DENY 'desc default.table1;' for hive using PII, PII-FINAL tags", + {"name":"ALLOW 'desc default.table1;' for hive using PII, PII-FINAL tags", "request":{ "resource":{"elements":{"database":"default", "table":"table1"}}, "accessType":"","user":"hive","userGroups":[],"requestData":"desc default.table1;' for hive" }, - "result":{"isAudited":true,"isAllowed":false,"policyId":3} + "result":{"isAudited":true,"isAllowed":true,"policyId":101} }, {"name":"DENY 'desc default.table2;' for user1 using PII-FINAL tag", "request":{ diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_for_show_databases.json b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_for_show_databases.json index f42df3eab9..97f7c27249 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_for_show_databases.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_for_show_databases.json @@ -174,14 +174,14 @@ }, "tests":[ - {"name":"ALLOW 'show databases;' for hive", + {"name":"DENY 'show databases;' for hive", "request":{ "resource":{"elements":{}}, "accessType":"","user":"hive","userGroups":["public"],"requestData":"show databases for hive" }, "result":{"isAudited":true,"isAllowed":true,"policyId":101} } - , + , {"name":"ALLOW 'use default;' for hive", "request":{ "resource":{"elements":{"database":"default"}}, @@ -190,7 +190,7 @@ "result":{"isAudited":true,"isAllowed":true,"policyId":101} } , - {"name":"ALLOW 'use employee;' for hive", + {"name":"DENY 'use employee;' for hive", "request":{ "resource":{"elements":{"database":"employee"}}, "accessType":"","user":"hive","userGroups":["public"],"requestData":"use employee for hive" diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_mask.json b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_mask.json index a97bd2b779..1d0bcb737c 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_mask.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_tag_hive_mask.json @@ -150,83 +150,34 @@ } }, "policies": [ - { - "id": 101, - "name": "db=*: audit-all-access", - "isEnabled": true, - "isAuditEnabled": true, - "resources": { - "database": { - "values": [ - "*" - ] - }, - "table": { - "values": [ - "*" - ] - }, - "column": { - "values": [ - "*" - ] - } - }, + { "id": 101, "name": "db=*: audit-all-access", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "*" ] }, "table": { "values": [ "*" ] }, "column": { "values": [ "*" ] } }, "policyItems": [ - { - "accesses": [ - { - "type": "all", - "isAllowed": true - } - ], - "users": [ - "hive", - "user1", - "user2" - ], - "groups": [ - "public" - ], - "delegateAdmin": false - } + { "accesses": [ { "type": "all", "isAllowed": true } ], "users": [ "hive", "user1", "user2" ], "groups": [ "public" ], "delegateAdmin": false } ] }, - { - "id": 102, - "name": "db=*, udf=*: audit-all-access", - "isEnabled": true, - "isAuditEnabled": true, - "resources": { - "database": { - "values": [ - "*" - ] - }, - "udf": { - "values": [ - "*" - ] - } - }, + { "id": 102, "name": "db=*, udf=*: audit-all-access", "isEnabled": true, "isAuditEnabled": true, + "resources": { "database": { "values": [ "*" ] }, "udf": { "values": [ "*" ] } }, "policyItems": [ - { - "accesses": [ - { - "type": "all", - "isAllowed": true - } - ], - "users": [ - "hive", - "user1", - "user2" - ], - "groups": [ - "public" - ], - "delegateAdmin": false - } + { "accesses": [ { "type": "all", "isAllowed": true } ], "users": [ "hive", "user1", "user2" ], "groups": [ "public" ], "delegateAdmin": false } + ] + }, + { "id": 103, "name": "masking: employee.personal.ssn - normal priority", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, "policyPriority": 0, + "resources": { "database": { "values": [ "employee" ] }, "table": { "values": [ "personal" ] }, "column": { "values": [ "ssn" ] } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "user1" ], "dataMaskInfo": { "dataMaskType": "NONE" } } + ] + }, + { "id": 104, "name": "masking: employee.personal.ssn - override priority", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, "policyPriority": 1, + "resources": { "database": { "values": [ "employee" ] }, "table": { "values": [ "personal" ] }, "column": { "values": [ "ssn" ] } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "user3" ], "dataMaskInfo": { "dataMaskType": "NONE" } } + ] + }, + { "id": 105, "name": "masking: employee.personal.name - normal priority", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, "policyPriority": 0, + "resources": { "database": { "values": [ "employee" ] }, "table": { "values": [ "personal" ] }, "column": { "values": [ "name" ] } }, + "dataMaskPolicyItems": [ + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "user1", "user2", "user4" ], "dataMaskInfo": { "dataMaskType": "NONE" } } ] } ], @@ -379,117 +330,77 @@ ] }, "tagPolicies": [ - { - "id": 1, - "name": "RESTRICTED_TAG_POLICY", - "isEnabled": true, - "isAuditEnabled": true, - "policyType": 1, - "resources": { - "tag": { - "values": [ - "RESTRICTED" - ], - "isRecursive": false - } - }, + { "id": 1, "name": "RESTRICTED", "isEnabled": true, "isAuditEnabled": true, "policyType": 1, "policyPriority": 0, + "resources": { "tag": { "values": [ "RESTRICTED" ], "isRecursive": false } }, "dataMaskPolicyItems": [ - { - "accesses": [ - { - "type": "select", - "isAllowed": true - } - ], - "users": [ - "user1" - ], - "groups": [], - "delegateAdmin": false, - "dataMaskInfo": { - "dataMaskType": "MASK" - } - }, - { - "accesses": [ - { - "type": "select", - "isAllowed": true - } - ], - "users": [ - "user2" - ], - "groups": [], - "delegateAdmin": false, - "dataMaskInfo": { - "dataMaskType": "SHUFFLE" - } - } + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "user1" ], "dataMaskInfo": { "dataMaskType": "MASK" } }, + { "accesses": [ { "type": "select", "isAllowed": true } ], "users": [ "user2", "user3" ], "dataMaskInfo": { "dataMaskType": "SHUFFLE" } } ] } ] }, "tests": [ - { - "name": "'select ssn from employee.personal;' for user1 - maskType=MASK", + { "name": "'select ssn from employee.personal;' for user1 - maskType=MASK", "request": { - "resource": { - "elements": { - "database": "employee", - "table": "personal", - "column": "ssn" - } - }, - "accessType": "select", - "user": "user1", - "userGroups": [], - "requestData": "select ssn from employee.personal;' for user1", - "context": { - "TAGS": "[{\"type\":\"RESTRICTED\"}]" - } + "resource": { "elements": { "database": "employee", "table": "personal", "column": "ssn" } }, + "accessType": "select", "user": "user1", "userGroups": [], "requestData": "select ssn from employee.personal;' for user1", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } }, - "dataMaskResult":{"additionalInfo":{"maskType":"MASK","maskCondition":null,"maskValue":null},"policyId":1} + "dataMaskResult": { "additionalInfo": { "maskType": "MASK", "maskCondition": null, "maskValue" :null }, "policyId": 1 } }, { "name": "'select ssn from employee.personal;' for user2 - maskType=SHUFFLE", "request": { - "resource": { - "elements": { - "database": "employee", - "table": "personal", - "column": "ssn" - } - }, - "accessType": "select", - "user": "user2", - "userGroups": [], - "requestData": "select ssn from employee.personal;' for user2", - "context": { - "TAGS": "[{\"type\":\"RESTRICTED\"}]" - } + "resource": { "elements": { "database": "employee", "table": "personal", "column": "ssn" } }, + "accessType": "select", "user": "user2", "userGroups": [], "requestData": "select ssn from employee.personal;' for user2", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } }, - "dataMaskResult":{"additionalInfo":{"maskType":"SHUFFLE","maskCondition":null,"maskValue":null},"policyId":1} + "dataMaskResult": { "additionalInfo": { "maskType": "SHUFFLE", "maskCondition": null, "maskValue": null }, "policyId": 1 } + }, + { + "name": "'select ssn from employee.personal;' for user3 - maskType=NONE (resource-policy override)", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal", "column": "ssn" } }, + "accessType": "select", "user": "user3", "requestData": "select ssn from employee.personal;' for user2", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } + }, + "dataMaskResult": { "additionalInfo": { "maskType": "NONE", "maskCondition": null, "maskValue": null }, "policyId": 104 } }, { "name": "'select ssn from employee.personal;' for hive - maskType=NONE", "request": { - "resource": { - "elements": { - "database": "employee", - "table": "personal", - "column": "ssn" - } - }, - "accessType": "select", - "user": "hive", - "userGroups": [], - "requestData": "select ssn from employee.personal;' for hive", - "context": { - "TAGS": "[{\"type\":\"RESTRICTED\"}]" - } + "resource": { "elements": { "database": "employee", "table": "personal", "column": "ssn" } }, + "accessType": "select", "user": "hive", "userGroups": [], "requestData": "select ssn from employee.personal;' for hive", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } + }, + "dataMaskResult": { "additionalInfo": { "maskType": null, "maskCondition": null, "maskValue": null }, "policyId": -1 } + }, + { + "name": "'select name from employee.personal;' for user1 - maskType=MASK", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal", "column": "name" } }, + "accessType": "select", "user": "user1", "userGroups": [], "requestData": "select name from employee.personal;' for user1", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } + }, + "dataMaskResult": { "additionalInfo": { "maskType": "MASK", "maskCondition": null, "maskValue": null }, "policyId": 1 } + }, + { + "name": "'select name from employee.personal;' for user2 - maskType=SHUFFLE", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal", "column": "name" } }, + "accessType": "select", "user": "user2", "userGroups": [], "requestData": "select name from employee.personal;' for user2", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } + }, + "dataMaskResult": { "additionalInfo": { "maskType": "SHUFFLE", "maskCondition": null, "maskValue": null }, "policyId": 1 } + }, + { + "name": "'select name from employee.personal;' for user4 - maskType=NONE", + "request": { + "resource": { "elements": { "database": "employee", "table": "personal", "column": "name" } }, + "accessType": "select", "user": "user4", "userGroups": [], "requestData": "select name from employee.personal;' for user2", + "context": { "TAGS": "[{\"type\":\"RESTRICTED\"}]" } }, - "dataMaskResult":{"additionalInfo":{"maskType":null,"maskCondition":null,"maskValue":null},"policyId":-1} + "dataMaskResult": { "additionalInfo": { "maskType": "NONE", "maskCondition": null, "maskValue": null }, "policyId": 105 } } ] } diff --git a/agents-common/src/test/resources/policyengine/test_policyengine_with_roles.json b/agents-common/src/test/resources/policyengine/test_policyengine_with_roles.json index c8352cc3b7..3f8fca0d0a 100644 --- a/agents-common/src/test/resources/policyengine/test_policyengine_with_roles.json +++ b/agents-common/src/test/resources/policyengine/test_policyengine_with_roles.json @@ -90,6 +90,9 @@ "lock" ] } + ], + "policyConditions":[ + { "name": "expression", "evaluator": "org.apache.ranger.plugin.conditionevaluator.RangerScriptConditionEvaluator" } ] }, "policies": [ @@ -162,10 +165,17 @@ "delegateAdmin": false } ] + }, + { + "id": 3, "name": "db=hr-fin", "isEnabled": true, "isAuditEnabled": true, "isDenyAllElse": true, + "resources": { "database": { "values": [ "hr-fin"] } }, + "policyItems": [ + { "accesses": [ { "type": "create" } ], "groups": [ "public"], "conditions": [ { "type": "expression", "values": [ "IS_IN_ROLE('fin-admin') && IS_IN_ROLE('hr-admin') "] } ] } + ] } ], - "userRoles": {"role-user1": ["fin-admin", "hr-admin"]}, - "groupRoles": {"role-group1": ["fin-group", "hr-group"]}, + "userRoles": {"role-user1": ["fin-admin", "hr-admin"], "admin-user": [ "fin-admin", "hr-admin" ], "fin-user": [ "fin-admin"], "hr-user": [ "hr-admin"] }, + "groupRoles": {"role-group1": ["fin-group", "hr-group"], "admin-group": [ "fin-admin", "hr-admin" ] }, "tests": [ {"name":"ALLOW 'create database default default ;' for user='role-user1'", "request":{ @@ -208,6 +218,34 @@ "accessType":"create","user":"user2","userGroups":[],"requestData":"create database default ; for user2" }, "result":{"isAudited":true,"isAllowed":false,"policyId":-1} + }, + { "name": "ALLOW 'create database hr-fin;' for user=admin-user", + "request": { + "resource": { "elements": { "database": "hr-fin" } }, + "accessType": "create", "user": "admin-user", "requestData": "create database hr-fin; for admin-user" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":3} + }, + { "name": "ALLOW 'create database hr-fin;' for group=admin-group", + "request": { + "resource": { "elements": { "database": "hr-fin" } }, + "accessType": "create", "user": "test-user1", "userGroups": [ "admin-group" ], "requestData": "create database hr-fin; for group=admin-group" + }, + "result":{"isAudited":true,"isAllowed":true,"policyId":3} + }, + { "name": "DENY 'create database hr-fin;' for fin-user", + "request": { + "resource": { "elements": { "database": "hr-fin" } }, + "accessType": "create", "user": "fin-user", "requestData": "create database hr-fin; for fin-user" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": 3} + }, + { "name": "DENY 'create database hr-fin;' for hr-user", + "request": { + "resource": { "elements": { "database": "hr-fin" } }, + "accessType": "create", "user": "hr-user", "requestData": "create database hr-fin; for hr-user" + }, + "result": { "isAudited": true, "isAllowed": false, "policyId": 3} } ] } \ No newline at end of file diff --git a/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json index 321df659f2..7b42cc38d0 100644 --- a/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json +++ b/agents-common/src/test/resources/policyengine/validityscheduler/test-validity-schedules-invalid.json @@ -210,5 +210,209 @@ "isApplicable": false, "validationFailureCount": 2 } + }, + { + "name": "invalid lower bound - minute", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "-1", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "1", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid upper bound - minute", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "60", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "1", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid lower bound - hour", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "-1", "dayOfMonth": "1", "dayOfWeek": "1", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid upper bound - hour", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "24", "dayOfMonth": "1", "dayOfWeek": "1", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid lower bound - dayOfMonth", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "0", "dayOfWeek": "1", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid upper bound - dayOfMonth", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "32", "dayOfWeek": "1", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid lower bound - dayOfWeek", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "0", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid upper bound - dayOfWeek", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "8", "month": "1", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid lower bound - month", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "1", "month": "0", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid upper bound - month", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "1", "month": "13", "year": "2018" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid lower bound - year", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "1", "month": 1, "year": "2016" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } + }, + { + "name": "invalid upper bound - year", + "validitySchedules": [ + { + "startTime": "2018/01/12 14:32:00", "endTime": "2050/01/23 06:30:00", + "recurrences": [ + { + "schedule": { "minute": "0", "hour": "0", "dayOfMonth": "1", "dayOfWeek": "1", "month": "1", "year": "2101" }, + "interval": { "minutes": 10 } + } + ] + } + ], + "accessTime": "20180112-18:32:27.000-0800", + "result": { "isValid": false, "isApplicable": false, "validationFailureCount": 1 + } } ] \ No newline at end of file diff --git a/agents-common/src/test/resources/resourcematcher/test_defaultpolicyresourcematcher_quoted.json b/agents-common/src/test/resources/resourcematcher/test_defaultpolicyresourcematcher_quoted.json new file mode 100644 index 0000000000..cebfb72e17 --- /dev/null +++ b/agents-common/src/test/resources/resourcematcher/test_defaultpolicyresourcematcher_quoted.json @@ -0,0 +1,1035 @@ +{ + "serviceDef": { + "name": "quoted", + "id": 3, + "resources": [ + { + "name": "database", + "level": 1, + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true, + "quotedCaseSensitive": true, + "quoteChars": "\"" + }, + "label": "Quoted Database", + "description": "Quoted Database", + "isExcludes": true + }, + { + "name": "table", + "level": 2, + "parent": "database", + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true, + "quotedCaseSensitive": true, + "quoteChars": "\"" + }, + "label": "Quoted Table", + "description": "Quoted Table", + "isExcludes": true + }, + { + "name": "column", + "level": 3, + "parent": "table", + "mandatory": true, + "lookupSupported": true, + "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", + "matcherOptions": { + "wildCard": true, + "ignoreCase": true, + "quotedCaseSensitive": true, + "quoteChars": "\"" + }, + "label": "Quoted Column", + "description": "Quoted Column", + "isExcludes": true + } + ], + "accessTypes": [ + { + "name": "select", + "label": "Select" + }, + { + "name": "update", + "label": "Update" + }, + { + "name": "create", + "label": "Create" + }, + { + "name": "drop", + "label": "Drop" + }, + { + "name": "alter", + "label": "Alter" + }, + { + "name": "index", + "label": "Index" + }, + { + "name": "lock", + "label": "Lock" + }, + { + "name": "all", + "label": "All" + } + ] + }, + "testCases": [ + { + "name": "Quoted values in policy: database=\"Test*:table=\"teSt*:column=*", + "policyResources": { + "database": {"values": [ "\"Test*"] }, + "table": {"values": ["\"teSt*"]}, + "column": {"values": ["*"]} + }, + "tests": [ + { + "name": "MATCH for exact '\"TestDB.\"teStTbl.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact '\"TestDB.\"teStTbl.\"ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"\"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "No MATCH for unquoted 'TestDB.\"teStTbl.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "TestDB", "table":"\"teStTbl", "column":"SSN"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "No MATCH for unquoted 'TestDB.teStTbl.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "\"TestDB", "table":"teStTbl", "column":"ssn"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "MATCH for parent with wildcards '\"TestDB.\"teStTbl'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "\"TestDB","table": "\"teStTbl"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards '\"TestDB'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "\"TestDB"} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "Quoted values in resource: database=Test*:table=teSt*:column=*", + "policyResources": { + "database": {"values": [ "Test*"] }, + "table": {"values": ["teSt*"]}, + "column": {"values": ["*"]} + }, + "tests": [ + { + "name": "No MATCH for quoted '\"TestDB.\"teStTbl.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"ssn"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "No MATCH for exact '\"TestDB.\"teStTbl.\"ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "\"TestDB", "table":"\"teStTbl", "column":"\"ssn"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "MATCH for unquoted 'testdb.testtbl.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "testdb", "table":"testtbl", "column":"SSN"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for unquoted 'TestDB.teStTbl.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "TestDB", "table":"teStTbl", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'TestDB.teStTbl'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "TestDB","table": "teStTbl"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'TestDB'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "TestDB"} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=*, isExclude=true:table=*, isExcludes=true", + "policyResources": { + "database": {"values": ["*"], "isExcludes": true}, + "table": {"values": ["*"], "isExcludes": true} + }, + "tests": [ + { + "name": "NO MATCH for invalid resource level", + "type": "anyMatch", + "resource" : { + "elements" : { "database":"finance", "invalid-resource-name":"any"} + }, + "evalContext": {}, + "result" : false + } + , + { + "name": "No MATCH for parent 'finance.tax.ssn'", + "type": "anyMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "No MATCH for exact 'finance:tax'", + "type": "anyMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "No MATCH for parent with wildcards 'finance'", + "type": "anyMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "No MATCH for any ''", + "type": "anyMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": false + } + ] + }, + { + "name": "database=*:table=tax, isExcludes=true", + "policyResources": { + "database": {"values": ["*"]}, + "table": {"values": ["tax"], "isExcludes": true} + }, + "tests": [ + { + "name": "NO MATCH for invalid resource level", + "type": "anyMatch", + "resource" : { + "elements" : { "database":"finance", "invalid-resource-name":"any"} + }, + "evalContext": {}, + "result" : false + } + , + { + "name": "No MATCH for parent 'finance.tax.ssn'", + "type": "anyMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "No MATCH for exact 'finance:tax'", + "type": "anyMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": false + } + , + { + "name": "MATCH for parent with wildcards 'finance'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child ''", + "type": "descendantMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=*:table=*", + "policyResources": { + "database": {"values": ["*"]}, + "table": {"values": ["*"]} + }, + "tests": [ + { + "name": "NO MATCH for invalid resource level", + "type": "anyMatch", + "resource" : { + "elements" : { "database":"finance", "invalid-resource-name":"any"} + }, + "evalContext": {}, + "result" : false + } + , + { + "name": "MATCH for parent 'finance.tax.ssn'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact 'finance:tax'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards ''", + "type": "selfAndAllDescendants", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=finance:table=tax", + "policyResources": { + "database": {"values": ["finance"]}, + "table": {"values": ["tax"]} + }, + "tests": [ + { + "name": "MATCH for parent 'finance.tax.ssn'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact 'finance:tax'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child ''", + "type": "descendantMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=*:table=tax", + "policyResources": { + "database": {"values": ["*"]}, + "table": {"values": ["tax"]} + }, + "tests": [ + { + "name": "MATCH for parent 'finance.tax.ssn'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact 'finance:tax'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child ''", + "type": "descendantMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=finance:table=*", + "policyResources": { + "database": {"values": ["finance"]}, + "table": {"values": ["*"]} + }, + "tests": [ + { + "name": "MATCH for parent 'finance.tax.ssn'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact 'finance:tax'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child ''", + "type": "descendantMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=finance", + "policyResources": { + "database": {"values": ["finance"]} + }, + "tests": [ + { + "name": "MATCH for parent 'finance.tax.ssn'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent 'finance:tax'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact 'finance'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child ''", + "type": "descendantMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=*", + "policyResources": { + "database": {"values": ["*"]} + }, + "tests": [ + { + "name": "NO MATCH for invalid resource level", + "type": "anyMatch", + "resource" : { + "elements" : { "database":"finance", "invalid-resource-name":"any"} + }, + "evalContext": {}, + "result" : false + } + , + { + "name": "MATCH for parent 'finance.tax.ssn'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent 'finance:tax'", + "type": "ancestorMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for exact 'finance'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards ''", + "type": "selfAndAllDescendants", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + }, + { + "name": "database=*:table=*:column=*", + "policyResources": { + "database": {"values": [ "*"] }, + "table": {"values": ["*"]}, + "column": {"values": ["*"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance:tax'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards ''", + "type": "selfAndAllDescendants", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "NO MATCH for any 'finance::ssn'", + "type": "anyMatch", + "resource": { + "elements": { + "database": "finance","column":"ssn" + } + }, + "evalContext": {}, + "result": false + } + ] + } + , + { + "name": "database=finance:table=tax:column=ssn", + "policyResources": { + "database": {"values": [ "finance"] }, + "table": {"values": ["tax"]}, + "column": {"values": ["ssn"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance:tax'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child ''", + "type": "descendantMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + ] + } + , + + { + "name": "database=*:table=*:column=ssn", + "policyResources": { + "database": {"values": [ "*"] }, + "table": {"values": ["*"]}, + "column": {"values": ["ssn"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance:tax'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + ] + } + , + + { + "name": "database=finance:table=*:column=*", + "policyResources": { + "database": {"values": [ "finance"] }, + "table": {"values": ["*"]}, + "column": {"values": ["*"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance:tax'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + ] + } + , + + { + "name": "database=*:table=tax:column=*", + "policyResources": { + "database": {"values": [ "*"] }, + "table": {"values": ["tax"]}, + "column": {"values": ["*"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance:tax'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + ] + } + , + { + "name": "database=*:table=tax:column=ssn", + "policyResources": { + "database": {"values": [ "*"] }, + "table": {"values": ["tax"]}, + "column": {"values": ["ssn"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance:tax'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + ] + } + , + { + "name": "database=finance:table=*:column=ssn", + "policyResources": { + "database": {"values": [ "finance"] }, + "table": {"values": ["*"]}, + "column": {"values": ["ssn"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance:tax'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + ] + } + , + { + "name": "database=finance:table=tax:column=*", + "policyResources": { + "database": {"values": [ "finance"] }, + "table": {"values": ["tax"]}, + "column": {"values": ["*"]} + }, + "tests": [ + { + "name": "MATCH for exact 'finance.tax.ssn'", + "type": "exactMatch", + "resource": { + "elements": {"database": "finance", "table":"tax", "column":"ssn"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent with wildcards 'finance:tax'", + "type": "selfAndAllDescendants", + "resource": { + "elements": {"database": "finance","table": "tax"} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for child 'finance'", + "type": "descendantMatch", + "resource": { + "elements": {"database": "finance"} + }, + "evalContext": {}, + "result": true + } + ] + } + , + { + "name": "empty policyresources", + "policyResources": { + }, + "tests": [ + { + "name": "MATCH for exact ''", + "type": "exactMatch", + "resource": { + "elements": {} + }, + "evalContext": {}, + "result": true + } + , + { + "name": "MATCH for parent 'default'", + "type": "anyMatch", + "resource": { + "elements": {"database": "default"} + }, + "evalContext": {}, + "result": false + } + ] + } + ] +} diff --git a/agents-common/src/test/resources/resourcematcher/test_resourcematcher_path.json b/agents-common/src/test/resources/resourcematcher/test_resourcematcher_path.json index 97765f94d1..7af8c92d50 100644 --- a/agents-common/src/test/resources/resourcematcher/test_resourcematcher_path.json +++ b/agents-common/src/test/resources/resourcematcher/test_resourcematcher_path.json @@ -180,7 +180,7 @@ "isRecursive": true }, "tests": [ - {"name": "seemingly-correct-path", "input": "/home/", "result": false}, + {"name": "seemingly-correct-path", "input": "/home/", "result": true}, {"name": "correct-path", "input": "/home/a.txt", "result": true} ] }, @@ -265,7 +265,7 @@ }, "policyResource": {"values": ["/home/"], "isRecursive": true}, "tests": [ - {"name": "slash-at-end-path", "input": "/home/", "result": false}, + {"name": "slash-at-end-path", "input": "/home/", "result": true}, {"name": "correct-path", "input": "/home/a.txt", "result": true}, {"name": "incomplete-path", "input": "/home", "result": false} ] diff --git a/agents-common/src/test/resources/test_servicedef-normalize.json b/agents-common/src/test/resources/test_servicedef-normalize.json new file mode 100644 index 0000000000..52825f4f54 --- /dev/null +++ b/agents-common/src/test/resources/test_servicedef-normalize.json @@ -0,0 +1,478 @@ +{ + "serviceId": 4, + "serviceName": "dev_hive", + "serviceDef": { + "id": 3, + "isEnabled": true, + "name": "hive", + "displayName": "Hadoop SQL", + "implClass": "org.apache.ranger.services.hive.RangerServiceHive", + "label": "Hive Server2", + "description": "Hive Server2", + "options": { "enableDenyAndExceptionsInPolicies": "true" }, + "configs": [ + { "itemId": 1, "name": "username", "type": "string", "mandatory": true }, + { "itemId": 2, "name": "password", "type": "password", "mandatory": true }, + { "itemId": 3, "name": "jdbc.driverClassName", "type": "string", "mandatory": true, "defaultValue": "org.apache.hive.jdbc.HiveDriver" }, + { "itemId": 4, "name": "jdbc.url", "type": "string", "mandatory": true }, + { "itemId": 5, "name": "commonNameForCertificate", "type": "string", "mandatory": false }, + { "itemId": 6, "name": "ranger.plugin.audit.filters", "type": "string", "mandatory": false, "defaultValue": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'actions':['METADATA OPERATION'], 'isAudited': false}, {'users':['hive','hue'],'actions':['SHOW_ROLES'],'isAudited':false} ]" } + ], + "resources": [ + { "itemId": 1, "name": "database", "type": "string", "level": 10, "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "true" }, "label": "Hive Database", "description": "Hive Database", "accessTypeRestrictions": [], "isValidLeaf": true }, + { "itemId": 2, "name": "table", "type": "string", "level": 20, "parent": "database", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "true" }, "label": "Hive Table", "description": "Hive Table", "accessTypeRestrictions": [], "isValidLeaf": true }, + { "itemId": 3, "name": "udf", "type": "string", "level": 20, "parent": "database", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "true" }, "label": "Hive UDF", "description": "Hive UDF", "accessTypeRestrictions": [], "isValidLeaf": true }, + { "itemId": 4, "name": "column", "type": "string", "level": 30, "parent": "table", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": true, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "true" }, "label": "Hive Column", "description": "Hive Column", "accessTypeRestrictions": [], "isValidLeaf": true }, + { "itemId": 5, "name": "url", "type": "string", "level": 10, "mandatory": true, "lookupSupported": false, "recursiveSupported": true, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerURLResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "false" }, "label": "URL", "description": "URL", "accessTypeRestrictions": [], "isValidLeaf": true }, + { "itemId": 6, "name": "hiveservice", "type": "string", "level": 10, "mandatory": true, "lookupSupported": false, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "false" }, "label": "Hive Service", "description": "Hive Service", "accessTypeRestrictions": [], "isValidLeaf": true }, + { "itemId": 7, "name": "global", "type": "string", "level": 10, "mandatory": false, "lookupSupported": false, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "true", "ignoreCase": "false" }, "label": "Global", "description": "Global", "accessTypeRestrictions": [], "isValidLeaf": true } + ], + "accessTypes": [ + { "itemId": 1, "name": "select", "label": "select" }, + { "itemId": 2, "name": "update", "label": "update" }, + { "itemId": 3, "name": "create", "label": "Create" }, + { "itemId": 4, "name": "drop", "label": "Drop" }, + { "itemId": 5, "name": "alter", "label": "Alter" }, + { "itemId": 6, "name": "index", "label": "Index" }, + { "itemId": 7, "name": "lock", "label": "Lock" }, + { "itemId": 8, "name": "all", "label": "All", "impliedGrants": [ "select", "update", "create", "drop", "alter", "index", "lock", "read", "write", "repladmin", "serviceadmin", "refresh" ] }, + { "itemId": 9, "name": "read", "label": "Read" }, + { "itemId": 10, "name": "write", "label": "Write" }, + { "itemId": 11, "name": "repladmin", "label": "ReplAdmin" }, + { "itemId": 12, "name": "serviceadmin", "label": "Service Admin" }, + { "itemId": 13, "name": "tempudfadmin", "label": "Temporary UDF Admin" }, + { "itemId": 14, "name": "refresh", "label": "Refresh" } + ], + "policyConditions": [], + "contextEnrichers": [], + "enums": [], + "dataMaskDef": { + "maskTypes": [ + { "itemId": 1, "name": "MASK", "label": "Redact", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", "transformer": "mask({col})", "dataMaskOptions": {} }, + { "itemId": 2, "name": "MASK_SHOW_LAST_4", "label": "Partial mask: show last 4", "description": "Show last 4 characters; replace rest with 'x'", "transformer": "mask_show_last_n({col}, 4, 'x', 'x', 'x', -1, '1')", "dataMaskOptions": {} }, + { "itemId": 3, "name": "MASK_SHOW_FIRST_4", "label": "Partial mask: show first 4", "description": "Show first 4 characters; replace rest with 'x'", "transformer": "mask_show_first_n({col}, 4, 'x', 'x', 'x', -1, '1')", "dataMaskOptions": {} }, + { "itemId": 4, "name": "MASK_HASH", "label": "Hash", "description": "Hash the value", "transformer": "mask_hash({col})", "dataMaskOptions": {} }, + { "itemId": 5, "name": "MASK_NULL", "label": "Nullify", "description": "Replace with NULL", "dataMaskOptions": {} }, + { "itemId": 6, "name": "MASK_NONE", "label": "Unmasked (retain original value)", "description": "No masking", "dataMaskOptions": {} }, + { "itemId": 12, "name": "MASK_DATE_SHOW_YEAR", "label": "Date: show only year", "description": "Date: show only year", "transformer": "mask({col}, 'x', 'x', 'x', -1, '1', 1, 0, -1)", "dataMaskOptions": {} }, + { "itemId": 13, "name": "CUSTOM", "label": "Custom", "description": "Custom", "dataMaskOptions": {} } + ], + "accessTypes": [ + { "itemId": 1, "name": "select", "label": "select" } + ], + "resources": [ + { "itemId": 1, "name": "database", "type": "string", "level": 10, "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, "uiHint": "{ \"singleValue\":true }", "label": "Hive Database", "description": "Hive Database", "accessTypeRestrictions": [], "isValidLeaf": false }, + { "itemId": 2, "name": "table", "type": "string", "level": 20, "parent": "database", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, "uiHint": "{ \"singleValue\":true }", "label": "Hive Table", "description": "Hive Table", "accessTypeRestrictions": [], "isValidLeaf": false }, + { "itemId": 4, "name": "column", "type": "string", "level": 30, "parent": "table", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, "uiHint": "{ \"singleValue\":true }", "label": "Hive Column", "description": "Hive Column", "accessTypeRestrictions": [], "isValidLeaf": true + } + ] + }, + "rowFilterDef": { + "accessTypes": [ { "itemId": 1, "name": "select", "label": "select" } ], + "resources": [ + { "itemId": 1, "name": "database", "type": "string", "level": 10, "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, "uiHint": "{ \"singleValue\":true }", "label": "Hive Database", "description": "Hive Database", "accessTypeRestrictions": [], "isValidLeaf": false }, + { "itemId": 2, "name": "table", "type": "string", "level": 20, "parent": "database", "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "false", "ignoreCase": "true" }, "uiHint": "{ \"singleValue\":true }", "label": "Hive Table", "description": "Hive Table", "accessTypeRestrictions": [], "isValidLeaf": true } + ] + }, + "markerAccessTypes": [ + { "itemId": 20, "name": "_ALL", "label": "_ALL", "impliedGrants": [ "drop", "all", "select", "read", "update", "index", "refresh", "tempudfadmin", "serviceadmin", "create", "lock", "repladmin", "write", "alter" ] } + ] + }, + "policies": [], + "auditMode": "audit-default", + "tagPolicies": { + "serviceId": 2, + "serviceName": "dev_tag", + "policies": [], + "serviceDef": { + "id": 100, + "name": "tag", + "isEnabled": true, + "implClass": "org.apache.ranger.services.tag.RangerServiceTag", + "options": { "enableDenyAndExceptionsInPolicies": "true", "ui.pages": "tag-based-policies" }, + "configs": [ + { "itemId": 1, "name": "ranger.plugin.audit.filters", "type": "string", "level": 1, "mandatory": false, "label": "Ranger Default Audit Filters" } + ], + "resources": [ + { "itemId": 1, "name": "tag", "type": "string", "level": 1, "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "wildCard": "false", "ignoreCase": "false" }, "uiHint": "{ \"singleValue\":true }", "label": "TAG", "description": "TAG", "isValidLeaf": true } + ], + "accessTypes": [ + { "itemId": 14015, "name": "sqoop:READ", "label": "READ" }, + { "itemId": 14016, "name": "sqoop:WRITE", "label": "WRITE" }, + { "itemId": 12013, "name": "kylin:QUERY", "label": "QUERY" }, + { "itemId": 12014, "name": "kylin:OPERATION", "label": "OPERATION" }, + { "itemId": 12015, "name": "kylin:MANAGEMENT", "label": "MANAGEMENT" }, + { "itemId": 12016, "name": "kylin:ADMIN", "label": "ADMIN" }, + { "itemId": 16017, "name": "elasticsearch:all", "label": "all", "impliedGrants": [ "elasticsearch:read", "elasticsearch:read_cross_cluster", "elasticsearch:index", "elasticsearch:create", "elasticsearch:delete", "elasticsearch:write", "elasticsearch:delete_index", "elasticsearch:create_index", "elasticsearch:indices_put", "elasticsearch:indices_search_shards", "elasticsearch:indices_bulk", "elasticsearch:monitor", "elasticsearch:indices_index", "elasticsearch:manage", "elasticsearch:view_index_metadata" ] }, + { "itemId": 16018, "name": "elasticsearch:monitor", "label": "monitor" }, + { "itemId": 16019, "name": "elasticsearch:manage", "label": "manage", "impliedGrants": [ "elasticsearch:monitor" ] }, + { "itemId": 16020, "name": "elasticsearch:view_index_metadata", "label": "view_index_metadata", "impliedGrants": [ "elasticsearch:indices_search_shards" ] }, + { "itemId": 16021, "name": "elasticsearch:read", "label": "read" }, + { "itemId": 16022, "name": "elasticsearch:read_cross_cluster", "label": "read_cross_cluster", "impliedGrants": [ "elasticsearch:indices_search_shards" ] }, + { "itemId": 16023, "name": "elasticsearch:index", "label": "index", "impliedGrants": [ "elasticsearch:indices_put", "elasticsearch:indices_bulk", "elasticsearch:indices_index" ] }, + { "itemId": 16024, "name": "elasticsearch:create", "label": "create", "impliedGrants": [ "elasticsearch:indices_put", "elasticsearch:indices_bulk", "elasticsearch:indices_index" ] }, + { "itemId": 16025, "name": "elasticsearch:delete", "label": "delete", "impliedGrants": [ "elasticsearch:indices_bulk" ] }, + { "itemId": 16026, "name": "elasticsearch:write", "label": "write", "impliedGrants": [ "elasticsearch:indices_put" ] }, + { "itemId": 16027, "name": "elasticsearch:delete_index", "label": "delete_index" }, + { "itemId": 16028, "name": "elasticsearch:create_index", "label": "create_index" }, + { "itemId": 203204, "name": "trino:select", "label": "Select" }, + { "itemId": 203205, "name": "trino:insert", "label": "Insert" }, + { "itemId": 203206, "name": "trino:create", "label": "Create" }, + { "itemId": 203207, "name": "trino:drop", "label": "Drop" }, + { "itemId": 203208, "name": "trino:delete", "label": "Delete" }, + { "itemId": 203209, "name": "trino:use", "label": "Use" }, + { "itemId": 203210, "name": "trino:alter", "label": "Alter" }, + { "itemId": 203211, "name": "trino:grant", "label": "Grant" }, + { "itemId": 203212, "name": "trino:revoke", "label": "Revoke" }, + { "itemId": 203213, "name": "trino:show", "label": "Show" }, + { "itemId": 203214, "name": "trino:impersonate", "label": "Impersonate" }, + { "itemId": 203215, "name": "trino:all", "label": "All", "impliedGrants": [ "trino:execute", "trino:delete", "trino:drop", "trino:create", "trino:insert", "trino:select", "trino:revoke", "trino:impersonate", "trino:show", "trino:use", "trino:alter", "trino:grant" ] }, + { "itemId": 203216, "name": "trino:execute", "label": "execute" }, + { "itemId": 17018, "name": "presto:select", "label": "Select" }, + { "itemId": 17019, "name": "presto:insert", "label": "Insert" }, + { "itemId": 17020, "name": "presto:create", "label": "Create" }, + { "itemId": 17021, "name": "presto:drop", "label": "Drop" }, + { "itemId": 17022, "name": "presto:delete", "label": "Delete" }, + { "itemId": 17023, "name": "presto:use", "label": "Use" }, + { "itemId": 17024, "name": "presto:alter", "label": "Alter" }, + { "itemId": 17025, "name": "presto:grant", "label": "Grant" }, + { "itemId": 17026, "name": "presto:revoke", "label": "Revoke" }, + { "itemId": 17027, "name": "presto:show", "label": "Show" }, + { "itemId": 17028, "name": "presto:impersonate", "label": "Impersonate" }, + { "itemId": 17029, "name": "presto:all", "label": "All", "impliedGrants": [ "presto:use", "presto:alter", "presto:execute", "presto:impersonate", "presto:show", "presto:revoke", "presto:grant", "presto:select", "presto:insert", "presto:create", "presto:delete", "presto:drop" ] }, + { "itemId": 17030, "name": "presto:execute", "label": "execute" }, + { "itemId": 201209, "name": "ozone:all", "label": "All", "impliedGrants": [ "ozone:create", "ozone:read", "ozone:write", "ozone:list", "ozone:delete", "ozone:read_acl", "ozone:write_acl" ] }, + { "itemId": 201202, "name": "ozone:read", "label": "Read" }, + { "itemId": 201203, "name": "ozone:write", "label": "Write" }, + { "itemId": 201204, "name": "ozone:create", "label": "Create" }, + { "itemId": 201205, "name": "ozone:list", "label": "List" }, + { "itemId": 201206, "name": "ozone:delete", "label": "Delete" }, + { "itemId": 201207, "name": "ozone:read_acl", "label": "Read_ACL" }, + { "itemId": 201208, "name": "ozone:write_acl", "label": "Write_ACL" }, + { "itemId": 105106, "name": "kudu:select", "label": "SELECT", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105107, "name": "kudu:insert", "label": "INSERT", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105108, "name": "kudu:update", "label": "UPDATE", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105109, "name": "kudu:delete", "label": "DELETE", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105110, "name": "kudu:alter", "label": "ALTER", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105111, "name": "kudu:create", "label": "CREATE", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105112, "name": "kudu:drop", "label": "DROP", "impliedGrants": [ "kudu:metadata" ] }, + { "itemId": 105113, "name": "kudu:metadata", "label": "METADATA" }, + { "itemId": 105114, "name": "kudu:all", "label": "ALL", "impliedGrants": [ "kudu:insert", "kudu:update", "kudu:delete", "kudu:alter", "kudu:create", "kudu:drop", "kudu:metadata", "kudu:select" ] }, + { "itemId": 205206, "name": "nestedstructure:read", "label": "Read" }, + { "itemId": 205207, "name": "nestedstructure:write", "label": "Write" }, + { "itemId": 1002, "name": "hdfs:read", "label": "Read" }, + { "itemId": 1003, "name": "hdfs:write", "label": "Write" }, + { "itemId": 1004, "name": "hdfs:execute", "label": "Execute" }, + { "itemId": 2003, "name": "hbase:read", "label": "Read" }, + { "itemId": 2004, "name": "hbase:write", "label": "Write" }, + { "itemId": 2005, "name": "hbase:create", "label": "Create" }, + { "itemId": 2006, "name": "hbase:admin", "label": "Admin", "impliedGrants": [ "hbase:write", "hbase:create", "hbase:read" ] }, + { "itemId": 2007, "name": "hbase:execute", "label": "Execute" }, + { "itemId": 3004, "name": "hive:select", "label": "select" }, + { "itemId": 3005, "name": "hive:update", "label": "update" }, + { "itemId": 3006, "name": "hive:create", "label": "Create" }, + { "itemId": 3007, "name": "hive:drop", "label": "Drop" }, + { "itemId": 3008, "name": "hive:alter", "label": "Alter" }, + { "itemId": 3009, "name": "hive:index", "label": "Index" }, + { "itemId": 3010, "name": "hive:lock", "label": "Lock" }, + { "itemId": 3011, "name": "hive:all", "label": "All", "impliedGrants": [ "hive:read", "hive:select", "hive:update", "hive:create", "hive:drop", "hive:alter", "hive:index", "hive:lock", "hive:write", "hive:repladmin", "hive:serviceadmin", "hive:refresh" ] }, + { "itemId": 3012, "name": "hive:read", "label": "Read" }, + { "itemId": 3013, "name": "hive:write", "label": "Write" }, + { "itemId": 3014, "name": "hive:repladmin", "label": "ReplAdmin" }, + { "itemId": 3015, "name": "hive:serviceadmin", "label": "Service Admin" }, + { "itemId": 3016, "name": "hive:tempudfadmin", "label": "Temporary UDF Admin" }, + { "itemId": 3017, "name": "hive:refresh", "label": "Refresh" }, + { "itemId": 7008, "name": "kms:create", "label": "Create" }, + { "itemId": 7009, "name": "kms:delete", "label": "Delete" }, + { "itemId": 7010, "name": "kms:rollover", "label": "Rollover" }, + { "itemId": 7011, "name": "kms:setkeymaterial", "label": "Set Key Material" }, + { "itemId": 7012, "name": "kms:get", "label": "Get" }, + { "itemId": 7013, "name": "kms:getkeys", "label": "Get Keys" }, + { "itemId": 7014, "name": "kms:getmetadata", "label": "Get Metadata" }, + { "itemId": 7015, "name": "kms:generateeek", "label": "Generate EEK" }, + { "itemId": 7016, "name": "kms:decrypteek", "label": "Decrypt EEK" }, + { "itemId": 5006, "name": "knox:allow", "label": "Allow" }, + { "itemId": 6007, "name": "storm:submitTopology", "label": "Submit Topology", "impliedGrants": [ "storm:fileUpload", "storm:fileDownload" ] }, + { "itemId": 6008, "name": "storm:fileUpload", "label": "File Upload" }, + { "itemId": 6011, "name": "storm:fileDownload", "label": "File Download" }, + { "itemId": 6012, "name": "storm:killTopology", "label": "Kill Topology" }, + { "itemId": 6013, "name": "storm:rebalance", "label": "Rebalance" }, + { "itemId": 6014, "name": "storm:activate", "label": "Activate" }, + { "itemId": 6015, "name": "storm:deactivate", "label": "Deactivate" }, + { "itemId": 6016, "name": "storm:getTopologyConf", "label": "Get Topology Conf" }, + { "itemId": 6017, "name": "storm:getTopology", "label": "Get Topology" }, + { "itemId": 6018, "name": "storm:getUserTopology", "label": "Get User Topology" }, + { "itemId": 6019, "name": "storm:getTopologyInfo", "label": "Get Topology Info" }, + { "itemId": 6020, "name": "storm:uploadNewCredentials", "label": "Upload New Credential" }, + { "itemId": 4005, "name": "yarn:submit-app", "label": "submit-app" }, + { "itemId": 4006, "name": "yarn:admin-queue", "label": "admin-queue", "impliedGrants": [ "yarn:submit-app" ] }, + { "itemId": 9010, "name": "kafka:publish", "label": "Publish", "impliedGrants": [ "kafka:describe" ] }, + { "itemId": 9011, "name": "kafka:consume", "label": "Consume", "impliedGrants": [ "kafka:describe" ] }, + { "itemId": 9014, "name": "kafka:configure", "label": "Configure", "impliedGrants": [ "kafka:describe" ] }, + { "itemId": 9015, "name": "kafka:describe", "label": "Describe" }, + { "itemId": 9016, "name": "kafka:kafka_admin", "label": "Kafka Admin", "impliedGrants": [ "kafka:consume", "kafka:configure", "kafka:alter_configs", "kafka:describe_configs", "kafka:create", "kafka:describe", "kafka:alter", "kafka:idempotent_write", "kafka:cluster_action", "kafka:delete", "kafka:publish" ] }, + { "itemId": 9017, "name": "kafka:create", "label": "Create" }, + { "itemId": 9018, "name": "kafka:delete", "label": "Delete", "impliedGrants": [ "kafka:describe" ] }, + { "itemId": 9019, "name": "kafka:idempotent_write", "label": "Idempotent Write" }, + { "itemId": 9020, "name": "kafka:describe_configs", "label": "Describe Configs" }, + { "itemId": 9021, "name": "kafka:alter_configs", "label": "Alter Configs", "impliedGrants": [ "kafka:describe_configs" ] }, + { "itemId": 9022, "name": "kafka:cluster_action", "label": "Cluster Action" }, + { "itemId": 9023, "name": "kafka:alter", "label": "Alter" }, + { "itemId": 8108, "name": "solr:query", "label": "Query" }, + { "itemId": 8208, "name": "solr:update", "label": "Update" }, + { "itemId": 202203, "name": "schema-registry:create", "label": "Create" }, + { "itemId": 202204, "name": "schema-registry:read", "label": "Read" }, + { "itemId": 202205, "name": "schema-registry:update", "label": "Update" }, + { "itemId": 202206, "name": "schema-registry:delete", "label": "Delete" }, + { "itemId": 10110, "name": "nifi:READ", "label": "Read" }, + { "itemId": 10210, "name": "nifi:WRITE", "label": "Write" }, + { "itemId": 13113, "name": "nifi-registry:READ", "label": "Read" }, + { "itemId": 13213, "name": "nifi-registry:WRITE", "label": "Write" }, + { "itemId": 13313, "name": "nifi-registry:DELETE", "label": "Delete" }, + { "itemId": 15016, "name": "atlas:type-create", "label": "Create Type", "impliedGrants": [ "atlas:type-read" ] }, + { "itemId": 15017, "name": "atlas:type-update", "label": "Update Type", "impliedGrants": [ "atlas:type-read" ] }, + { "itemId": 15018, "name": "atlas:type-delete", "label": "Delete Type", "impliedGrants": [ "atlas:type-read" ] }, + { "itemId": 15019, "name": "atlas:entity-read", "label": "Read Entity" }, + { "itemId": 15020, "name": "atlas:entity-create", "label": "Create Entity" }, + { "itemId": 15021, "name": "atlas:entity-update", "label": "Update Entity" }, + { "itemId": 15022, "name": "atlas:entity-delete", "label": "Delete Entity" }, + { "itemId": 15023, "name": "atlas:entity-add-classification", "label": "Add Classification" }, + { "itemId": 15024, "name": "atlas:entity-update-classification", "label": "Update Classification" }, + { "itemId": 15025, "name": "atlas:entity-remove-classification", "label": "Remove Classification" }, + { "itemId": 15026, "name": "atlas:admin-export", "label": "Admin Export" }, + { "itemId": 15027, "name": "atlas:admin-import", "label": "Admin Import" }, + { "itemId": 15028, "name": "atlas:add-relationship", "label": "Add Relationship" }, + { "itemId": 15029, "name": "atlas:update-relationship", "label": "Update Relationship" }, + { "itemId": 15030, "name": "atlas:remove-relationship", "label": "Remove Relationship" }, + { "itemId": 15031, "name": "atlas:admin-purge", "label": "Admin Purge" }, + { "itemId": 15032, "name": "atlas:entity-add-label", "label": "Add Label" }, + { "itemId": 15033, "name": "atlas:entity-remove-label", "label": "Remove Label" }, + { "itemId": 15034, "name": "atlas:entity-update-business-metadata", "label": "Update Business Metadata" }, + { "itemId": 15035, "name": "atlas:type-read", "label": "Read Type" }, + { "itemId": 15036, "name": "atlas:admin-audits", "label": "Admin Audits" } + ], + "policyConditions": [], + "contextEnrichers": [], + "dataMaskDef": { + "maskTypes": [ + { "itemId": 203204, "name": "trino:MASK", "label": "Redact", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", "transformer": "cast(regexp_replace(regexp_replace(regexp_replace({col},'([A-Z])', 'X'),'([a-z])','x'),'([0-9])','0') as {type})" + }, + { "itemId": 203205, "name": "trino:MASK_SHOW_LAST_4", "label": "Partial mask: show last 4", "description": "Show last 4 characters; replace rest with 'X'", "transformer": "cast(regexp_replace({col}, '(.*)(.{4}$)', x -> regexp_replace(x[1], '.', 'X') || x[2]) as {type})" }, + { "itemId": 203206, "name": "trino:MASK_SHOW_FIRST_4", "label": "Partial mask: show first 4", "description": "Show first 4 characters; replace rest with 'x'", "transformer": "cast(regexp_replace({col}, '(^.{4})(.*)', x -> x[1] || regexp_replace(x[2], '.', 'X')) as {type})" }, + { "itemId": 203207, "name": "trino:MASK_HASH", "label": "Hash", "description": "Hash the value of a varchar with sha256", "transformer": "cast(to_hex(sha256(to_utf8({col}))) as {type})" }, + { "itemId": 203208, "name": "trino:MASK_NULL", "label": "Nullify", "description": "Replace with NULL" }, + { "itemId": 203209, "name": "trino:MASK_NONE", "label": "Unmasked (retain original value)", "description": "No masking" }, + { "itemId": 203215, "name": "trino:MASK_DATE_SHOW_YEAR", "label": "Date: show only year", "description": "Date: show only year", "transformer": "date_trunc('year', {col})" }, + { "itemId": 203216, "name": "trino:CUSTOM", "label": "Custom", "description": "Custom" }, + { "itemId": 17018, "name": "presto:MASK", "label": "Redact", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", "transformer": "cast(regexp_replace(regexp_replace(regexp_replace({col},'([A-Z])', 'X'),'([a-z])','x'),'([0-9])','0') as {type})" }, + { "itemId": 17019, "name": "presto:MASK_SHOW_LAST_4", "label": "Partial mask: show last 4", "description": "Show last 4 characters; replace rest with 'X'", "transformer": "cast(regexp_replace({col}, '(.*)(.{4}$)', x -> regexp_replace(x[1], '.', 'X') || x[2]) as {type})" }, + { "itemId": 17020, "name": "presto:MASK_SHOW_FIRST_4", "label": "Partial mask: show first 4", "description": "Show first 4 characters; replace rest with 'x'", "transformer": "cast(regexp_replace({col}, '(^.{4})(.*)', x -> x[1] || regexp_replace(x[2], '.', 'X')) as {type})" }, + { "itemId": 17021, "name": "presto:MASK_HASH", "label": "Hash", "description": "Hash the value of a varchar with sha256", "transformer": "cast(to_hex(sha256(to_utf8({col}))) as {type})" }, + { "itemId": 17022, "name": "presto:MASK_NULL", "label": "Nullify", "description": "Replace with NULL" }, + { "itemId": 17023, "name": "presto:MASK_NONE", "label": "Unmasked (retain original value)", "description": "No masking" }, + { "itemId": 17029, "name": "presto:MASK_DATE_SHOW_YEAR", "label": "Date: show only year", "description": "Date: show only year", "transformer": "date_trunc('year', {col})" }, + { "itemId": 17030, "name": "presto:CUSTOM", "label": "Custom", "description": "Custom" }, + { "itemId": 205206, "name": "nestedstructure:MASK", "label": "Redact", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", "transformer": "mask({field})" }, + { "itemId": 205207, "name": "nestedstructure:MASK_SHOW_LAST_4", "label": "Partial mask: show last 4", "description": "Show last 4 characters; replace rest with 'x'", "transformer": "mask_show_last_n({field}, 4, 'x', 'x', 'x', -1, '1')" }, + { "itemId": 205208, "name": "nestedstructure:MASK_SHOW_FIRST_4", "label": "Partial mask: show first 4", "description": "Show first 4 characters; replace rest with 'x'", "transformer": "mask_show_first_n({field}, 4, 'x', 'x', 'x', -1, '1')" }, + { "itemId": 205209, "name": "nestedstructure:MASK_HASH", "label": "Hash", "description": "Hash the value", "transformer": "mask_hash({field})" }, + { "itemId": 205210, "name": "nestedstructure:MASK_NULL", "label": "Nullify", "description": "Replace with NULL" }, + { "itemId": 205211, "name": "nestedstructure:MASK_NONE", "label": "Unmasked (retain original value)", "description": "No masking" }, + { "itemId": 205217, "name": "nestedstructure:MASK_DATE_SHOW_YEAR", "label": "Date: show only year", "description": "Date: show only year", "transformer": "mask({field}, 'x', 'x', 'x', -1, '1', 1, 0, -1)" }, + { "itemId": 205218, "name": "nestedstructure:CUSTOM", "label": "Custom", "description": "Custom" }, + { "itemId": 3004, "name": "hive:MASK", "label": "Redact", "description": "Replace lowercase with 'x', uppercase with 'X', digits with '0'", "transformer": "mask({col})" }, + { "itemId": 3005, "name": "hive:MASK_SHOW_LAST_4", "label": "Partial mask: show last 4", "description": "Show last 4 characters; replace rest with 'x'", "transformer": "mask_show_last_n({col}, 4, 'x', 'x', 'x', -1, '1')" }, + { "itemId": 3006, "name": "hive:MASK_SHOW_FIRST_4", "label": "Partial mask: show first 4", "description": "Show first 4 characters; replace rest with 'x'", "transformer": "mask_show_first_n({col}, 4, 'x', 'x', 'x', -1, '1')" }, + { "itemId": 3007, "name": "hive:MASK_HASH", "label": "Hash", "description": "Hash the value", "transformer": "mask_hash({col})" }, + { "itemId": 3008, "name": "hive:MASK_NULL", "label": "Nullify", "description": "Replace with NULL" }, + { "itemId": 3009, "name": "hive:MASK_NONE", "label": "Unmasked (retain original value)", "description": "No masking" }, + { "itemId": 3015, "name": "hive:MASK_DATE_SHOW_YEAR", "label": "Date: show only year", "description": "Date: show only year", "transformer": "mask({col}, 'x', 'x', 'x', -1, '1', 1, 0, -1)" }, + { "itemId": 3016, "name": "hive:CUSTOM", "label": "Custom", "description": "Custom" } + ], + "accessTypes": [ + { "itemId": 203204, "name": "trino:select", "label": "Select" }, + { "itemId": 17018, "name": "presto:select", "label": "Select" }, + { "itemId": 205206, "name": "nestedstructure:read", "label": "Read" }, + { "itemId": 3004, "name": "hive:select", "label": "select" } + ], + "resources": [ + { "itemId": 1, "name": "tag", "type": "string", "level": 1, "mandatory": true, "lookupSupported": true, "recursiveSupported": false, "excludesSupported": false, "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher", "matcherOptions": { "__isValidLeaf": "true", "wildCard": "false", "__accessTypeRestrictions": "[]", "ignoreCase": "false" }, "uiHint": "{ \"singleValue\":true }", "label": "TAG", "description": "TAG", "isValidLeaf": true } + ] + }, + "rowFilterDef": {}, + "markerAccessTypes": [ + { + "itemId": 205213, + "name": "_ALL", + "label": "_ALL", + "impliedGrants": [ + "ozone:write_acl", + "kms:get", + "atlas:type-delete", + "atlas:entity-create", + "hbase:execute", + "storm:fileDownload", + "kms:getkeys", + "storm:getTopologyConf", + "ozone:create", + "hive:tempudfadmin", + "kafka:idempotent_write", + "hive:write", + "trino:use", + "trino:grant", + "hdfs:execute", + "elasticsearch:indices_index", + "atlas:admin-export", + "kafka:configure", + "atlas:entity-read", + "presto:alter", + "atlas:entity-update-classification", + "trino:select", + "presto:execute", + "kafka:describe", + "atlas:admin-purge", + "kafka:consume", + "trino:delete", + "atlas:add-relationship", + "elasticsearch:indices_bulk", + "trino:drop", + "kudu:select", + "presto:use", + "hive:serviceadmin", + "elasticsearch:create_index", + "hive:index", + "nestedstructure:read", + "kms:setkeymaterial", + "kylin:OPERATION", + "schema-registry:create", + "presto:impersonate", + "storm:deactivate", + "elasticsearch:index", + "kafka:alter", + "atlas:entity-add-classification", + "atlas:entity-remove-classification", + "atlas:entity-add-label", + "schema-registry:update", + "kafka:kafka_admin", + "presto:drop", + "hive:refresh", + "atlas:entity-delete", + "presto:all", + "atlas:entity-update", + "elasticsearch:monitor", + "trino:insert", + "kudu:update", + "schema-registry:read", + "atlas:admin-audits", + "elasticsearch:delete_index", + "presto:show", + "ozone:write", + "hive:read", + "ozone:all", + "atlas:entity-remove-label", + "trino:all", + "presto:grant", + "kms:rollover", + "elasticsearch:view_index_metadata", + "presto:create", + "kafka:publish", + "kafka:create", + "trino:execute", + "hive:all", + "kms:delete", + "kylin:ADMIN", + "atlas:remove-relationship", + "kylin:MANAGEMENT", + "storm:killTopology", + "elasticsearch:indices_put", + "kudu:create", + "hive:update", + "sqoop:READ", + "presto:delete", + "atlas:entity-update-business-metadata", + "yarn:submit-app", + "solr:update", + "trino:create", + "hdfs:read", + "elasticsearch:write", + "hive:alter", + "kafka:alter_configs", + "storm:getUserTopology", + "storm:uploadNewCredentials", + "presto:insert", + "atlas:type-update", + "storm:submitTopology", + "storm:activate", + "kms:generateeek", + "kafka:cluster_action", + "trino:impersonate", + "hdfs:write", + "hbase:admin", + "elasticsearch:create", + "atlas:admin-import", + "kms:decrypteek", + "solr:query", + "atlas:type-create", + "kudu:all", + "nifi:WRITE", + "sqoop:WRITE", + "storm:getTopologyInfo", + "storm:getTopology", + "hbase:read", + "kms:getmetadata", + "storm:fileUpload", + "trino:alter", + "hive:drop", + "hive:create", + "yarn:admin-queue", + "kafka:describe_configs", + "ozone:delete", + "nifi-registry:READ", + "hive:repladmin", + "kudu:metadata", + "schema-registry:delete", + "nifi-registry:DELETE", + "kms:create", + "ozone:read", + "trino:show", + "ozone:read_acl", + "nifi:READ", + "hive:select", + "kudu:delete", + "atlas:type-read", + "kafka:delete", + "kylin:QUERY", + "elasticsearch:indices_search_shards", + "elasticsearch:read_cross_cluster", + "elasticsearch:manage", + "presto:select", + "ozone:list", + "nestedstructure:write", + "atlas:update-relationship", + "trino:revoke", + "nifi-registry:WRITE", + "storm:rebalance", + "hive:lock", + "presto:revoke", + "knox:allow", + "hbase:create", + "elasticsearch:delete", + "kudu:drop", + "hbase:write", + "elasticsearch:read", + "kudu:alter", + "kudu:insert", + "elasticsearch:all" + ] + } + ] + }, + "auditMode": "audit-default", + "serviceConfig": { + "ranger.plugin.audit.filters": "[ {'accessResult': 'DENIED', 'isAudited': true} ]" + } + }, + "serviceConfig": { + "ranger.plugin.audit.filters": "[ {'accessResult': 'DENIED', 'isAudited': true}, {'actions':['METADATA OPERATION'], 'isAudited': false}, {'users':['hive','hue'],'actions':['SHOW_ROLES'],'isAudited':false} ]" + } +} diff --git a/agents-cred/pom.xml b/agents-cred/pom.xml index cd1b8f3e3a..0d2850a39f 100644 --- a/agents-cred/pom.xml +++ b/agents-cred/pom.xml @@ -27,28 +27,102 @@ org.apache.ranger ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT .. - commons-logging - commons-logging - ${commons.logging.version} + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt.version} + + + net.minidev + json-smart + ${jsonsmart.version} org.apache.hadoop hadoop-common ${hadoop.version} + + + net.minidev + json-smart + + + io.netty + netty-handler + + + io.netty + netty-transport-native-epoll + + + org.apache.commons + commons-text + + + org.apache.commons + commons-configuration2 + + + log4j + * + + + org.slf4j + * + + + org.apache.commons + commons-compress + + + org.apache.zookeeper + zookeeper + + + + + org.apache.commons + commons-text + ${commons.text.version} + + + org.apache.commons + commons-configuration2 + ${commons.configuration.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.apache.commons + commons-compress + ${commons.compress.version} + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test - log4j - log4j - ${log4j.version} + org.junit.vintage + junit-vintage-engine + ${junit.jupiter.version} + test - junit - junit + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test diff --git a/agents-cred/src/main/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProvider.java b/agents-cred/src/main/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProvider.java index 07bae00abf..ac7ccd23c8 100644 --- a/agents-cred/src/main/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProvider.java +++ b/agents-cred/src/main/java/org/apache/ranger/authorization/hadoop/utils/RangerCredentialProvider.java @@ -24,13 +24,13 @@ import org.apache.hadoop.security.alias.CredentialProvider; import org.apache.hadoop.security.alias.CredentialProviderFactory; import org.apache.hadoop.conf.Configuration; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public final class RangerCredentialProvider { - private static final Log LOG = LogFactory.getLog(RangerCredentialProvider.class); + private static final Logger LOG = LoggerFactory.getLogger(RangerCredentialProvider.class); private static final RangerCredentialProvider CRED_PROVIDER = new RangerCredentialProvider(); diff --git a/agents-cred/src/test/resources/logback.xml b/agents-cred/src/test/resources/logback.xml new file mode 100644 index 0000000000..4e991c3f2b --- /dev/null +++ b/agents-cred/src/test/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + System.out + + %d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n + + + INFO + + + + + + diff --git a/agents-installer/pom.xml b/agents-installer/pom.xml index cbda5dd01f..d9340765d9 100644 --- a/agents-installer/pom.xml +++ b/agents-installer/pom.xml @@ -24,7 +24,7 @@ org.apache.ranger ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT .. @@ -33,5 +33,10 @@ commons-cli ${commons.cli.version} + + org.apache.commons + commons-compress + ${commons.compress.version} + diff --git a/build_ranger_using_docker.sh b/build_ranger_using_docker.sh index cf8cf13292..9aadfb5f72 100755 --- a/build_ranger_using_docker.sh +++ b/build_ranger_using_docker.sh @@ -67,23 +67,33 @@ RUN yum install -y wget RUN yum install -y git RUN yum install -y gcc RUN yum install -y bzip2 fontconfig +RUN yum install -y diffutils +RUN yum install -y python3 + +RUN ln -s /usr/bin/python3 /usr/bin/python + +RUN yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel +#ENV JAVA_HOME /etc/alternatives/jre +ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk/ +ENV PATH $JAVA_HOME/bin:$PATH #Download and install JDK8 from AWS s3's docker-assets -RUN wget https://s3.eu-central-1.amazonaws.com/docker-assets/dist/jdk-8u101-linux-x64.rpm -RUN rpm -i jdk-8u101-linux-x64.rpm +#RUN wget https://s3.eu-central-1.amazonaws.com/docker-assets/dist/jdk-8u101-linux-x64.rpm +#RUN rpm -i jdk-8u101-linux-x64.rpm +#ENV JAVA_HOME /usr/java/latest +#ENV PATH $JAVA_HOME/bin:$PATH -ENV JAVA_HOME /usr/java/latest -ENV PATH $JAVA_HOME/bin:$PATH +ADD https://www.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz.sha512 /tools +ADD http://www-us.apache.org/dist/maven/maven-3/3.6.3/binaries/apache-maven-3.6.3-bin.tar.gz /tools +RUN sha512sum apache-maven-3.6.3-bin.tar.gz | cut -f 1 -d " " > tmp.sha1 -ADD https://www.apache.org/dist/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz.sha512 /tools -ADD http://www-us.apache.org/dist/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz /tools -RUN sha512sum apache-maven-3.5.4-bin.tar.gz | cut -f 1 -d " " > tmp.sha1 +RUN cat apache-maven-3.6.3-bin.tar.gz.sha512 | cut -f 1 -d " " > tmp.sha1.download -RUN diff -w tmp.sha1 apache-maven-3.5.4-bin.tar.gz.sha512 +RUN diff -w tmp.sha1 tmp.sha1.download -RUN tar xfz apache-maven-3.5.4-bin.tar.gz -RUN ln -sf /tools/apache-maven-3.5.4 /tools/maven +RUN tar xfz apache-maven-3.6.3-bin.tar.gz +RUN ln -sf /tools/apache-maven-3.6.3 /tools/maven ENV PATH /tools/maven/bin:$PATH ENV MAVEN_OPTS "-Xmx2048m -XX:MaxPermSize=512m" diff --git a/credentialbuilder/pom.xml b/credentialbuilder/pom.xml index 4055a47d88..e54c933918 100644 --- a/credentialbuilder/pom.xml +++ b/credentialbuilder/pom.xml @@ -24,7 +24,7 @@ org.apache.ranger ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT .. @@ -44,38 +44,121 @@ ${commons.configuration.version} - commons-lang - commons-lang - ${commons.lang.version} - - - commons-logging - commons-logging - ${commons.logging.version} + org.apache.commons + commons-lang3 + ${commons.lang3.version} - com.google.guava - guava - ${google.guava.version} + org.slf4j + slf4j-api + ${slf4j.version} - junit - junit + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt.version} - org.slf4j - slf4j-api - ${slf4j-api.version} + net.minidev + json-smart + ${jsonsmart.version} org.apache.hadoop hadoop-common ${hadoop.version} + + + org.apache.commons + commons-compress + + + org.apache.commons + commons-lang3 + + + io.netty + netty-handler + + + io.netty + netty-transport-native-epoll + + + org.apache.commons + commons-text + + + org.apache.commons + commons-configuration2 + + + log4j + * + + + org.slf4j + * + + + org.apache.zookeeper + zookeeper + + + + + org.apache.commons + commons-text + ${commons.text.version} org.apache.hadoop hadoop-auth ${hadoop.version} + + + log4j + * + + + org.slf4j + * + + + org.apache.zookeeper + zookeeper + + + + + org.apache.commons + commons-compress + ${commons.compress.version} + + + org.codehaus.woodstox + stax2-api + ${codehaus.woodstox.stax2api.version} + + + com.fasterxml.woodstox + woodstox-core + ${fasterxml.woodstox.version} + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test + diff --git a/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/CredentialReader.java b/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/CredentialReader.java index 42497e357f..679b5721d4 100644 --- a/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/CredentialReader.java +++ b/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/CredentialReader.java @@ -21,14 +21,15 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang3.StringUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.alias.CredentialProvider; import org.apache.hadoop.security.alias.CredentialProviderFactory; import org.apache.hadoop.security.alias.JavaKeyStoreProvider; public class CredentialReader { - - public static String getDecryptedString(String CrendentialProviderPath,String alias) { + + public static String getDecryptedString(String CrendentialProviderPath,String alias, String storeType) { String credential=null; try{ if(CrendentialProviderPath==null || alias==null){ @@ -39,17 +40,31 @@ public static String getDecryptedString(String CrendentialProviderPath,String al String crendentialProviderPrefixJceks=JavaKeyStoreProvider.SCHEME_NAME + "://file"; String crendentialProviderPrefixLocalJceks="localjceks://file"; crendentialProviderPrefixJceks=crendentialProviderPrefixJceks.toLowerCase(); + + String crendentialProviderPrefixBcfks = "bcfks" + "://file"; + String crendentialProviderPrefixLocalBcfks= "localbcfks" + "://file"; + crendentialProviderPrefixBcfks=crendentialProviderPrefixBcfks.toLowerCase(); + crendentialProviderPrefixLocalBcfks=crendentialProviderPrefixLocalBcfks.toLowerCase(); + CrendentialProviderPath=CrendentialProviderPath.trim(); alias=alias.trim(); - if(CrendentialProviderPath.toLowerCase().startsWith(crendentialProviderPrefixJceks) || CrendentialProviderPath.toLowerCase().startsWith(crendentialProviderPrefixLocalJceks)){ + if(CrendentialProviderPath.toLowerCase().startsWith(crendentialProviderPrefixJceks) || + CrendentialProviderPath.toLowerCase().startsWith(crendentialProviderPrefixLocalJceks) || + CrendentialProviderPath.toLowerCase().startsWith(crendentialProviderPrefixBcfks) || + CrendentialProviderPath.toLowerCase().startsWith(crendentialProviderPrefixLocalBcfks)){ conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, //UserProvider.SCHEME_NAME + ":///," + CrendentialProviderPath); }else{ if(CrendentialProviderPath.startsWith("/")){ - conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, - //UserProvider.SCHEME_NAME + ":///," + - JavaKeyStoreProvider.SCHEME_NAME + "://file" + CrendentialProviderPath); + if(StringUtils.equalsIgnoreCase(storeType, "bcfks")) { + conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, CrendentialProviderPath); + } else { + conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, + //UserProvider.SCHEME_NAME + ":///," + + JavaKeyStoreProvider.SCHEME_NAME + "://file" + CrendentialProviderPath); + } + }else{ conf.set(CredentialProviderFactory.CREDENTIAL_PROVIDER_PATH, //UserProvider.SCHEME_NAME + ":///," + @@ -64,7 +79,7 @@ public static String getDecryptedString(String CrendentialProviderPath,String al aliasesList=provider.getAliases(); if(aliasesList!=null && aliasesList.contains(alias.toLowerCase())){ credEntry=null; - credEntry= provider.getCredentialEntry(alias); + credEntry= provider.getCredentialEntry(alias.toLowerCase()); pass = credEntry.getCredential(); if(pass!=null && pass.length>0){ credential=String.valueOf(pass); @@ -78,4 +93,4 @@ public static String getDecryptedString(String CrendentialProviderPath,String al } return credential; } -} \ No newline at end of file +} diff --git a/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/buildks.java b/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/buildks.java index cb391cc005..4ee7080cfb 100644 --- a/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/buildks.java +++ b/credentialbuilder/src/main/java/org/apache/ranger/credentialapi/buildks.java @@ -23,7 +23,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Field; - +import java.security.KeyStore; +import java.util.Arrays; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.alias.CredentialShell; import org.apache.hadoop.util.GenericOptionsParser; @@ -64,8 +65,10 @@ public int createCredential(String args[]){ String providerOption=null; String providerPath=null; String tempCredential=null; + String storeTypeOption="storeType"; + String storeType= KeyStore.getDefaultType(); try{ - if(args!=null && args.length==6) + if(args!=null && (args.length == 6 || args.length==8)) { command=args[0]; alias=args[1]; @@ -73,11 +76,15 @@ public int createCredential(String args[]){ credential=args[3]; providerOption=args[4]; providerPath=args[5]; - if(!isValidCreateCommand(command,alias,valueOption,credential,providerOption,providerPath)){ + if (args.length == 8) { + storeTypeOption = args[6]; + storeType = args[7]; + } + if(!isValidCreateCommand(command,alias,valueOption,credential,providerOption,providerPath,storeTypeOption,storeType)){ return returnCode; } deleteInvalidKeystore(providerPath); - tempCredential=CredentialReader.getDecryptedString(providerPath, alias); + tempCredential=CredentialReader.getDecryptedString(providerPath, alias, storeType); }else{ return returnCode; } @@ -115,8 +122,10 @@ public int createKeyStore(String args[]){ String valueOption=null; String credential=null; String providerOption=null; - String providerPath=null; - if(args!=null && args.length==6) + String providerPath=null; + String storeTypeOption="storeType"; + String storeType=KeyStore.getDefaultType(); + if(args!=null && (args.length == 6 || args.length==8)) { command=args[0]; alias=args[1]; @@ -124,7 +133,11 @@ public int createKeyStore(String args[]){ credential=args[3]; providerOption=args[4]; providerPath=args[5]; - if(!isValidCreateCommand(command,alias,valueOption,credential,providerOption,providerPath)){ + if (args.length == 8) { + storeTypeOption = args[6]; + storeType = args[7]; + } + if(!isValidCreateCommand(command,alias,valueOption,credential,providerOption,providerPath,storeTypeOption,storeType)){ return returnCode; } displayCommand(args); @@ -139,16 +152,16 @@ public int createKeyStore(String args[]){ //set the configuration back, so that Tool can configure itself cs.setConf(conf); //get valid and remaining argument - String[] toolArgs = parser.getRemainingArgs(); + String[] toolArgs = parser.getRemainingArgs(); //execute command in CredentialShell // int i = 0; // for(String s : toolArgs) { // System.out.println("TooArgs [" + i + "] = [" + s + "]"); // i++; // } - returnCode= cs.run(toolArgs); + String[] finalArgs = Arrays.copyOfRange(toolArgs, 0, 6); + returnCode= cs.run(finalArgs); //if response code is zero then success else failure - //System.out.println("Response Code:"+returnCode); }catch(IOException ex){ ex.printStackTrace(); } catch(Exception ex){ @@ -165,7 +178,9 @@ public int createCredentialFromUserInput(){ String valueOption=null; String credential=null; String providerOption=null; - String providerPath=null; + String providerPath=null; + String storeTypeOption=null; + String storeType=null; //below code can ask user to input if command line input fails System.out.println("Enter Alias Name:"); BufferedReader bufferRead = new BufferedReader(new InputStreamReader(System.in)); @@ -174,7 +189,9 @@ public int createCredentialFromUserInput(){ credential = bufferRead.readLine(); System.out.println("Enter .jceks output file name with path:"); providerPath = bufferRead.readLine(); - if(providerPath!=null && !providerPath.trim().isEmpty() && !providerPath.startsWith("localjceks://file")&&!providerPath.startsWith("jceks://file")) + if(providerPath!=null && !providerPath.trim().isEmpty() && + (!providerPath.startsWith("localjceks://file")&&!providerPath.startsWith("jceks://file") && + !providerPath.startsWith("localbcfks://file")&&!providerPath.startsWith("bcfks://file"))) { if(providerPath.startsWith("/")){ providerPath="jceks://file"+providerPath; @@ -185,7 +202,7 @@ public int createCredentialFromUserInput(){ command="create"; valueOption="-value"; providerOption="-provider"; - if(!isValidCreateCommand(command,alias,valueOption,credential,providerOption,providerPath)){ + if(!isValidCreateCommand(command,alias,valueOption,credential,providerOption,providerPath,storeTypeOption,storeType)){ return returnCode; } args=new String[6]; @@ -220,13 +237,14 @@ public int listCredential(String args[]){ String command=null; String providerOption=null; String providerPath=null; + String storeType = KeyStore.getDefaultType(); try{ if(args!=null && args.length==3) { command=args[0]; providerOption=args[1]; providerPath=args[2]; - if(!isValidListCommand(command,providerOption,providerPath)){ + if(!isValidListCommand(command,providerOption,providerPath, storeType)){ return returnCode; } //display command which need to be executed or entered @@ -285,7 +303,8 @@ public int deleteCredential(String args[], boolean isSilentMode){ //get valid and remaining argument String[] toolArgs = parser.getRemainingArgs(); //execute command in CredentialShell - returnCode= cs.run(toolArgs); +// String[] finalArgs = Arrays.copyOfRange(toolArgs, 0, 6); + returnCode= cs.run(toolArgs); //if response code is zero then success else failure //System.out.println("Response Code:"+returnCode); }catch(IOException ex){ @@ -296,7 +315,9 @@ public int deleteCredential(String args[], boolean isSilentMode){ return returnCode; } - public static boolean isValidCreateCommand(String command,String alias,String valueOption,String credential,String providerOption,String providerPath) + public static boolean isValidCreateCommand(String command,String alias,String valueOption,String credential, + String providerOption,String providerPath, String storeTypeOption, + String storeType) { boolean isValid=true; try{ @@ -304,68 +325,63 @@ public static boolean isValidCreateCommand(String command,String alias,String va { System.out.println("Invalid create phrase in credential creation command!!"); System.out.println("Expected:'create' Found:'"+command+"'"); - displaySyntax("create"); + displaySyntax("create", storeType); return false; } if(alias==null || "".equalsIgnoreCase(alias.trim())) { System.out.println("Invalid alias name phrase in credential creation command!!"); System.out.println("Found:'"+alias+"'"); - displaySyntax("create"); - return false; - } - if(valueOption==null || !"-value".equalsIgnoreCase(valueOption.trim())) - { - System.out.println("Invalid value option switch in credential creation command!!"); - System.out.println("Expected:'-value' Found:'"+valueOption+"'"); - displaySyntax("create"); + displaySyntax("create", storeType); return false; } if(valueOption==null || !"-value".equalsIgnoreCase(valueOption.trim())) { System.out.println("Invalid value option in credential creation command!!"); System.out.println("Expected:'-value' Found:'"+valueOption+"'"); - displaySyntax("create"); + displaySyntax("create", storeType); return false; } if(credential==null) { System.out.println("Invalid credential value in credential creation command!!"); System.out.println("Found:"+credential); - displaySyntax("create"); + displaySyntax("create", storeType); return false; } if(providerOption==null || !"-provider".equalsIgnoreCase(providerOption.trim())) { System.out.println("Invalid provider option in credential creation command!!"); System.out.println("Expected:'-provider' Found:'"+providerOption+"'"); - displaySyntax("create"); + displaySyntax("create", storeType); return false; } - if(providerPath==null || "".equalsIgnoreCase(providerPath.trim()) || (!providerPath.startsWith("localjceks://") && !providerPath.startsWith("jceks://"))) + if(providerPath==null || "".equalsIgnoreCase(providerPath.trim()) || + ((!providerPath.startsWith("localjceks://") && !providerPath.startsWith("jceks://")) && + (!providerPath.startsWith("localbcfks://") && !providerPath.startsWith("bcfks://")))) { System.out.println("Invalid provider option in credential creation command!!"); System.out.println("Found:'"+providerPath+"'"); - displaySyntax("create"); + displaySyntax("create", storeType); return false; } }catch(Exception ex){ System.out.println("Invalid input or runtime error! Please try again."); System.out.println("Input:"+command+" "+alias+" "+valueOption+" "+credential+" "+providerOption+" "+providerPath); - displaySyntax("create"); + displaySyntax("create", storeType); ex.printStackTrace(); return false; } return isValid; } - public static boolean isValidListCommand(String command,String providerOption,String providerPath){ + public static boolean isValidListCommand(String command,String providerOption,String providerPath, String storeTpe){ boolean isValid=true; try{ if(command==null || !"list".equalsIgnoreCase(command.trim())){ System.out.println("Invalid list phrase in credential get command!!"); System.out.println("Expected:'list' Found:'"+command+"'"); - displaySyntax("list"); + displaySyntax("list", storeTpe); return false; } @@ -373,20 +389,22 @@ public static boolean isValidListCommand(String command,String providerOption,St { System.out.println("Invalid provider option in credential get command!!"); System.out.println("Expected:'-provider' Found:'"+providerOption+"'"); - displaySyntax("list"); + displaySyntax("list", storeTpe); return false; } - if(providerPath==null || "".equalsIgnoreCase(providerPath.trim()) || (!providerPath.startsWith("localjceks://") && !providerPath.startsWith("jceks://"))) + if(providerPath==null || "".equalsIgnoreCase(providerPath.trim()) || + ((!providerPath.startsWith("localjceks://") && !providerPath.startsWith("jceks://")) && + (!providerPath.startsWith("localbcfks://") && !providerPath.startsWith("bcfks://")))) { System.out.println("Invalid provider option in credential get command!!"); System.out.println("Found:'"+providerPath+"'"); - displaySyntax("list"); + displaySyntax("list", storeTpe); return false; } }catch(Exception ex){ System.out.println("Invalid input or runtime error! Please try again."); System.out.println("Input:"+command+" "+providerOption+" "+providerPath); - displaySyntax("list"); + displaySyntax("list", storeTpe); ex.printStackTrace(); return false; } @@ -407,19 +425,35 @@ public static void displayCommand(String args[]) } } - public static void displaySyntax(String command){ - if(command!=null && command.trim().equalsIgnoreCase("create")){ - System.out.println("Correct syntax is:create -value -provider "); - System.out.println("sample command is:create myalias -value password123 -provider jceks://file/tmp/ks/myks.jceks"); - } - if(command!=null && command.trim().equalsIgnoreCase("list")){ - System.out.println("Correct syntax is:list -provider "); - System.out.println("sample command is:list -provider jceks://file/tmp/ks/myks.jceks"); - } - if(command!=null && command.trim().equalsIgnoreCase("get")){ - System.out.println("Correct syntax is:get -provider "); - System.out.println("sample command is:get myalias -provider jceks://file/tmp/ks/myks.jceks"); + public static void displaySyntax(String command, String storeType){ + if ("bcfks".equalsIgnoreCase(storeType)) { + if (command != null && command.trim().equalsIgnoreCase("create")) { + System.out.println("Correct syntax is:create -value -provider "); + System.out.println("sample command is:create myalias -value password123 -provider bcfks://file/tmp/ks/myks.bcfks"); + } + if (command != null && command.trim().equalsIgnoreCase("list")) { + System.out.println("Correct syntax is:list -provider "); + System.out.println("sample command is:list -provider bcfks://file/tmp/ks/myks.bcfks"); + } + if (command != null && command.trim().equalsIgnoreCase("get")) { + System.out.println("Correct syntax is:get -provider "); + System.out.println("sample command is:get myalias -provider bcfks://file/tmp/ks/myks.bcfks"); + } + } else { + if (command != null && command.trim().equalsIgnoreCase("create")) { + System.out.println("Correct syntax is:create -value -provider "); + System.out.println("sample command is:create myalias -value password123 -provider jceks://file/tmp/ks/myks.jceks"); + } + if (command != null && command.trim().equalsIgnoreCase("list")) { + System.out.println("Correct syntax is:list -provider "); + System.out.println("sample command is:list -provider jceks://file/tmp/ks/myks.jceks"); + } + if (command != null && command.trim().equalsIgnoreCase("get")) { + System.out.println("Correct syntax is:get -provider "); + System.out.println("sample command is:get myalias -provider jceks://file/tmp/ks/myks.jceks"); + } } + } public String getCredential(String args[]){ String command=null; @@ -427,19 +461,20 @@ public String getCredential(String args[]){ String providerOption=null; String providerPath=null; String tempCredential=null; + String storeType=KeyStore.getDefaultType(); try{ if(args!=null && args.length==4){ command=args[0]; alias=args[1]; providerOption=args[2]; providerPath=args[3]; - if(!isValidGetCommand(command,alias,providerOption,providerPath)){ - displaySyntax("get"); + if(!isValidGetCommand(command,alias,providerOption,providerPath,storeType)){ + displaySyntax("get", storeType); }else{ - tempCredential=CredentialReader.getDecryptedString(providerPath, alias); + tempCredential=CredentialReader.getDecryptedString(providerPath, alias, storeType); } }else{ - displaySyntax("get"); + displaySyntax("get", storeType); } if(tempCredential==null){ System.out.println("Alias "+ alias +" does not exist!!"); @@ -450,40 +485,42 @@ public String getCredential(String args[]){ return tempCredential; } - public static boolean isValidGetCommand(String command,String alias,String providerOption,String providerPath){ + public static boolean isValidGetCommand(String command,String alias,String providerOption,String providerPath,String storeType){ boolean isValid=true; try{ if(command==null || !"get".equalsIgnoreCase(command.trim())){ System.out.println("Invalid get phrase in credential get command!!"); System.out.println("Expected:'get' Found:'"+command+"'"); - displaySyntax("get"); + displaySyntax("get", storeType); return false; } if(alias==null || "".equalsIgnoreCase(alias.trim())) { System.out.println("Invalid alias name phrase in credential get command!!"); System.out.println("Found:'"+alias+"'"); - displaySyntax("get"); + displaySyntax("get", storeType); return false; } if(providerOption==null || !"-provider".equalsIgnoreCase(providerOption.trim())) { System.out.println("Invalid provider option in credential get command!!"); System.out.println("Expected:'-provider' Found:'"+providerOption+"'"); - displaySyntax("get"); + displaySyntax("get", storeType); return false; } - if(providerPath==null || "".equalsIgnoreCase(providerPath.trim()) || (!providerPath.startsWith("localjceks://") && !providerPath.startsWith("jceks://"))) + if(providerPath==null || "".equalsIgnoreCase(providerPath.trim()) || + (!providerPath.startsWith("localjceks://") && !providerPath.startsWith("jceks://")) && + (!providerPath.startsWith("localbcfks://") && !providerPath.startsWith("bcfks://"))) { System.out.println("Invalid provider option in credential get command!!"); System.out.println("Found:'"+providerPath+"'"); - displaySyntax("get"); + displaySyntax("get", storeType); return false; } }catch(Exception ex){ System.out.println("Invalid input or runtime error! Please try again."); System.out.println("Input:"+command+" "+alias+" "+providerOption+" "+providerPath); - displaySyntax("get"); + displaySyntax("get", storeType); ex.printStackTrace(); return false; } @@ -522,6 +559,10 @@ public void deleteInvalidKeystore(String providerPath){ keystore=providerPath.replace("jceks://file",""); }else if(providerPath.startsWith("localjceks://file")){ keystore=providerPath.replace("jceks://file",""); + }else if(providerPath.startsWith("bcfks://file")){ + keystore=providerPath.replace("bcfks://file",""); + }else if(providerPath.startsWith("localbcfks://file")){ + keystore=providerPath.replace("bcfks://file",""); }else{ keystore=providerPath; } diff --git a/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/TestCredentialReader.java b/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/TestCredentialReader.java index 006986c6ab..ab36a51d8b 100644 --- a/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/TestCredentialReader.java +++ b/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/TestCredentialReader.java @@ -17,19 +17,19 @@ */ package org.apache.ranger.credentialapi; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.File; import org.apache.commons.io.FileUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class TestCredentialReader { private String keystoreFile; - @Before + @BeforeEach public void setup() throws Exception { String basedir = System.getProperty("basedir"); if (basedir == null) { @@ -43,14 +43,14 @@ public void setup() throws Exception { assertEquals(0, rc2); } - @After + @AfterEach public void cleanup() throws Exception { FileUtils.deleteQuietly(new File(keystoreFile)); } @Test public void testPassword() throws Exception { - String password = CredentialReader.getDecryptedString(keystoreFile, "TestCredential2"); + String password = CredentialReader.getDecryptedString(keystoreFile, "TestCredential2", "jceks"); assertEquals("PassworD123", password); String[] argsdeleteCommand = new String[] {"delete", "TestCredential2", "-provider", "jceks://file@/" + keystoreFile}; diff --git a/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/Testbuildks.java b/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/Testbuildks.java index 87634d7778..dbe2ec6007 100644 --- a/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/Testbuildks.java +++ b/credentialbuilder/src/test/java/org/apache/ranger/credentialapi/Testbuildks.java @@ -17,21 +17,22 @@ */ package org.apache.ranger.credentialapi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.util.Arrays; import org.apache.commons.io.FileUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class Testbuildks { private String keystoreFile; - @Before + @BeforeEach public void setup() throws Exception { String basedir = System.getProperty("basedir"); if (basedir == null) { @@ -40,7 +41,7 @@ public void setup() throws Exception { keystoreFile = basedir + File.separator + "target" + File.separator + "testkeystore.jceks"; } - @After + @AfterEach public void cleanup() throws Exception { FileUtils.deleteQuietly(new File(keystoreFile)); } @@ -48,7 +49,7 @@ public void cleanup() throws Exception { @Test public void testBuildKSsuccess() throws Exception { buildks buildksOBJ = new buildks(); - String[] argsCreateCommand = {"create", "TestCredential1", "-value", "PassworD123", "-provider", "jceks://file@/" + keystoreFile}; + String[] argsCreateCommand = {"create", "TestCredential1", "-value", "PassworD123", "-provider", "jceks://file@/" + keystoreFile, "","jceks"}; int rc1 = buildksOBJ.createCredential(argsCreateCommand); assertEquals(0, rc1); @@ -57,6 +58,7 @@ public void testBuildKSsuccess() throws Exception { assertEquals(0, rc2); String[] argsGetCommand = {"get", "TestCredential1", "-provider", "jceks://file@/" +keystoreFile }; + System.out.println("Get command = " + Arrays.toString(argsGetCommand)); String pw = buildksOBJ.getCredential(argsGetCommand); assertEquals("PassworD123", pw); assertTrue(pw.equals("PassworD123")); diff --git a/dev-support/findbugsIncludeFile.xml b/dev-support/findbugsIncludeFile.xml deleted file mode 100644 index 8623906bda..0000000000 --- a/dev-support/findbugsIncludeFile.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - diff --git a/dev-support/ranger-docker/.dockerignore b/dev-support/ranger-docker/.dockerignore new file mode 100644 index 0000000000..1125a66ffb --- /dev/null +++ b/dev-support/ranger-docker/.dockerignore @@ -0,0 +1,17 @@ +* +!config +!dist/version +!dist/ranger-*-admin.tar.gz +!dist/ranger-*-kms.tar.gz +!dist/ranger-*-usersync.tar.gz +!dist/ranger-*-tagsync.tar.gz +!dist/ranger-*-hdfs-plugin.tar.gz +!dist/ranger-*-yarn-plugin.tar.gz +!dist/ranger-*-hive-plugin.tar.gz +!dist/ranger-*-hbase-plugin.tar.gz +!dist/ranger-*-kafka-plugin.tar.gz +!dist/ranger-*-knox-plugin.tar.gz +!dist/ranger-*-trino-plugin.tar.gz +!dist/ranger-*-ozone-plugin.tar.gz +!downloads/* +!scripts/* diff --git a/dev-support/ranger-docker/.env b/dev-support/ranger-docker/.env new file mode 100644 index 0000000000..00d85b9873 --- /dev/null +++ b/dev-support/ranger-docker/.env @@ -0,0 +1,74 @@ +BUILD_HOST_SRC=true +SKIPTESTS=true +GIT_URL=https://github.com/apache/ranger.git +BRANCH=master +PROFILE= +BUILD_OPTS= + +# To build Trino plugins package, use following PROFILE +# PROFILE=ranger-jdk11,!all,!linux + +# Java version for RangerBase ubuntu image. +# This image gets used as base docker image for all images. +# Valid values: 8, 11, 17 +RANGER_BASE_JAVA_VERSION=8 + +# Java version for RangerBase ubi image. +# This image gets used as base docker image for all images. +# Valid values: 1.8.0, 11, 17 +RANGER_BASE_UBI_JAVA_VERSION=1.8.0 + +# Java version to use to build Apache Ranger +# Valid values: 8, 11, 17 +# Trino builds on jdk 11 and above +RANGER_BUILD_JAVA_VERSION=8 + +# Java version to use to run Ranger Admin server +# Valid values: 8, 11, 17 +# Should be same as RANGER_BASE_UBI_JAVA_VERSION when running on UBI BASE image. +RANGER_ADMIN_JAVA_VERSION=8 + +# base image versions +UBUNTU_VERSION=20.04 +UBI_VERSION=latest + +# third party image versions +MARIADB_VERSION=10.7.3 +POSTGRES_VERSION=12 +ENABLE_DB_MOUNT=true +ZK_VERSION=3.9.2 +SOLR_VERSION=8.11.3 + +# service versions +HADOOP_VERSION=3.3.6 +HBASE_VERSION=2.6.0 +HIVE_VERSION=3.1.3 +HIVE_HADOOP_VERSION=3.1.1 +KAFKA_VERSION=2.8.2 +KNOX_VERSION=2.0.0 +TRINO_VERSION=377 +OZONE_VERSION=1.4.0 +OZONE_RUNNER_VERSION=20230615-1 +OZONE_RUNNER_IMAGE=apache/ozone-runner +OZONE_OPTS= + +# versions of ranger services +RANGER_VERSION=3.0.0-SNAPSHOT +KMS_VERSION=3.0.0-SNAPSHOT +USERSYNC_VERSION=3.0.0-SNAPSHOT +TAGSYNC_VERSION=3.0.0-SNAPSHOT + +# plugin versions +HDFS_PLUGIN_VERSION=3.0.0-SNAPSHOT +YARN_PLUGIN_VERSION=3.0.0-SNAPSHOT +HIVE_PLUGIN_VERSION=3.0.0-SNAPSHOT +HBASE_PLUGIN_VERSION=3.0.0-SNAPSHOT +KAFKA_PLUGIN_VERSION=3.0.0-SNAPSHOT +KNOX_PLUGIN_VERSION=3.0.0-SNAPSHOT +TRINO_PLUGIN_VERSION=3.0.0-SNAPSHOT +OZONE_PLUGIN_VERSION=3.0.0-SNAPSHOT + +# To enable debug logs +DEBUG_ADMIN=false +DEBUG_USERSYNC=false +DEBUG_TAGSYNC=false diff --git a/dev-support/ranger-docker/.gitignore b/dev-support/ranger-docker/.gitignore new file mode 100644 index 0000000000..12e97ed7d9 --- /dev/null +++ b/dev-support/ranger-docker/.gitignore @@ -0,0 +1,9 @@ +*.class +*.iml +.settings/ +.metadata +.classpath +.project +/target/ +.DS_Store +.idea diff --git a/dev-support/ranger-docker/Dockerfile.ranger b/dev-support/ranger-docker/Dockerfile.ranger new file mode 100644 index 0000000000..c938fa9f54 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger @@ -0,0 +1,60 @@ +# 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. +ARG RANGER_DB_TYPE + +FROM ranger-base:latest AS ranger + +ARG RANGER_VERSION +ARG RANGER_DB_TYPE +ARG TARGETARCH +ARG RANGER_ADMIN_JAVA_VERSION + +RUN if [ "${OS_NAME}" == "UBUNTU" ]; then\ + ENV JAVA_HOME /usr/lib/jvm/java-${RANGER_ADMIN_JAVA_VERSION}-openjdk-${TARGETARCH}\ + update-java-alternatives --set /usr/lib/jvm/java-1.${RANGER_ADMIN_JAVA_VERSION}.0-openjdk-${TARGETARCH};\ + fi + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${RANGER_VERSION}-admin.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger.sh ${RANGER_SCRIPTS}/ +COPY ./scripts/ranger-admin-install-${RANGER_DB_TYPE}.properties ${RANGER_SCRIPTS}/ranger-admin-install.properties +COPY ./scripts/create-ranger-services.py ${RANGER_SCRIPTS}/ + +RUN tar xvfz /home/ranger/dist/ranger-${RANGER_VERSION}-admin.tar.gz --directory=${RANGER_HOME} \ + && ln -s ${RANGER_HOME}/ranger-${RANGER_VERSION}-admin ${RANGER_HOME}/admin \ + && rm -f /home/ranger/dist/ranger-${RANGER_VERSION}-admin.tar.gz \ + && cp -f ${RANGER_SCRIPTS}/ranger-admin-install.properties ${RANGER_HOME}/admin/install.properties \ + && mkdir -p /var/run/ranger \ + && mkdir -p /var/log/ranger \ + && chown -R ranger:ranger ${RANGER_HOME}/admin/ ${RANGER_SCRIPTS}/ /var/run/ranger/ /var/log/ranger/ \ + && mkdir -p /usr/share/java/ + +FROM ranger AS ranger_postgres +COPY ./downloads/postgresql-42.2.16.jre7.jar /home/ranger/dist/ +RUN mv /home/ranger/dist/postgresql-42.2.16.jre7.jar /usr/share/java/postgresql.jar + +FROM ranger AS ranger_mysql +COPY ./downloads/mysql-connector-java-8.0.28.jar /home/ranger/dist/ +COPY ./downloads/log4jdbc-1.2.jar /home/ranger/dist/ +RUN mv /home/ranger/dist/mysql-connector-java-8.0.28.jar /usr/share/java/mysql-connector.jar \ + && mv /home/ranger/dist/log4jdbc-1.2.jar ${RANGER_HOME}/admin/ews/webapp/WEB-INF/lib/log4jdbc-1.2.jar + +FROM ranger_${RANGER_DB_TYPE} + +USER ranger + +ENTRYPOINT [ "/home/ranger/scripts/ranger.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-base b/dev-support/ranger-docker/Dockerfile.ranger-base new file mode 100644 index 0000000000..4414c13dc9 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-base @@ -0,0 +1,62 @@ +# 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. + +ARG UBUNTU_VERSION +FROM ubuntu:${UBUNTU_VERSION} + +ARG TARGETARCH +ARG RANGER_BASE_JAVA_VERSION +ENV OS_NAME UBUNTU + +# Install tzdata, Python, Java, python-requests +RUN apt-get update && \ + DEBIAN_FRONTEND="noninteractive" apt-get -y install tzdata vim\ + python3 python3-pip openjdk-8-jdk openjdk-11-jdk openjdk-17-jdk bc iputils-ping ssh pdsh xmlstarlet && \ + pip3 install apache-ranger && \ + pip3 install requests + +# Set environment variables +ENV JAVA_HOME /usr/lib/jvm/java-${RANGER_BASE_JAVA_VERSION}-openjdk-${TARGETARCH} +ENV RANGER_DIST /home/ranger/dist +ENV RANGER_SCRIPTS /home/ranger/scripts +ENV RANGER_HOME /opt/ranger +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +RUN update-java-alternatives --set /usr/lib/jvm/java-1.${RANGER_BASE_JAVA_VERSION}.0-openjdk-${TARGETARCH} + +# setup groups, users, directories +RUN groupadd ranger && \ + useradd -g ranger -ms /bin/bash ranger && \ + useradd -g ranger -ms /bin/bash rangeradmin && \ + useradd -g ranger -ms /bin/bash rangerusersync && \ + useradd -g ranger -ms /bin/bash rangertagsync && \ + useradd -g ranger -ms /bin/bash rangerkms && \ + groupadd hadoop && \ + useradd -g hadoop -ms /bin/bash hdfs && \ + useradd -g hadoop -ms /bin/bash yarn && \ + useradd -g hadoop -ms /bin/bash hive && \ + useradd -g hadoop -ms /bin/bash hbase && \ + useradd -g hadoop -ms /bin/bash kafka && \ + useradd -g hadoop -ms /bin/bash ozone && \ + groupadd knox && \ + useradd -g knox -ms /bin/bash knox && \ + mkdir -p /home/ranger/dist && \ + mkdir -p /home/ranger/scripts && \ + chown -R ranger:ranger /home/ranger && \ + mkdir -p /opt/ranger && \ + chown -R ranger:ranger /opt/ranger + +ENTRYPOINT [ "/bin/bash" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-base-ubi b/dev-support/ranger-docker/Dockerfile.ranger-base-ubi new file mode 100644 index 0000000000..bac55fec4a --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-base-ubi @@ -0,0 +1,83 @@ +# 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. + +ARG UBI_VERSION +FROM registry.access.redhat.com/ubi9/ubi-minimal:${UBI_VERSION} + +USER root +ARG RANGER_BASE_UBI_JAVA_VERSION +ENV OS_NAME RHEL + +RUN microdnf --setopt=install_weak_deps=0 --setopt=tsflags=nodocs \ + install -y java-${RANGER_BASE_UBI_JAVA_VERSION}-openjdk-devel \ + && microdnf clean all \ + && rpm -q java-${RANGER_BASE_UBI_JAVA_VERSION}-openjdk-devel + +ENV JAVA_HOME="/usr/lib/jvm/java-${RANGER_BASE_UBI_JAVA_VERSION}" \ + JAVA_VENDOR="openjdk" \ + JAVA_VERSION="${RANGER_BASE_UBI_JAVA_VERSION}" \ + JBOSS_CONTAINER_OPENJDK_JDK_MODULE="/opt/jboss/container/openjdk/jdk" + +# Install tzdata, Python, python-requests +RUN microdnf install -y python3 python3-pip bc iputils hostname +RUN microdnf install -y tar +RUN microdnf install -y gzip +RUN microdnf install -y procps +RUN microdnf install -y vim + +# for command useradd, groupadd +RUN microdnf install -y shadow-utils + +# for command su +RUN microdnf install -y util-linux-user +RUN microdnf install -y sudo + +# for command service +RUN microdnf install -y initscripts +RUN microdnf install -y openssh-clients +RUN microdnf install -y openssh-server +RUN pip3 install apache-ranger + +# Set environment variables +ENV RANGER_HOME /opt/ranger +ENV RANGER_DIST /home/ranger/dist +ENV RANGER_SCRIPTS /home/ranger/scripts +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + +RUN sudo sed -i 's/^HOME_MODE.*/HOME_MODE 0755/' /etc/login.defs + +# setup groups, users, directories +RUN groupadd ranger \ + && groupadd knox \ + && groupadd hadoop \ + && useradd -g ranger -ms /bin/bash ranger \ + && useradd -g ranger -ms /bin/bash rangeradmin \ + && useradd -g ranger -ms /bin/bash rangerusersync \ + && useradd -g ranger -ms /bin/bash rangertagsync \ + && useradd -g ranger -ms /bin/bash rangerkms \ + && useradd -g knox -ms /bin/bash knox \ + && useradd -g hadoop -ms /bin/bash hdfs \ + && useradd -g hadoop -ms /bin/bash yarn \ + && useradd -g hadoop -ms /bin/bash hive \ + && useradd -g hadoop -ms /bin/bash hbase \ + && useradd -g hadoop -ms /bin/bash kafka \ + && mkdir -p /home/ranger/dist \ + && mkdir -p /home/ranger/scripts \ + && chown -R ranger:ranger /home/ranger \ + && mkdir -p /opt/ranger \ + && chown -R ranger:ranger /opt/ranger + +ENTRYPOINT [ "/bin/bash" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-build b/dev-support/ranger-docker/Dockerfile.ranger-build new file mode 100644 index 0000000000..bf5972d6b4 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-build @@ -0,0 +1,52 @@ +# 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. + +FROM ranger-base:latest + +ARG RANGER_BUILD_JAVA_VERSION +ARG TARGETARCH + +# Install necessary packages to build Ranger +RUN if [ "${OS_NAME}" == "UBUNTU" ]; then\ + apt-get update && apt-get -y install git maven build-essential\ + update-java-alternatives --set /usr/lib/jvm/java-1.${RANGER_BUILD_JAVA_VERSION}.0-openjdk-${TARGETARCH}\ + ENV JAVA_HOME /usr/lib/jvm/java-${RANGER_BUILD_JAVA_VERSION}-openjdk-${TARGETARCH};\ + fi + +RUN if [ "${OS_NAME}" == "RHEL" ]; then\ + microdnf install -y git maven gcc;\ + fi + +# Set environment variables +ENV MAVEN_HOME /usr/share/maven +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/apache-maven/bin + +# setup ranger group, and users +RUN mkdir -p /home/ranger/git && \ + mkdir -p /home/ranger/.m2 && \ + chown -R ranger:ranger /home/ranger + +COPY ./scripts/ranger-build.sh /home/ranger/scripts/ + +VOLUME /home/ranger/.m2 +VOLUME /home/ranger/scripts +VOLUME /home/ranger/patches +VOLUME /home/ranger/dist +VOLUME /home/ranger/src + +USER ranger + +ENTRYPOINT [ "/home/ranger/scripts/ranger-build.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-hadoop b/dev-support/ranger-docker/Dockerfile.ranger-hadoop new file mode 100644 index 0000000000..e1fa764e3b --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-hadoop @@ -0,0 +1,57 @@ +# 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. + +FROM ranger-base:latest + +ARG HADOOP_VERSION +ARG HDFS_PLUGIN_VERSION +ARG YARN_PLUGIN_VERSION + + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${HDFS_PLUGIN_VERSION}-hdfs-plugin.tar.gz /home/ranger/dist/ +COPY ./dist/ranger-${YARN_PLUGIN_VERSION}-yarn-plugin.tar.gz /home/ranger/dist/ +COPY ./downloads/hadoop-${HADOOP_VERSION}.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger-hadoop-setup.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hadoop.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hadoop-mkdir.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hdfs-plugin-install.properties /home/ranger/scripts/ +COPY ./scripts/ranger-yarn-plugin-install.properties /home/ranger/scripts/ + +RUN tar xvfz /home/ranger/dist/hadoop-${HADOOP_VERSION}.tar.gz --directory=/opt/ && \ + ln -s /opt/hadoop-${HADOOP_VERSION} /opt/hadoop && \ + rm -f /home/ranger/dist/hadoop-${HADOOP_VERSION}.tar.gz && \ + tar xvfz /home/ranger/dist/ranger-${HDFS_PLUGIN_VERSION}-hdfs-plugin.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/ranger-${HDFS_PLUGIN_VERSION}-hdfs-plugin /opt/ranger/ranger-hdfs-plugin && \ + rm -f /home/ranger/dist/ranger-${HDFS_PLUGIN_VERSION}-hdfs-plugin.tar.gz && \ + cp -f /home/ranger/scripts/ranger-hdfs-plugin-install.properties /opt/ranger/ranger-hdfs-plugin/install.properties && \ + tar xvfz /home/ranger/dist/ranger-${YARN_PLUGIN_VERSION}-yarn-plugin.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/ranger-${YARN_PLUGIN_VERSION}-yarn-plugin /opt/ranger/ranger-yarn-plugin && \ + rm -f /home/ranger/dist/ranger-${YARN_PLUGIN_VERSION}-yarn-plugin.tar.gz && \ + cp -f /home/ranger/scripts/ranger-yarn-plugin-install.properties /opt/ranger/ranger-yarn-plugin/install.properties && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-hadoop-setup.sh ${RANGER_SCRIPTS}/ranger-hadoop.sh ${RANGER_SCRIPTS}/ranger-hadoop-mkdir.sh && \ + chown hdfs:hadoop ${RANGER_SCRIPTS}/ranger-hadoop-mkdir.sh + +ENV HADOOP_HOME /opt/hadoop +ENV HADOOP_CONF_DIR /opt/hadoop/etc/hadoop +ENV HADOOP_HDFS_HOME /opt/hadoop +ENV HADOOP_MAPRED_HOME /opt/hadoop +ENV HADOOP_COMMON_HOME /opt/hadoop +ENV YARN_HOME /opt/hadoop +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/hadoop/bin + +ENTRYPOINT [ "/home/ranger/scripts/ranger-hadoop.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-hbase b/dev-support/ranger-docker/Dockerfile.ranger-hbase new file mode 100644 index 0000000000..15beee7c37 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-hbase @@ -0,0 +1,44 @@ +# 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. + +FROM ranger-base:latest + +ARG HBASE_VERSION +ARG HBASE_PLUGIN_VERSION + + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${HBASE_PLUGIN_VERSION}-hbase-plugin.tar.gz /home/ranger/dist/ +COPY ./downloads/hbase-${HBASE_VERSION}-bin.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger-hbase-setup.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hbase.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hbase-plugin-install.properties /home/ranger/scripts/ +COPY ./scripts/hbase-site.xml /home/ranger/scripts/ + +RUN tar xvfz /home/ranger/dist/hbase-${HBASE_VERSION}-bin.tar.gz --directory=/opt/ && \ + ln -s /opt/hbase-${HBASE_VERSION} /opt/hbase && \ + rm -f /home/ranger/dist/hbase-${HBASE_VERSION}-bin.tar.gz && \ + tar xvfz /home/ranger/dist/ranger-${HBASE_PLUGIN_VERSION}-hbase-plugin.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/ranger-${HBASE_PLUGIN_VERSION}-hbase-plugin /opt/ranger/ranger-hbase-plugin && \ + rm -f /home/ranger/dist/ranger-${HBASE_PLUGIN_VERSION}-hbase-plugin.tar.gz && \ + cp -f /home/ranger/scripts/ranger-hbase-plugin-install.properties /opt/ranger/ranger-hbase-plugin/install.properties && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-hbase-setup.sh ${RANGER_SCRIPTS}/ranger-hbase.sh + +ENV HBASE_HOME /opt/hbase +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/hbase/bin + +ENTRYPOINT [ "/home/ranger/scripts/ranger-hbase.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-hive b/dev-support/ranger-docker/Dockerfile.ranger-hive new file mode 100644 index 0000000000..dd326f8549 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-hive @@ -0,0 +1,54 @@ +# 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. + +FROM ranger-base:latest + +ARG HIVE_VERSION +ARG HIVE_HADOOP_VERSION +ARG HIVE_PLUGIN_VERSION +ARG RANGER_DB_TYPE + + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${HIVE_PLUGIN_VERSION}-hive-plugin.tar.gz /home/ranger/dist/ +COPY ./downloads/apache-hive-${HIVE_VERSION}-bin.tar.gz /home/ranger/dist/ +COPY ./downloads/hadoop-${HIVE_HADOOP_VERSION}.tar.gz /home/ranger/dist/ +COPY ./downloads/mysql-connector-java-8.0.28.jar /home/ranger/dist + +COPY ./scripts/ranger-hive-setup.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hive.sh /home/ranger/scripts/ +COPY ./scripts/ranger-hive-plugin-install.properties /home/ranger/scripts/ +COPY ./scripts/hive-site-${RANGER_DB_TYPE}.xml /home/ranger/scripts/hive-site.xml + +RUN tar xvfz /home/ranger/dist/apache-hive-${HIVE_VERSION}-bin.tar.gz --directory=/opt/ && \ + ln -s /opt/apache-hive-${HIVE_VERSION}-bin /opt/hive && \ + rm -f /home/ranger/dist/apache-hive-${HIVE_VERSION}-bin.tar.gz && \ + mv /home/ranger/dist/mysql-connector-java-8.0.28.jar /opt/hive/lib/ && \ + tar xvfz /home/ranger/dist/hadoop-${HIVE_HADOOP_VERSION}.tar.gz --directory=/opt/ && \ + ln -s /opt/hadoop-${HIVE_HADOOP_VERSION} /opt/hadoop && \ + rm -f /home/ranger/dist/hadoop-${HIVE_HADOOP_VERSION}.tar.gz && \ + tar xvfz /home/ranger/dist/ranger-${HIVE_PLUGIN_VERSION}-hive-plugin.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/ranger-${HIVE_PLUGIN_VERSION}-hive-plugin /opt/ranger/ranger-hive-plugin && \ + rm -f /home/ranger/dist/ranger-${HIVE_PLUGIN_VERSION}-hive-plugin.tar.gz && \ + cp -f /home/ranger/scripts/ranger-hive-plugin-install.properties /opt/ranger/ranger-hive-plugin/install.properties && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-hive-setup.sh ${RANGER_SCRIPTS}/ranger-hive.sh + +ENV HIVE_HOME /opt/hive +ENV HADOOP_HOME /opt/hadoop +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/hive/bin:/opt/hadoop/bin + + +ENTRYPOINT [ "/home/ranger/scripts/ranger-hive.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-kafka b/dev-support/ranger-docker/Dockerfile.ranger-kafka new file mode 100644 index 0000000000..c03452e380 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-kafka @@ -0,0 +1,43 @@ +# 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. + +FROM ranger-base:latest + +ARG KAFKA_VERSION +ARG KAFKA_PLUGIN_VERSION + + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${KAFKA_PLUGIN_VERSION}-kafka-plugin.tar.gz /home/ranger/dist/ +COPY ./downloads/kafka_2.12-${KAFKA_VERSION}.tgz /home/ranger/dist/ + +COPY ./scripts/ranger-kafka-setup.sh /home/ranger/scripts/ +COPY ./scripts/ranger-kafka.sh /home/ranger/scripts/ +COPY ./scripts/ranger-kafka-plugin-install.properties /home/ranger/scripts/ + +RUN tar xvfz /home/ranger/dist/kafka_2.12-${KAFKA_VERSION}.tgz --directory=/opt/ && \ + ln -s /opt/kafka_2.12-${KAFKA_VERSION} /opt/kafka && \ + rm -f /home/ranger/dist/kafka_2.12-${KAFKA_VERSION}.tgz && \ + tar xvfz /home/ranger/dist/ranger-${KAFKA_PLUGIN_VERSION}-kafka-plugin.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/ranger-${KAFKA_PLUGIN_VERSION}-kafka-plugin /opt/ranger/ranger-kafka-plugin && \ + rm -f /home/ranger/dist/ranger-${KAFKA_PLUGIN_VERSION}-kafka-plugin.tar.gz && \ + cp -f /home/ranger/scripts/ranger-kafka-plugin-install.properties /opt/ranger/ranger-kafka-plugin/install.properties && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-kafka-setup.sh ${RANGER_SCRIPTS}/ranger-kafka.sh + +ENV KAFKA_HOME /opt/kafka +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/kafka/bin + +ENTRYPOINT [ "/home/ranger/scripts/ranger-kafka.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-kms b/dev-support/ranger-docker/Dockerfile.ranger-kms new file mode 100644 index 0000000000..c3431c5b1e --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-kms @@ -0,0 +1,59 @@ +# 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. +ARG RANGER_DB_TYPE + +FROM ranger-base:latest AS ranger-kms + +ARG KMS_VERSION +ARG RANGER_DB_TYPE + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${KMS_VERSION}-kms.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger-kms.sh ${RANGER_SCRIPTS}/ +COPY ./scripts/ranger-kms-install-${RANGER_DB_TYPE}.properties ${RANGER_SCRIPTS}/ranger-kms-install.properties + +RUN tar xvfz /home/ranger/dist/ranger-${KMS_VERSION}-kms.tar.gz --directory=${RANGER_HOME} && \ + ln -s ${RANGER_HOME}/ranger-${KMS_VERSION}-kms ${RANGER_HOME}/kms && \ + rm -f /home/ranger/dist/ranger-${KMS_VERSION}-kms.tar.gz && \ + cp -f ${RANGER_SCRIPTS}/ranger-kms-install.properties ${RANGER_HOME}/kms/install.properties && \ + mkdir -p /var/run/ranger_kms && \ + mkdir -p /var/log/ranger/kms && \ + mkdir -p /etc/ranger && \ + touch /etc/init.d/ranger-kms && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + ln -s /etc/init.d/ranger-kms /etc/rc2.d/S88ranger-kms && \ + ln -s /etc/init.d/ranger-kms /etc/rc2.d/K90ranger-kms && \ + ln -s /etc/init.d/ranger-kms /etc/rc3.d/S88ranger-kms && \ + ln -s /etc/init.d/ranger-kms /etc/rc3.d/K90ranger-kms && \ + ln -s ${RANGER_HOME}/kms/ranger-kms-services.sh /usr/bin/ranger-kms-services.sh && \ + chown -R rangerkms:ranger ${RANGER_HOME}/kms/ ${RANGER_SCRIPTS}/ /var/run/ranger_kms/ /var/log/ranger/ && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-kms.sh && \ + mkdir -p /usr/share/java/ + +FROM ranger-kms AS ranger_postgres +COPY ./downloads/postgresql-42.2.16.jre7.jar /home/ranger/dist/ +RUN mv /home/ranger/dist/postgresql-42.2.16.jre7.jar /usr/share/java/postgresql.jar + +FROM ranger-kms AS ranger_mysql +COPY ./downloads/mysql-connector-java-8.0.28.jar /home/ranger/dist/ +COPY ./downloads/log4jdbc-1.2.jar /home/ranger/dist/ +RUN mv /home/ranger/dist/mysql-connector-java-8.0.28.jar /usr/share/java/mysql-connector-java.jar && \ + mv /home/ranger/dist/log4jdbc-1.2.jar ${RANGER_HOME}/kms/ews/webapp/WEB-INF/lib/log4jdbc-1.2.jar + +FROM ranger_${RANGER_DB_TYPE} + +ENTRYPOINT [ "/home/ranger/scripts/ranger-kms.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-knox b/dev-support/ranger-docker/Dockerfile.ranger-knox new file mode 100644 index 0000000000..43928cbcee --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-knox @@ -0,0 +1,51 @@ +# 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. + +FROM ranger-base:latest + +ARG KNOX_VERSION +ARG KNOX_PLUGIN_VERSION + + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${KNOX_PLUGIN_VERSION}-knox-plugin.tar.gz /home/ranger/dist/ +COPY ./downloads/knox-${KNOX_VERSION}.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger-knox-setup.sh /home/ranger/scripts/ +COPY ./scripts/ranger-knox.sh /home/ranger/scripts/ +COPY ./scripts/ranger-knox-plugin-install.properties /home/ranger/scripts/ +COPY ./scripts/ranger-knox-expect.py /home/ranger/scripts/ +COPY ./scripts/ranger-knox-sandbox.xml /home/ranger/scripts/ + +RUN tar xvfz /home/ranger/dist/knox-${KNOX_VERSION}.tar.gz --directory=/opt/ && \ + ln -s /opt/knox-${KNOX_VERSION} /opt/knox && \ + rm -f /home/ranger/dist/knox-${KNOX_VERSION}.tar.gz && \ + tar xvfz /home/ranger/dist/ranger-${KNOX_PLUGIN_VERSION}-knox-plugin.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/ranger-${KNOX_PLUGIN_VERSION}-knox-plugin /opt/ranger/ranger-knox-plugin && \ + rm -f /home/ranger/dist/ranger-${KNOX_PLUGIN_VERSION}-knox-plugin.tar.gz && \ + cp -f /home/ranger/scripts/ranger-knox-plugin-install.properties /opt/ranger/ranger-knox-plugin/install.properties && \ + cp -f /home/ranger/scripts/ranger-knox-sandbox.xml /opt/knox/conf/topologies/sandbox.xml && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-knox-setup.sh ${RANGER_SCRIPTS}/ranger-knox.sh ${RANGER_SCRIPTS}/ranger-knox-expect.py + +ENV KNOX_HOME /opt/knox +ENV PATH /usr/java/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/knox/bin + +RUN chmod a+rwx /home/ranger/scripts/ranger-knox-expect.py + +RUN pip3 install pexpect +RUN python3 /home/ranger/scripts/ranger-knox-expect.py + +ENTRYPOINT [ "/home/ranger/scripts/ranger-knox.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-mysql b/dev-support/ranger-docker/Dockerfile.ranger-mysql new file mode 100644 index 0000000000..fd0eb22ef3 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-mysql @@ -0,0 +1,29 @@ +# 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. + +ARG MARIADB_VERSION +FROM mariadb:${MARIADB_VERSION} + +# Copy DB init script +USER 0 + +RUN mkdir -p /docker-entrypoint-initdb.d /home/mysql +COPY config/init_mysql.sql /docker-entrypoint-initdb.d/ +COPY config/my.cnf /home/mysql/.my.cnf +RUN sed -i "s/skip-name-resolve/# skip-name-resolve/" /etc/mysql/mariadb.cnf +RUN chown -R mysql:mysql /docker-entrypoint-initdb.d/ /home/mysql + +ENV MYSQL_ROOT_PASSWORD rangerR0cks! diff --git a/plugin-kafka/src/test/resources/log4j.properties b/dev-support/ranger-docker/Dockerfile.ranger-ozone similarity index 61% rename from plugin-kafka/src/test/resources/log4j.properties rename to dev-support/ranger-docker/Dockerfile.ranger-ozone index 4ad14dec69..3c1f6ef6fe 100644 --- a/plugin-kafka/src/test/resources/log4j.properties +++ b/dev-support/ranger-docker/Dockerfile.ranger-ozone @@ -1,4 +1,3 @@ -# # 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 @@ -7,22 +6,25 @@ # "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 +# 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. -# -# Root logger option -log4j.rootLogger=INFO, stdout -log4j.logger.kafka=WARN -log4j.logger.org.apache.zookeeper=WARN +ARG OZONE_RUNNER_IMAGE +ARG OZONE_RUNNER_VERSION +FROM ${OZONE_RUNNER_IMAGE}:${OZONE_RUNNER_VERSION} + +ARG OZONE_HOME +ARG OZONE_PLUGIN_VERSION + +USER root + +RUN useradd -g hadoop -ms /bin/bash ozone +RUN mkdir -p -m 755 /var/log/ozone -# Direct log messages to stdout -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.Target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{10}:%L - %m%n +# Required to locate the plugin jars and the config files +ENV OZONE_MANAGER_CLASSPATH="${OZONE_HOME}/ranger-ozone-plugin/lib/libext/*:${OZONE_HOME}/ranger-ozone-plugin/conf" diff --git a/ranger-examples/sample-client/src/main/resources/log4j.properties b/dev-support/ranger-docker/Dockerfile.ranger-postgres similarity index 66% rename from ranger-examples/sample-client/src/main/resources/log4j.properties rename to dev-support/ranger-docker/Dockerfile.ranger-postgres index 9c8bd003f2..c14666b628 100644 --- a/ranger-examples/sample-client/src/main/resources/log4j.properties +++ b/dev-support/ranger-docker/Dockerfile.ranger-postgres @@ -1,4 +1,3 @@ -# # 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 @@ -7,17 +6,20 @@ # "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 +# 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. -# -log4j.rootLogger = INFO,console -log4j.appender.console=org.apache.log4j.ConsoleAppender -log4j.appender.console.Threshold=INFO -log4j.appender.console.Target=System.out -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n \ No newline at end of file + +ARG POSTGRES_VERSION +FROM postgres:${POSTGRES_VERSION} + +# Copy DB init script +USER 0 +RUN mkdir -p /docker-entrypoint-initdb.d +COPY config/init_postgres.sh /docker-entrypoint-initdb.d/ +RUN chown -R postgres:postgres /docker-entrypoint-initdb.d/ +ENV POSTGRES_PASSWORD rangerR0cks! diff --git a/intg/src/main/resources/log4j.properties b/dev-support/ranger-docker/Dockerfile.ranger-solr similarity index 66% rename from intg/src/main/resources/log4j.properties rename to dev-support/ranger-docker/Dockerfile.ranger-solr index 9c8bd003f2..8b212d4c52 100644 --- a/intg/src/main/resources/log4j.properties +++ b/dev-support/ranger-docker/Dockerfile.ranger-solr @@ -1,4 +1,3 @@ -# # 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 @@ -7,17 +6,21 @@ # "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 +# 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. -# -log4j.rootLogger = INFO,console -log4j.appender.console=org.apache.log4j.ConsoleAppender -log4j.appender.console.Threshold=INFO -log4j.appender.console.Target=System.out -log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.conversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p - %m%n \ No newline at end of file + +ARG SOLR_VERSION +FROM solr:${SOLR_VERSION} + +# Copy audit config set +USER 0 +RUN mkdir -p /opt/solr/server/solr/configsets/ranger_audits/conf +COPY config/solr-ranger_audits/* /opt/solr/server/solr/configsets/ranger_audits/conf/ +RUN chown -R solr:solr /opt/solr/server/solr/configsets/ranger_audits/ + +USER solr diff --git a/dev-support/ranger-docker/Dockerfile.ranger-tagsync b/dev-support/ranger-docker/Dockerfile.ranger-tagsync new file mode 100644 index 0000000000..6e41ae8083 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-tagsync @@ -0,0 +1,52 @@ +# 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. + +FROM ranger-base:latest + +ARG TAGSYNC_VERSION + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${TAGSYNC_VERSION}-tagsync.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger-tagsync.sh ${RANGER_SCRIPTS}/ +COPY ./scripts/ranger-tagsync-install.properties ${RANGER_SCRIPTS}/ +COPY ./scripts/ranger-tagsync-tags.json ${RANGER_SCRIPTS}/ + +RUN tar xvfz /home/ranger/dist/ranger-${TAGSYNC_VERSION}-tagsync.tar.gz --directory=${RANGER_HOME} && \ + ln -s ${RANGER_HOME}/ranger-${TAGSYNC_VERSION}-tagsync ${RANGER_HOME}/tagsync && \ + rm -f /home/ranger/dist/ranger-${TAGSYNC_VERSION}-tagsync.tar.gz && \ + mkdir -p /opt/ranger/tagsync/data && \ + cp -f ${RANGER_SCRIPTS}/ranger-tagsync-install.properties ${RANGER_HOME}/tagsync/install.properties && \ + cp -f ${RANGER_SCRIPTS}/ranger-tagsync-tags.json ${RANGER_HOME}/tagsync/data/tags.json && \ + mkdir -p /var/run/ranger && \ + mkdir -p /var/log/ranger/tagsync && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + mkdir -p /etc/ranger && \ + mkdir /etc/init.d || true && \ + mkdir /etc/rc2.d || true && \ + mkdir /etc/rc3.d || true && \ + touch /etc/init.d/ranger-tagsync && \ + ln -s /etc/init.d/ranger-tagsync /etc/rc2.d/S99ranger-tagsync && \ + ln -s /etc/init.d/ranger-tagsync /etc/rc2.d/K00ranger-tagsync && \ + ln -s /etc/init.d/ranger-tagsync /etc/rc3.d/S99ranger-tagsync && \ + ln -s /etc/init.d/ranger-tagsync /etc/rc3.d/K00ranger-tagsync && \ + ln -s ${RANGER_HOME}/tagsync/ranger-tagsync-services.sh /usr/bin/ranger-tagsync-services.sh && \ + chown -R ranger:ranger ${RANGER_HOME}/tagsync/ ${RANGER_SCRIPTS}/ /var/run/ranger/ /var/log/ranger/ /etc/ranger /etc/init.d/ranger-tagsync && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-tagsync.sh + +USER ranger + +ENTRYPOINT [ "/home/ranger/scripts/ranger-tagsync.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-trino b/dev-support/ranger-docker/Dockerfile.ranger-trino new file mode 100644 index 0000000000..c376b18ba6 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-trino @@ -0,0 +1,74 @@ +# 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. + +ARG TRINO_VERSION +FROM trinodb/trino:${TRINO_VERSION} + +# trino base image layer has undergone changes in base os image with time. +########################################## +# Trino Versions | OS Layer # +# 359 - 369 | centos 11 # +# 370 - 389 | ubi8 # +# 390 - 391 | azul openjdk # +# 392 | ubi8 # +# 393 - 431 | eclipse-temurin # +# 432 - current | ubi9 # +########################################## + +USER root + +ARG TRINO_VERSION +ARG TRINO_PLUGIN_VERSION +ENV PLUGIN_DIR=ranger-${TRINO_PLUGIN_VERSION}-trino-plugin + +RUN mkdir -p /home/ranger/dist /home/ranger/scripts /opt/ranger + +COPY ./dist/version /home/ranger/dist +COPY ./dist/ranger-${TRINO_PLUGIN_VERSION}-trino-plugin.tar.gz /home/ranger/dist +COPY ./scripts/ranger-trino.sh /home/ranger/scripts +COPY ./scripts/ranger-trino-setup.sh /home/ranger/scripts +COPY ./scripts/ranger-trino-plugin-install.properties /home/ranger/scripts + +RUN if [ $TRINO_VERSION -ge 370 ] && [ $TRINO_VERSION -lt 390 ] || [ $TRINO_VERSION -eq 392 ]; then\ + dnf install -y initscripts;\ + dnf install -y openssh-clients;\ + dnf install -y openssh-server;\ + dnf install -y sudo;\ + elif [ $TRINO_VERSION -ge 432 ]; then\ + microdnf install -y gzip;\ + microdnf install -y initscripts;\ + microdnf install -y openssh-clients;\ + microdnf install -y openssh-server;\ + microdnf install -y sudo;\ + else\ + apt-get update; DEBIAN_FRONTEND="noninteractive" apt-get -y install ssh sudo;\ + fi + +RUN tar xvfz /home/ranger/dist/${PLUGIN_DIR}.tar.gz --directory=/opt/ranger && \ + ln -s /opt/ranger/${PLUGIN_DIR} /opt/ranger/ranger-trino-plugin && \ + rm -f /home/ranger/dist/${PLUGIN_DIR}.tar.gz && \ + cp -f /home/ranger/scripts/ranger-trino-plugin-install.properties /opt/ranger/ranger-trino-plugin/install.properties && \ + chown -R trino:trino /home/ranger /opt/ranger && \ + chown root:root /home/ranger/scripts /home/ranger/scripts/ranger-trino-setup.sh && \ + chmod 744 /home/ranger/scripts/ranger-trino-setup.sh /home/ranger/scripts/ranger-trino.sh + +# enable trino user to execute setup script as root +RUN echo "trino ALL=(ALL) NOPASSWD:/home/ranger/scripts/ranger-trino-setup.sh" > /etc/sudoers.d/trino + + +USER trino + +ENTRYPOINT ["/home/ranger/scripts/ranger-trino.sh"] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-usersync b/dev-support/ranger-docker/Dockerfile.ranger-usersync new file mode 100644 index 0000000000..c1bfe92891 --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-usersync @@ -0,0 +1,50 @@ +# 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. + +FROM ranger-base:latest + +ARG USERSYNC_VERSION + +COPY ./dist/version /home/ranger/dist/ +COPY ./dist/ranger-${USERSYNC_VERSION}-usersync.tar.gz /home/ranger/dist/ + +COPY ./scripts/ranger-usersync.sh ${RANGER_SCRIPTS}/ +COPY ./scripts/ranger-usersync-install.properties ${RANGER_SCRIPTS}/ +COPY ./scripts/ugsync-file-source.csv ${RANGER_SCRIPTS}/ + +RUN tar xvfz /home/ranger/dist/ranger-${USERSYNC_VERSION}-usersync.tar.gz --directory=${RANGER_HOME} && \ + ln -s ${RANGER_HOME}/ranger-${USERSYNC_VERSION}-usersync ${RANGER_HOME}/usersync && \ + rm -f /home/ranger/dist/ranger-${USERSYNC_VERSION}-usersync.tar.gz && \ + cp -f ${RANGER_SCRIPTS}/ranger-usersync-install.properties ${RANGER_HOME}/usersync/install.properties && \ + mkdir -p /var/run/ranger && \ + mkdir -p /var/log/ranger/usersync && \ + ln -s /usr/bin/python3 /usr/bin/python && \ + mkdir -p /etc/ranger && \ + mkdir /etc/init.d || true && \ + mkdir /etc/rc2.d || true && \ + mkdir /etc/rc3.d || true && \ + touch /etc/init.d/ranger-usersync && \ + ln -s /etc/init.d/ranger-usersync /etc/rc2.d/S99ranger-usersync && \ + ln -s /etc/init.d/ranger-usersync /etc/rc2.d/K00ranger-usersync && \ + ln -s /etc/init.d/ranger-usersync /etc/rc3.d/S99ranger-usersync && \ + ln -s /etc/init.d/ranger-usersync /etc/rc3.d/K00ranger-usersync && \ + ln -s ${RANGER_HOME}/usersync/ranger-usersync-services.sh /usr/bin/ranger-usersync && \ + chown -R ranger:ranger ${RANGER_HOME}/usersync/ ${RANGER_SCRIPTS}/ /var/run/ranger/ /var/log/ranger/ /etc/ranger /etc/init.d/ranger-usersync && \ + chmod 744 ${RANGER_SCRIPTS}/ranger-usersync.sh + +USER ranger + +ENTRYPOINT [ "/home/ranger/scripts/ranger-usersync.sh" ] diff --git a/dev-support/ranger-docker/Dockerfile.ranger-zk b/dev-support/ranger-docker/Dockerfile.ranger-zk new file mode 100644 index 0000000000..b0fbf8588a --- /dev/null +++ b/dev-support/ranger-docker/Dockerfile.ranger-zk @@ -0,0 +1,18 @@ +# 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. + +ARG ZK_VERSION +FROM zookeeper:${ZK_VERSION} diff --git a/dev-support/ranger-docker/README.md b/dev-support/ranger-docker/README.md new file mode 100644 index 0000000000..361656aa5d --- /dev/null +++ b/dev-support/ranger-docker/README.md @@ -0,0 +1,118 @@ + + +## Overview + +Use Dockerfiles in this directory to create docker images and run them to build Apache Ranger, deploy Apache Ranger and dependent services in containers. + +### Environment Setup + +- Ensure that you have recent version of Docker installed from [docker.io](http://www.docker.io) (as of this writing: Engine 20.10.5, Compose 1.28.5). + Make sure to configure docker with at least 6gb of memory. + +- Update environment variables in ```.env``` file, if necessary + +- Set ```dev-support/ranger-docker``` as your working directory. + +- Execute following command to download necessary archives to setup Ranger/HDFS/Hive/HBase/Kafka/Knox/Ozone services: + ~~~ + chmod +x download-archives.sh + # use a subset of the below to download specific services + ./download-archives.sh hadoop hive hbase kafka knox ozone + ~~~ + +- Execute following commands to set environment variables to build Apache Ranger docker containers: + ~~~ + export DOCKER_BUILDKIT=1 + export COMPOSE_DOCKER_CLI_BUILD=1 + export RANGER_DB_TYPE=postgres + ~~~ + +### Apache Ranger Build + +#### In containers using docker-compose + +Execute following command to build Apache Ranger: +~~~ +docker-compose -f docker-compose.ranger-base.yml -f docker-compose.ranger-build.yml up +~~~ +Time taken to complete the build might vary (upto an hour), depending on status of ```${HOME}/.m2``` directory cache. + + +#### OR +#### Regular build + +~~~ +cd ./../../ +mvn clean package -DskipTests +cp target/ranger-* dev-support/ranger-docker/dist/ +cp target/version dev-support/ranger-docker/dist/ +cd dev-support/ranger-docker +~~~ + +### Docker Image Build + +#### Prerequisite: ranger-base image build +~~~ +# ubuntu base image: +docker-compose -f docker-compose.ranger-base.yml build --no-cache +# OR +# ubi base image: +docker-compose -f docker-compose.ranger-base-ubi.yml build --no-cache +~~~ +#### Bring up ranger, usersync and tagsync containers +~~~ +# To enable file based sync source for usersync do: +# export ENABLE_FILE_SYNC_SOURCE=true + +# valid values for RANGER_DB_TYPE: mysql/postgres + +docker-compose -f docker-compose.ranger.yml -f docker-compose.ranger-${RANGER_DB_TYPE}.yml -f docker-compose.ranger-usersync.yml -f docker-compose.ranger-tagsync.yml up -d + +# Ranger Admin can be accessed at http://localhost:6080 (admin/rangerR0cks!) +~~~ +#### Bring up hive container +~~~ +docker-compose -f docker-compose.ranger.yml -f docker-compose.ranger-${RANGER_DB_TYPE}.yml -f docker-compose.ranger-hadoop.yml -f docker-compose.ranger-hive.yml up -d +~~~ +#### Bring up hbase container +~~~ +docker-compose -f docker-compose.ranger.yml -f docker-compose.ranger-${RANGER_DB_TYPE}.yml -f docker-compose.ranger-hadoop.yml -f docker-compose.ranger-hbase.yml up -d +~~~ +#### Bring up ozone containers +~~~ +./scripts/ozone-plugin-docker-setup.sh +docker-compose -f docker-compose.ranger.yml -f docker-compose.ranger-${RANGER_DB_TYPE}.yml -f docker-compose.ranger-ozone.yml up -d +~~~ +#### Bring up trino container (requires docker build with jdk 11): +~~~ +docker-compose -f docker-compose.ranger.yml -f docker-compose.ranger-${RANGER_DB_TYPE}.yml -f docker-compose.ranger-trino.yml up -d +~~~ +Similarly, check the `depends` section of the `docker-compose.ranger-service.yaml` file and add docker-compose files for these services when trying to bring up the `service` container. + +#### Bring up all containers +~~~ +./scripts/ozone-plugin-docker-setup.sh +docker compose -f docker-compose.ranger-${RANGER_DB_TYPE}.yml -f docker-compose.ranger.yml -f docker-compose.ranger-usersync.yml -f docker-compose.ranger-tagsync.yml -f docker-compose.ranger-kms.yml -f docker-compose.ranger-hadoop.yml -f docker-compose.ranger-hbase.yml -f docker-compose.ranger-kafka.yml -f docker-compose.ranger-hive.yml -f docker-compose.ranger-knox.yml -f docker-compose.ranger-ozone.yml up -d +~~~ + +#### To rebuild specific images and start containers with the new image: +~~~ +docker-compose -f docker-compose.ranger.yml -f docker-compose.ranger-usersync.yml -f docker-compose.ranger-tagsync.yml -f docker-compose.ranger-kms.yml -f docker-compose.ranger-hadoop.yml -f docker-compose.ranger-hbase.yml -f docker-compose.ranger-kafka.yml -f docker-compose.ranger-hive.yml -f docker-compose.ranger-trino.yml -f docker-compose.ranger-knox.yml up -d --no-deps --force-recreate --build +~~~ diff --git a/dev-support/ranger-docker/config/init_mysql.sql b/dev-support/ranger-docker/config/init_mysql.sql new file mode 100644 index 0000000000..fc4c1b41e2 --- /dev/null +++ b/dev-support/ranger-docker/config/init_mysql.sql @@ -0,0 +1,38 @@ +#!/bin/bash + +# 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. + + +CREATE USER 'rangeradmin'@'%' IDENTIFIED BY 'rangerR0cks!'; +CREATE USER 'rangeradmin'@'localhost' IDENTIFIED BY 'rangerR0cks!'; +CREATE USER 'rangeradmin'@'ranger-db' IDENTIFIED BY 'rangerR0cks!'; + +CREATE DATABASE ranger; +GRANT ALL PRIVILEGES ON ranger.* TO 'rangeradmin'@'%'; +GRANT ALL PRIVILEGES ON ranger.* TO 'rangeradmin'@'localhost'; +GRANT ALL PRIVILEGES ON ranger.* TO 'rangeradmin'@'ranger-db'; + +CREATE USER 'hive'@'%' IDENTIFIED BY 'rangerR0cks!'; +CREATE USER 'hive'@'localhost' IDENTIFIED BY 'rangerR0cks!'; +CREATE USER 'hive'@'ranger-db' IDENTIFIED BY 'rangerR0cks!'; + +CREATE DATABASE hive; +GRANT ALL PRIVILEGES ON hive.* TO 'hive'@'%'; +GRANT ALL PRIVILEGES ON hive.* TO 'hive'@'localhost'; +GRANT ALL PRIVILEGES ON hive.* TO 'hive'@'ranger-db'; + +FLUSH PRIVILEGES; diff --git a/dev-support/ranger-docker/config/init_postgres.sh b/dev-support/ranger-docker/config/init_postgres.sh new file mode 100644 index 0000000000..2a739d199a --- /dev/null +++ b/dev-support/ranger-docker/config/init_postgres.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# 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. + + +set -e + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + CREATE USER rangeradmin WITH PASSWORD 'rangerR0cks!'; + CREATE DATABASE ranger; + GRANT ALL PRIVILEGES ON DATABASE ranger TO rangeradmin; + + CREATE USER hive WITH PASSWORD 'rangerR0cks!'; + CREATE DATABASE hive; + GRANT ALL PRIVILEGES ON DATABASE hive TO hive; +EOSQL diff --git a/dev-support/ranger-docker/config/my.cnf b/dev-support/ranger-docker/config/my.cnf new file mode 100644 index 0000000000..77e958eaca --- /dev/null +++ b/dev-support/ranger-docker/config/my.cnf @@ -0,0 +1,23 @@ +# The MariaDB configuration file +# +# The MariaDB/MySQL tools read configuration files in the following order: +# 0. "/etc/mysql/my.cnf" +# 1. "/etc/mysql/mariadb.cnf" to set global defaults, +# 2. "/etc/mysql/conf.d/*.cnf" to set global options. +# 3. "/etc/mysql/mariadb.conf.d/*.cnf" to set MariaDB-only options. +# 4. "~/.my.cnf" (this file) to set user-specific options. +# + +# +# This group is read both by the client and the server +# use it for options that affect everything +# +[client-server] + +# Import all .cnf files from configuration directory +[mariadbd] +collation-server = utf8_unicode_ci +init-connect = 'SET NAMES utf8' +character_set_server = utf8 +# transaction-isolation = REPEATABLE-READ +# transaction-isolation = READ-COMMITTED diff --git a/dev-support/ranger-docker/config/ozone/docker-config b/dev-support/ranger-docker/config/ozone/docker-config new file mode 100644 index 0000000000..ae06dde876 --- /dev/null +++ b/dev-support/ranger-docker/config/ozone/docker-config @@ -0,0 +1,68 @@ +# 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. + +CORE-SITE.XML_fs.defaultFS=ofs://om +CORE-SITE.XML_fs.trash.interval=1 +# For HttpFS service it is required to enable proxying users. +CORE-SITE.XML_hadoop.proxyuser.hadoop.hosts=* +CORE-SITE.XML_hadoop.proxyuser.hadoop.groups=* + +OZONE-SITE.XML_ozone.om.address=om +OZONE-SITE.XML_ozone.om.http-address=om:9874 +OZONE-SITE.XML_ozone.scm.http-address=scm:9876 +OZONE-SITE.XML_ozone.scm.container.size=1GB +OZONE-SITE.XML_ozone.scm.block.size=1MB +OZONE-SITE.XML_ozone.scm.datanode.ratis.volume.free-space.min=10MB +OZONE-SITE.XML_ozone.scm.pipeline.creation.interval=30s +OZONE-SITE.XML_ozone.scm.pipeline.owner.container.count=1 +OZONE-SITE.XML_ozone.scm.names=scm +OZONE-SITE.XML_ozone.scm.datanode.id.dir=/data +OZONE-SITE.XML_ozone.scm.block.client.address=scm +OZONE-SITE.XML_ozone.metadata.dirs=/data/metadata +#OZONE-SITE.XML_ozone.recon.db.dir=/data/metadata/recon +OZONE-SITE.XML_ozone.scm.client.address=scm +OZONE-SITE.XML_hdds.datanode.dir=/data/hdds +OZONE-SITE.XML_hdds.datanode.volume.min.free.space=100MB +#OZONE-SITE.XML_ozone.recon.address=recon:9891 +#OZONE-SITE.XML_ozone.recon.http-address=0.0.0.0:9888 +#OZONE-SITE.XML_ozone.recon.https-address=0.0.0.0:9889 +#OZONE-SITE.XML_ozone.recon.om.snapshot.task.interval.delay=1m +OZONE-SITE.XML_ozone.datanode.pipeline.limit=1 +OZONE-SITE.XML_hdds.scmclient.max.retry.timeout=30s +OZONE-SITE.XML_hdds.container.report.interval=60s +OZONE-SITE.XML_ozone.scm.stale.node.interval=30s +OZONE-SITE.XML_ozone.scm.dead.node.interval=45s +OZONE-SITE.XML_hdds.heartbeat.interval=5s +OZONE-SITE.XML_ozone.scm.close.container.wait.duration=5s +OZONE-SITE.XML_hdds.scm.replication.thread.interval=15s +OZONE-SITE.XML_hdds.scm.replication.under.replicated.interval=5s +OZONE-SITE.XML_hdds.scm.replication.over.replicated.interval=5s +OZONE-SITE.XML_hdds.scm.wait.time.after.safemode.exit=30s +OZONE-SITE.XML_ozone.http.basedir=/tmp/ozone_http + +OZONE-SITE.XML_hdds.container.ratis.datastream.enabled=true + +OZONE_CONF_DIR=/etc/hadoop +OZONE_LOG_DIR=/var/log/hadoop + +no_proxy=om,scm,s3g,recon,kdc,localhost,127.0.0.1 + +# Explicitly enable filesystem snapshot feature for this Docker compose cluster +OZONE-SITE.XML_ozone.filesystem.snapshot.enabled=true + +# To enable Ranger as the Authorizer in Ozone +OZONE-SITE.XML_ozone.acl.enabled=true +OZONE-SITE.XML_ozone.acl.authorizer.class=org.apache.ranger.authorization.ozone.authorizer.RangerOzoneAuthorizer diff --git a/dev-support/ranger-docker/config/ozone/enable-ozone-plugin.sh b/dev-support/ranger-docker/config/ozone/enable-ozone-plugin.sh new file mode 100644 index 0000000000..ac78458a34 --- /dev/null +++ b/dev-support/ranger-docker/config/ozone/enable-ozone-plugin.sh @@ -0,0 +1,497 @@ +#!/bin/bash + +# 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. + +function getInstallProperty() { + local propertyName=$1 + local propertyValue="" + + for file in "${COMPONENT_INSTALL_ARGS}" "${INSTALL_ARGS}" + do + if [ -f "${file}" ] + then + propertyValue=`grep "^${propertyName}[ \t]*=" ${file} | awk -F= '{ sub("^[ \t]*", "", $2); sub("[ \t]*$", "", $2); print $2 }'` + if [ "${propertyValue}" != "" ] + then + break + fi + fi + done + + echo ${propertyValue} +} + +# +# Base env variable for Ranger related files/directories +# +PROJ_NAME=ranger + +# +# The script should be run by "root" user +# + +if [ ! -w /etc/passwd ] +then + echo "ERROR: $0 script should be run as root." + exit 1 +fi + +#Check for JAVA_HOME +if [ "${JAVA_HOME}" == "" ] +then + echo "ERROR: JAVA_HOME environment property not defined, aborting installation." + exit 1 +fi + +# +# Identify the component, action from the script file +# + +basedir=`dirname $0` +if [ "${basedir}" = "." ] +then + basedir=`pwd` +elif [ "${basedir}" = ".." ] +then + basedir=`(cd .. ;pwd)` +fi + +# +# As this script is common to all component, find the component name based on the script-name +# + +COMPONENT_NAME=`basename $0 | cut -d. -f1 | sed -e 's:^disable-::' | sed -e 's:^enable-::'` + +echo "${COMPONENT_NAME}" | grep 'plugin' > /dev/null 2>&1 + +if [ $? -ne 0 ] +then + echo "$0 : is not applicable for component [${COMPONENT_NAME}]. It is applicable only for ranger plugin component; Exiting ..." + exit 0 +fi + +HCOMPONENT_NAME=`echo ${COMPONENT_NAME} | sed -e 's:-plugin::'` + +CFG_OWNER_INF="${HCOMPONENT_NAME}:${HCOMPONENT_NAME}" + +if [ "${HCOMPONENT_NAME}" = "hdfs" ] +then + HCOMPONENT_NAME="hadoop" +fi + +# +# Based on script name, identify if the action is enabled or disabled +# + +basename $0 | cut -d. -f1 | grep '^enable-' > /dev/null 2>&1 + +if [ $? -eq 0 ] +then + action=enable +else + action=disable +fi + + +# +# environment variables for enable|disable scripts +# + +PROJ_INSTALL_DIR=`(cd ${basedir} ; pwd)` +SET_ENV_SCRIPT_NAME=set-${COMPONENT_NAME}-env.sh +SET_ENV_SCRIPT_TEMPLATE=${PROJ_INSTALL_DIR}/install/conf.templates/enable/${SET_ENV_SCRIPT_NAME} +DEFAULT_XML_CONFIG=${PROJ_INSTALL_DIR}/install/conf.templates/default/configuration.xml +PROJ_LIB_DIR=${PROJ_INSTALL_DIR}/lib +PROJ_INSTALL_LIB_DIR="${PROJ_INSTALL_DIR}/install/lib" +INSTALL_ARGS="${PROJ_INSTALL_DIR}/install.properties" +COMPONENT_INSTALL_ARGS="${PROJ_INSTALL_DIR}/${COMPONENT_NAME}-install.properties" +JAVA=$JAVA_HOME/bin/java + +PLUGIN_DEPENDENT_LIB_DIR=lib/"${PROJ_NAME}-${COMPONENT_NAME}-impl" +PROJ_LIB_PLUGIN_DIR=${PROJ_INSTALL_DIR}/${PLUGIN_DEPENDENT_LIB_DIR} + +HCOMPONENT_INSTALL_DIR_NAME=$(getInstallProperty 'COMPONENT_INSTALL_DIR_NAME') + +CUSTOM_USER=$(getInstallProperty 'CUSTOM_USER') +CUSTOM_USER=${CUSTOM_USER// } + +CUSTOM_GROUP=$(getInstallProperty 'CUSTOM_GROUP') +CUSTOM_GROUP=${CUSTOM_GROUP// } + +CUSTOM_GROUP_STATUS=${CUSTOM_GROUP}; +CUSTOM_USER_STATUS=${CUSTOM_USER}; +egrep "^$CUSTOM_GROUP" /etc/group >& /dev/null +if [ $? -ne 0 ] +then + CUSTOM_GROUP_STATUS="" +fi +id -u ${CUSTOM_USER} > /dev/null 2>&1 +if [ $? -ne 0 ] +then + CUSTOM_USER_STATUS="" +fi + +if [ ! -z "${CUSTOM_USER_STATUS}" ] && [ ! -z "${CUSTOM_GROUP_STATUS}" ] +then + echo "Custom user and group is available, using custom user and group." + CFG_OWNER_INF="${CUSTOM_USER}:${CUSTOM_GROUP}" +elif [ ! -z "${CUSTOM_USER_STATUS}" ] && [ -z "${CUSTOM_GROUP_STATUS}" ] +then + echo "Custom user is available, using custom user and default group." + CFG_OWNER_INF="${CUSTOM_USER}:${HCOMPONENT_NAME}" +elif [ -z "${CUSTOM_USER_STATUS}" ] && [ ! -z "${CUSTOM_GROUP_STATUS}" ] +then + echo "Custom group is available, using default user and custom group." + CFG_OWNER_INF="${HCOMPONENT_NAME}:${CUSTOM_GROUP}" +else + echo "Custom user and group are not available, using default user and group." + CFG_OWNER_INF="${HCOMPONENT_NAME}:${HCOMPONENT_NAME}" +fi + +if [ "${HCOMPONENT_INSTALL_DIR_NAME}" = "" ] +then + HCOMPONENT_INSTALL_DIR_NAME=${HCOMPONENT_NAME} +fi + +firstletter=${HCOMPONENT_INSTALL_DIR_NAME:0:1} +if [ "$firstletter" = "/" ]; then + hdir=${HCOMPONENT_INSTALL_DIR_NAME} +else + hdir=${PROJ_INSTALL_DIR}/../${HCOMPONENT_INSTALL_DIR_NAME} +fi + +# +# TEST - START +# +if [ ! -d ${hdir} ] +then + mkdir -p ${hdir} +fi +# +# TEST - END +# +HCOMPONENT_INSTALL_DIR=`(cd ${hdir} ; pwd)` +HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/lib +if [ "${HCOMPONENT_NAME}" = "hadoop" ] || + [ "${HCOMPONENT_NAME}" = "yarn" ]; then + HCOMPONENT_LIB_DIR=${HCOMPONENT_INSTALL_DIR}/share/hadoop/hdfs/lib +fi + +HCOMPONENT_CONF_DIR=${HCOMPONENT_INSTALL_DIR}/conf +HCOMPONENT_ARCHIVE_CONF_DIR=${HCOMPONENT_CONF_DIR}/.archive +SET_ENV_SCRIPT=${HCOMPONENT_CONF_DIR}/${SET_ENV_SCRIPT_NAME} + + +if [ ! -d "${HCOMPONENT_INSTALL_DIR}" ] +then + echo "ERROR: Unable to find the install directory of component [${HCOMPONENT_NAME}]; dir [${HCOMPONENT_INSTALL_DIR}] not found." + echo "Exiting installation." + exit 1 +fi + +if [ ! -d "${HCOMPONENT_CONF_DIR}" ] +then + echo "ERROR: Unable to find the conf directory of component [${HCOMPONENT_NAME}]; dir [${HCOMPONENT_CONF_DIR}] not found." + echo "Exiting installation." + exit 1 +fi + +if [ ! -d "${HCOMPONENT_LIB_DIR}" ] +then + mkdir -p "${HCOMPONENT_LIB_DIR}" + if [ ! -d "${HCOMPONENT_LIB_DIR}" ] + then + echo "ERROR: Unable to find the lib directory of component [${HCOMPONENT_NAME}]; dir [${HCOMPONENT_LIB_DIR}] not found." + echo "Exiting installation." + exit 1 + fi +fi + +# +# Common functions used by all enable/disable scripts +# + +log() { + echo "+ `date` : $*" +} + + +create_jceks() { + + alias=$1 + pass=$2 + jceksFile=$3 + + if [ -f "${jceksFile}" ] + then + jcebdir=`dirname ${jceksFile}` + jcebname=`basename ${jceksFile}` + archive_jce=${jcebdir}/.${jcebname}.`date '+%Y%m%d%H%M%S'` + log "Saving current JCE file: ${jceksFile} to ${archive_jce} ..." + cp ${jceksFile} ${archive_jce} + fi + + tempFile=/tmp/jce.$$.out + + $JAVA_HOME/bin/java -cp ":${PROJ_INSTALL_LIB_DIR}/*:" org.apache.ranger.credentialapi.buildks create "${alias}" -value "${pass}" -provider "jceks://file${jceksFile}" > ${tempFile} 2>&1 + + if [ $? -ne 0 ] + then + echo "Unable to store password in non-plain text format. Error: [`cat ${tempFile}`]" + echo "Exiting plugin installation" + rm -f ${tempFile} + exit 0 + fi + + rm -f ${tempFile} +} + +log "${HCOMPONENT_NAME}: lib folder=$HCOMPONENT_LIB_DIR conf folder=$HCOMPONENT_CONF_DIR" + +# +# If there is a set-ranger-${COMPONENT}-env.sh, install it +# +dt=`date '+%Y%m%d-%H%M%S'` + +if [ -f "${SET_ENV_SCRIPT_TEMPLATE}" ] +then + # + # If the setenv script already exists, move it to the archive folder + # + if [ -f "${SET_ENV_SCRIPT}" ] + then + if [ ! -d "${HCOMPONENT_ARCHIVE_CONF_DIR}" ] + then + mkdir -p ${HCOMPONENT_ARCHIVE_CONF_DIR} + fi + log "Saving current ${SET_ENV_SCRIPT_NAME} to ${HCOMPONENT_ARCHIVE_CONF_DIR} ..." + mv ${SET_ENV_SCRIPT} ${HCOMPONENT_ARCHIVE_CONF_DIR}/${SET_ENV_SCRIPT_NAME}.${dt} + fi + + if [ "${action}" = "enable" ] + then + + cp ${SET_ENV_SCRIPT_TEMPLATE} ${SET_ENV_SCRIPT} + + DEST_SCRIPT_FILE=${HCOMPONENT_INSTALL_DIR}/libexec/${HCOMPONENT_NAME}-config.sh + + DEST_SCRIPT_ARCHIVE_FILE=${HCOMPONENT_INSTALL_DIR}/libexec/.${HCOMPONENT_NAME}-config.sh.${dt} + + if [ -f "${DEST_SCRIPT_FILE}" ] + then + + log "Saving current ${DEST_SCRIPT_FILE} to ${DEST_SCRIPT_ARCHIVE_FILE} ..." + + cp ${DEST_SCRIPT_FILE} ${DEST_SCRIPT_ARCHIVE_FILE} + + grep 'xasecure-.*-env.sh' ${DEST_SCRIPT_FILE} > /dev/null 2>&1 + if [ $? -eq 0 ] + then + ts=`date '+%Y%m%d%H%M%S'` + grep -v 'xasecure-.*-env.sh' ${DEST_SCRIPT_FILE} > ${DEST_SCRIPT_FILE}.${ts} + if [ $? -eq 0 ] + then + log "Removing old reference to xasecure setenv source ..." + cat ${DEST_SCRIPT_FILE}.${ts} > ${DEST_SCRIPT_FILE} + rm -f ${DEST_SCRIPT_FILE}.${ts} + fi + fi + + grep "[ \t]*.[ \t]*${SET_ENV_SCRIPT}" ${DEST_SCRIPT_FILE} > /dev/null + if [ $? -ne 0 ] + then + log "Appending sourcing script, ${SET_ENV_SCRIPT_NAME} in the file: ${DEST_SCRIPT_FILE} " + cat >> ${DEST_SCRIPT_FILE} <\n`date`\n" > ${HCOMPONENT_CONF_DIR}/ranger-security.xml + chown ${CFG_OWNER_INF} ${HCOMPONENT_CONF_DIR}/ranger-security.xml + chmod a+r ${HCOMPONENT_CONF_DIR}/ranger-security.xml + for cf in ${PROJ_INSTALL_DIR}/install/conf.templates/${action}/*.xml + do + cfb=`basename ${cf}` + if [ -f "${HCOMPONENT_CONF_DIR}/${cfb}" ] + then + log "Saving ${HCOMPONENT_CONF_DIR}/${cfb} to ${HCOMPONENT_CONF_DIR}/.${cfb}.${dt} ..." + cp ${HCOMPONENT_CONF_DIR}/${cfb} ${HCOMPONENT_CONF_DIR}/.${cfb}.${dt} + fi + cp ${cf} ${HCOMPONENT_CONF_DIR}/ + chown ${CFG_OWNER_INF} ${HCOMPONENT_CONF_DIR}/${cfb} + chmod a+r ${HCOMPONENT_CONF_DIR}/${cfb} + done + else + if [ -f ${HCOMPONENT_CONF_DIR}/ranger-security.xml ] + then + mv ${HCOMPONENT_CONF_DIR}/ranger-security.xml ${HCOMPONENT_CONF_DIR}/.ranger-security.xml.`date '+%Y%m%d%H%M%S'` + fi + fi + + # + # Ensure that POLICY_CACHE_FILE_PATH is accessible + # + REPO_NAME=$(getInstallProperty 'REPOSITORY_NAME') + export POLICY_CACHE_FILE_PATH=/etc/${PROJ_NAME}/${REPO_NAME}/policycache + export CREDENTIAL_PROVIDER_FILE=/etc/${PROJ_NAME}/${REPO_NAME}/cred.jceks + if [ ! -d ${POLICY_CACHE_FILE_PATH} ] + then + mkdir -p ${POLICY_CACHE_FILE_PATH} + fi + chmod a+rx /etc/${PROJ_NAME} + chmod a+rx /etc/${PROJ_NAME}/${REPO_NAME} + chmod a+rx ${POLICY_CACHE_FILE_PATH} + chown -R ${CFG_OWNER_INF} /etc/${PROJ_NAME}/${REPO_NAME} + + for f in ${PROJ_INSTALL_DIR}/install/conf.templates/${action}/*.cfg + do + if [ -f "${f}" ] + then + fn=`basename $f` + orgfn=`echo $fn | sed -e 's:-changes.cfg:.xml:'` + fullpathorgfn="${HCOMPONENT_CONF_DIR}/${orgfn}" + if [ ! -f ${fullpathorgfn} ] + then + if [ -f ${DEFAULT_XML_CONFIG} ] + then + log "Creating default file from [${DEFAULT_XML_CONFIG}] for [${fullpathorgfn}] .." + cp ${DEFAULT_XML_CONFIG} ${fullpathorgfn} + chown ${CFG_OWNER_INF} ${fullpathorgfn} + chmod a+r ${fullpathorgfn} + else + echo "ERROR: Unable to find ${fullpathorgfn}" + exit 1 + fi + fi + archivefn="${HCOMPONENT_CONF_DIR}/.${orgfn}.${dt}" + newfn="${HCOMPONENT_CONF_DIR}/.${orgfn}-new.${dt}" + log "Saving current config file: ${fullpathorgfn} to ${archivefn} ..." + cp ${fullpathorgfn} ${archivefn} + if [ $? -eq 0 ] + then + ${JAVA} -cp "${INSTALL_CP}" org.apache.ranger.utils.install.XmlConfigChanger -i ${archivefn} -o ${newfn} -c ${f} -p ${INSTALL_ARGS} + if [ $? -eq 0 ] + then + diff -w ${newfn} ${fullpathorgfn} > /dev/null 2>&1 + if [ $? -ne 0 ] + then + cat ${newfn} > ${fullpathorgfn} + fi + + else + echo "ERROR: Unable to make changes to config. file: ${fullpathorgfn}" + echo "exiting ...." + exit 1 + fi + else + echo "ERROR: Unable to save config. file: ${fullpathorgfn} to ${archivefn}" + echo "exiting ...." + exit 1 + fi + fi + done +fi + +# +# Create library link +# +if [ "${action}" = "enable" ] +then + dt=`date '+%Y%m%d%H%M%S'` + # + # Encrypt the password and keep it secure in Credential Provider API + # + CredFile=${CREDENTIAL_PROVIDER_FILE} + if ! [ `echo ${CredFile} | grep '^/.*'` ] + then + echo "ERROR:Please enter the Credential File Store with proper file path" + exit 1 + fi + + pardir=`dirname ${CredFile}` + + if [ ! -d "${pardir}" ] + then + mkdir -p "${pardir}" + if [ $? -ne 0 ] + then + echo "ERROR: Unable to create credential store file path" + exit 1 + fi + chmod a+rx "${pardir}" + fi +fi + + +#Check Properties whether in File, return code 1 if not exist +#$1 -> propertyName; $2 -> fileName +checkPropertyInFile(){ + validate=$(sed '/^\#/d' $2 | grep "^$1" | tail -n 1 | cut -d "=" -f1-) # for validation + if test -z "$validate" ; then return 1; fi +} + +#Add Properties to File +#$1 -> propertyName; $2 -> newPropertyValue; $3 -> fileName +addPropertyToFile(){ + echo "$1=$2">>$3 + validate=$(sed '/^\#/d' $3 | grep "^$1" | tail -n 1 | cut -d "=" -f2-) # for validation + if test -z "$validate" ; then log "[E] Failed to add properties '$1' to $3 file!"; exit 1; fi + echo "Property $1 added successfully with : '$2'" +} + +#Update Properties to File +#$1 -> propertyName; $2 -> newPropertyValue; $3 -> fileName +updatePropertyToFile(){ + sed -i 's@^'$1'=[^ ]*$@'$1'='$2'@g' $3 + validate=$(sed '/^\#/d' $3 | grep "^$1" | tail -n 1 | cut -d "=" -f2-) # for validation + if test -z "$validate" ; then log "[E] '$1' not found in $3 file while Updating....!!"; exit 1; fi + echo "Property $1 updated successfully with : '$2'" +} + +#Add or Update Properties to File +#$1 -> propertyName; $2 -> newPropertyValue; $3 -> fileName +addOrUpdatePropertyToFile(){ + checkPropertyInFile $1 $3 + if [ $? -eq 1 ] + then + addPropertyToFile $1 $2 $3 + else + updatePropertyToFile $1 $2 $3 + fi +} + + +# Set notice to restart the ${HCOMPONENT_NAME} +echo "Ranger Plugin for ${HCOMPONENT_NAME} has been ${action}d. Please restart ${HCOMPONENT_NAME} to ensure that changes are effective." + +exit 0 diff --git a/dev-support/ranger-docker/config/ozone/ranger-ozone-plugin-install.properties b/dev-support/ranger-docker/config/ozone/ranger-ozone-plugin-install.properties new file mode 100644 index 0000000000..b0e4ee1955 --- /dev/null +++ b/dev-support/ranger-docker/config/ozone/ranger-ozone-plugin-install.properties @@ -0,0 +1,83 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_ozone +COMPONENT_INSTALL_DIR_NAME=ranger-ozone-plugin + +CUSTOM_USER=ozone +CUSTOM_GROUP=hadoop + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=__REPLACE__LOG_DIR/ozone/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=__REPLACE__LOG_DIR/ozone/audit/archive +XAAUDIT.HDFS.DESTINTATION_FILE=%hostname%-audit.log +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SUMMARY.ENABLE=true + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/ozone/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=false +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/ozone/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=true +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=true +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/dev-support/ranger-docker/config/ozone/ranger-ozone-setup.sh b/dev-support/ranger-docker/config/ozone/ranger-ozone-setup.sh new file mode 100644 index 0000000000..8c52474fd0 --- /dev/null +++ b/dev-support/ranger-docker/config/ozone/ranger-ozone-setup.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# 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. + +cd "${OZONE_HOME}"/ranger-ozone-plugin || exit + +if [[ ! -f "${OZONE_HOME}"/.setupDone ]]; +then + if [ ! -d conf ]; then + mkdir -p conf + echo "conf directory created!" + else + echo "conf directory exists already!" + fi + echo "export JAVA_HOME=${JAVA_HOME}" >> conf/ozone-env.sh + sudo JAVA_HOME=/usr/lib/jvm/jre/ ./enable-ozone-plugin.sh + touch "${OZONE_HOME}"/.setupDone +else + echo "Ranger Ozone Plugin Installation is already complete!" +fi diff --git a/dev-support/ranger-docker/config/solr-ranger_audits/managed-schema b/dev-support/ranger-docker/config/solr-ranger_audits/managed-schema new file mode 100644 index 0000000000..df53a05dfb --- /dev/null +++ b/dev-support/ranger-docker/config/solr-ranger_audits/managed-schema @@ -0,0 +1,97 @@ + + + + id + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev-support/ranger-docker/config/solr-ranger_audits/solrconfig.xml b/dev-support/ranger-docker/config/solr-ranger_audits/solrconfig.xml new file mode 100644 index 0000000000..33891833c4 --- /dev/null +++ b/dev-support/ranger-docker/config/solr-ranger_audits/solrconfig.xml @@ -0,0 +1,1148 @@ + + + + + + + + + 8.4.1 + + + + + + + + + + + + + ${solr.data.dir:} + + + + + + + + + + ${solr.hdfs.home:} + + ${solr.hdfs.confdir:} + + ${solr.hdfs.blockcache.enabled:true} + + ${solr.hdfs.blockcache.global:true} + + + + + + + + true + managed-schema + + + + + + + + + + + + + 128 + + + + + + + + + + + + + ${solr.lock.type:native} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${solr.ulog.dir:} + ${solr.ulog.tlogDfsReplication:3} + ${solr.ulog.numVersionBuckets:65536} + + + + + ${solr.autoCommit.maxTime:60000} + false + + + + + + ${solr.autoSoftCommit.maxTime:15000} + + + + + + + + + + + + + + + + + ${solr.max.booleanClauses:1024} + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + 20 + + + 200 + + + + + + + + + + + + + + + + false + + + 5000 + + + + + + + + + + + + + + + + + + + + + + + explicit + 10 + + + + + + + + + + + + + + + + explicit + json + true + + + + + + _text_ + + + + + + + + + text_general + + + + + + default + _text_ + solr.DirectSolrSpellChecker + + internal + + 0.5 + + 2 + + 1 + + 5 + + 4 + + 0.01 + + + + + + + + + + + + default + on + true + 10 + 5 + 5 + true + true + 10 + 5 + + + spellcheck + + + + + + + + + + true + false + + + terms + + + + + + + + + + + 100 + + + + + + + + 70 + + 0.5 + + [-\w ,/\n\"']{20,200} + + + + + + + ]]> + ]]> + + + + + + + + + + + + + + + + + + + + + + + + ,, + ,, + ,, + ,, + ,]]> + ]]> + + + + + + 10 + .,!? + + + + + + + WORD + + + en + US + + + + + + + + + + + + [^\w-\.] + _ + + + + + + + yyyy-MM-dd['T'[HH:mm[:ss[.SSS]][z + yyyy-MM-dd['T'[HH:mm[:ss[,SSS]][z + yyyy-MM-dd HH:mm[:ss[.SSS]][z + yyyy-MM-dd HH:mm[:ss[,SSS]][z + [EEE, ]dd MMM yyyy HH:mm[:ss] z + EEEE, dd-MMM-yy HH:mm:ss z + EEE MMM ppd HH:mm:ss [z ]yyyy + + + + key_lower_case + + java.lang.Boolean + boolean + + + java.util.Date + tdate + + + java.lang.Long + java.lang.Integer + tlong + + + java.lang.Number + tdouble + + + + + + + + + + + + + + + + + + + + text/plain; charset=UTF-8 + + + + + + + + + + + + + + diff --git a/dev-support/ranger-docker/dist/.gitignore b/dev-support/ranger-docker/dist/.gitignore new file mode 100644 index 0000000000..72e8ffc0db --- /dev/null +++ b/dev-support/ranger-docker/dist/.gitignore @@ -0,0 +1 @@ +* diff --git a/dev-support/ranger-docker/docker-compose.ranger-base-ubi.yml b/dev-support/ranger-docker/docker-compose.ranger-base-ubi.yml new file mode 100644 index 0000000000..80db5b4160 --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-base-ubi.yml @@ -0,0 +1,19 @@ +version: '3' +services: + ranger-base: + build: + context: . + dockerfile: Dockerfile.ranger-base-ubi + args: + - UBI_VERSION=${UBI_VERSION} + - RANGER_BASE_UBI_JAVA_VERSION=${RANGER_BASE_UBI_JAVA_VERSION} + image: ranger-base + container_name: ranger-base + networks: + - ranger + environment: + - RANGER_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-base.yml b/dev-support/ranger-docker/docker-compose.ranger-base.yml new file mode 100644 index 0000000000..e7d17408ae --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-base.yml @@ -0,0 +1,19 @@ +version: '3' +services: + ranger-base: + build: + context: . + dockerfile: Dockerfile.ranger-base + args: + - UBUNTU_VERSION=${UBUNTU_VERSION} + - RANGER_BASE_JAVA_VERSION=${RANGER_BASE_JAVA_VERSION} + image: ranger-base + container_name: ranger-base + networks: + - ranger + environment: + - RANGER_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-build.yml b/dev-support/ranger-docker/docker-compose.ranger-build.yml new file mode 100644 index 0000000000..c8760aab5a --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-build.yml @@ -0,0 +1,33 @@ +version: '3' +services: + ranger-build: + build: + context: . + dockerfile: Dockerfile.ranger-build + args: + - RANGER_BUILD_JAVA_VERSION=${RANGER_BUILD_JAVA_VERSION} + image: ranger-build + container_name: ranger-build + hostname: ranger-build.example.com + networks: + - ranger + volumes: + - ${HOME:-~}/.m2:/home/ranger/.m2:delegated + - ./scripts:/home/ranger/scripts + - ./patches:/home/ranger/patches + - ./dist:/home/ranger/dist:delegated + - ${RANGER_HOME:-./../../}:/home/ranger/src:delegated + depends_on: + - ranger-base + environment: + - BRANCH + - BUILD_HOST_SRC + - BUILD_OPTS + - PROFILE + - GIT_URL + - RANGER_VERSION + - SKIPTESTS + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-hadoop.yml b/dev-support/ranger-docker/docker-compose.ranger-hadoop.yml new file mode 100644 index 0000000000..c6aade0e72 --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-hadoop.yml @@ -0,0 +1,37 @@ +version: '3' +services: + ranger-hadoop: + build: + context: . + dockerfile: Dockerfile.ranger-hadoop + args: + - HADOOP_VERSION=${HADOOP_VERSION} + - HDFS_PLUGIN_VERSION=${HDFS_PLUGIN_VERSION} + - YARN_PLUGIN_VERSION=${YARN_PLUGIN_VERSION} + image: ranger-hadoop + container_name: ranger-hadoop + hostname: ranger-hadoop.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "9000:9000" + - "8088:8088" + depends_on: + ranger: + condition: service_started + healthcheck: + test: "hdfs dfs -ls /hbase" + interval: 1m30s + timeout: 10s + retries: 30 + start_period: 40s + environment: + - HADOOP_VERSION + - HDFS_PLUGIN_VERSION + - YARN_PLUGIN_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-hbase.yml b/dev-support/ranger-docker/docker-compose.ranger-hbase.yml new file mode 100644 index 0000000000..5df951c7b5 --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-hbase.yml @@ -0,0 +1,33 @@ +version: '3' +services: + ranger-hbase: + build: + context: . + dockerfile: Dockerfile.ranger-hbase + args: + - HBASE_VERSION=${HBASE_VERSION} + - HBASE_PLUGIN_VERSION=${HBASE_PLUGIN_VERSION} + image: ranger-hbase + container_name: ranger-hbase + hostname: ranger-hbase.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "16000:16000" + - "16010:16010" + - "16020:16020" + - "16030:16030" + depends_on: + ranger-hadoop: + condition: service_healthy + ranger-zk: + condition: service_started + environment: + - HBASE_VERSION + - HBASE_PLUGIN_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-hive.yml b/dev-support/ranger-docker/docker-compose.ranger-hive.yml new file mode 100644 index 0000000000..01cd4b8b7c --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-hive.yml @@ -0,0 +1,36 @@ +version: '3' +services: + ranger-hive: + build: + context: . + dockerfile: Dockerfile.ranger-hive + args: + - HIVE_HADOOP_VERSION=${HIVE_HADOOP_VERSION} + - HIVE_VERSION=${HIVE_VERSION} + - HIVE_PLUGIN_VERSION=${HIVE_PLUGIN_VERSION} + - RANGER_DB_TYPE=${RANGER_DB_TYPE} + image: ranger-hive + container_name: ranger-hive + hostname: ranger-hive.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "10000:10000" + depends_on: + ranger: + condition: service_started + ranger-zk: + condition: service_started + ranger-hadoop: + condition: service_healthy + environment: + - HIVE_HADOOP_VERSION + - HIVE_VERSION + - HIVE_PLUGIN_VERSION + - RANGER_DB_TYPE + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-kafka.yml b/dev-support/ranger-docker/docker-compose.ranger-kafka.yml new file mode 100644 index 0000000000..c59847173b --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-kafka.yml @@ -0,0 +1,30 @@ +version: '3' +services: + ranger-kafka: + build: + context: . + dockerfile: Dockerfile.ranger-kafka + args: + - KAFKA_VERSION=${KAFKA_VERSION} + - KAFKA_PLUGIN_VERSION=${KAFKA_PLUGIN_VERSION} + image: ranger-kafka + container_name: ranger-kafka + hostname: ranger-kafka.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "6667:6667" + depends_on: + ranger: + condition: service_started + ranger-zk: + condition: service_started + environment: + - KAFKA_VERSION + - KAFKA_PLUGIN_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-kms.yml b/dev-support/ranger-docker/docker-compose.ranger-kms.yml new file mode 100644 index 0000000000..3808e424ef --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-kms.yml @@ -0,0 +1,30 @@ +version: '3' +services: + ranger-kms: + build: + context: . + dockerfile: Dockerfile.ranger-kms + args: + - KMS_VERSION=${KMS_VERSION} + - RANGER_DB_TYPE=${RANGER_DB_TYPE} + image: ranger-kms:latest + container_name: ranger-kms + hostname: ranger-kms.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "9292:9292" + depends_on: + ranger: + condition: service_started + environment: + - KMS_VERSION + - RANGER_DB_TYPE + command: + - /home/ranger/scripts/ranger-kms.sh + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-knox.yml b/dev-support/ranger-docker/docker-compose.ranger-knox.yml new file mode 100644 index 0000000000..265f6e4bf3 --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-knox.yml @@ -0,0 +1,30 @@ +version: '3' +services: + ranger-knox: + build: + context: . + dockerfile: Dockerfile.ranger-knox + args: + - KNOX_VERSION=${KNOX_VERSION} + - KNOX_PLUGIN_VERSION=${KNOX_PLUGIN_VERSION} + image: ranger-knox + container_name: ranger-knox + hostname: ranger-knox.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "8443:8443" + depends_on: + ranger: + condition: service_started + ranger-zk: + condition: service_started + environment: + - KNOX_VERSION + - KNOX_PLUGIN_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-mysql.yml b/dev-support/ranger-docker/docker-compose.ranger-mysql.yml new file mode 100644 index 0000000000..b55099c74b --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-mysql.yml @@ -0,0 +1,24 @@ +version: '3' +services: + ranger-db: + build: + context: . + dockerfile: Dockerfile.ranger-mysql + args: + - MARIADB_VERSION=${MARIADB_VERSION} + image: ranger-mysql + command: --default-authentication-plugin=mysql_native_password + container_name: ranger-mysql + hostname: ranger-db.example.com + networks: + - ranger + healthcheck: + # Double dollar($$) is required to expand the env variable + test: "mysql -u root -p$$MYSQL_ROOT_PASSWORD ranger -e 'select 1' > /dev/null" + interval: 10s + timeout: 2s + retries: 30 + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-ozone.yml b/dev-support/ranger-docker/docker-compose.ranger-ozone.yml new file mode 100644 index 0000000000..a0ee4abc9d --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-ozone.yml @@ -0,0 +1,88 @@ +# 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. + +version: "3" +services: + datanode: + image: ${OZONE_RUNNER_IMAGE}:${OZONE_RUNNER_VERSION} + container_name: ozone-datanode + volumes: + - ./downloads/ozone-${OZONE_VERSION}:/opt/hadoop + networks: + - ranger + ports: + - 9864 + command: ["/opt/hadoop/bin/ozone","datanode"] + env_file: + - ./config/ozone/docker-config + environment: + OZONE_OPTS: + om: + build: + context: . + dockerfile: Dockerfile.ranger-ozone + args: + - OZONE_RUNNER_IMAGE=${OZONE_RUNNER_IMAGE} + - OZONE_RUNNER_VERSION=${OZONE_RUNNER_VERSION} + - OZONE_HOME=/opt/hadoop + - OZONE_PLUGIN_VERSION=${OZONE_PLUGIN_VERSION} + image: ranger-ozone:latest + container_name: ozone-om + hostname: om + volumes: + - ./downloads/ozone-${OZONE_VERSION}:/opt/hadoop + - ./dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin:/opt/hadoop/ranger-ozone-plugin + networks: + - ranger + ports: + - 9874:9874 + - 9862:9862 + depends_on: + ranger: + condition: service_started + ranger-solr: + condition: service_started + environment: + ENSURE_OM_INITIALIZED: /data/metadata/om/current/VERSION + OZONE_OPTS: -Dcom.sun.net.ssl.checkRevocation=false + OZONE_HOME: /opt/hadoop + OZONE_PLUGIN_VERSION: ${OZONE_PLUGIN_VERSION} + OZONE_VERSION: ${OZONE_VERSION} + env_file: + - ./config/ozone/docker-config + command: bash -c "/opt/hadoop/ranger-ozone-plugin/ranger-ozone-setup.sh && /opt/hadoop/bin/ozone om" + scm: + image: ${OZONE_RUNNER_IMAGE}:${OZONE_RUNNER_VERSION} + container_name: ozone-scm + hostname: scm + volumes: + - ./downloads/ozone-${OZONE_VERSION}:/opt/hadoop + networks: + - ranger + ports: + - 9876:9876 + - 9860:9860 + env_file: + - ./config/ozone/docker-config + environment: + ENSURE_SCM_INITIALIZED: /data/metadata/scm/current/VERSION + OZONE-SITE.XML_hdds.scm.safemode.min.datanode: "${OZONE_SAFEMODE_MIN_DATANODES:-1}" + OZONE_OPTS: + command: ["/opt/hadoop/bin/ozone","scm"] + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-postgres-mounted.yml b/dev-support/ranger-docker/docker-compose.ranger-postgres-mounted.yml new file mode 100644 index 0000000000..e38510af11 --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-postgres-mounted.yml @@ -0,0 +1,23 @@ +version: '3' +services: + ranger-db: + build: + context: . + dockerfile: Dockerfile.ranger-postgres + args: + - POSTGRES_VERSION=${POSTGRES_VERSION} + image: ranger-postgres + container_name: ranger-postgres + hostname: ranger-db.example.com + networks: + - ranger + volumes: + - ./postgres-db-mount:/var/lib/postgresql/data + healthcheck: + test: 'su -c "pg_isready -q" postgres' + interval: 10s + timeout: 2s + retries: 30 +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-postgres.yml b/dev-support/ranger-docker/docker-compose.ranger-postgres.yml new file mode 100644 index 0000000000..f891684092 --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-postgres.yml @@ -0,0 +1,22 @@ +version: '3' +services: + ranger-db: + build: + context: . + dockerfile: Dockerfile.ranger-postgres + args: + - POSTGRES_VERSION=${POSTGRES_VERSION} + image: ranger-postgres + container_name: ranger-postgres + hostname: ranger-db.example.com + networks: + - ranger + healthcheck: + test: 'su -c "pg_isready -q" postgres' + interval: 10s + timeout: 2s + retries: 30 + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-tagsync.yml b/dev-support/ranger-docker/docker-compose.ranger-tagsync.yml new file mode 100644 index 0000000000..b4f7f2cb3d --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-tagsync.yml @@ -0,0 +1,25 @@ +version: '3' +services: + ranger-tagsync: + build: + context: . + dockerfile: Dockerfile.ranger-tagsync + args: + - TAGSYNC_VERSION=${TAGSYNC_VERSION} + image: ranger-tagsync + container_name: ranger-tagsync + hostname: ranger-tagsync.example.com + stdin_open: true + tty: true + networks: + - ranger + depends_on: + ranger: + condition: service_started + environment: + - TAGSYNC_VERSION + - DEBUG_TAGSYNC=${DEBUG_TAGSYNC:-false} + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-trino.yml b/dev-support/ranger-docker/docker-compose.ranger-trino.yml new file mode 100644 index 0000000000..14b42c2dbf --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-trino.yml @@ -0,0 +1,28 @@ +version: '3' +services: + trino: + build: + context: . + dockerfile: Dockerfile.ranger-trino + args: + - TRINO_PLUGIN_VERSION=${TRINO_PLUGIN_VERSION} + - TRINO_VERSION=${TRINO_VERSION} + image: ranger-trino + hostname: ranger-trino + container_name: ranger-trino + stdin_open: true + tty: true + networks: + - ranger + ports: + - 8080:8080 + depends_on: + ranger: + condition: service_started + environment: + - TRINO_PLUGIN_VERSION + - TRINO_VERSION + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger-usersync.yml b/dev-support/ranger-docker/docker-compose.ranger-usersync.yml new file mode 100644 index 0000000000..4fdca510cf --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger-usersync.yml @@ -0,0 +1,26 @@ +version: '3' +services: + ranger-usersync: + build: + context: . + dockerfile: Dockerfile.ranger-usersync + args: + - USERSYNC_VERSION=${USERSYNC_VERSION} + image: ranger-usersync + container_name: ranger-usersync + hostname: ranger-usersync.example.com + stdin_open: true + tty: true + networks: + - ranger + depends_on: + ranger: + condition: service_started + environment: + - USERSYNC_VERSION + - ENABLE_FILE_SYNC_SOURCE + - DEBUG_USERSYNC=${DEBUG_USERSYNC:-false} + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/docker-compose.ranger.yml b/dev-support/ranger-docker/docker-compose.ranger.yml new file mode 100644 index 0000000000..9a75bd582d --- /dev/null +++ b/dev-support/ranger-docker/docker-compose.ranger.yml @@ -0,0 +1,68 @@ +version: '3' +services: + ranger: + build: + context: . + dockerfile: Dockerfile.ranger + args: + - RANGER_VERSION=${RANGER_VERSION} + - RANGER_DB_TYPE=${RANGER_DB_TYPE} + - RANGER_ADMIN_JAVA_VERSION=${RANGER_ADMIN_JAVA_VERSION} + image: ranger:latest + container_name: ranger + hostname: ranger.example.com + stdin_open: true + tty: true + networks: + - ranger + ports: + - "6080:6080" + depends_on: + ranger-zk: + condition: service_started + ranger-db: + condition: service_healthy + ranger-solr: + condition: service_started + environment: + - RANGER_VERSION + - RANGER_DB_TYPE + - DEBUG_ADMIN=${DEBUG_ADMIN:-false} + command: + - /home/ranger/scripts/ranger.sh + + ranger-zk: + build: + context: . + dockerfile: Dockerfile.ranger-zk + args: + - ZK_VERSION=${ZK_VERSION} + image: ranger-zk + container_name: ranger-zk + hostname: ranger-zk.example.com + networks: + - ranger + ports: + - "2181:2181" + + ranger-solr: + build: + context: . + dockerfile: Dockerfile.ranger-solr + args: + - SOLR_VERSION=${SOLR_VERSION} + image: ranger-solr + container_name: ranger-solr + hostname: ranger-solr.example.com + networks: + - ranger + ports: + - "8983:8983" + command: + - solr-precreate + - ranger_audits + - /opt/solr/server/solr/configsets/ranger_audits/ + +networks: + ranger: + name: rangernw diff --git a/dev-support/ranger-docker/download-archives.sh b/dev-support/ranger-docker/download-archives.sh new file mode 100755 index 0000000000..b073f6f47c --- /dev/null +++ b/dev-support/ranger-docker/download-archives.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# 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. + +# +# Downloads HDFS/Hive/HBase/Kafka/Knox/Ozone archives to a local cache directory. +# The downloaded archives will be used while building docker images that run these services. +# + +# +# source .env file to get versions to download +# +source .env + + +downloadIfNotPresent() { + local fileName=$1 + local urlBase=$2 + + if [ ! -f "downloads/${fileName}" ] + then + echo "downloading ${urlBase}/${fileName}.." + + curl -L ${urlBase}/${fileName} --output downloads/${fileName} + else + echo "file already in cache: ${fileName}" + fi +} + +downloadIfNotPresent postgresql-42.2.16.jre7.jar "https://search.maven.org/remotecontent?filepath=org/postgresql/postgresql/42.2.16.jre7" +downloadIfNotPresent mysql-connector-java-8.0.28.jar "https://search.maven.org/remotecontent?filepath=mysql/mysql-connector-java/8.0.28" +downloadIfNotPresent log4jdbc-1.2.jar https://repo1.maven.org/maven2/com/googlecode/log4jdbc/log4jdbc/1.2 + +if [[ $# -eq 0 ]] +then + downloadIfNotPresent hadoop-${HADOOP_VERSION}.tar.gz https://archive.apache.org/dist/hadoop/common/hadoop-${HADOOP_VERSION} + downloadIfNotPresent hbase-${HBASE_VERSION}-bin.tar.gz https://archive.apache.org/dist/hbase/${HBASE_VERSION} + downloadIfNotPresent apache-hive-${HIVE_VERSION}-bin.tar.gz https://archive.apache.org/dist/hive/hive-${HIVE_VERSION} + downloadIfNotPresent hadoop-${HIVE_HADOOP_VERSION}.tar.gz https://archive.apache.org/dist/hadoop/common/hadoop-${HIVE_HADOOP_VERSION} + downloadIfNotPresent kafka_2.12-${KAFKA_VERSION}.tgz https://archive.apache.org/dist/kafka/${KAFKA_VERSION} + downloadIfNotPresent knox-${KNOX_VERSION}.tar.gz https://archive.apache.org/dist/knox/${KNOX_VERSION} + downloadIfNotPresent ozone-${OZONE_VERSION}.tar.gz https://archive.apache.org/dist/ozone/${OZONE_VERSION} + if [ ! -d downloads/ozone-${OZONE_VERSION} ] + then + tar xvfz downloads/ozone-${OZONE_VERSION}.tar.gz --directory=downloads/ + fi +else + for arg in "$@"; do + if [[ $arg == 'hadoop' ]] + then + downloadIfNotPresent hadoop-${HADOOP_VERSION}.tar.gz https://archive.apache.org/dist/hadoop/common/hadoop-${HADOOP_VERSION} + elif [[ $arg == 'hbase' ]] + then + downloadIfNotPresent hbase-${HBASE_VERSION}-bin.tar.gz https://archive.apache.org/dist/hbase/${HBASE_VERSION} + elif [[ $arg == 'hive' ]] + then + downloadIfNotPresent apache-hive-${HIVE_VERSION}-bin.tar.gz https://archive.apache.org/dist/hive/hive-${HIVE_VERSION} + downloadIfNotPresent hadoop-${HIVE_HADOOP_VERSION}.tar.gz https://archive.apache.org/dist/hadoop/common/hadoop-${HIVE_HADOOP_VERSION} + elif [[ $arg == 'kafka' ]] + then + downloadIfNotPresent kafka_2.12-${KAFKA_VERSION}.tgz https://archive.apache.org/dist/kafka/${KAFKA_VERSION} + elif [[ $arg == 'knox' ]] + then + downloadIfNotPresent knox-${KNOX_VERSION}.tar.gz https://archive.apache.org/dist/knox/${KNOX_VERSION} + elif [[ $arg == 'ozone' ]] + then + downloadIfNotPresent ozone-${OZONE_VERSION}.tar.gz https://archive.apache.org/dist/ozone/${OZONE_VERSION} + if [ ! -d downloads/ozone-${OZONE_VERSION} ] + then + tar xvfz downloads/ozone-${OZONE_VERSION}.tar.gz --directory=downloads/ + fi + else + echo "Passed argument $arg is invalid!" + fi + done +fi diff --git a/dev-support/ranger-docker/downloads/.gitignore b/dev-support/ranger-docker/downloads/.gitignore new file mode 100644 index 0000000000..72e8ffc0db --- /dev/null +++ b/dev-support/ranger-docker/downloads/.gitignore @@ -0,0 +1 @@ +* diff --git a/dev-support/ranger-docker/patches/.gitignore b/dev-support/ranger-docker/patches/.gitignore new file mode 100644 index 0000000000..72e8ffc0db --- /dev/null +++ b/dev-support/ranger-docker/patches/.gitignore @@ -0,0 +1 @@ +* diff --git a/dev-support/ranger-docker/scripts/create-ranger-services.py b/dev-support/ranger-docker/scripts/create-ranger-services.py new file mode 100644 index 0000000000..45d6a77915 --- /dev/null +++ b/dev-support/ranger-docker/scripts/create-ranger-services.py @@ -0,0 +1,75 @@ +from apache_ranger.model.ranger_service import RangerService +from apache_ranger.client.ranger_client import RangerClient +from json import JSONDecodeError + +ranger_client = RangerClient('http://ranger:6080', ('admin', 'rangerR0cks!')) + + +def service_not_exists(service): + try: + svc = ranger_client.get_service(service.name) + except JSONDecodeError: + return 1 + return 0 if svc is not None else 1 + + +hdfs = RangerService({'name': 'dev_hdfs', 'type': 'hdfs', + 'configs': {'username': 'hdfs', 'password': 'hdfs', + 'fs.default.name': 'hdfs://ranger-hadoop:9000', + 'hadoop.security.authentication': 'simple', + 'hadoop.security.authorization': 'true'}}) + +hive = RangerService({'name': 'dev_hive', 'type': 'hive', + 'configs': {'username': 'hive', 'password': 'hive', + 'jdbc.driverClassName': 'org.apache.hive.jdbc.HiveDriver', + 'jdbc.url': 'jdbc:hive2://ranger-hive:10000', + 'hadoop.security.authorization': 'true'}}) + +kafka = RangerService({'name': 'dev_kafka', 'type': 'kafka', + 'configs': {'username': 'kafka', 'password': 'kafka', + 'zookeeper.connect': 'ranger-zk.example.com:2181'}}) + +knox = RangerService({'name': 'dev_knox', 'type': 'knox', + 'configs': {'username': 'knox', 'password': 'knox', 'knox.url': 'https://ranger-knox:8443'}}) + +yarn = RangerService({'name': 'dev_yarn', 'type': 'yarn', + 'configs': {'username': 'yarn', 'password': 'yarn', + 'yarn.url': 'http://ranger-hadoop:8088'}}) + +hbase = RangerService({'name': 'dev_hbase', 'type': 'hbase', + 'configs': {'username': 'hbase', 'password': 'hbase', + 'hadoop.security.authentication': 'simple', + 'hbase.security.authentication': 'simple', + 'hadoop.security.authorization': 'true', + 'hbase.zookeeper.property.clientPort': '2181', + 'hbase.zookeeper.quorum': 'ranger-zk', + 'zookeeper.znode.parent': '/hbase'}}) + +kms = RangerService({'name': 'dev_kms', 'type': 'kms', + 'configs': {'username': 'keyadmin', 'password': 'rangerR0cks!', + 'provider': 'http://ranger-kms:9292'}}) + +trino = RangerService({'name': 'dev_trino', + 'type': 'trino', + 'configs': { + 'username': 'trino', + 'password': 'trino', + 'jdbc.driverClassName': 'io.trino.jdbc.TrinoDriver', + 'jdbc.url': 'jdbc:trino://ranger-trino:8080', + }}) + +ozone = RangerService({'name': 'dev_ozone', + 'type': 'ozone', + 'displayName': 'dev_ozone', + 'configs': {'username': 'hdfs', 'password': 'hdfs', + 'ozone.om.http-address': 'http://om:9874', + 'hadoop.security.authentication': 'simple'}}) + +services = [hdfs, yarn, hive, hbase, kafka, knox, kms, trino, ozone] +for service in services: + try: + if service_not_exists(service): + ranger_client.create_service(service) + print(f" {service.name} service created!") + except Exception as e: + print(f"An exception occured: {e}") diff --git a/dev-support/ranger-docker/scripts/hbase-site.xml b/dev-support/ranger-docker/scripts/hbase-site.xml new file mode 100644 index 0000000000..6789bf19f7 --- /dev/null +++ b/dev-support/ranger-docker/scripts/hbase-site.xml @@ -0,0 +1,52 @@ + + + + + hbase.cluster.distributed + true + + + hbase.rootdir + hdfs://ranger-hadoop.example.com:9000/hbase + + + hbase.zookeeper.quorum + ranger-zk.example.com + + diff --git a/dev-support/ranger-docker/scripts/hive-site-mysql.xml b/dev-support/ranger-docker/scripts/hive-site-mysql.xml new file mode 100644 index 0000000000..118cdbbca9 --- /dev/null +++ b/dev-support/ranger-docker/scripts/hive-site-mysql.xml @@ -0,0 +1,52 @@ + + + + + javax.jdo.option.ConnectionURL + jdbc:mysql://ranger-db/hive + + + javax.jdo.option.ConnectionDriverName + com.mysql.jdbc.Driver + + + javax.jdo.option.ConnectionUserName + hive + + + javax.jdo.option.ConnectionPassword + rangerR0cks! + + + + hive.server2.enable.doAs + false + + + + hive.zookeeper.quorum + ranger-zk.example.com + + + hive.zookeeper.client.port + 2181 + + diff --git a/dev-support/ranger-docker/scripts/hive-site-postgres.xml b/dev-support/ranger-docker/scripts/hive-site-postgres.xml new file mode 100644 index 0000000000..55343a3234 --- /dev/null +++ b/dev-support/ranger-docker/scripts/hive-site-postgres.xml @@ -0,0 +1,52 @@ + + + + + javax.jdo.option.ConnectionURL + jdbc:postgresql://ranger-db/hive + + + javax.jdo.option.ConnectionDriverName + org.postgresql.Driver + + + javax.jdo.option.ConnectionUserName + hive + + + javax.jdo.option.ConnectionPassword + rangerR0cks! + + + + hive.server2.enable.doAs + false + + + + hive.zookeeper.quorum + ranger-zk.example.com + + + hive.zookeeper.client.port + 2181 + + diff --git a/dev-support/ranger-docker/scripts/ozone-plugin-docker-setup.sh b/dev-support/ranger-docker/scripts/ozone-plugin-docker-setup.sh new file mode 100755 index 0000000000..295fb41d74 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ozone-plugin-docker-setup.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# 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. + +source .env + +if [ ! -d dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin ] +then + tar xvfz dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin.tar.gz --directory=dist/ +fi + +cp -f config/ozone/ranger-ozone-plugin-install.properties dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin/install.properties +cp -f config/ozone/ranger-ozone-setup.sh dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin/ +cp -f config/ozone/enable-ozone-plugin.sh dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin/ +chmod +x dist/ranger-${OZONE_PLUGIN_VERSION}-ozone-plugin/ranger-ozone-setup.sh diff --git a/dev-support/ranger-docker/scripts/ranger-admin-install-mysql.properties b/dev-support/ranger-docker/scripts/ranger-admin-install-mysql.properties new file mode 100644 index 0000000000..4f4ed58980 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-admin-install-mysql.properties @@ -0,0 +1,92 @@ +# 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. + +# +# This file provides a list of the deployment variables for the Policy Manager Web Application +# + +PYTHON_COMMAND_INVOKER=python3 +RANGER_ADMIN_LOG_DIR=/var/log/ranger +RANGER_PID_DIR_PATH=/var/run/ranger +DB_FLAVOR=MYSQL +SQL_CONNECTOR_JAR=/usr/share/java/mysql-connector.jar +RANGER_ADMIN_LOGBACK_CONF_FILE=/opt/ranger/admin/ews/webapp/WEB-INF/classes/conf/logback.xml + +db_root_user=root +db_root_password=rangerR0cks! +db_host=ranger-db + +db_name=ranger +db_user=rangeradmin +db_password=rangerR0cks! + +postgres_core_file=db/postgres/optimized/current/ranger_core_db_postgres.sql +postgres_audit_file=db/postgres/xa_audit_db_postgres.sql +mysql_core_file=db/mysql/optimized/current/ranger_core_db_mysql.sql +mysql_audit_file=db/mysql/xa_audit_db.sql + +rangerAdmin_password=rangerR0cks! +rangerTagsync_password=rangerR0cks! +rangerUsersync_password=rangerR0cks! +keyadmin_password=rangerR0cks! + + +audit_store=solr +audit_solr_urls=http://ranger-solr:8983/solr/ranger_audits +audit_solr_collection_name=ranger_audits + +# audit_store=elasticsearch +audit_elasticsearch_urls= +audit_elasticsearch_port=9200 +audit_elasticsearch_protocol=http +audit_elasticsearch_user=elastic +audit_elasticsearch_password=elasticsearch +audit_elasticsearch_index=ranger_audits +audit_elasticsearch_bootstrap_enabled=true + +policymgr_external_url=http://ranger-admin:6080 +policymgr_http_enabled=true + +unix_user=ranger +unix_user_pwd=ranger +unix_group=ranger + +# Following variables are referenced in db_setup.py. Do not remove these +oracle_core_file= +sqlserver_core_file= +sqlanywhere_core_file= +cred_keystore_filename= + +# ################# DO NOT MODIFY ANY VARIABLES BELOW ######################### +# +# --- These deployment variables are not to be modified unless you understand the full impact of the changes +# +################################################################################ +XAPOLICYMGR_DIR=$PWD +app_home=$PWD/ews/webapp +TMPFILE=$PWD/.fi_tmp +LOGFILE=$PWD/logfile +LOGFILES="$LOGFILE" + +JAVA_BIN='java' +JAVA_VERSION_REQUIRED='1.8' + +ranger_admin_max_heap_size=1g +#retry DB and Java patches after the given time in seconds. +PATCH_RETRY_INTERVAL=120 +STALE_PATCH_ENTRY_HOLD_TIME=10 + +hadoop_conf= +authentication_method=UNIX diff --git a/dev-support/ranger-docker/scripts/ranger-admin-install-postgres.properties b/dev-support/ranger-docker/scripts/ranger-admin-install-postgres.properties new file mode 100644 index 0000000000..26ba2b8acd --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-admin-install-postgres.properties @@ -0,0 +1,92 @@ +# 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. + +# +# This file provides a list of the deployment variables for the Policy Manager Web Application +# + +PYTHON_COMMAND_INVOKER=python3 +RANGER_ADMIN_LOG_DIR=/var/log/ranger +RANGER_PID_DIR_PATH=/var/run/ranger +DB_FLAVOR=POSTGRES +SQL_CONNECTOR_JAR=/usr/share/java/postgresql.jar +RANGER_ADMIN_LOGBACK_CONF_FILE=/opt/ranger/admin/ews/webapp/WEB-INF/classes/conf/logback.xml + +db_root_user=postgres +db_root_password=rangerR0cks! +db_host=ranger-db + +db_name=ranger +db_user=rangeradmin +db_password=rangerR0cks! + +postgres_core_file=db/postgres/optimized/current/ranger_core_db_postgres.sql +postgres_audit_file=db/postgres/xa_audit_db_postgres.sql +mysql_core_file=db/mysql/optimized/current/ranger_core_db_mysql.sql +mysql_audit_file=db/mysql/xa_audit_db.sql + +rangerAdmin_password=rangerR0cks! +rangerTagsync_password=rangerR0cks! +rangerUsersync_password=rangerR0cks! +keyadmin_password=rangerR0cks! + + +audit_store=solr +audit_solr_urls=http://ranger-solr:8983/solr/ranger_audits +audit_solr_collection_name=ranger_audits + +# audit_store=elasticsearch +audit_elasticsearch_urls= +audit_elasticsearch_port=9200 +audit_elasticsearch_protocol=http +audit_elasticsearch_user=elastic +audit_elasticsearch_password=elasticsearch +audit_elasticsearch_index=ranger_audits +audit_elasticsearch_bootstrap_enabled=true + +policymgr_external_url=http://ranger-admin:6080 +policymgr_http_enabled=true + +unix_user=ranger +unix_user_pwd=ranger +unix_group=ranger + +# Following variables are referenced in db_setup.py. Do not remove these +oracle_core_file= +sqlserver_core_file= +sqlanywhere_core_file= +cred_keystore_filename= + +# ################# DO NOT MODIFY ANY VARIABLES BELOW ######################### +# +# --- These deployment variables are not to be modified unless you understand the full impact of the changes +# +################################################################################ +XAPOLICYMGR_DIR=$PWD +app_home=$PWD/ews/webapp +TMPFILE=$PWD/.fi_tmp +LOGFILE=$PWD/logfile +LOGFILES="$LOGFILE" + +JAVA_BIN='java' +JAVA_VERSION_REQUIRED='1.8' + +ranger_admin_max_heap_size=1g +#retry DB and Java patches after the given time in seconds. +PATCH_RETRY_INTERVAL=120 +STALE_PATCH_ENTRY_HOLD_TIME=10 + +hadoop_conf= +authentication_method=UNIX diff --git a/dev-support/ranger-docker/scripts/ranger-build.sh b/dev-support/ranger-docker/scripts/ranger-build.sh new file mode 100755 index 0000000000..0d3d75ea6d --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-build.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +# 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. + + +if [ "${BRANCH}" == "" ] +then + BRANCH=master +fi + +if [ "${GIT_URL}" == "" ] +then + GIT_URL=https://github.com/apache/ranger.git +fi + +if [ "${PROFILE}" != "" ] +then + ARG_PROFILES="-P${PROFILE}" +fi + +if [ "${SKIPTESTS}" == "" ] +then + ARG_SKIPTESTS="-DskipTests" +else + ARG_SKIPTESTS="-DskipTests=${SKIPTESTS}" +fi + +if [ "${BUILD_HOST_SRC}" == "" ] +then + BUILD_HOST_SRC=true +fi + +export MAVEN_OPTS="-Xms2g -Xmx2g" +export M2=/home/ranger/.m2 + + +if [ "${BUILD_HOST_SRC}" == "true" ] +then + if [ ! -f /home/ranger/src/pom.xml ] + then + echo "ERROR: BUILD_HOST_SRC=${BUILD_HOST_SRC}, but /home/ranger/src/pom.xml is not found " + exit 1 + fi + + echo "Building from /home/ranger/src" + + cd /home/ranger/src +else + echo "Building ${BRANCH} branch from ${GIT_URL}" + + cd /home/ranger/git + + if [ -d ranger ] + then + renamedDir=ranger-`date +"%Y%m%d-%H%M%S"` + + echo "Renaming existing directory `pwd`/ranger to ${renamedDir}" + + mv ranger $renamedDir + fi + + git clone --single-branch --branch ${BRANCH} ${GIT_URL} + + cd /home/ranger/git/ranger + + for patch in `ls -1 /home/ranger/patches | sort` + do + echo "applying patch /home/ranger/patches/${patch}" + git apply /home/ranger/patches/${patch} + done +fi + +mvn ${ARG_PROFILES} ${BUILD_OPTS} ${ARG_SKIPTESTS} -DskipDocs clean package + +mv -f target/version /home/ranger/dist/ +mv -f target/ranger-* /home/ranger/dist/ diff --git a/dev-support/ranger-docker/scripts/ranger-hadoop-mkdir.sh b/dev-support/ranger-docker/scripts/ranger-hadoop-mkdir.sh new file mode 100755 index 0000000000..09bbc49c98 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hadoop-mkdir.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# 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. + +# setup directories for Ranger audits +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/hdfs +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/yarn +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/hbaseMaster +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/hbaseRegional +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/kafka +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/hiveServer2 +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /ranger/audit/knox + +${HADOOP_HOME}/bin/hdfs dfs -chown hdfs:hadoop /ranger/audit/hdfs +${HADOOP_HOME}/bin/hdfs dfs -chown yarn:hadoop /ranger/audit/yarn +${HADOOP_HOME}/bin/hdfs dfs -chown hbase:hadoop /ranger/audit/hbaseMaster +${HADOOP_HOME}/bin/hdfs dfs -chown hbase:hadoop /ranger/audit/hbaseRegional +${HADOOP_HOME}/bin/hdfs dfs -chown kafka:hadoop /ranger/audit/kafka +${HADOOP_HOME}/bin/hdfs dfs -chown hive:hadoop /ranger/audit/hiveServer2 +${HADOOP_HOME}/bin/hdfs dfs -chown knox:hadoop /ranger/audit/knox + +# setup directories for HBase +${HADOOP_HOME}/bin/hdfs dfs -mkdir /hbase +${HADOOP_HOME}/bin/hdfs dfs -chown hbase:hadoop /hbase + +# setup directories for Hive +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /user/hive/warehouse +${HADOOP_HOME}/bin/hdfs dfs -mkdir -p /tmp/hive +${HADOOP_HOME}/bin/hdfs dfs -chown -R hive:hadoop /tmp/hive /user/hive +${HADOOP_HOME}/bin/hdfs dfs -chmod 777 /tmp/hive diff --git a/dev-support/ranger-docker/scripts/ranger-hadoop-setup.sh b/dev-support/ranger-docker/scripts/ranger-hadoop-setup.sh new file mode 100755 index 0000000000..10f04acd9f --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hadoop-setup.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# 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. + +echo "export JAVA_HOME=${JAVA_HOME}" >> ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh + +cat < /etc/ssh/ssh_config +Host * + StrictHostKeyChecking no + UserKnownHostsFile=/dev/null +EOF + +cat < ${HADOOP_HOME}/etc/hadoop/core-site.xml + + + fs.defaultFS + hdfs://ranger-hadoop:9000 + + +EOF + +cat < ${HADOOP_HOME}/etc/hadoop/hdfs-site.xml + + + dfs.replication + 1 + + + dfs.webhdfs.enabled + true + + +EOF + +cat < ${HADOOP_HOME}/etc/hadoop/yarn-site.xml + + + yarn.nodemanager.aux-services + mapreduce_shuffle + + + yarn.nodemanager.env-whitelist + JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME + + +EOF + +mkdir -p /opt/hadoop/logs +chown -R hdfs:hadoop /opt/hadoop/ +chmod g+w /opt/hadoop/logs + +cd ${RANGER_HOME}/ranger-hdfs-plugin +./enable-hdfs-plugin.sh + +cd ${RANGER_HOME}/ranger-yarn-plugin +./enable-yarn-plugin.sh diff --git a/dev-support/ranger-docker/scripts/ranger-hadoop.sh b/dev-support/ranger-docker/scripts/ranger-hadoop.sh new file mode 100755 index 0000000000..1ca5af31dc --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hadoop.sh @@ -0,0 +1,71 @@ +#!/bin/bash + +# 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. +if [ "${OS_NAME}" = "UBUNTU" ]; then + service ssh start +fi + +CREATE_HDFS_DIR=false + +if [ ! -e ${HADOOP_HOME}/.setupDone ] +then + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" hdfs + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" hdfs + su -c "chmod 0600 ~/.ssh/authorized_keys" hdfs + + if [ "${OS_NAME}" = "RHEL" ]; then + ssh-keygen -A + /usr/sbin/sshd + fi + + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" yarn + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" yarn + su -c "chmod 0600 ~/.ssh/authorized_keys" yarn + + # pdsh is unavailable with microdnf in rhel based image. + echo "ssh" > /etc/pdsh/rcmd_default + + + if "${RANGER_SCRIPTS}"/ranger-hadoop-setup.sh; + then + su -c "${HADOOP_HOME}/bin/hdfs namenode -format" hdfs + + CREATE_HDFS_DIR=true + + touch "${HADOOP_HOME}"/.setupDone + else + echo "Ranger Hadoop Setup Script didn't complete proper execution." + fi +fi + +su -c "${HADOOP_HOME}/sbin/start-dfs.sh" hdfs +su -c "${HADOOP_HOME}/sbin/start-yarn.sh" yarn + +if [ "${CREATE_HDFS_DIR}" == "true" ] +then + su -c "${RANGER_SCRIPTS}/ranger-hadoop-mkdir.sh" hdfs +fi + +NAMENODE_PID=`ps -ef | grep -v grep | grep -i "org.apache.hadoop.hdfs.server.namenode.NameNode" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$NAMENODE_PID" ] +then + echo "The NameNode process probably exited, no process id found!" +else + tail --pid=$NAMENODE_PID -f /dev/null +fi \ No newline at end of file diff --git a/dev-support/ranger-docker/scripts/ranger-hbase-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-hbase-plugin-install.properties new file mode 100644 index 0000000000..11734b3d24 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hbase-plugin-install.properties @@ -0,0 +1,85 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_hbase +COMPONENT_INSTALL_DIR_NAME=/opt/hbase + +CUSTOM_USER=hbase +CUSTOM_GROUP=hadoop + +XAAUDIT.SUMMARY.ENABLE=true +UPDATE_XAPOLICIES_ON_GRANT_REVOKE=true + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hadoop +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/hadoop/hbase/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/hadoop/hbase/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/hadoop/hbase/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hadoop/hbase/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/kms/src/main/resources/log4j-kmsaudit.properties b/dev-support/ranger-docker/scripts/ranger-hbase-setup.sh old mode 100644 new mode 100755 similarity index 63% rename from kms/src/main/resources/log4j-kmsaudit.properties rename to dev-support/ranger-docker/scripts/ranger-hbase-setup.sh index 0747f1e33e..95a1bdf21e --- a/kms/src/main/resources/log4j-kmsaudit.properties +++ b/dev-support/ranger-docker/scripts/ranger-hbase-setup.sh @@ -1,4 +1,5 @@ -# +#!/bin/bash + # 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 @@ -7,22 +8,24 @@ # "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 +# 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. -# -log4j.rootLogger=INFO, kms-audit +echo "export JAVA_HOME=${JAVA_HOME}" >> ${HBASE_HOME}/conf/hbase-env.sh + +cat < /etc/ssh/ssh_config +Host * + StrictHostKeyChecking no + UserKnownHostsFile=/dev/null +EOF -# LOG Appender -log4j.appender.kms-audit=org.apache.log4j.ConsoleAppender -log4j.appender.kms-audit.Target=System.err -log4j.appender.kms-audit.layout=org.apache.log4j.PatternLayout -log4j.appender.kms-audit.layout.ConversionPattern=%m +cp ${RANGER_SCRIPTS}/hbase-site.xml /opt/hbase/conf/hbase-site.xml +chown -R hbase:hadoop /opt/hbase/ -# disable default logging in KMSAudit class -log4j.logger.org.apache.hadoop.crypto.key.kms.server.KMSAudit=OFF +cd ${RANGER_HOME}/ranger-hbase-plugin +./enable-hbase-plugin.sh diff --git a/dev-support/ranger-docker/scripts/ranger-hbase.sh b/dev-support/ranger-docker/scripts/ranger-hbase.sh new file mode 100755 index 0000000000..16ca5efce3 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hbase.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# 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. + +if [ "${OS_NAME}" = "UBUNTU" ]; then + service ssh start +fi + +if [ ! -e ${HBASE_HOME}/.setupDone ] +then + if [ "${OS_NAME}" = "RHEL" ]; then + ssh-keygen -A + /usr/sbin/sshd + fi + + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" hbase + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" hbase + su -c "chmod 0600 ~/.ssh/authorized_keys" hbase + + # pdsh is unavailable with microdnf in rhel based image. + echo "ssh" > /etc/pdsh/rcmd_default + + + if "${RANGER_SCRIPTS}"/ranger-hbase-setup.sh; + then + touch "${HBASE_HOME}"/.setupDone + else + echo "Ranger Hbase Setup Script didn't complete proper execution." + fi +fi + +su -c "${HBASE_HOME}/bin/start-hbase.sh" hbase + +HBASE_MASTER_PID=`ps -ef | grep -v grep | grep -i "org.apache.hadoop.hbase.master.HMaster" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$HBASE_MASTER_PID" ] +then + echo "The HBase process probably exited, no process id found!" +else + tail --pid=$HBASE_MASTER_PID -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-hdfs-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-hdfs-plugin-install.properties new file mode 100644 index 0000000000..83b171025d --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hdfs-plugin-install.properties @@ -0,0 +1,82 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_hdfs +COMPONENT_INSTALL_DIR_NAME=/opt/hadoop + +CUSTOM_USER=hdfs +CUSTOM_GROUP=hadoop + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hadoop +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/hadoop/hdfs/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/hadoop/hdfs/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/hadoop/hdfs/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hadoop/hdfs/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/dev-support/ranger-docker/scripts/ranger-hive-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-hive-plugin-install.properties new file mode 100644 index 0000000000..1a5dde9ae3 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hive-plugin-install.properties @@ -0,0 +1,83 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_hive +COMPONENT_INSTALL_DIR_NAME=/opt/hive +UPDATE_XAPOLICIES_ON_GRANT_REVOKE=true + +CUSTOM_USER=hive +CUSTOM_GROUP=hadoop + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hive +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/hive/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/hive/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/hive/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hive/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hive/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hive/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/dev-support/ranger-docker/scripts/ranger-hive-setup.sh b/dev-support/ranger-docker/scripts/ranger-hive-setup.sh new file mode 100755 index 0000000000..c0e7ee4063 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hive-setup.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# 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. + +echo "export JAVA_HOME=${JAVA_HOME}" >> ${HADOOP_HOME}/etc/hadoop/hadoop-env.sh + +cat < /etc/ssh/ssh_config +Host * + StrictHostKeyChecking no + UserKnownHostsFile=/dev/null +EOF + +cat < ${HADOOP_HOME}/etc/hadoop/core-site.xml + + + fs.defaultFS + hdfs://ranger-hadoop:9000 + + +EOF + +cp ${RANGER_SCRIPTS}/hive-site.xml ${HIVE_HOME}/conf/hive-site.xml +cp ${RANGER_SCRIPTS}/hive-site.xml ${HIVE_HOME}/conf/hiveserver2-site.xml +su -c "${HIVE_HOME}/bin/schematool -dbType ${RANGER_DB_TYPE} -initSchema" hive + +mkdir -p /opt/hive/logs +chown -R hive:hadoop /opt/hive/ +chmod g+w /opt/hive/logs + +cd ${RANGER_HOME}/ranger-hive-plugin +./enable-hive-plugin.sh diff --git a/dev-support/ranger-docker/scripts/ranger-hive.sh b/dev-support/ranger-docker/scripts/ranger-hive.sh new file mode 100755 index 0000000000..6e8dc4f847 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-hive.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# 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. + +if [ "${OS_NAME}" = "UBUNTU" ]; then + service ssh start +fi + +if [ ! -e ${HIVE_HOME}/.setupDone ] +then + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" hdfs + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" hdfs + su -c "chmod 0600 ~/.ssh/authorized_keys" hdfs + + if [ "${OS_NAME}" = "RHEL" ]; then + ssh-keygen -A + /usr/sbin/sshd + fi + + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" yarn + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" yarn + su -c "chmod 0600 ~/.ssh/authorized_keys" yarn + + # pdsh is unavailable with microdnf in rhel based image. + echo "ssh" > /etc/pdsh/rcmd_default + + + if "${RANGER_SCRIPTS}"/ranger-hive-setup.sh; + then + touch "${HIVE_HOME}"/.setupDone + else + echo "Ranger Hive Setup Script didn't complete proper execution." + fi +fi + +cd "${HIVE_HOME}" || exit + +# Start Hive MetaStore +su -c "nohup ${HIVE_HOME}/bin/hive --service metastore > metastore.log 2>&1 &" hive + +# Start HiveServer2 +su -c "nohup ${HIVE_HOME}/bin/hiveserver2 > hive-server2.log 2>&1 &" hive + +sleep 10 + +HIVE_SERVER2_PID=`ps -ef | grep -v grep | grep -i "org.apache.hive.service.server.HiveServer2" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$HIVE_SERVER2_PID" ] +then + echo "The HiveServer2 process probably exited, no process id found!" +else + tail --pid="$HIVE_SERVER2_PID" -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-kafka-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-kafka-plugin-install.properties new file mode 100644 index 0000000000..fb9900c156 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-kafka-plugin-install.properties @@ -0,0 +1,85 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_kafka +COMPONENT_INSTALL_DIR_NAME=/opt/kafka + +CUSTOM_USER=kafka +CUSTOM_GROUP=hadoop + +XAAUDIT.SUMMARY.ENABLE=true +UPDATE_XAPOLICIES_ON_GRANT_REVOKE=true + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hadoop +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/kafka/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/kafka/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/kafka/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/kafka/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/dev-support/ranger-docker/scripts/ranger-kafka-setup.sh b/dev-support/ranger-docker/scripts/ranger-kafka-setup.sh new file mode 100755 index 0000000000..51c91195f6 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-kafka-setup.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# 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. + +cat < /etc/ssh/ssh_config +Host * + StrictHostKeyChecking no + UserKnownHostsFile=/dev/null +EOF + +chown -R kafka:hadoop /opt/kafka/ + +cd ${RANGER_HOME}/ranger-kafka-plugin +./enable-kafka-plugin.sh + +sed -i 's/localhost:2181/ranger-zk.example.com:2181/' ${KAFKA_HOME}/config/server.properties + +echo >> ${KAFKA_HOME}/config/server.properties +echo "authorizer.class.name=org.apache.ranger.authorization.kafka.authorizer.RangerKafkaAuthorizer" >> ${KAFKA_HOME}/config/server.properties diff --git a/dev-support/ranger-docker/scripts/ranger-kafka.sh b/dev-support/ranger-docker/scripts/ranger-kafka.sh new file mode 100755 index 0000000000..c1f6139b18 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-kafka.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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. + +if [ "${OS_NAME}" = "UBUNTU" ]; then + service ssh start +fi + +if [ ! -e ${KAFKA_HOME}/.setupDone ] +then + if [ "${OS_NAME}" = "RHEL" ]; then + ssh-keygen -A + /usr/sbin/sshd + fi + + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" kafka + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" kafka + su -c "chmod 0600 ~/.ssh/authorized_keys" kafka + + # pdsh is unavailable with microdnf in rhel based image. + echo "ssh" > /etc/pdsh/rcmd_default + + + if "${RANGER_SCRIPTS}"/ranger-kafka-setup.sh; + then + touch "${KAFKA_HOME}"/.setupDone + else + echo "Ranger Kafka Setup Script didn't complete proper execution." + fi +fi + +su -c "cd ${KAFKA_HOME} && CLASSPATH=${KAFKA_HOME}/config ./bin/kafka-server-start.sh config/server.properties" kafka diff --git a/dev-support/ranger-docker/scripts/ranger-kms-install-mysql.properties b/dev-support/ranger-docker/scripts/ranger-kms-install-mysql.properties new file mode 100755 index 0000000000..ed7ab2d82a --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-kms-install-mysql.properties @@ -0,0 +1,225 @@ +# 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. + +# +# This file provides a list of the deployment variables for the Ranger KMS Web Application +# + +PYTHON_COMMAND_INVOKER=python3 +DB_FLAVOR=MYSQL +SQL_CONNECTOR_JAR=/usr/share/java/mysql-connector-java.jar + +db_root_user=root +db_root_password=rangerR0cks! +db_host=ranger-db + +db_name=rangerkms +db_user=rangerkms +db_password=rangerR0cks! + +mysql_core_file=db/mysql/kms_core_db.sql +postgres_core_file=db/postgres/kms_core_db_postgres.sql + +#SSL config +db_ssl_enabled=false +db_ssl_required=false +db_ssl_verifyServerCertificate=false +#db_ssl_auth_type=1-way|2-way, where 1-way represents standard one way ssl authentication and 2-way represents mutual ssl authentication +db_ssl_auth_type=2-way +javax_net_ssl_keyStore= +javax_net_ssl_keyStorePassword= +javax_net_ssl_trustStore= +javax_net_ssl_trustStorePassword= +javax_net_ssl_trustStore_type=jks +javax_net_ssl_keyStore_type=jks + +# For postgresql db +db_ssl_certificate_file= + +#For over-riding the jdbc url. +is_override_db_connection_string=false +db_override_connection_string= + + +#------------------------- DB CONFIG - END ---------------------------------- +#KMS Server config +ranger_kms_http_enabled=true +ranger_kms_https_keystore_file= +ranger_kms_https_keystore_keyalias=rangerkms +ranger_kms_https_keystore_password= + +#------------------------- RANGER KMS Install Dir ------------------ +COMPONENT_INSTALL_DIR_NAME=/opt/ranger/kms + +#------------------------- RANGER KMS Master Key Crypt Key ------------------ +KMS_MASTER_KEY_PASSWD=Str0ngPassw0rd + +#------------------------- Ranger KMS Kerberos Configuration --------------------------- +kms_principal= +kms_keytab= +hadoop_conf= + +#------------------------- Ranger KMS HSM CONFIG ------------------------------ +HSM_TYPE=LunaProvider +HSM_ENABLED=false +HSM_PARTITION_NAME=par19 +HSM_PARTITION_PASSWORD=S@fenet123 + +#------------------------- Ranger SAFENET KEYSECURE CONFIG ------------------------------ +KEYSECURE_ENABLED=false +KEYSECURE_USER_PASSWORD_AUTHENTICATION=true +KEYSECURE_MASTERKEY_NAME=safenetkeysecure +KEYSECURE_USERNAME=user1 +KEYSECURE_PASSWORD=t1e2s3t4 +KEYSECURE_HOSTNAME=SunPKCS11-keysecurehn +KEYSECURE_MASTER_KEY_SIZE=256 +KEYSECURE_LIB_CONFIG_PATH=/opt/safenetConf/64/8.3.1/sunpkcs11.cfg + +#------------------------- Ranger Azure Key Vault ------------------------------ +AZURE_KEYVAULT_ENABLED=false +AZURE_KEYVAULT_SSL_ENABLED=false +AZURE_CLIENT_ID=50fd7ca6-fd4f-4785-a13f-1a6cc4e95e42 +AZURE_CLIENT_SECRET= +AZURE_AUTH_KEYVAULT_CERTIFICATE_PATH=/home/machine/Desktop/azureAuthCertificate/keyvault-MyCert.pfx +# Initialize below prop if your certificate file has any password +#AZURE_AUTH_KEYVAULT_CERTIFICATE_PASSWORD=certPass +AZURE_MASTERKEY_NAME=RangerMasterKey +# E.G. RSA, RSA_HSM, EC, EC_HSM, OCT +AZURE_MASTER_KEY_TYPE=RSA +# E.G. RSA_OAEP, RSA_OAEP_256, RSA1_5, RSA_OAEP +ZONE_KEY_ENCRYPTION_ALGO=RSA_OAEP +AZURE_KEYVAULT_URL=https://shahkeyvault.vault.azure.net/ + +#------------------------- Ranger Google Cloud HSM ------------------------------ +IS_GCP_ENABLED=false +GCP_KEYRING_ID= +GCP_CRED_JSON_FILE=/full/path/to/credfile.json +GCP_PROJECT_ID= +GCP_LOCATION_ID= +GCP_MASTER_KEY_NAME=MyMasterKeyNameChangeIt + +#------------------------- Ranger Tencent KMS ------------------------------ +TENCENT_KMS_ENABLED=false +TENCENT_MASTERKEY_ID=b756b016-6e11-11ec-a735-525400fe0300 +TENCENT_CLIENT_ID=AKIDrXx6ybx2qNdiaBWaNs76pGQJvFJ6crpW +TENCENT_CLIENT_SECRET= +TENCENT_CLIENT_REGION=ap-beijing + +# ------- UNIX User CONFIG ---------------- +# +unix_user=rangerkms +unix_user_pwd=kms +unix_group=ranger + +# Following variables are referenced in db_setup.py. Do not remove these +oracle_core_file= +sqlserver_core_file= +sqlanywhere_core_file= +cred_keystore_filename= + +# +# ------- UNIX User CONFIG - END ---------------- +# + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_kms + +# AUDIT configuration with V3 properties +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SUMMARY.ENABLE=true + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hive +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/hive/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/hive/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/hive/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hive/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hive/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hive/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit + + +# Custom log directory path +RANGER_KMS_LOG_DIR=/var/log/ranger/kms + +#PID file path +RANGER_KMS_PID_DIR_PATH=/var/run/ranger_kms +# ################# DO NOT MODIFY ANY VARIABLES BELOW ######################### +# +# --- These deployment variables are not to be modified unless you understand the full impact of the changes +# +################################################################################ +KMS_DIR=$PWD +app_home=$PWD/ews/webapp +TMPFILE=$PWD/.fi_tmp +LOGFILE=$PWD/logfile + +JAVA_BIN='java' +JAVA_VERSION_REQUIRED='1.8' +JAVA_ORACLE='Java(TM) SE Runtime Environment' + + +cred_keystore_filename=$app_home/WEB-INF/classes/conf/.jceks/rangerkms.jceks + +KMS_BLACKLIST_DECRYPT_EEK=hdfs diff --git a/dev-support/ranger-docker/scripts/ranger-kms-install-postgres.properties b/dev-support/ranger-docker/scripts/ranger-kms-install-postgres.properties new file mode 100755 index 0000000000..35a3690078 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-kms-install-postgres.properties @@ -0,0 +1,225 @@ +# 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. + +# +# This file provides a list of the deployment variables for the Ranger KMS Web Application +# + +PYTHON_COMMAND_INVOKER=python3 +DB_FLAVOR=POSTGRES +SQL_CONNECTOR_JAR=/usr/share/java/postgresql.jar + +db_root_user=postgres +db_root_password=rangerR0cks! +db_host=ranger-db + +db_name=rangerkms +db_user=rangerkms +db_password=rangerR0cks! + +mysql_core_file=db/mysql/kms_core_db.sql +postgres_core_file=db/postgres/kms_core_db_postgres.sql + +#SSL config +db_ssl_enabled=false +db_ssl_required=false +db_ssl_verifyServerCertificate=false +#db_ssl_auth_type=1-way|2-way, where 1-way represents standard one way ssl authentication and 2-way represents mutual ssl authentication +db_ssl_auth_type=2-way +javax_net_ssl_keyStore= +javax_net_ssl_keyStorePassword= +javax_net_ssl_trustStore= +javax_net_ssl_trustStorePassword= +javax_net_ssl_trustStore_type=jks +javax_net_ssl_keyStore_type=jks + +# For postgresql db +db_ssl_certificate_file= + +#For over-riding the jdbc url. +is_override_db_connection_string=false +db_override_connection_string= + + +#------------------------- DB CONFIG - END ---------------------------------- +#KMS Server config +ranger_kms_http_enabled=true +ranger_kms_https_keystore_file= +ranger_kms_https_keystore_keyalias=rangerkms +ranger_kms_https_keystore_password= + +#------------------------- RANGER KMS Install Dir ------------------ +COMPONENT_INSTALL_DIR_NAME=/opt/ranger/kms + +#------------------------- RANGER KMS Master Key Crypt Key ------------------ +KMS_MASTER_KEY_PASSWD=Str0ngPassw0rd + +#------------------------- Ranger KMS Kerberos Configuration --------------------------- +kms_principal= +kms_keytab= +hadoop_conf= + +#------------------------- Ranger KMS HSM CONFIG ------------------------------ +HSM_TYPE=LunaProvider +HSM_ENABLED=false +HSM_PARTITION_NAME=par19 +HSM_PARTITION_PASSWORD=S@fenet123 + +#------------------------- Ranger SAFENET KEYSECURE CONFIG ------------------------------ +KEYSECURE_ENABLED=false +KEYSECURE_USER_PASSWORD_AUTHENTICATION=true +KEYSECURE_MASTERKEY_NAME=safenetkeysecure +KEYSECURE_USERNAME=user1 +KEYSECURE_PASSWORD=t1e2s3t4 +KEYSECURE_HOSTNAME=SunPKCS11-keysecurehn +KEYSECURE_MASTER_KEY_SIZE=256 +KEYSECURE_LIB_CONFIG_PATH=/opt/safenetConf/64/8.3.1/sunpkcs11.cfg + +#------------------------- Ranger Azure Key Vault ------------------------------ +AZURE_KEYVAULT_ENABLED=false +AZURE_KEYVAULT_SSL_ENABLED=false +AZURE_CLIENT_ID=50fd7ca6-fd4f-4785-a13f-1a6cc4e95e42 +AZURE_CLIENT_SECRET= +AZURE_AUTH_KEYVAULT_CERTIFICATE_PATH=/home/machine/Desktop/azureAuthCertificate/keyvault-MyCert.pfx +# Initialize below prop if your certificate file has any password +#AZURE_AUTH_KEYVAULT_CERTIFICATE_PASSWORD=certPass +AZURE_MASTERKEY_NAME=RangerMasterKey +# E.G. RSA, RSA_HSM, EC, EC_HSM, OCT +AZURE_MASTER_KEY_TYPE=RSA +# E.G. RSA_OAEP, RSA_OAEP_256, RSA1_5, RSA_OAEP +ZONE_KEY_ENCRYPTION_ALGO=RSA_OAEP +AZURE_KEYVAULT_URL=https://shahkeyvault.vault.azure.net/ + +#------------------------- Ranger Google Cloud HSM ------------------------------ +IS_GCP_ENABLED=false +GCP_KEYRING_ID= +GCP_CRED_JSON_FILE=/full/path/to/credfile.json +GCP_PROJECT_ID= +GCP_LOCATION_ID= +GCP_MASTER_KEY_NAME=MyMasterKeyNameChangeIt + +#------------------------- Ranger Tencent KMS ------------------------------ +TENCENT_KMS_ENABLED=false +TENCENT_MASTERKEY_ID=b756b016-6e11-11ec-a735-525400fe0300 +TENCENT_CLIENT_ID=AKIDrXx6ybx2qNdiaBWaNs76pGQJvFJ6crpW +TENCENT_CLIENT_SECRET= +TENCENT_CLIENT_REGION=ap-beijing + +# ------- UNIX User CONFIG ---------------- +# +unix_user=rangerkms +unix_user_pwd=kms +unix_group=ranger + +# Following variables are referenced in db_setup.py. Do not remove these +oracle_core_file= +sqlserver_core_file= +sqlanywhere_core_file= +cred_keystore_filename= + +# +# ------- UNIX User CONFIG - END ---------------- +# + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_kms + +# AUDIT configuration with V3 properties +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SUMMARY.ENABLE=true + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hive +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/hive/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/hive/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/hive/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hive/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hive/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hive/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit + + +# Custom log directory path +RANGER_KMS_LOG_DIR=/var/log/ranger/kms + +#PID file path +RANGER_KMS_PID_DIR_PATH=/var/run/ranger_kms +# ################# DO NOT MODIFY ANY VARIABLES BELOW ######################### +# +# --- These deployment variables are not to be modified unless you understand the full impact of the changes +# +################################################################################ +KMS_DIR=$PWD +app_home=$PWD/ews/webapp +TMPFILE=$PWD/.fi_tmp +LOGFILE=$PWD/logfile + +JAVA_BIN='java' +JAVA_VERSION_REQUIRED='1.8' +JAVA_ORACLE='Java(TM) SE Runtime Environment' + + +cred_keystore_filename=$app_home/WEB-INF/classes/conf/.jceks/rangerkms.jceks + +KMS_BLACKLIST_DECRYPT_EEK=hdfs diff --git a/dev-support/ranger-docker/scripts/ranger-kms.sh b/dev-support/ranger-docker/scripts/ranger-kms.sh new file mode 100755 index 0000000000..be5519e40d --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-kms.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# 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. + + +if [ ! -e ${RANGER_HOME}/.setupDone ] +then + SETUP_RANGER=true +else + SETUP_RANGER=false +fi + +if [ "${SETUP_RANGER}" == "true" ] +then + cd "${RANGER_HOME}"/kms || exit + if ./setup.sh; + then + touch "${RANGER_HOME}"/.setupDone + else + echo "Ranger KMS Setup Script didn't complete proper execution." + fi +fi + +# delete PID file if exists +rm -f /var/run/ranger_kms/rangerkms.pid + +cd ${RANGER_HOME}/kms && ./ranger-kms-services.sh start + +RANGER_KMS_PID=`ps -ef | grep -v grep | grep "Dproc_rangerkms" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$RANGER_KMS_PID" ] +then + echo "Ranger KMS process probably exited, no process id found!" +else + tail --pid=$RANGER_KMS_PID -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-knox-expect.py b/dev-support/ranger-docker/scripts/ranger-knox-expect.py new file mode 100644 index 0000000000..2707c73865 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-knox-expect.py @@ -0,0 +1,24 @@ +# +# 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. See accompanying LICENSE file. +# +import pexpect + +child = pexpect.spawn('/opt/knox/bin/knoxcli.sh create-master --force') + +child.expect('Enter master secret:') +child.send("admin\r") + +child.expect("Enter master secret again:") +child.send("admin\r") + +child.expect("Master secret has been persisted to disk.") diff --git a/dev-support/ranger-docker/scripts/ranger-knox-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-knox-plugin-install.properties new file mode 100644 index 0000000000..2e05ddafe2 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-knox-plugin-install.properties @@ -0,0 +1,82 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_knox +COMPONENT_INSTALL_DIR_NAME=/opt/knox + +CUSTOM_USER=knox +CUSTOM_GROUP=knox + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hadoop +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/knox/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/knox/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/knox/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hadoop/knox/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/dev-support/ranger-docker/scripts/ranger-knox-sandbox.xml b/dev-support/ranger-docker/scripts/ranger-knox-sandbox.xml new file mode 100644 index 0000000000..c6ae986058 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-knox-sandbox.xml @@ -0,0 +1,175 @@ + + + + + + + + authentication + ShiroProvider + true + + + sessionTimeout + 30 + + + main.ldapRealm + org.apache.knox.gateway.shirorealm.KnoxLdapRealm + + + main.ldapContextFactory + org.apache.knox.gateway.shirorealm.KnoxLdapContextFactory + + + main.ldapRealm.contextFactory + $ldapContextFactory + + + main.ldapRealm.userDnTemplate + uid={0},ou=people,dc=hadoop,dc=apache,dc=org + + + main.ldapRealm.contextFactory.url + ldap://localhost:33389 + + + main.ldapRealm.contextFactory.authenticationMechanism + simple + + + urls./** + authcBasic + + + + + authorization + AclsAuthz + true + + + + identity-assertion + Default + true + + + + hostmap + static + true + + localhost + sandbox,sandbox.hortonworks.com + + + + + + + NAMENODE + hdfs://ranger-hadoop.example.com:8020 + + + + JOBTRACKER + rpc://ranger-hadoop.example.com:8050 + + + + WEBHDFS + http://ranger-hadoop.example.com:9870/webhdfs + + + + WEBHCAT + http://ranger-hive.example.com:50111/templeton + + + + OOZIE + http://localhost:11000/oozie + + replayBufferSize + 8 + + + + + WEBHBASE + http://ranger-hbase.example.com:60080 + + replayBufferSize + 8 + + + + + HIVE + http://ranger-hive.example.com:10001/cliservice + + replayBufferSize + 8 + + + + + RESOURCEMANAGER + http://ranger-hadoop.example.com:8088/ws + + + + DRUID-COORDINATOR-UI + http://localhost:8081 + + + + DRUID-COORDINATOR + http://localhost:8081 + + + + DRUID-BROKER + http://localhost:8082 + + + + DRUID-ROUTER + http://localhost:8082 + + + + DRUID-OVERLORD + http://localhost:8090 + + + + DRUID-OVERLORD-UI + http://localhost:8090 + + + + HUE + http://localhost:8889 + + diff --git a/dev-support/ranger-docker/scripts/ranger-knox-setup.sh b/dev-support/ranger-docker/scripts/ranger-knox-setup.sh new file mode 100755 index 0000000000..c5c9bcac78 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-knox-setup.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# 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. + +cat < /etc/ssh/ssh_config +Host * + StrictHostKeyChecking no + UserKnownHostsFile=/dev/null +EOF + +chown -R knox:knox /opt/knox/ + +mkdir -p /opt/knox/logs +chown -R knox:knox /opt/knox/ +chmod g+w /opt/knox/logs + +cd ${RANGER_HOME}/ranger-knox-plugin +./enable-knox-plugin.sh diff --git a/dev-support/ranger-docker/scripts/ranger-knox.sh b/dev-support/ranger-docker/scripts/ranger-knox.sh new file mode 100755 index 0000000000..ddd04e2444 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-knox.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# 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. + +if [ "${OS_NAME}" = "UBUNTU" ]; then + service ssh start +fi + +if [ ! -e "${KNOX_HOME}"/.setupDone ] +then + if [ "${OS_NAME}" = "RHEL" ]; then + ssh-keygen -A + /usr/sbin/sshd + fi + + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" knox + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" knox + su -c "chmod 0600 ~/.ssh/authorized_keys" knox + + # pdsh is unavailable with microdnf in rhel based image. + echo "ssh" > /etc/pdsh/rcmd_default + + + if "${RANGER_SCRIPTS}"/ranger-knox-setup.sh; + then + touch "${KNOX_HOME}"/.setupDone + else + echo "Ranger Knox Setup Script didn't complete proper execution." + fi +fi + +su -c "${KNOX_HOME}/bin/ldap.sh start" knox + +su -c "${KNOX_HOME}/bin/gateway.sh start" knox + +KNOX_GATEWAY_PID=`ps -ef | grep -v grep | grep -i "gateway.jar" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$KNOX_GATEWAY_PID" ] +then + echo "The Knox Gateway process probably exited, no process id found!" +else + tail --pid="$KNOX_GATEWAY_PID" -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-tagsync-install.properties b/dev-support/ranger-docker/scripts/ranger-tagsync-install.properties new file mode 100644 index 0000000000..7a3291c6ac --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-tagsync-install.properties @@ -0,0 +1,130 @@ +# 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. +# +# +# This file provides a list of the deployment variables for the Ranger Tagsync service +# +# +# +# +# URL for tag destination - Ranger +TAG_DEST_RANGER_ENDPOINT = http://ranger:6080 + +# SSL config file name for HTTPS messages to tag destination - Ranger +TAG_DEST_RANGER_SSL_CONFIG_FILENAME = + +TAG_SOURCE_ATLAS_ENABLED = false + +# Endpoint specifications needed by Atlas +TAG_SOURCE_ATLAS_KAFKA_BOOTSTRAP_SERVERS = ranger-kafka.example.com:6667 +TAG_SOURCE_ATLAS_KAFKA_ZOOKEEPER_CONNECT = ranger-zk.example.com:2181 +TAG_SOURCE_ATLAS_KAFKA_ENTITIES_GROUP_ID = ranger_entities_consumer + +TAG_SOURCE_ATLAS_KAFKA_SERVICE_NAME = kafka +TAG_SOURCE_ATLAS_KAFKA_SECURITY_PROTOCOL = PLAINTEXTSASL + +TAG_SOURCE_ATLAS_KERBEROS_PRINCIPAL = +TAG_SOURCE_ATLAS_KERBEROS_KEYTAB = + +TAG_SOURCE_ATLASREST_ENABLED = false + +TAG_SOURCE_ATLASREST_ENDPOINT = http://localhost:21000 +TAG_SOURCE_ATLASREST_DOWNLOAD_INTERVAL_IN_MILLIS = 900000 + +TAG_SOURCE_ATLASREST_USERNAME = +TAG_SOURCE_ATLASREST_PASSWORD = + +TAG_SOURCE_FILE_ENABLED = true + +TAG_SOURCE_FILE_FILENAME = /opt/ranger/tagsync/data/tags.json +TAG_SOURCE_FILE_CHECK_INTERVAL_IN_MILLIS = 60000 + +# Mapping from Atlas hive cluster-name to Ranger service-name +# this needs to be in format clusterName,componentType,serviceName;clusterName2,componentType2,serviceName2 +# Note that there are no blanks anywhere in the value-string +# +# For Hive, the notifications from Atlas include the name of the entities in the following format: +# dbName@clusterName +# dbName.tblName@clusterName +# dbName.tblName.colName@clusterName +# +# Ranger-tagsync needs to derive the name of the Hive service (in Ranger) from the above entity names. +# By default, Ranger computes Hive service name as: clusterName + “_hive". +# If the name of the Hive service (in Ranger) is different in your environment, please use +# following property to enable Ranger-tagsync to derive the correct Hive service name. +# +# TAGSYNC_ATLAS_TO_RANGER_SERVICE_MAPPING = clusterName,hive,rangerServiceName +# + +TAGSYNC_ATLAS_TO_RANGER_SERVICE_MAPPING= + +# A comma separated list of custom mapper class names which convert Atlas entities to +# RangerServiceResource structures are specified here. If there are no custom mappers, +# then it can be left blank + +TAGSYNC_ATLAS_CUSTOM_RESOURCE_MAPPERS= + +# +# The file where all credential is kept in cryptic format +# +TAGSYNC_KEYSTORE_FILENAME = /etc/ranger/tagsync/conf/rangertagsync.jceks + +# File where Atlas credentials is kept in cryptic format + +TAG_SOURCE_ATLASREST_KEYSTORE_FILENAME = /etc/ranger/tagsync/conf/atlasuser.jceks + +# SSL config file name for HTTPS messages to tag source - Atlas-REST +TAG_SOURCE_ATLASREST_SSL_CONFIG_FILENAME = + +#User and group for the tagsync process +unix_user=ranger +unix_group=ranger + +#change password of rangerTagsync user. Please note that this password should be as per rangerTagsync user in ranger +rangerTagsync_password=rangerR0cks! + +# Logs are stored in logdir +logdir = /var/log/ranger/tagsync + +#PID file path +TAGSYNC_PID_DIR_PATH=/var/run/ranger + +#Set to run in kerberos environment +is_secure = false +tagsync_principal= +tagsync_keytab= + + + +hadoop_conf=/etc/hadoop/conf + +# if you want to enable or disable jvm metrics for tagsync process +# valid values: true, false +# any value other than true would be treated as false +# default value: false +# if the value is false, jvm metrics is not created +JVM_METRICS_ENABLED= + +# filename of jvm metrics created for tagsync process +# default value: ranger_tagsync_metric.json +JVM_METRICS_FILENAME= + +#file directory for jvm metrics +# default value : logdir +JVM_METRICS_FILEPATH= + +#frequency for jvm metrics to be updated +# default value : 10000 milliseconds +JVM_METRICS_FREQUENCY_TIME_IN_MILLIS= diff --git a/dev-support/ranger-docker/scripts/ranger-tagsync-tags.json b/dev-support/ranger-docker/scripts/ranger-tagsync-tags.json new file mode 100644 index 0000000000..ae9f5deb1e --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-tagsync-tags.json @@ -0,0 +1,50 @@ +{ + "op": "add_or_update", + "serviceName": "dev_hive", + "tagVersion": 0, + "tagDefinitions": { + "0": { "name": "PII" }, + "1": { "name": "EMPLOYEE_ID" }, + "2": { "name": "ADDRESS" } + }, + "tags": { + "0": { "type": "PII" }, + "1": { "type": "EMPLOYEE_ID" }, + "2": { "type": "ADDRESS" } + }, + "serviceResources": [ + { + "id": 0, + "serviceName": "dev_hive", + "resourceElements": { + "database": { "values": [ "hr" ] }, + "table": { "values": [ "employee" ] }, + "column": { "values": [ "ssn" ] } + } + }, + { + "id": 1, + "serviceName": "dev_hive", + "resourceElements": { + "database": { "values": [ "hr" ] }, + "table": { "values": [ "employee" ] }, + "column": { "values": [ "address" ] } + } + }, + { + "id": 2, + "serviceName": "dev_hive", + "serviceName": "dev_hive", + "resourceElements": { + "database": { "values": [ "hr" ] }, + "table": { "values": [ "employee" ] }, + "column": { "values": [ "id" ] } + } + } + ], + "resourceToTagIds": { + "0": [ "0" ], + "1": [ "0", "2" ], + "2": [ "1" ] + } +} diff --git a/dev-support/ranger-docker/scripts/ranger-tagsync.sh b/dev-support/ranger-docker/scripts/ranger-tagsync.sh new file mode 100755 index 0000000000..c676d39775 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-tagsync.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# 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. + + +if [ ! -e ${RANGER_HOME}/.setupDone ] +then + SETUP_RANGER=true +else + SETUP_RANGER=false +fi + +if [ "${SETUP_RANGER}" == "true" ] +then + cd "${RANGER_HOME}"/tagsync || exit + if ./setup.sh; + then + touch "${RANGER_HOME}"/.setupDone + else + echo "Ranger TagSync Setup Script didn't complete proper execution." + fi +fi + +cd ${RANGER_HOME}/tagsync && ./ranger-tagsync-services.sh start + +RANGER_TAGSYNC_PID=`ps -ef | grep -v grep | grep -i "org.apache.ranger.tagsync.process.TagSynchronizer" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$RANGER_TAGSYNC_PID" ] +then + echo "The TagSync process probably exited, no process id found!" +else + tail --pid=$RANGER_TAGSYNC_PID -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-trino-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-trino-plugin-install.properties new file mode 100644 index 0000000000..8446f9383e --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-trino-plugin-install.properties @@ -0,0 +1,169 @@ +# 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. +# +# Location of Policy Manager URL +# +POLICY_MGR_URL=http://ranger:6080 +# +# This is the repository name created within policy manager +# +# Example: +# REPOSITORY_NAME=trinodev +# +REPOSITORY_NAME=dev_trino +# Custom added property to correctly configure ranger plugin for docker environment. This is required because trino uses different directories +# for plugin and configuration for docker environment +INSTALL_ENV=docker + +# Custom added property to correctly configure ranger plugin for docker environment. This is required because trino uses different directories +# for plugin and configuration for docker environment +COMPONENT_PLUGIN_DIR_NAME=/usr/lib/trino/plugin/ranger + +# Configure INSTALL_ENV=docker if running trino in docker environment +#INSTALL_ENV=docker +# +# Name of the directory where the component's lib and conf directory exist. +# This location should be relative to the parent of the directory containing +# the plugin installation files. +# +COMPONENT_INSTALL_DIR_NAME=/etc/trino + +# Enable audit logs to Solr +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=solr +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/trino/audit/solr/spool + +# Enable audit logs to ElasticSearch +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=http://ranger-es:9200 +XAAUDIT.ELASTICSEARCH.USER=elastic +XAAUDIT.ELASTICSEARCH.PASSWORD=elasticsearch +XAAUDIT.ELASTICSEARCH.INDEX=ranger_audits +XAAUDIT.ELASTICSEARCH.PORT=9200 +XAAUDIT.ELASTICSEARCH.PROTOCOL=http + +# Enable audit logs to HDFS +#Example +#XAAUDIT.HDFS.ENABLE=true +#XAAUDIT.HDFS.HDFS_DIR=hdfs://node-1.example.com:8020/ranger/audit +# If using Azure Blob Storage +#XAAUDIT.HDFS.HDFS_DIR=wasb[s]://@.blob.core.windows.net/ +#XAAUDIT.HDFS.HDFS_DIR=wasb://ranger_audit_container@my-azure-account.blob.core.windows.net/ranger/audit +#XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/trino/audit/hdfs/spool + +XAAUDIT.HDFS.ENABLE=false +XAAUDIT.HDFS.HDFS_DIR=hdfs://__REPLACE__NAME_NODE_HOST:8020/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/trino/audit/hdfs/spool + +# Following additional propertis are needed When auditing to Azure Blob Storage via HDFS +# Get these values from your /etc/hadoop/conf/core-site.xml +#XAAUDIT.HDFS.HDFS_DIR=wasb[s]://@.blob.core.windows.net/ +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +#Log4j Audit Provider +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=true +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +# Enable audit logs to Amazon CloudWatch Logs +#Example +#XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=true +#XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=ranger_audits +#XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM={instance_id} +#XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=/var/log/hive/audit/amazon_cloudwatch/spool + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +# End of V3 properties +# +# Audit to HDFS Configuration +# +# If XAAUDIT.HDFS.IS_ENABLED is set to true, please replace tokens +# that start with __REPLACE__ with appropriate values +# XAAUDIT.HDFS.IS_ENABLED=true +# XAAUDIT.HDFS.DESTINATION_DIRECTORY=hdfs://__REPLACE__NAME_NODE_HOST:8020/ranger/audit/%app-type%/%time:yyyyMMdd% +# XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=__REPLACE__LOG_DIR/trino/audit +# XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=__REPLACE__LOG_DIR/trino/audit/archive +# +# Example: +# XAAUDIT.HDFS.IS_ENABLED=true +# XAAUDIT.HDFS.DESTINATION_DIRECTORY=hdfs://namenode.example.com:8020/ranger/audit/%app-type%/%time:yyyyMMdd% +# XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/trino/audit +# XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/trino/audit/archive +# +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=hdfs://__REPLACE__NAME_NODE_HOST:8020/ranger/audit/%app-type%/%time:yyyyMMdd% +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=__REPLACE__LOG_DIR/trino/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=__REPLACE__LOG_DIR/trino/audit/archive + +XAAUDIT.HDFS.DESTINTATION_FILE=%hostname%-audit.log +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +#Solr Audit Provider +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:6083/solr/ranger_audits + +# End of V2 properties + +# +# SSL Client Certificate Information +# +# Example: +# SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +# SSL_KEYSTORE_PASSWORD=none +# SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +# SSL_TRUSTSTORE_PASSWORD=none +# +# You do not need use SSL between agent and security admin tool, please leave these sample value as it is. +# +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit + +# +# Custom component user +# CUSTOM_COMPONENT_USER= +# keep blank if component user is default +CUSTOM_USER= + + +# +# Custom component group +# CUSTOM_COMPONENT_GROUP= +# keep blank if component group is default +CUSTOM_GROUP= +XAAUDIT.SUMMARY.ENABLE=false diff --git a/dev-support/ranger-docker/scripts/ranger-trino-setup.sh b/dev-support/ranger-docker/scripts/ranger-trino-setup.sh new file mode 100755 index 0000000000..c3ac73a1bb --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-trino-setup.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# 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. + + +source /tmp/trino-setup-env.sh + +TRINO_PLUGIN_HOME=/opt/ranger/ranger-trino-plugin + +ssh-keygen -A +/usr/sbin/sshd + +if [ ! -e ${TRINO_PLUGIN_HOME}/.setupDone ] +then + su -c "ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa" trino + su -c "cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys" trino + su -c "chmod 0600 ~/.ssh/authorized_keys" trino + + cat < /etc/ssh/ssh_config +Host * + StrictHostKeyChecking no + UserKnownHostsFile=/dev/null +EOF + + cd ${TRINO_PLUGIN_HOME} || exit + ./enable-trino-plugin.sh + + touch ${TRINO_PLUGIN_HOME}/.setupDone + echo "Ranger Trino Plugin Installation is complete!" +fi diff --git a/dev-support/ranger-docker/scripts/ranger-trino.sh b/dev-support/ranger-docker/scripts/ranger-trino.sh new file mode 100644 index 0000000000..e41bb02492 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-trino.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# 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. + + +echo "export JAVA_HOME=${JAVA_HOME}" >> /tmp/trino-setup-env.sh + +sudo /home/ranger/scripts/ranger-trino-setup.sh + +/usr/lib/trino/bin/run-trino + +TRINO_PID=$(ps -ef | grep -v grep | grep -i "io.trino.server.TrinoServer" | awk '{print $2}') + +# prevent the container from exiting +if [ -z "$TRINO_PID" ] +then + echo "The Trino process probably exited, no process id found!" +else + tail --pid="$TRINO_PID" -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-usersync-install.properties b/dev-support/ranger-docker/scripts/ranger-usersync-install.properties new file mode 100644 index 0000000000..8233aa29ab --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-usersync-install.properties @@ -0,0 +1,237 @@ +# 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. + +# The base path for the usersync process +ranger_base_dir = /opt/ranger/usersync + +# +# The following URL should be the base URL for connecting to the policy manager web application +# For example: +# +# POLICY_MGR_URL = http://policymanager.xasecure.net:6080 +# +POLICY_MGR_URL = http://ranger:6080 + +# sync source, only unix and ldap are supported at present +# defaults to unix +SYNC_SOURCE = unix + +# +# Minimum Unix User-id to start SYNC. +# This should avoid creating UNIX system-level users in the Policy Manager +# +MIN_UNIX_USER_ID_TO_SYNC = 500 + +# Minimum Unix Group-id to start SYNC. +# This should avoid creating UNIX system-level users in the Policy Manager +# +MIN_UNIX_GROUP_ID_TO_SYNC = 500 + +# sync interval in minutes +# user, groups would be synced again at the end of each sync interval +# defaults to 5 if SYNC_SOURCE is unix +# defaults to 360 if SYNC_SOURCE is ldap +SYNC_INTERVAL = + +#User and group for the usersync process +unix_user=ranger +unix_group=ranger + +#change password of rangerusersync user. Please note that this password should be as per rangerusersync user in ranger +rangerUsersync_password=rangerR0cks! + +#Set to run in kerberos environment +usersync_principal= +usersync_keytab= +hadoop_conf=/etc/hadoop/conf +# +# The file where all credential is kept in cryptic format +# +CRED_KEYSTORE_FILENAME=/opt/ranger/usersync/conf/rangerusersync.jceks + +# SSL Authentication +AUTH_SSL_ENABLED=true +AUTH_SSL_KEYSTORE_FILE=/opt/ranger/usersync/conf/cert/unixauthservice.jks +AUTH_SSL_KEYSTORE_PASSWORD=UnIx529p +AUTH_SSL_TRUSTSTORE_FILE= +AUTH_SSL_TRUSTSTORE_PASSWORD= + +# --------------------------------------------------------------- +# The following properties are relevant only if SYNC_SOURCE = ldap +# --------------------------------------------------------------- + +# The below properties ROLE_ASSIGNMENT_LIST_DELIMITER, USERS_GROUPS_ASSIGNMENT_LIST_DELIMITER, USERNAME_GROUPNAME_ASSIGNMENT_LIST_DELIMITER, +#and GROUP_BASED_ROLE_ASSIGNMENT_RULES can be used to assign role to LDAP synced users and groups +#NOTE all the delimiters should have different values and the delimiters should not contain characters that are allowed in userName or GroupName + +# default value ROLE_ASSIGNMENT_LIST_DELIMITER = & +ROLE_ASSIGNMENT_LIST_DELIMITER = & + +#default value USERS_GROUPS_ASSIGNMENT_LIST_DELIMITER = : +USERS_GROUPS_ASSIGNMENT_LIST_DELIMITER = : + +#default value USERNAME_GROUPNAME_ASSIGNMENT_LIST_DELIMITER = , +USERNAME_GROUPNAME_ASSIGNMENT_LIST_DELIMITER = , + +# with above mentioned delimiters a sample value would be ROLE_SYS_ADMIN:u:userName1,userName2&ROLE_SYS_ADMIN:g:groupName1,groupName2&ROLE_KEY_ADMIN:u:userName&ROLE_KEY_ADMIN:g:groupName&ROLE_USER:u:userName3,userName4&ROLE_USER:g:groupName3 +#&ROLE_ADMIN_AUDITOR:u:userName&ROLE_KEY_ADMIN_AUDITOR:u:userName&ROLE_KEY_ADMIN_AUDITOR:g:groupName&ROLE_ADMIN_AUDITOR:g:groupName +GROUP_BASED_ROLE_ASSIGNMENT_RULES = + +# URL of source ldap +# a sample value would be: ldap://ldap.example.com:389 +# Must specify a value if SYNC_SOURCE is ldap +SYNC_LDAP_URL = + +# ldap bind dn used to connect to ldap and query for users and groups +# a sample value would be cn=admin,ou=users,dc=hadoop,dc=apache,dc=org +# Must specify a value if SYNC_SOURCE is ldap +SYNC_LDAP_BIND_DN = + +# ldap bind password for the bind dn specified above +# please ensure read access to this file is limited to root, to protect the password +# Must specify a value if SYNC_SOURCE is ldap +# unless anonymous search is allowed by the directory on users and group +SYNC_LDAP_BIND_PASSWORD = + +# ldap delta sync flag used to periodically sync users and groups based on the updates in the server +# please customize the value to suit your deployment +# default value is set to true when is SYNC_SOURCE is ldap +SYNC_LDAP_DELTASYNC = + +# search base for users and groups +# sample value would be dc=hadoop,dc=apache,dc=org +SYNC_LDAP_SEARCH_BASE = + +# search base for users +# sample value would be ou=users,dc=hadoop,dc=apache,dc=org +# overrides value specified in SYNC_LDAP_SEARCH_BASE +SYNC_LDAP_USER_SEARCH_BASE = + +# search scope for the users, only base, one and sub are supported values +# please customize the value to suit your deployment +# default value: sub +SYNC_LDAP_USER_SEARCH_SCOPE = sub + +# objectclass to identify user entries +# please customize the value to suit your deployment +# default value: person +SYNC_LDAP_USER_OBJECT_CLASS = person + +# optional additional filter constraining the users selected for syncing +# a sample value would be (dept=eng) +# please customize the value to suit your deployment +# default value is empty +SYNC_LDAP_USER_SEARCH_FILTER = + +# attribute from user entry that would be treated as user name +# please customize the value to suit your deployment +# default value: cn +SYNC_LDAP_USER_NAME_ATTRIBUTE = cn + +# attribute from user entry whose values would be treated as +# group values to be pushed into Policy Manager database +# You could provide multiple attribute names separated by comma +# default value: memberof, ismemberof +SYNC_LDAP_USER_GROUP_NAME_ATTRIBUTE = memberof,ismemberof +# +# UserSync - Case Conversion Flags +# possible values: none, lower, upper +SYNC_LDAP_USERNAME_CASE_CONVERSION=lower +SYNC_LDAP_GROUPNAME_CASE_CONVERSION=lower + +#user sync log path +logdir=/var/log/ranger/usersync + +# PID DIR PATH +USERSYNC_PID_DIR_PATH=/var/run/ranger + +# do we want to do ldapsearch to find groups instead of relying on user entry attributes +# valid values: true, false +# any value other than true would be treated as false +# default value: false +SYNC_GROUP_SEARCH_ENABLED= + +# do we want to do ldapsearch to find groups instead of relying on user entry attributes and +# sync memberships of those groups +# valid values: true, false +# any value other than true would be treated as false +# default value: false +SYNC_GROUP_USER_MAP_SYNC_ENABLED= + +# search base for groups +# sample value would be ou=groups,dc=hadoop,dc=apache,dc=org +# overrides value specified in SYNC_LDAP_SEARCH_BASE, SYNC_LDAP_USER_SEARCH_BASE +# if a value is not specified, takes the value of SYNC_LDAP_SEARCH_BASE +# if SYNC_LDAP_SEARCH_BASE is also not specified, takes the value of SYNC_LDAP_USER_SEARCH_BASE +SYNC_GROUP_SEARCH_BASE= + +# search scope for the groups, only base, one and sub are supported values +# please customize the value to suit your deployment +# default value: sub +SYNC_GROUP_SEARCH_SCOPE= + +# objectclass to identify group entries +# please customize the value to suit your deployment +# default value: groupofnames +SYNC_GROUP_OBJECT_CLASS= + +# optional additional filter constraining the groups selected for syncing +# a sample value would be (dept=eng) +# please customize the value to suit your deployment +# default value is empty +SYNC_LDAP_GROUP_SEARCH_FILTER= + +# attribute from group entry that would be treated as group name +# please customize the value to suit your deployment +# default value: cn +SYNC_GROUP_NAME_ATTRIBUTE= + +# attribute from group entry that is list of members +# please customize the value to suit your deployment +# default value: member +SYNC_GROUP_MEMBER_ATTRIBUTE_NAME= + +# do we want to use paged results control during ldapsearch for user entries +# valid values: true, false +# any value other than true would be treated as false +# default value: true +# if the value is false, typical AD would not return more than 1000 entries +SYNC_PAGED_RESULTS_ENABLED= + +# page size for paged results control +# search results would be returned page by page with the specified number of entries per page +# default value: 500 +SYNC_PAGED_RESULTS_SIZE= +#LDAP context referral could be ignore or follow +SYNC_LDAP_REFERRAL =ignore + +# if you want to enable or disable jvm metrics for usersync process +# valid values: true, false +# any value other than true would be treated as false +# default value: false +# if the value is false, jvm metrics is not created +JVM_METRICS_ENABLED= + +# filename of jvm metrics created for usersync process +# default value: ranger_usersync_metric.json +JVM_METRICS_FILENAME= + +#file directory for jvm metrics +# default value : logdir +JVM_METRICS_FILEPATH= + +#frequency for jvm metrics to be updated +# default value : 10000 milliseconds +JVM_METRICS_FREQUENCY_TIME_IN_MILLIS= diff --git a/dev-support/ranger-docker/scripts/ranger-usersync.sh b/dev-support/ranger-docker/scripts/ranger-usersync.sh new file mode 100755 index 0000000000..8e56ce5ff4 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-usersync.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# 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. + + +if [ ! -e ${RANGER_HOME}/.setupDone ] +then + SETUP_RANGER=true +else + SETUP_RANGER=false +fi + +if [ "${SETUP_RANGER}" == "true" ] +then + cd "${RANGER_HOME}"/usersync || exit + if ./setup.sh; + then + touch "${RANGER_HOME}"/.setupDone + else + echo "Ranger UserSync Setup Script didn't complete proper execution." + fi +fi + +cd ${RANGER_HOME}/usersync && ./start.sh + +RANGER_USERSYNC_PID=`ps -ef | grep -v grep | grep -i "org.apache.ranger.authentication.UnixAuthenticationService" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$RANGER_USERSYNC_PID" ] +then + echo "The UserSync process probably exited, no process id found!" +else + tail --pid=$RANGER_USERSYNC_PID -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ranger-yarn-plugin-install.properties b/dev-support/ranger-docker/scripts/ranger-yarn-plugin-install.properties new file mode 100644 index 0000000000..52ebb83df0 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger-yarn-plugin-install.properties @@ -0,0 +1,82 @@ +# 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. + +POLICY_MGR_URL=http://ranger:6080 +REPOSITORY_NAME=dev_yarn +COMPONENT_INSTALL_DIR_NAME=/opt/hadoop + +CUSTOM_USER=yarn +CUSTOM_GROUP=hadoop + +XAAUDIT.SOLR.IS_ENABLED=true +XAAUDIT.SOLR.MAX_QUEUE_SIZE=1 +XAAUDIT.SOLR.MAX_FLUSH_INTERVAL_MS=1000 +XAAUDIT.SOLR.SOLR_URL=http://ranger-solr:8983/solr/ranger_audits + +# Following properties are needed to get past installation script! Please don't remove +XAAUDIT.HDFS.IS_ENABLED=false +XAAUDIT.HDFS.DESTINATION_DIRECTORY=/ranger/audit +XAAUDIT.HDFS.DESTINTATION_FILE=hadoop +XAAUDIT.HDFS.DESTINTATION_FLUSH_INTERVAL_SECONDS=900 +XAAUDIT.HDFS.DESTINTATION_ROLLOVER_INTERVAL_SECONDS=86400 +XAAUDIT.HDFS.DESTINTATION_OPEN_RETRY_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_DIRECTORY=/var/log/hadoop/yarn/audit +XAAUDIT.HDFS.LOCAL_ARCHIVE_DIRECTORY=/var/log/hadoop/yarn/audit/archive +XAAUDIT.HDFS.LOCAL_BUFFER_FILE=%time:yyyyMMdd-HHmm.ss%.log +XAAUDIT.HDFS.LOCAL_BUFFER_FLUSH_INTERVAL_SECONDS=60 +XAAUDIT.HDFS.LOCAL_BUFFER_ROLLOVER_INTERVAL_SECONDS=600 +XAAUDIT.HDFS.LOCAL_ARCHIVE_MAX_FILE_COUNT=10 + +XAAUDIT.SOLR.ENABLE=true +XAAUDIT.SOLR.URL=http://ranger-solr:8983/solr/ranger_audits +XAAUDIT.SOLR.USER=NONE +XAAUDIT.SOLR.PASSWORD=NONE +XAAUDIT.SOLR.ZOOKEEPER=NONE +XAAUDIT.SOLR.FILE_SPOOL_DIR=/var/log/hadoop/yarn/audit/solr/spool + +XAAUDIT.ELASTICSEARCH.ENABLE=false +XAAUDIT.ELASTICSEARCH.URL=NONE +XAAUDIT.ELASTICSEARCH.USER=NONE +XAAUDIT.ELASTICSEARCH.PASSWORD=NONE +XAAUDIT.ELASTICSEARCH.INDEX=NONE +XAAUDIT.ELASTICSEARCH.PORT=NONE +XAAUDIT.ELASTICSEARCH.PROTOCOL=NONE + +XAAUDIT.HDFS.ENABLE=true +XAAUDIT.HDFS.HDFS_DIR=hdfs://ranger-hadoop:9000/ranger/audit +XAAUDIT.HDFS.FILE_SPOOL_DIR=/var/log/hadoop/yarn/audit/hdfs/spool + +XAAUDIT.HDFS.AZURE_ACCOUNTNAME=__REPLACE_AZURE_ACCOUNT_NAME +XAAUDIT.HDFS.AZURE_ACCOUNTKEY=__REPLACE_AZURE_ACCOUNT_KEY +XAAUDIT.HDFS.AZURE_SHELL_KEY_PROVIDER=__REPLACE_AZURE_SHELL_KEY_PROVIDER +XAAUDIT.HDFS.AZURE_ACCOUNTKEY_PROVIDER=__REPLACE_AZURE_ACCOUNT_KEY_PROVIDER + +XAAUDIT.LOG4J.ENABLE=false +XAAUDIT.LOG4J.IS_ASYNC=false +XAAUDIT.LOG4J.ASYNC.MAX.QUEUE.SIZE=10240 +XAAUDIT.LOG4J.ASYNC.MAX.FLUSH.INTERVAL.MS=30000 +XAAUDIT.LOG4J.DESTINATION.LOG4J=false +XAAUDIT.LOG4J.DESTINATION.LOG4J.LOGGER=xaaudit + +XAAUDIT.AMAZON_CLOUDWATCH.ENABLE=false +XAAUDIT.AMAZON_CLOUDWATCH.LOG_GROUP=NONE +XAAUDIT.AMAZON_CLOUDWATCH.LOG_STREAM_PREFIX=NONE +XAAUDIT.AMAZON_CLOUDWATCH.FILE_SPOOL_DIR=NONE +XAAUDIT.AMAZON_CLOUDWATCH.REGION=NONE + +SSL_KEYSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-keystore.jks +SSL_KEYSTORE_PASSWORD=myKeyFilePassword +SSL_TRUSTSTORE_FILE_PATH=/etc/hadoop/conf/ranger-plugin-truststore.jks +SSL_TRUSTSTORE_PASSWORD=changeit diff --git a/dev-support/ranger-docker/scripts/ranger.sh b/dev-support/ranger-docker/scripts/ranger.sh new file mode 100755 index 0000000000..666a07b227 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ranger.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# 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. + + +if [ ! -e ${RANGER_HOME}/.setupDone ] +then + SETUP_RANGER=true +else + SETUP_RANGER=false +fi + +if [ "${SETUP_RANGER}" == "true" ] +then + cd "${RANGER_HOME}"/admin || exit + if ./setup.sh; + then + touch "${RANGER_HOME}"/.setupDone + else + echo "Ranger Admin Setup Script didn't complete proper execution." + fi +fi + +cd ${RANGER_HOME}/admin && ./ews/ranger-admin-services.sh start + +if [ "${SETUP_RANGER}" == "true" ] +then + # Wait for Ranger Admin to become ready + sleep 30 + python3 ${RANGER_SCRIPTS}/create-ranger-services.py +fi + +RANGER_ADMIN_PID=`ps -ef | grep -v grep | grep -i "org.apache.ranger.server.tomcat.EmbeddedServer" | awk '{print $2}'` + +# prevent the container from exiting +if [ -z "$RANGER_ADMIN_PID" ] +then + echo "Ranger Admin process probably exited, no process id found!" +else + tail --pid=$RANGER_ADMIN_PID -f /dev/null +fi diff --git a/dev-support/ranger-docker/scripts/ugsync-file-source.csv b/dev-support/ranger-docker/scripts/ugsync-file-source.csv new file mode 100644 index 0000000000..f4e55fbd39 --- /dev/null +++ b/dev-support/ranger-docker/scripts/ugsync-file-source.csv @@ -0,0 +1,10 @@ +testuser_1,testgroup_1A,testgroup_1B,testgroup_3A,testgroup_3B, +testuser_2,testgroup_2A,testgroup_2B,testgroup_3A,testgroup_3B, +testuser_3,testgroup_3A,testgroup_3B,testgroup_3A,testgroup_3B, +testuser_4,testgroup_4A,testgroup_4B,testgroup_3A,testgroup_3B, +testuser_5,testgroup_5A,testgroup_5B,testgroup_3A,testgroup_3B, +testuser_6,testgroup_6A,testgroup_6B,testgroup_3A,testgroup_3B, +testuser_7,testgroup_7A,testgroup_7B,testgroup_3A,testgroup_3B, +testuser_8,testgroup_8A,testgroup_8B,testgroup_3A,testgroup_3B, +testuser_9,testgroup_9A,testgroup_9B,testgroup_3A,testgroup_3B, +testuser_10,testgroup_10A,testgroup_10B,testgroup_3A,testgroup_3B, \ No newline at end of file diff --git a/dev-support/ranger-pmd-ruleset.xml b/dev-support/ranger-pmd-ruleset.xml index 65a4f1da4e..2899f7ca9e 100644 --- a/dev-support/ranger-pmd-ruleset.xml +++ b/dev-support/ranger-pmd-ruleset.xml @@ -24,27 +24,98 @@ Apache Ranger - PMD rule set - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev-support/spotbugsIncludeFile.xml b/dev-support/spotbugsIncludeFile.xml new file mode 100644 index 0000000000..9a0a9261a9 --- /dev/null +++ b/dev-support/spotbugsIncludeFile.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/distro/pom.xml b/distro/pom.xml index 6c4fdaeb7a..239d395f1d 100644 --- a/distro/pom.xml +++ b/distro/pom.xml @@ -24,7 +24,7 @@ org.apache.ranger ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT .. @@ -40,8 +40,8 @@ maven-assembly-plugin ${assembly.plugin.version} - ranger-${project.version} - ../target + ranger-${project.version} + ../target @@ -75,6 +75,8 @@ src/main/assembly/plugin-elasticsearch.xml src/main/assembly/plugin-schema-registry.xml src/main/assembly/plugin-presto.xml + src/main/assembly/plugin-trino.xml + src/main/assembly/sample-client.xml @@ -83,7 +85,6 @@ - ranger-admin @@ -524,6 +525,35 @@ + + ranger-trino-plugin + + + + maven-assembly-plugin + ${assembly.plugin.version} + + ranger-${project.version} + ../target + + + + + single + + package + + false + + src/main/assembly/plugin-trino.xml + + + + + + + + ranger-presto-plugin @@ -642,10 +672,312 @@ + + org.apache.ranger + ranger-hdfs-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-hdfs-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-hive-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-hive-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-hbase-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-hbase-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-knox-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-knox-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-yarn-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-yarn-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-ozone-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-ozone-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-storm-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-storm-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-kafka-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-kafka-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-solr-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-solr-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-atlas-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-atlas-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-kms-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-kms-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-presto-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-presto-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-schema-registry-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-sqoop-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-sqoop-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-kylin-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-kylin-plugin-shim + ${project.version} + provided + + + org.apache.ranger + ranger-elasticsearch-plugin + ${project.version} + provided + org.apache.ranger ranger-elasticsearch-plugin-shim ${project.version} + provided + + + org.apache.ranger + ranger-nifi-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-nifi-registry-plugin + ${project.version} + provided + + + org.apache.ranger + ranger-kudu-plugin + ${project.version} + provided + + + org.apache.ranger + embeddedwebserver + ${project.version} + provided + + + org.apache.ranger + credentialbuilder + ${project.version} + provided + + + org.apache.ranger + ranger-plugins-installer + ${project.version} + provided + + + org.apache.ranger + ranger-intg + ${project.version} + provided + + + org.apache.ranger + ranger-plugins-cred + ${project.version} + provided + + + org.apache.ranger + ranger-plugins-common + ${project.version} + provided + + + org.apache.ranger + ranger-plugins-audit + ${project.version} + provided + + + org.apache.ranger + jisql + ${project.version} + provided + + + org.apache.ranger + security-admin-web + ${project.version} + provided + war + + + org.apache.ranger + ranger-plugin-classloader + ${project.version} + provided + + + org.apache.ranger + ranger-tools + ${project.version} + provided + + + org.apache.ranger + ranger-tagsync + ${project.version} + provided + + + org.apache.ranger + unixusersync + ${project.version} + provided + + + org.apache.ranger + ugsync-util + ${project.version} + provided + + + org.apache.ranger + ranger-util + ${project.version} + provided + + + org.apache.ranger + unixauthclient + ${project.version} + provided + + + org.apache.ranger + unixauthservice + ${project.version} + provided diff --git a/distro/src/main/assembly/admin-web.xml b/distro/src/main/assembly/admin-web.xml index 0c47d0d776..54fba59ba7 100644 --- a/distro/src/main/assembly/admin-web.xml +++ b/distro/src/main/assembly/admin-web.xml @@ -19,7 +19,6 @@ admin tar.gz - zip ${project.parent.name}-${project.version}-admin true @@ -102,10 +101,13 @@ org.apache.hbase:hbase-protocol:jar:${hbase.version} org.apache.hbase:hbase-server:jar:${hbase.version} org.apache.hbase:hbase-protocol-shaded:jar:${hbase.version} + org.apache.hbase:hbase-shaded-client:jar:${hbase.version} org.apache.hbase.thirdparty:hbase-shaded-protobuf:jar:${hbase-shaded-protobuf} org.apache.hbase.thirdparty:hbase-shaded-netty:jar:${hbase-shaded-netty} org.apache.hbase.thirdparty:hbase-shaded-miscellaneous:jar:${hbase-shaded-miscellaneous} - org.apache.htrace:htrace-core4:jar:${htrace-core.version} + io.opentelemetry:opentelemetry-api:jar:${io.opentelemetry.version} + io.opentelemetry:opentelemetry-context:jar:${io.opentelemetry.version} + io.opentelemetry:opentelemetry-semconv:jar:${io.opentelemetry-semconv.version} io.dropwizard.metrics:metrics-core @@ -125,8 +127,8 @@ commons-lang:commons-lang:jar:${commons.lang.version} commons-collections:commons-collections:jar:${commons.collections.version} - org.codehaus.jackson:jackson-core-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-mapper-asl:jar:${codehaus.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} @@ -178,21 +180,25 @@ 755 644 - org.apache.hadoop:hadoop-ozone-common:jar:${ozone.version} - org.apache.hadoop:hadoop-ozone-client:jar:${ozone.version} - org.apache.hadoop:hadoop-hdds-common:jar:${ozone.version} - org.apache.hadoop:hadoop-hdds-client:jar:${ozone.version} - org.apache.commons:commons-compress:jar:1.4.1 + org.apache.ozone:ozone-common:jar:${ozone.version} + org.apache.ozone:ozone-client:jar:${ozone.version} + org.apache.ozone:hdds-common:jar:${ozone.version} + org.apache.ozone:hdds-client:jar:${ozone.version} + org.apache.ozone:hdds-config:jar:${ozone.version} + org.apache.ozone:hdds-interface-client:jar:${ozone.version} + org.apache.ozone:ozone-interface-client:jar:${ozone.version} + org.apache.ratis:ratis-common:jar:${ratis.version} + org.apache.ratis:ratis-proto:jar:${ratis.version} + org.apache.ratis:ratis-thirdparty-misc:jar:${ratis-thirdparty.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.bouncycastle:bcpkix-jdk15on:jar:${org.bouncycastle.bcpkix-jdk15on} commons-net:commons-net:jar:${commons.net.version} - com.google.guava:guava - io.jaegertracing:jaeger-core:jar:0.33.1 - io.opentracing:opentracing-api:jar:0.31.0 - io.opentracing:opentracing-noop:jar:0.31.0 - io.opentracing:opentracing-util:jar:0.31.0 - io.opentracing.contrib:opentracing-tracerresolver:jar:0.1.5 - com.google.protobuf:protobuf-java - org.apache.ratis:ratis-common:jar:0.2.0 - org.apache.ratis:ratis-thirdparty-misc:jar:0.2.0 + io.jaegertracing:jaeger-core:jar:1.6.0 + io.opentracing:opentracing-api:jar:0.33.0 + io.opentracing:opentracing-noop:jar:0.33.0 + io.opentracing:opentracing-util:jar:0.33.0 + io.opentracing.contrib:opentracing-tracerresolver:jar:0.1.8 + com.google.protobuf:protobuf-java:jar:${protobuf-java.version} @@ -229,28 +235,25 @@ org.apache.tomcat.embed:tomcat-embed* org.apache.tomcat:tomcat-annotations-api* org.eclipse.jdt.core.compiler:ecj:jar:P20140317-1600 - log4j:log4j org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.apache.ranger:ranger-plugins-common - org.slf4j:slf4j-api + org.slf4j:slf4j-api:jar:${slf4j.version} org.apache.hadoop:hadoop-common commons-logging:commons-logging com.sun.jersey.contribs:jersey-multipart - com.google.guava:guava commons-collections:commons-collections commons-lang:commons-lang commons-io:commons-io - org.apache.solr:solr-solrj - org.apache.httpcomponents:httpclient - org.apache.httpcomponents:httpcore - org.noggit:noggit + org.apache.solr:solr-solrj:jar:${solr.version} + org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} + org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.apache.zookeeper:zookeeper:jar:${zookeeper.version} - org.apache.httpcomponents:httpmime + org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} commons-codec:commons-codec - org.apache.htrace:htrace-core4:jar:${htrace-core.version} com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -262,15 +265,20 @@ org.elasticsearch:elasticsearch org.elasticsearch.plugin:lang-mustache-client org.elasticsearch.plugin:rank-eval-client - org.apache.httpcomponents:httpasyncclient + org.apache.httpcomponents:httpasyncclient:jar:${httpcomponents.httpasyncclient.version} org.apache.httpcomponents:httpcore-nio com.fasterxml.jackson.core:jackson-core - org.apache.logging.log4j:log4j-core - org.apache.logging.log4j:log4j-api org.apache.lucene:lucene-core com.carrotsearch:hppc joda-time:joda-time org.apache.ranger:ranger-plugins-cred + org.apache.ranger:ugsyn-util + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.slf4j:log4j-over-slf4j:jar:${slf4j.version} + ch.qos.logback:logback-classic:jar:${logback.version} + ch.qos.logback:logback-core:jar:${logback.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -292,16 +300,16 @@ commons-configuration:commons-configuration commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang - commons-lang3:commons-lang3 commons-logging:commons-logging - com.google.guava:guava - org.slf4j:slf4j-api + org.slf4j:slf4j-api:jar:${slf4j.version} + org.apache.commons:commons-lang3 org.apache.hadoop:hadoop-common org.apache.hadoop:hadoop-auth - org.apache.htrace:htrace-core4:jar:${htrace-core.version} com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -479,6 +487,25 @@ + + true + + org.apache.ranger:ranger-trino-plugin + + + ews/webapp/WEB-INF/classes/ranger-plugins/trino + true + false + 755 + 644 + + org.apache.ranger:ranger-trino-plugin + io.trino:trino-spi:jar:${trino.version} + io.trino:trino-jdbc:jar:${trino.version} + + + + true @@ -577,6 +604,24 @@ + + ews/webapp/apidocs + ${project.parent.basedir}/docs/target/apidocs/ui + + index.html + + + swagger.json + + 544 + + + + ews/webapp/apidocs + ${project.parent.basedir}/docs/src/site/resources + 544 + + ${project.parent.basedir}/security-admin @@ -602,5 +647,12 @@ 0755 0755 + + + stability-tests + ${project.parent.basedir}/security-admin/src/test/resources/stability-tests + 0755 + 0755 + diff --git a/distro/src/main/assembly/hbase-agent.xml b/distro/src/main/assembly/hbase-agent.xml index f73a660ce6..37e2903a46 100644 --- a/distro/src/main/assembly/hbase-agent.xml +++ b/distro/src/main/assembly/hbase-agent.xml @@ -19,7 +19,6 @@ hbase-plugin tar.gz - zip ${project.parent.name}-${project.version}-hbase-plugin true @@ -54,15 +53,17 @@ 755 644 - com.google.code.gson:gson* - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + com.sun.jersey:jersey-client:jar:${jersey-bundle.version} + com.sun.jersey:jersey-core:jar:${jersey-bundle.version} + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} - org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} - org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj - com.google.guava:guava:jar:${google.guava.version} + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -73,11 +74,15 @@ org.elasticsearch.client:elasticsearch-rest-high-level-client org.elasticsearch.plugin:rank-eval-client org.elasticsearch.plugin:lang-mustache-client - org.apache.httpcomponents:httpcore-nio:jar:${httpcomponents.httpcore.version} org.apache.httpcomponents:httpasyncclient:jar:${httpcomponents.httpasyncclient.version} org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -99,13 +104,14 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/hdfs-agent.xml b/distro/src/main/assembly/hdfs-agent.xml index 1bc9ff5c26..8b133d993f 100644 --- a/distro/src/main/assembly/hdfs-agent.xml +++ b/distro/src/main/assembly/hdfs-agent.xml @@ -19,7 +19,6 @@ hdfs-plugin tar.gz - zip ${project.parent.name}-${project.version}-hdfs-plugin true @@ -53,16 +52,17 @@ commons-cli:commons-cli commons-collections:commons-collections org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -82,13 +82,13 @@ 755 644 - org.eclipse.persistence:javax.persistence - org.eclipse.persistence:eclipselink + commons-lang:commons-lang + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -104,6 +104,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/hive-agent.xml b/distro/src/main/assembly/hive-agent.xml index 33965046f4..9b9bc5b3af 100644 --- a/distro/src/main/assembly/hive-agent.xml +++ b/distro/src/main/assembly/hive-agent.xml @@ -19,7 +19,6 @@ hive-plugin tar.gz - zip ${project.parent.name}-${project.version}-hive-plugin true @@ -54,13 +53,12 @@ 755 644 - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -76,6 +74,7 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -94,16 +93,17 @@ commons-cli:commons-cli commons-collections:commons-collections org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/kms.xml b/distro/src/main/assembly/kms.xml index ccc39a5a89..46c0edb00b 100755 --- a/distro/src/main/assembly/kms.xml +++ b/distro/src/main/assembly/kms.xml @@ -19,7 +19,6 @@ kms tar.gz - zip ${project.parent.name}-${project.version}-kms true @@ -31,35 +30,30 @@ org.apache.ranger:ranger-kms - ews/webapp/lib + ews/webapp/WEB-INF/classes true - false - + true + + + ews/webapp/WEB-INF/lib/ + false + org.apache.hadoop:hadoop-common:jar:${hadoop.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.eclipse.persistence:eclipselink org.eclipse.persistence:javax.persistence com.googlecode.log4jdbc:log4jdbc - log4j:log4j - org.slf4j:slf4j-api - org.slf4j:slf4j-log4j12 com.codahale.metrics:metrics-core org.slf4j:jul-to-slf4j - commons-logging:commons-logging - com.google.guava:guava - com.sun.jersey:jersey-core - com.sun.jersey:jersey-server javax.servlet:servlet-api org.mortbay.jetty:jetty org.mortbay.jetty:jetty-util - commons-collections:commons-collections - commons-lang:commons-lang org.apache.zookeeper:zookeeper:jar:${zookeeper.version} org.apache.curator:curator-framework org.apache.curator:curator-client org.apache.curator:curator-test asm:asm-all - com.sun.jersey:jersey-bundle org.apache.httpcomponents:httpclient javax.activation:activation org.apache.directory.server:apacheds-i18n @@ -67,33 +61,23 @@ org.apache.directory.api:api-asn1-api org.apache.directory.api:api-i18n org.apache.directory.api:api-util - org.apache.avro:avro commons-beanutils:commons-beanutils commons-cli:commons-cli commons-codec:commons-codec:jar:${commons.codec.version} - org.apache.commons:commons-compress - org.apache.commons:commons-configuration2 commons-digester:commons-digester commons-io:commons-io org.apache.commons:commons-math3 commons-net:commons-net:jar:${commons.net.version} org.apache.curator:curator-recipes - com.google.code.gson:gson org.apache.hadoop:hadoop-annotations - org.apache.htrace:htrace-core4 org.apache.httpcomponents:httpcore - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc javax.xml.bind:jaxb-api com.sun.xml.bind:jaxb-impl - com.sun.jersey:jersey-json - org.codehaus.jettison:jettison + org.codehaus.jettison:jettison:jar:${jettison.version} jline:jline com.jcraft:jsch com.google.code.findbugs:jsr305 - io.netty:netty + io.netty:netty-all com.thoughtworks.paranamer:paranamer org.xerial.snappy:snappy-java xmlenc:xmlenc @@ -101,14 +85,13 @@ org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${kms.httpcomponents.httpclient.version} org.noggit:noggit:jar:${noggit.version} - com.google.protobuf:protobuf-java:jar:${protobuf-java.version} org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version} - org.apache.htrace:htrace-core4:jar:${htrace-core.version} - org.codehaus.woodstox:stax2-api - com.fasterxml.woodstox:woodstox-core - com.fasterxml.jackson.core:jackson-core - com.fasterxml.jackson.core:jackson-annotations - com.fasterxml.jackson.core:jackson-databind + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + org.apache.kerby:kerb-core:jar:${kerby.version} + org.apache.kerby:kerb-util:jar:${kerby.version} + org.apache.kerby:kerb-crypto:jar:${kerby.version} + org.apache.kerby:kerby-asn1:jar:${kerby.version} com.microsoft.azure:azure:jar:${com.microsoft.azure.version} com.microsoft.azure:azure-keyvault:jar:${com.microsoft.azure.azure-keyvault.version} com.microsoft.azure:azure-mgmt-keyvault:jar:${com.microsoft.azure.azure-mgmt-keyvault.version} @@ -121,7 +104,7 @@ com.squareup.retrofit2:retrofit com.squareup.retrofit2:adapter-rxjava com.squareup.okhttp3:okhttp-urlconnection - com.fasterxml.jackson.datatype:jackson-datatype-joda + com.fasterxml.jackson.datatype:jackson-datatype-joda:jar:${fasterxml.jackson.version} joda-time:okhttp-urlconnection joda-time:joda-time com.nimbusds:oauth2-oidc-sdk @@ -130,7 +113,6 @@ javax.mail:javax.mail-api com.sun.mail:javax.mail com.nimbusds:nimbus-jose-jwt - org.apache.commons:commons-lang3 com.microsoft.azure:azure-keyvault-webkey org.bouncycastle:bcprov-jdk15on org.bouncycastle:bcpkix-jdk15on @@ -160,19 +142,53 @@ com.microsoft.azure:azure-mgmt-eventhub com.microsoft.azure:azure-mgmt-eventhub com.microsoft.azure:azure-keyvault-cryptography - - - - - - true - - org.apache.ranger:ranger-kms - - - ews/webapp - false - true + + com.google.cloud:google-cloud-kms + io.grpc:grpc-api + io.grpc:grpc-context + io.grpc:grpc-stub + io.grpc:grpc-protobuf + io.grpc:grpc-protobuf-lite + com.google.api:api-common + javax.annotation:javax.annotation-api + com.google.auto.value:auto-value-annotations + com.google.api.grpc:proto-google-common-protos + com.google.api.grpc:proto-google-cloud-kms-v1 + com.google.api:gax + io.opencensus:opencensus-api + com.google.api:gax-grpc + com.google.auth:google-auth-library-credentials + io.grpc:grpc-netty-shaded + io.perfmark:perfmark-api + io.grpc:grpc-core + com.google.android:annotations + io.grpc:grpc-alts + io.grpc:grpc-grpclb + com.google.protobuf:protobuf-java-util:jar:${gcp.protobuf-java.version} + org.conscrypt:conscrypt-openjdk-uber + org.threeten:threetenbp + io.grpc:grpc-auth + com.google.api.grpc:proto-google-iam-v1 + com.google.auth:google-auth-library-oauth2-http + com.google.http-client:google-http-client + io.opencensus:opencensus-contrib-http-util + com.google.http-client:google-http-client-gson + io.grpc:grpc-xds + io.grpc:grpc-services + io.opencensus:opencensus-proto + + com.amazonaws:aws-java-sdk-core + com.amazonaws:aws-java-sdk-kms + + com.tencentcloudapi:tencentcloud-sdk-java-kms + com.tencentcloudapi:tencentcloud-sdk-java-common + com.squareup.okhttp:logging-interceptor + com.squareup.okhttp:okhttp + + org.apache.ranger:ranger-metrics + + + @@ -182,7 +198,7 @@ org.apache.ranger:embeddedwebserver - ews/webapp/WEB-INF/classes/lib + ews/lib true false 755 @@ -191,15 +207,36 @@ org.apache.tomcat.embed:tomcat-embed* org.apache.tomcat:tomcat-annotations-api* org.eclipse.jdt.core.compiler:ecj:jar:P20140317-1600 - com.google.protobuf:protobuf-java:jar:${protobuf-java.version} + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} + com.google.protobuf:protobuf-java:jar:${gcp.protobuf-java.version} org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version} - org.apache.htrace:htrace-core4:jar:${htrace-core.version} - org.apache.solr:solr-solrj + org.apache.hadoop:hadoop-common:jar:${hadoop.version} + org.apache.hadoop:hadoop-auth:jar:${hadoop.version} + org.apache.solr:solr-solrj:jar:${solr.version} org.apache.ranger:ranger-plugins-common com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} org.apache.ranger:credentialbuilder + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.commons:commons-lang3 + org.apache.commons:commons-configuration2 + commons-lang:commons-lang + commons-collections:commons-collections + commons-logging:commons-logging + org.slf4j:slf4j-api + org.slf4j:log4j-over-slf4j:jar:${slf4j.version} + ch.qos.logback:logback-classic:jar:${logback.version} + ch.qos.logback:logback-core:jar:${logback.version} + com.google.code.gson:gson + com.sun.jersey:jersey-bundle + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + org.codehaus.woodstox:stax2-api + com.fasterxml.woodstox:woodstox-core + org.apache.commons:commons-text:jar:${commons.text.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -225,12 +262,13 @@ true + org.apache.ranger:ranger-util org.apache.ranger:ranger-kms-plugin-shim org.apache.ranger:ranger-plugin-classloader org.apache.ranger:credentialbuilder - ews/webapp/WEB-INF/classes/lib + ews/webapp/WEB-INF/lib false false 755 @@ -247,14 +285,14 @@ org.apache.ranger:ranger-kms-plugin - ews/webapp/WEB-INF/classes/lib/ranger-kms-plugin-impl + ews/webapp/WEB-INF/lib/ranger-kms-plugin-impl true false 755 644 - ews/webapp/WEB-INF/classes/lib/ranger-kms-plugin-impl + ews/webapp/WEB-INF/lib/ranger-kms-plugin-impl false org.apache.commons:commons-configuration2:jar:${commons.configuration.version} @@ -263,13 +301,12 @@ org.eclipse.persistence:javax.persistence commons-collections:commons-collections commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.noggit:noggit:jar:${noggit.version} org.apache.zookeeper:zookeeper:jar:${zookeeper.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -285,6 +322,10 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} @@ -299,7 +340,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -307,6 +347,7 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -345,13 +386,14 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging - com.google.guava:guava org.slf4j:slf4j-api org.apache.hadoop:hadoop-common org.apache.hadoop:hadoop-auth - org.apache.htrace:htrace-core4 org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.commons:commons-lang3 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -388,6 +430,7 @@ DBMKTOKEYSECURE.sh DBMKTOAZUREKEYVAULT.sh KEYSECUREMKTOKMSDB.sh + MigrateMKeyStorageDbToGCP.sh 544 @@ -399,6 +442,12 @@ 700 + + ews/webapp + 0700 + ${project.parent.basedir}/kms/src/main/webapp + 0600 + ews/webapp/WEB-INF/classes/conf.dist 0700 diff --git a/distro/src/main/assembly/knox-agent.xml b/distro/src/main/assembly/knox-agent.xml index 79856e952c..a7906fe4f8 100644 --- a/distro/src/main/assembly/knox-agent.xml +++ b/distro/src/main/assembly/knox-agent.xml @@ -19,7 +19,6 @@ knox-plugin tar.gz - zip ${project.parent.name}-${project.version}-knox-plugin true @@ -55,6 +54,7 @@ 755 644 + com.sun.jersey:jersey-client:jar:${jersey-bundle.version} org.apache.commons:commons-configuration2 com.google.code.gson:gson* org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} @@ -64,10 +64,10 @@ com.google.protobuf:protobuf-java:jar:${protobuf-java.version} org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} - org.apache.htrace:htrace-core4:jar:${htrace-core.version} - org.codehaus.jackson:jackson-core-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-mapper-asl:jar:${codehaus.jackson.version} - org.apache.solr:solr-solrj + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -83,6 +83,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -100,17 +105,18 @@ commons-cli:commons-cli commons-collections:commons-collections + org.apache.commons:commons-lang3:jar:${commons.lang3.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/migration-util.xml b/distro/src/main/assembly/migration-util.xml index 4c723b8072..f3780e2037 100644 --- a/distro/src/main/assembly/migration-util.xml +++ b/distro/src/main/assembly/migration-util.xml @@ -19,7 +19,6 @@ migration-util tar.gz - zip ${project.parent.name}-${project.version}-migration-util true diff --git a/distro/src/main/assembly/plugin-atlas.xml b/distro/src/main/assembly/plugin-atlas.xml index 62114c6af0..59b229c18e 100644 --- a/distro/src/main/assembly/plugin-atlas.xml +++ b/distro/src/main/assembly/plugin-atlas.xml @@ -19,7 +19,6 @@ atlas-plugin tar.gz - zip ${project.parent.name}-${project.version}-atlas-plugin true @@ -55,22 +54,19 @@ 755 644 - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence com.sun.jersey:jersey-bundle - com.google.guava:guava:jar:${google.guava.version} - org.codehaus.jackson:jackson-jaxrs + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} - org.apache.solr:solr-solrj - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} - org.apache.htrace:htrace-core4:jar:${htrace-core.version} com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} @@ -92,6 +88,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -114,16 +115,17 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.apache.ranger:ranger-plugins-cred org.apache.ranger:credentialbuilder - org.apache.htrace:htrace-core4:jar:${htrace-core.version} com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-elasticsearch.xml b/distro/src/main/assembly/plugin-elasticsearch.xml index 6a57410ad4..64a68b649c 100644 --- a/distro/src/main/assembly/plugin-elasticsearch.xml +++ b/distro/src/main/assembly/plugin-elasticsearch.xml @@ -19,7 +19,6 @@ elasticsearch-plugin tar.gz - zip ${project.parent.name}-${project.version}-elasticsearch-plugin true @@ -66,21 +65,20 @@ org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version} com.google.code.gson:gson - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} com.sun.jersey:jersey-bundle commons-logging:commons-logging:jar:${commons.logging.version} commons-io:commons-io - com.google.guava:guava:jar:${google.guava.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.noggit:noggit:jar:${noggit.version} - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc - org.apache.solr:solr-solrj + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + org.apache.solr:solr-solrj:jar:${solr.version} commons-codec:commons-codec com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} @@ -97,6 +95,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -122,12 +125,12 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.apache.ranger:ranger-plugins-cred org.apache.ranger:credentialbuilder + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-kafka.xml b/distro/src/main/assembly/plugin-kafka.xml index 4298088d73..4fe600cd91 100644 --- a/distro/src/main/assembly/plugin-kafka.xml +++ b/distro/src/main/assembly/plugin-kafka.xml @@ -14,7 +14,6 @@ kafka-plugin tar.gz - zip ${project.parent.name}-${project.version}-kafka-plugin @@ -56,11 +55,10 @@ org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version} com.google.code.gson:gson - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} commons-collections:commons-collections com.sun.jersey:jersey-bundle - commons-logging:commons-logging:jar:${commons.logging.version} + commons-logging:commons-logging commons-lang:commons-lang commons-io:commons-io org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} @@ -68,15 +66,15 @@ org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} com.google.protobuf:protobuf-java:jar:${protobuf-java.version} org.noggit:noggit:jar:${noggit.version} - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc - org.apache.solr:solr-solrj + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + org.apache.solr:solr-solrj:jar:${solr.version} commons-codec:commons-codec org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -92,6 +90,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -117,7 +120,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -125,7 +127,7 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-kms.xml b/distro/src/main/assembly/plugin-kms.xml index c3738ce935..605bdeff9b 100755 --- a/distro/src/main/assembly/plugin-kms.xml +++ b/distro/src/main/assembly/plugin-kms.xml @@ -19,7 +19,6 @@ kms-plugin tar.gz - zip ${project.parent.name}-${project.version}-kms-plugin true @@ -57,20 +56,23 @@ org.apache.commons:commons-configuration2:jar:${commons.configuration.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} com.google.code.gson:gson - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} commons-collections:commons-collections commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.noggit:noggit:jar:${noggit.version} org.apache.zookeeper:zookeeper:jar:${zookeeper.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -96,7 +98,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -104,7 +105,7 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-kylin.xml b/distro/src/main/assembly/plugin-kylin.xml index c0885c2ae1..b4a0076163 100644 --- a/distro/src/main/assembly/plugin-kylin.xml +++ b/distro/src/main/assembly/plugin-kylin.xml @@ -19,7 +19,6 @@ kylin-plugin tar.gz - zip ${project.parent.name}-${project.version}-kylin-plugin true @@ -58,13 +57,12 @@ lib/ranger-kylin-plugin-impl false - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -80,6 +78,10 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} @@ -94,7 +96,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -102,7 +103,7 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-ozone.xml b/distro/src/main/assembly/plugin-ozone.xml index 62a31ffafa..5f1e6a5387 100644 --- a/distro/src/main/assembly/plugin-ozone.xml +++ b/distro/src/main/assembly/plugin-ozone.xml @@ -19,12 +19,10 @@ ozone-plugin tar.gz - zip ${project.parent.name}-${project.version}-ozone-plugin true - true @@ -51,28 +49,25 @@ true false - org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-configuration2:jar:${commons.configuration.version} commons-configuration:commons-configuration:jar:${commons.configuration1.version} commons-cli:commons-cli commons-collections:commons-collections commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} - org.apache.hadoop:hadoop-ozone:jar:0.4.0.3.0.100.0-SNAPSHOT - org.apache.hadoop:hadoop-hdds:jar:0.4.0.3.0.100.0-SNAPSHOT com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} com.sun.jersey:jersey-core com.sun.jersey:jersey-client com.sun.jersey:jersey-bundle - org.apache.htrace:htrace-core4:jar:${htrace-core.version} com.kstruct:gethostname4j net.java.dev.jna:jna net.java.dev.jna:jna-platform + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -92,28 +87,27 @@ 755 644 - org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.commons:commons-configuration2:jar:${commons.configuration.version} commons-configuration:commons-configuration:jar:${commons.configuration1.version} - org.eclipse.persistence:javax.persistence - org.eclipse.persistence:eclipselink + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} - org.apache.zookeeper:zookeeper:jar:${zookeeper.version} - org.apache.zookeeper:zookeeper-jute:jar:${zookeeper.version} + org.apache.zookeeper:zookeeper:jar:${zookeeper.version} + org.apache.zookeeper:zookeeper-jute:jar:${zookeeper.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} com.sun.jersey:jersey-core com.sun.jersey:jersey-client com.sun.jersey:jersey-bundle - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-xc - com.sun.xml.bind:jaxb-impl - org.apache.htrace:htrace-core4:jar:${htrace-core.version} + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + com.sun.xml.bind:jaxb-impl commons-lang:commons-lang com.kstruct:gethostname4j net.java.dev.jna:jna @@ -130,6 +124,10 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} diff --git a/distro/src/main/assembly/plugin-presto.xml b/distro/src/main/assembly/plugin-presto.xml index 4f1197886c..e4101237a2 100644 --- a/distro/src/main/assembly/plugin-presto.xml +++ b/distro/src/main/assembly/plugin-presto.xml @@ -19,7 +19,6 @@ presto-plugin tar.gz - zip ${project.parent.name}-${project.version}-presto-plugin true @@ -38,7 +37,6 @@ 644 com.google.inject:guice:jar:${presto.guice.version} - com.google.guava:guava:jar:${presto.guava.version} io.airlift:bootstrap:jar:${presto.airlift.version} io.airlift:log:jar:${presto.airlift.version} io.airlift:log-manager:jar:${presto.airlift.version} @@ -68,13 +66,12 @@ 644 com.google.code.gson:gson* - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.sun.jersey:jersey-core com.sun.jersey:jersey-server commons-cli:commons-cli @@ -84,7 +81,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} com.google.protobuf:protobuf-java:jar:${protobuf-java.version} com.google.re2j:re2j:jar:${presto.re2j.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} @@ -96,13 +92,13 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 com.sun.jersey:jersey-bundle com.sun.jersey:jersey-json - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} org.apache.zookeeper:zookeeper:jar:${zookeeper.version} net.java.dev.jna:jna net.java.dev.jna:jna-platform @@ -119,6 +115,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -136,18 +137,16 @@ commons-cli:commons-cli commons-collections:commons-collections - com.google.guava:guava:jar:${presto.guava.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-solr.xml b/distro/src/main/assembly/plugin-solr.xml index 91f7bfee9e..b1b1104211 100644 --- a/distro/src/main/assembly/plugin-solr.xml +++ b/distro/src/main/assembly/plugin-solr.xml @@ -13,7 +13,6 @@ solr-plugin tar.gz - zip ${project.parent.name}-${project.version}-solr-plugin @@ -50,14 +49,12 @@ 644 com.google.code.gson:gson - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence com.sun.jersey:jersey-bundle - com.google.guava:guava:jar:${google.guava.version} - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -73,6 +70,10 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} @@ -95,7 +96,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -104,7 +104,7 @@ org.apache.ranger:ranger-solr-plugin org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-sqoop.xml b/distro/src/main/assembly/plugin-sqoop.xml index af18811f74..2230d90672 100644 --- a/distro/src/main/assembly/plugin-sqoop.xml +++ b/distro/src/main/assembly/plugin-sqoop.xml @@ -19,7 +19,6 @@ sqoop-plugin tar.gz - zip ${project.parent.name}-${project.version}-sqoop-plugin true @@ -54,13 +53,12 @@ 755 644 - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -76,6 +74,10 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} @@ -99,7 +101,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -107,7 +108,7 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/plugin-trino.xml b/distro/src/main/assembly/plugin-trino.xml new file mode 100644 index 0000000000..16e2ddec7d --- /dev/null +++ b/distro/src/main/assembly/plugin-trino.xml @@ -0,0 +1,213 @@ + + + + trino-plugin + + tar.gz + + ${project.parent.name}-${project.version}-trino-plugin + true + + + true + + org.apache.ranger:ranger-plugins-audit + org.apache.ranger:ranger-plugins-cred + org.apache.ranger:ranger-plugins-common + org.apache.ranger:ranger-trino-plugin + + + lib + true + false + 755 + 644 + + com.google.inject:guice:jar:${trino.guice.version} + io.airlift:bootstrap:jar:${trino.airlift.version} + io.airlift:log:jar:${trino.airlift.version} + io.airlift:log-manager:jar:${trino.airlift.version} + io.airlift:configuration:jar:${trino.airlift.version} + javax.validation:validation-api:jar:${trino.validation-api.version} + javax.inject:javax.inject:jar:${javax-inject.version} + org.apache.bval:bval-jsr:jar:${trino.bval-jsr.version} + org.slf4j:slf4j-api:jar:${slf4j-api.version} + org.slf4j:slf4j-simple:jar:${slf4j.version} + javax.annotation:javax.annotation-api:jar:${javax.annotation-api} + com.fasterxml.jackson.core:jackson-core + com.fasterxml.jackson.core:jackson-databind + + com.google.code.gson:gson* + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} + org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} + org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} + org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} + org.noggit:noggit:jar:${noggit.version} + org.apache.solr:solr-solrj:jar:${solr.version} + com.sun.jersey:jersey-core + com.sun.jersey:jersey-server + commons-cli:commons-cli + commons-collections:commons-collections + org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + commons-codec:commons-codec + commons-io:commons-io:jar:${commons.io.version} + commons-lang:commons-lang:jar:${commons.lang.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.slf4j:jcl-over-slf4j:jar:${slf4j.version} + org.slf4j:slf4j-api:jar:${slf4j-api.version} + ch.qos.logback:logback-classic:jar:${logback.version} + ch.qos.logback:logback-core:jar:${logback.version} + com.google.protobuf:protobuf-java:jar:${protobuf-java.version} + com.google.re2j:re2j:jar:${trino.re2j.version} + org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.hadoop:hadoop-common:jar:${hadoop.version} + org.apache.hadoop:hadoop-common-plus:jar:${hadoop.version} + org.apache.hadoop:hadoop-auth:jar:${hadoop.version} + org.apache.hadoop:hadoop-hdfs:jar:${hadoop.version} + org.apache.ranger:ranger-plugins-cred + org.apache.ranger:credentialbuilder + org.codehaus.woodstox:stax2-api + com.fasterxml.woodstox:woodstox-core + com.sun.jersey:jersey-bundle + com.sun.jersey:jersey-json + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + org.apache.zookeeper:zookeeper:jar:${zookeeper.version} + net.java.dev.jna:jna + net.java.dev.jna:jna-platform + com.kstruct:gethostname4j + org.elasticsearch:elasticsearch + org.elasticsearch:elasticsearch-core + org.elasticsearch:elasticsearch-x-content + org.elasticsearch.client:elasticsearch-rest-client + org.elasticsearch.client:elasticsearch-rest-high-level-client + org.elasticsearch.plugin:rank-eval-client + org.elasticsearch.plugin:lang-mustache-client + org.apache.httpcomponents:httpcore-nio:jar:${httpcomponents.httpcore.version} + org.apache.httpcomponents:httpasyncclient:jar:${httpcomponents.httpasyncclient.version} + org.apache.lucene:lucene-core + joda-time:joda-time + com.carrotsearch:hppc + dnsjava:dnsjava:jar:${dnsjava.version} + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} + + + + + + true + + org.apache.ranger:ranger-plugins-installer + org.apache.ranger:credentialbuilder + + + install/lib + true + false + + commons-cli:commons-cli + commons-collections:commons-collections + org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + commons-io:commons-io:jar:${commons.io.version} + commons-lang:commons-lang + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + commons-logging:commons-logging:jar:${commons.logging.version} + org.slf4j:slf4j-api:jar:${slf4j-api.version} + org.apache.hadoop:hadoop-common:jar:${hadoop.version} + org.apache.hadoop:hadoop-auth:jar:${hadoop.version} + org.codehaus.woodstox:stax2-api + com.fasterxml.woodstox:woodstox-core + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} + + + + + + + + + install/conf.templates/enable + ../plugin-trino/conf + + *.sh + + 700 + + + install/conf.templates/disable + ../plugin-trino/disable-conf + 700 + + + install/conf.templates/default + ../plugin-trino/template + 700 + + + + + ${project.build.outputDirectory} + + version + + 444 + + + + + + + ${project.parent.basedir}/plugin-trino/scripts/enable-trino-plugin.sh + + enable-trino-plugin.sh + 755 + + + ${project.parent.basedir}/plugin-trino/scripts/enable-trino-plugin.sh + + disable-trino-plugin.sh + 755 + + + ${project.parent.basedir}/plugin-trino/scripts/install.properties + + install.properties + 755 + + + ${project.parent.basedir}/plugin-trino/scripts/trino-ranger-plugin-logback.xml + + trino-ranger-plugin-logback.xml + 755 + + + ${project.parent.basedir}/security-admin/scripts/ranger_credential_helper.py + + 755 + + + diff --git a/distro/src/main/assembly/plugin-yarn.xml b/distro/src/main/assembly/plugin-yarn.xml index f9f310433a..98e2b39cb2 100644 --- a/distro/src/main/assembly/plugin-yarn.xml +++ b/distro/src/main/assembly/plugin-yarn.xml @@ -19,7 +19,6 @@ yarn-plugin tar.gz - zip ${project.parent.name}-${project.version}-yarn-plugin true @@ -54,13 +53,13 @@ 755 644 - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + commons-lang:commons-lang:jar:${commons.lang.version} + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.noggit:noggit:jar:${noggit.version} - org.apache.solr:solr-solrj + org.apache.solr:solr-solrj:jar:${solr.version} com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -76,6 +75,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -99,7 +103,6 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-auth:jar:${hadoop.version} @@ -107,7 +110,7 @@ org.apache.ranger:credentialbuilder org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -120,8 +123,15 @@ install/lib - false + true false + 755 + 644 + + commons-logging:commons-logging:jar:${commons.logging.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.commons:commons-lang3:jar:${commons.lang3.version} + diff --git a/distro/src/main/assembly/ranger-src.xml b/distro/src/main/assembly/ranger-src.xml index 4cd36fa862..87915d3745 100644 --- a/distro/src/main/assembly/ranger-src.xml +++ b/distro/src/main/assembly/ranger-src.xml @@ -19,7 +19,6 @@ src tar.gz - zip ${project.parent.name}-${project.version}-src true @@ -55,6 +54,10 @@ **/debugfiles.list **/debuglinks.list **/debugsources.list + dev-support/ranger-docker/downloads/** + dev-support/ranger-docker/dist/** + intg/src/main/python/dist/** + security-admin/src/main/webapp/react-webapp/dist/** diff --git a/distro/src/main/assembly/ranger-tools.xml b/distro/src/main/assembly/ranger-tools.xml index 26e632c91c..f12159ff9f 100644 --- a/distro/src/main/assembly/ranger-tools.xml +++ b/distro/src/main/assembly/ranger-tools.xml @@ -19,7 +19,6 @@ ranger-tools tar.gz - zip ${project.parent.name}-${project.version}-ranger-tools true @@ -43,9 +42,8 @@ commons-cli:commons-cli commons-logging:commons-logging:jar:${commons.logging.version} commons-lang:commons-lang - com.google.code.gson:gson + org.apache.commons:commons-lang3:jar:${commons.lang3.version} log4j:log4j - org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} org.apache.hadoop:hadoop-common:jar:${hadoop.version} org.apache.hadoop:hadoop-common-plus:jar:${hadoop.version} @@ -56,20 +54,28 @@ commons-collections:commons-collections com.sun.jersey:jersey-bundle commons-io:commons-io - com.google.guava:guava:jar:${google.guava.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} org.noggit:noggit:jar:${noggit.version} - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.codehaus.jackson:jackson-xc + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} + com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} org.apache.ranger:ranger-plugins-common org.apache.ranger:ranger-plugins-audit com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} + org.slf4j:slf4j-api + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} + org.slf4j:log4j-over-slf4j:jar:${slf4j.version} + ch.qos.logback:logback-classic:jar:${logback.version} + ch.qos.logback:logback-core:jar:${logback.version} + com.google.code.gson:gson:jar:${gson.version} @@ -132,11 +138,41 @@ ranger-plugin-perftester.sh 755 + + ${project.parent.basedir}/ranger-tools/scripts/ranger-mem-sizing.sh + + ranger-mem-sizing.sh + 755 + ${project.parent.basedir}/ranger-tools/scripts/README.txt README.txt 644 + + ${project.parent.basedir}/ranger-tools/scripts/create_requests.py + + create_requests.py + 644 + + + ${project.parent.basedir}/ranger-tools/scripts/gen_service_tags.sh + + gen_service_tags.sh + 755 + + + ${project.parent.basedir}/ranger-tools/scripts/gen_service_policies.sh + + gen_service_policies.sh + 755 + + + ${project.parent.basedir}/ranger-tools/scripts/create_tags_file.sh + + create_tags_file.sh + 755 + diff --git a/distro/src/main/assembly/sample-client.xml b/distro/src/main/assembly/sample-client.xml new file mode 100644 index 0000000000..56f3677c2a --- /dev/null +++ b/distro/src/main/assembly/sample-client.xml @@ -0,0 +1,104 @@ + + + + sample-client + + tar.gz + + ${project.parent.name}-${project.version}-sample-client + true + + + true + + org.apache.ranger:sample-client + org.apache.ranger:ranger-intg + org.apache.ranger:ranger-plugins-common + org.apache.ranger:ranger-plugins-cred + + + lib + true + false + 755 + 644 + + commons-cli:commons-cli + commons-logging:commons-logging:jar:${commons.logging.version} + commons-lang:commons-lang + org.apache.commons:commons-lang3 + org.apache.commons:commons-compress + com.google.code.gson:gson + org.slf4j:slf4j-api:jar:${slf4j-api.version} + org.slf4j:slf4j-log4j12 + log4j:log4j + org.apache.commons:commons-configuration2:jar:${commons.configuration.version} + org.apache.hadoop:hadoop-common:jar:${hadoop.version} + org.apache.hadoop:hadoop-auth:jar:${hadoop.version} + org.eclipse.persistence:eclipselink + org.eclipse.persistence:javax.persistence + commons-collections:commons-collections + com.sun.jersey:jersey-bundle + commons-io:commons-io + org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} + org.noggit:noggit:jar:${noggit.version} + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} + org.apache.ranger:ranger-plugins-audit + com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} + net.java.dev.jna:jna:jar:${jna.version} + net.java.dev.jna:jna-platform:jar:${jna-platform.version} + com.fasterxml.woodstox:woodstox-core:jar:${fasterxml.woodstox.version} + org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} + + + + + + + + + ${project.parent.basedir}/ranger-examples/sample-client/scripts + + *.sh + + 755 + + + + ${project.parent.basedir}/ranger-examples/sample-client/conf + + *.xml + + 755 + + + 755 + 644 + lib + ${project.parent.basedir}/ranger-examples/sample-client/src/main/resources + + *.properties + + + + diff --git a/distro/src/main/assembly/storm-agent.xml b/distro/src/main/assembly/storm-agent.xml index ddbb60e564..083bbbf225 100644 --- a/distro/src/main/assembly/storm-agent.xml +++ b/distro/src/main/assembly/storm-agent.xml @@ -19,7 +19,6 @@ storm-plugin tar.gz - zip ${project.parent.name}-${project.version}-storm-plugin true @@ -62,11 +61,9 @@ org.apache.hadoop:hadoop-common org.apache.hadoop:hadoop-common-plus com.google.code.gson:gson - org.eclipse.persistence:eclipselink - org.eclipse.persistence:javax.persistence + org.eclipse.jetty:jetty-client:jar:${jetty-client.version} commons-collections:commons-collections commons-logging:commons-logging:jar:${commons.logging.version} - com.google.guava:guava:jar:${google.guava.version} org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} org.apache.httpcomponents:httpcore:jar:${httpcomponents.httpcore.version} org.apache.httpcomponents:httpmime:jar:${httpcomponents.httpmime.version} @@ -77,10 +74,14 @@ commons-cli:commons-cli:jar:${commons.cli.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-io:commons-io:jar:${commons.io.version} - org.codehaus.jackson:jackson-core-asl - org.codehaus.jackson:jackson-jaxrs - org.codehaus.jackson:jackson-mapper-asl - org.apache.solr:solr-solrj + com.fasterxml.jackson.core:jackson-annotations:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-core:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.core:jackson-databind:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider + com.fasterxml.jackson.core:jackson-databind + com.fasterxml.jackson.core:jackson-core + org.apache.solr:solr-solrj:jar:${solr.version} commons-codec:commons-codec com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} @@ -97,6 +98,11 @@ org.apache.lucene:lucene-core joda-time:joda-time com.carrotsearch:hppc + org.apache.hive:hive-storage-api:jar:${hive.storage-api.version} + org.apache.orc:orc-core:jar:${orc.version} + org.apache.orc:orc-shims:jar:${orc.version} + io.airlift:aircompressor:jar:${aircompressor.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} @@ -111,12 +117,12 @@ commons-io:commons-io:jar:${commons.io.version} commons-lang:commons-lang:jar:${commons.lang.version} commons-logging:commons-logging - com.google.guava:guava:jar:${google.guava.version} org.slf4j:slf4j-api:jar:${slf4j-api.version} org.apache.hadoop:hadoop-common:jar org.apache.hadoop:hadoop-auth:jar org.apache.ranger:ranger-plugins-cred org.apache.ranger:credentialbuilder + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/tagsync.xml b/distro/src/main/assembly/tagsync.xml index 2400cd5cf2..da30a2d827 100644 --- a/distro/src/main/assembly/tagsync.xml +++ b/distro/src/main/assembly/tagsync.xml @@ -19,7 +19,6 @@ tagsync tar.gz - zip ${project.parent.name}-${project.version}-tagsync true @@ -28,6 +27,7 @@ true org.apache.ranger:ranger-tagsync + org.apache.ranger:ranger-common-ha dist @@ -42,7 +42,6 @@ com.101tec:zkclient com.google.code.gson:gson:jar:${gson.version} - com.google.guava:guava:jar:${google.guava.version} com.sun.jersey:jersey-bundle:jar:${jersey-bundle.version} com.sun.jersey.contribs:jersey-multipart:jar:${sun-jersey-bundle.version} org.apache.atlas:atlas-notification:jar:${atlas.version} @@ -54,7 +53,6 @@ org.apache.hadoop:hadoop-auth org.apache.hadoop:hadoop-common org.apache.commons:commons-compress - org.apache.kafka:kafka_${scala.binary.version}:jar:${kafka.version} org.apache.kafka:kafka-clients:jar:${kafka.version} org.apache.ranger:credentialbuilder org.apache.ranger:ranger-plugins-cred @@ -67,12 +65,9 @@ com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${atlas.jackson.version} com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${atlas.jackson.version} com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:${atlas.jackson.version} - org.codehaus.jackson:jackson-core-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-mapper-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-jaxrs:jar:${codehaus.jackson.version} org.codehaus.jettison:jettison:jar:${jettison.version} org.scala-lang:scala-library:jar:${scala.version} - org.slf4j:slf4j-api + org.slf4j:slf4j-api:jar:${slf4j.version} aopalliance:aopalliance:jar:${aopalliance.version} commons-cli:commons-cli:jar:${commons.cli.version} commons-codec:commons-codec:jar:${commons.codec.version} @@ -83,10 +78,8 @@ commons-logging:commons-logging:jar:${commons.logging.version} javax.inject:javax.inject:jar:${javax-inject.version} joda-time:joda-time:jar:${joda-time.version} - log4j:log4j:jar:${log4j.version} org.codehaus.woodstox:stax2-api com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -94,6 +87,17 @@ org.codehaus.woodstox:stax2-api:jar:${codehaus.woodstox.stax2api.version} org.apache.commons:commons-configuration2:jar:${commons.configuration.version} org.cloudera.logredactor:logredactor + org.apache.commons:commons-lang3 + org.slf4j:log4j-over-slf4j:jar:${slf4j.version} + ch.qos.logback:logback-classic:jar:${logback.version} + ch.qos.logback:logback-core:jar:${logback.version} + org.apache.ranger:ranger-common-ha:jar:${project.version} + org.apache.curator:curator-framework:jar:${curator.version} + org.apache.curator:curator-recipes:jar:${curator.version} + org.apache.curator:curator-client:jar:${curator.version} + org.apache.zookeeper:zookeeper:jar:${zookeeper.version} + org.apache.zookeeper:zookeeper-jute:jar:${zookeeper.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/distro/src/main/assembly/usersync.xml b/distro/src/main/assembly/usersync.xml index 3193bc605d..e6874a29a6 100644 --- a/distro/src/main/assembly/usersync.xml +++ b/distro/src/main/assembly/usersync.xml @@ -19,7 +19,6 @@ usersync tar.gz - zip ${project.parent.name}-${project.version}-usersync true @@ -43,15 +42,12 @@ com.google.code.gson:gson com.sun.jersey:jersey-bundle - log4j:log4j commons-cli:commons-cli commons-collections:commons-collections - org.apache.commons:commons-configuration2 commons-lang:commons-lang commons-logging:commons-logging - com.google.guava:guava org.apache.hadoop:hadoop-auth - org.slf4j:slf4j-api + org.slf4j:slf4j-api:jar:${slf4j.version} org.apache.hadoop:hadoop-common org.apache.commons:commons-csv org.apache.ranger:credentialbuilder @@ -60,9 +56,13 @@ org.apache.httpcomponents:httpclient:jar:${httpcomponents.httpclient.version} commons-codec:commons-codec org.apache.ranger:ranger-plugins-common - org.codehaus.woodstox:stax2-api - com.fasterxml.woodstox:woodstox-core - org.apache.htrace:htrace-core4 + org.apache.ranger:ranger-common-ha:jar:${project.version} + org.apache.curator:curator-framework:jar:${curator.version} + org.apache.curator:curator-recipes:jar:${curator.version} + org.apache.curator:curator-client:jar:${curator.version} + org.apache.zookeeper:zookeeper:jar:${zookeeper.version} + org.apache.zookeeper:zookeeper-jute:jar:${zookeeper.version} + org.apache.ranger:ugsync-util com.kstruct:gethostname4j:jar:${kstruct.gethostname4j.version} net.java.dev.jna:jna:jar:${jna.version} net.java.dev.jna:jna-platform:jar:${jna-platform.version} @@ -76,11 +76,13 @@ com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:jar:${fasterxml.jackson.version} com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:jar:${fasterxml.jackson.version} com.fasterxml.jackson.module:jackson-module-jaxb-annotations:jar:${fasterxml.jackson.version} - org.codehaus.jackson:jackson-core-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-mapper-asl:jar:${codehaus.jackson.version} - org.codehaus.jackson:jackson-jaxrs:jar:${codehaus.jackson.version} org.codehaus.jettison:jettison:jar:${jettison.version} - org.codehaus.jackson:jackson-xc:jar:${codehaus.jackson.version} + org.apache.commons:commons-compress:jar:${commons.compress.version} + org.apache.commons:commons-lang3 + ch.qos.logback:logback-classic:jar:${logback.version} + org.slf4j:log4j-over-slf4j:jar:${${slf4j.version}} + ch.qos.logback:logback-core:jar:${logback.version} + org.apache.hadoop.thirdparty:hadoop-shaded-guava:jar:${hadoop-shaded-guava.version} diff --git a/docs/pom.xml b/docs/pom.xml index 0d592eb0ef..c084da8e04 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -16,6 +16,10 @@ limitations under the License. --> + + ISO-8859-1 + ISO-8859-1 + Apache Ranger Apache Ranger is a framework to enable, monitor and manage comprehensive data security across the Hadoop platform. Apache Ranger currently provides a centralized security adminstration, fine grain access control and detailed auditing for user access within Apache Hadoop, Apache Hive, Apache HBase and other Apache components @@ -24,7 +28,7 @@ http://ranger.apache.org/ 4.0.0 org.apache.ranger - 2.1.0-SNAPSHOT + 3.0.0-SNAPSHOT ranger pom @@ -84,7 +88,19 @@ Committer - Hortonworks + Cloudera + + + + abhi + Abhishek Kumar + abhi@apache.org + -8 + + Committer + + + Cloudera Inc @@ -109,7 +125,7 @@ Mentor - Hortonworks + Datadog @@ -124,22 +140,36 @@ Freestone Infotech + + bganesan + Balaji Ganesan + bganesan@apache.org + -8 + + PMC + Committer + + + Privacera + + bpatel Bhavik Patel bpatel@apache.org Asia/Kolkata + PMC Committer - Freestone Infotech + Visa Inc - bganesan - Balaji Ganesan - bganesan@apache.org + bdurai + Don Bosco Durai + bosco@apache.org -8 PMC @@ -149,13 +179,24 @@ Privacera + + brijesh + Brijesh Bhalala + brijesh@apache.org + Asia/Kolkata + + Committer + + + Freestone Infotech + + coheigea Colm O hEigeartaigh coheigea@apache.org +0 - PMC Committer @@ -174,28 +215,52 @@ Quokka IvS + + deepaksharma + Deepak Sharma + deepaksharma@apache.org + Asia/Kolkata + + Committer + + + Twitter + + ddas Devaraj Das - ddas@hortonworks.com + ddas@apache.org -8 PMC - Hortonworks - + Oracle + - deepaksharma - Deepak Sharma - deepaksharma@apache.org + dhavalrajpara + Dhaval Rajpara + dhavalrajpara@apache.org + Asia/Kolkata + + Committer + + + Freestone Infotech + + + + dhavalshah9131 + Dhaval Shah + dhavalshah9131@apache.org Asia/Kolkata Committer - Hortonworks + Freestone Infotech @@ -208,20 +273,19 @@ Committer - BlueTalon + Ordr Inc. - bdurai - Don Bosco Durai - bosco@apache.org - -8 + dineshkumar + Dineshkumar Yadav + dineshkumar@apache.org + Asia/Kolkata - PMC Committer - Privacera + Freestone Infotech @@ -237,16 +301,16 @@ Freestone Infotech - - jghoman - Jakob Homan - jghoman@apache.org - -8 + + pengjianhua + Jianhua Peng + pengjianhua@apache.org + +8 - Mentor + Committer - Microsoft + NIIT @@ -261,6 +325,18 @@ + + kishor + Kishor Gollapalliwar + kishor@apache.org + Asia/Kolkata + + Committer + + + Freestone Infotech + + lmccay Larry Mccay @@ -271,10 +347,35 @@ Committer - Hortonworks + Cloudera - + + mneethiraj + Madhanmohan Neethiraj + madhan@apache.org + -8 + + PMC + Committer + + + Cloudera + + + + maheshbandal + Mahesh Bandal + maheshbandal@apache.org + Asia/Kolkata + + Committer + + + Freestone Infotech + + + mehul Mehul Parikh mehul@apache.org @@ -285,18 +386,17 @@ Freestone Infotech - + - mneethiraj - Madhanmohan Neethiraj - madhan@apache.org - -8 + mugdha + Mugdha Varadkar + mugdha@apache.org + Asia/Kolkata - PMC Committer - Hortonworks + Freestone Infotech @@ -323,18 +423,6 @@ Freestone Infotech - - omalley - Owen O'Malley - omalley@apache.org - -8 - - Champion, Committer, Mentor - - - Hortonworks - - pradeep Pradeep Agrawal @@ -344,20 +432,33 @@ Committer - Freestone Infotech + Cloudera + + zhangqiang + zhang qiang + zhangqiang@apache.org + +8 + + PMC + Committer + + + ZTE + + rmani Ramesh Mani - mani@hortonworks.com + rmani@apache.org -8 PMC Committer - Hortonworks + Cloudera @@ -369,20 +470,20 @@ Committer - Hortonworks + Cloudera sradia Sanjay Radia - sanjay@hortonworks.com + sanjay@cloudera.com -8 PMC Committer - Hortonworks + Cloudera @@ -391,11 +492,12 @@ sneethir@apache.org -5 + PMC-Chair PMC Committer - InfoTekies + InfoTekies Corp. @@ -408,32 +510,7 @@ Committer - Hortonworks - - - - zhangqiang - zhang qiang - zhangqiang@apache.org - +8 - - PMC - Committer - - - ZTE - - - - pengjianhua - Jianhua Peng - pengjianhua@apache.org - +8 - - Committer - - - NIIT + Cloudera @@ -445,170 +522,414 @@ Committer - Hortonworks + Tset - Alejandro Fernandez - + Aakash Nand + + + + Alejandro Fernandez + + + + Alexa Yuqin D + + + + Alvaro Queiroz + + + + Andor Molnar + + + + Andras Katona + Huawei + + + Andras Salamo + + + + Andrew Charneski + + + + Andrew Luo Technologies + + + + Aneela Saleem + + + + ankitap0302 + + + + Anna Shaverdian + + + + Arshad Mohammad + Huawei + + + Asger Askov Blekinge + + + + Ashish Singh + + + + Attila Csoma + + + + Austin Nobis + + + + Barbara Eckman + + + + Belugabehr + + + + Bennet Daniel + + + + Bolke de Bruin + ING + + + Brijesh Bhalala + + + + Bryan Bende + + + + caozhiqiang + + + + Chia-Ping Tsai + + + + csabakoncz + + + + deepakrgr + - Aneela Saleem - + Denes Bodo + - Ankit Singhal - + Dongjoon Hyun + - Anna Shaverdian - + Dongying Jiao + - Arshad Mohammad - Huawei + Edward Zhang + - Ashish Singh - + egyedt + - Bennet Daniel - + Endre Kovacs + - Bolke de Bruin - ING + Enis Soztutar + - Bryan Bende - + Fateh Singh + - Dhaval Rajpara - Freestone + Fatima Amjad Khan + Freestone Infotech - Dongying Jiao - + gavin.wang + - Edward Zhang - + Grant Henke + - Endre Kovacs - + Guru Thejus Arveti + - Fatima Amjad Khan - Freestone Infotech + Haihui Xu + - Haihui Xu - + Hanish Bansal + - Hanish Bansal - + haohaoc + - Jim Halfpenny - + Himanshu Maurya + - Kent Yao - + Jan Hentschel + - Kevin Risden - + jialiang + - Koji Kawamura - + Jim Halfpenny + - Mack Hendricks - Hortonworks Inc., + Joey + - Madhavi Amirneni - + Josh Elser + - Mani Raman - + Kengo Seki + - Mugdha Varadkar - Freestone Infotech + Kent Yao + - Nicholas Hughes - + Kevin Risden + - Nigel Jones - + kirbyzhou + - Nixon Rodrigues - + Koji Kawamura + - Paul Otto - + kulkabhay + - Peng Xing - + Lars Francke + - Rich Haase - Pandora + Laszlo Denes Terjek + - Rohit Sinha - + leslizhang + - Shi Wang - + Mack Hendricks + Hortonworks Inc - Sree Vaddi - + Madhavi Amirneni + - Suneel Marthi - + Mahesh Bandal + - Tushar Dudhatra - + mallikagogoi7 + - Varun Rao - Accenture + Mani Raman + - Vishal Suvagia - Freestone Infotech + Martin Grigoro + - Wang Yuan - + Mateen Mansoori + - Yan Zhou - + Naoki Takezoe + - Yujie Li - + Neil Joshi + + + + Nicholas Hughes + + + + Nigel Jones + + + + Nixon Rodrigue + + + + Patrik Marton + + + + Paul Otto + + + + Peng Xing + + + + Rakesh Gupta + + + + Ramachandran + + + + Rich Haase + Pandora + + + Ricky M + + + + Rob Vesse + + + + Rohit Sinha + + + + rujia1019 + + + + Sanjar Matin + + + + Shi Wang + + + + Sree Vaddi + + + + Starphinliu + + + + Suneel Marthi + + + + Symious + + + + Thejas nair + + + + Tomas Sokorai Sch + + + + Tushar Dudhatra + + + + Varun Rao + Accenture + + + Viktor Somogyi-Vas + + + + Vipin Rathor + + + + Vishal Suvagia + Freestone Infotech + + + Vomoshkovskyi + + + + Wang Yuan + + + + Xuze Yang + + + + Yan Zhou + + + + Yaolei + + + + Yujie Li + + + + yzhou2001 + + + + zhouyifan279 + + + + Zilong Zh + @@ -642,6 +963,11 @@ maven-site-plugin 3.7 + + io.github.olamy.maven.skins + reflow-velocity-tools + 2.0.0 + org.apache.velocity velocity diff --git a/docs/src/site/resources/background_design.png b/docs/src/site/resources/background_design.png new file mode 100644 index 0000000000..2fdde87ae6 Binary files /dev/null and b/docs/src/site/resources/background_design.png differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig01-policy_ussales_rib.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig01-policy_ussales_rib.jpg new file mode 100644 index 0000000000..68e646747f Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig01-policy_ussales_rib.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig02-policy_globalsales_highly_sensitive.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig02-policy_globalsales_highly_sensitive.jpg new file mode 100644 index 0000000000..b85d273828 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig02-policy_globalsales_highly_sensitive.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig03-policy_globalsales_sensitive.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig03-policy_globalsales_sensitive.jpg new file mode 100644 index 0000000000..c2cfed0740 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig03-policy_globalsales_sensitive.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig04-policy_globalsales_non_sensitive.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig04-policy_globalsales_non_sensitive.jpg new file mode 100644 index 0000000000..cb7b5c75d7 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig04-policy_globalsales_non_sensitive.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig05-policy_globalsales_row_filter_sales_region.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig05-policy_globalsales_row_filter_sales_region.jpg new file mode 100644 index 0000000000..f0173a836f Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig05-policy_globalsales_row_filter_sales_region.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig06-roles_capturing_sl_sr.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig06-roles_capturing_sl_sr.jpg new file mode 100644 index 0000000000..3e0306aa10 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig06-roles_capturing_sl_sr.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig07-policy_ussales_tag_attribute_based.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig07-policy_ussales_tag_attribute_based.jpg new file mode 100644 index 0000000000..8c0da6c09e Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig07-policy_ussales_tag_attribute_based.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig08-policy_tag_based_sl.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig08-policy_tag_based_sl.jpg new file mode 100644 index 0000000000..4ee3d4a889 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig08-policy_tag_based_sl.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig09-policy_globalsales_row_filter_sr_roles.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig09-policy_globalsales_row_filter_sr_roles.jpg new file mode 100644 index 0000000000..d50c520cae Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig09-policy_globalsales_row_filter_sr_roles.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig10-roles_capturing_sl_sr_sp.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig10-roles_capturing_sl_sr_sp.jpg new file mode 100644 index 0000000000..d6e7b7dc46 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig10-roles_capturing_sl_sr_sp.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig11-policy_globalsalespartners_row_filter_sr_sp.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig11-policy_globalsalespartners_row_filter_sr_sp.jpg new file mode 100644 index 0000000000..622f1ac218 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/fig11-policy_globalsalespartners_row_filter_sr_sp.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_globalsales.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_globalsales.jpg new file mode 100644 index 0000000000..b75b2f9567 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_globalsales.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_globalsalespartners.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_globalsalespartners.jpg new file mode 100644 index 0000000000..c7bb989074 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_globalsalespartners.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_ussales.jpg b/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_ussales.jpg new file mode 100644 index 0000000000..5aa6c8a272 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_1.files/table_ussales.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_1.html b/docs/src/site/resources/blogs/adventures_in_abac_1.html new file mode 100644 index 0000000000..d43fbf5e00 --- /dev/null +++ b/docs/src/site/resources/blogs/adventures_in_abac_1.html @@ -0,0 +1,437 @@ + + + + + + + Adventures in ABAC - Part 1 + + + + +
+

Adventures in attribute-based access control (ABAC) - Part 1

+

 

+
+

Barbara Eckman, Ph.D., Distinguished Architect, Comcast

+

Apr 29, 2023

+
+

 

+ +
+

Introduction

+ +

+ Simply put, data access control enforces constraints on who is permitted to access the data. An access control policy + specifies 1) which data may be accessed by 2) which users and optionally 3) for how long + [1]. +

+ +

 

+ +

Data can be specified in access control policies in multiple ways:

+ +

1.    Resource-based access control: data is specified by its logical identifier e.g., table name, column name, Kafka topic name, AWS S3 bucket name.

+ +

2.    Tag-based access control (TBAC): data is specified by one or more of its properties, represented by a tag on its metadata, e.g., a Sales Region or Sensitivity Level.

+ +

3.    Row access control: specifies rows/records that are visible to the user at run time by setting up filters based on the value of an attribute, e.g., Sales Region is "US”.

+ +

4.    Masking access control: specifies if the data should be masked before making it available the user. + [2] +

+ +

 

+ +

Users can be specified in access control policies in multiple ways:

+ +

1.    by their individual IDs, or by their group IDs.

+ +

2.    by the roles the users belong to e.g., "USSalesPerson”. This approach is generally called Role-Based Access Control (RBAC).

+ +

 

+ +

It’s generally acknowledged that RBAC and TBAC are more maintainable, easier to understand, and therefore less error-prone than resource-based access control and identifying users by their IDs or group IDs. However, these are not sufficient for even moderately complex access control constraints, as we will see.

+ +

 

+ +

In this blog, we will consider various access control approaches for the following users in specified regions, having access to a given level of sensitive data. Users with access to Highly Sensitive data may also access Sensitive data.

+ +

 

+ + + + + + + + + + + + + + + + + +

User

Region

Access Level

Bob

US

Highly Sensitive

Celestine

EMEA

Sensitive

+ +

 

+ +

Resource and Identity-based Access Control: USSales

+

Consider the following table containing data from the US sales region.

+

+ Table USSales +

+

 

+

Resource and identity-based access-control policies might include:

+

-     Allow Bob to access all columns because he’s from the US and has access to Highly Sensitive data.

+

-     Deny access to Celestine on the table because she’s from EMEA, i.e., not from US.

+

 

+

+ Fig 1. Apache Ranger™ resource and identity-based access policy for table USSales +

+

 

+

Fig 1. Apache Ranger™ resource and identity-based access policy for table USSales

+

 

+

 

+

This isn’t too onerous with two users, one table, and two Sales Regions.

+ +

 

+ +

Resource and Identity-based Access Control: GlobalSales

+

Let’s add a bit of complexity. Consider the following table containing data from several sales regions, including US and EMEA.

+

+ Table: GlobalSales +

+

 

+

Resource-based and identity-based access-control policy might include:

+

-     Allow Bob to access all columns in rows having salesRegion=US because he’s from the US and has access to Highly Sensitive data.

+

-     Allow Celestine to access columns c1-c10 in rows having salesRegion=EMEA because she’s from EMEA and has access to Sensitive data.

+

-     Deny users from non-matching regions any access to the table.

+

 

+

+ Fig 2. Apache Ranger™ access policy for highly sensitive data in table GlobalSales +

+

 

+

Fig 2. Apache Ranger™ access policy for highly sensitive data in table GlobalSales

+

 

+

 

+

+ Fig 3. Apache Ranger™ access policy for sensitive data in table GlobalSales +

+

 

+

Fig 3. Apache Ranger™ access policy for sensitive data in table GlobalSales

+

 

+

 

+

+ Fig 4. Apache Ranger™ access policy for non-sensitive data in table GlobalSales +

+

 

+

Fig 4. Apache Ranger™ access policy for non-sensitive data in table GlobalSales

+

 

+

 

+

+ Fig 5. Apache Ranger™ row-filter policy to restrict access to data in table GlobalSales based on sales region +

+

 

+

Fig 5. Apache Ranger™ row-filter policy to restrict access to data in table GlobalSales based on sales region

+

 

+

 

+

Note that the fact that Bob is from the US with access to highly sensitive data is not explicitly captured in the above policies. Nor are the sensitivity levels of the two sets of columns. This knowledge is implicit only, making the policies difficult to maintain over time as business rules change.

+

 

+ +

Tag-based and Role-based Access Control: USSales

+

In this section we will explore using tags and roles (TBAC and RBAC) to set up access control on the USSales table.

+

 

+

Let’s use tags to capture metadata relevant to access control, and assign them to tables and columns as shown below:

+

 

+ + + + + + + + + + + + + + + + + + + + + + +

Table/Columns

Tag

Tag Attribute

USSales

salesRegion

value="US"

c1, c2, c3, c4, c5

sensitivityLevel

value="sensitive"

c6, c7, c8

sensitivityLevel

value="highlySensitive"

+ +

 

+

 

+

Let’s use the following roles to capture users’ access scope by sensitivity level and region, and assign users as members of the appropriate roles:

+

 

+ +

+ Fig 6. Apache Ranger™ roles to capture sensitivity level and sales region for users +

+ +

 

+

Fig 6. Apache Ranger™ roles to capture sensitivity level and sales region for users

+

 

+

 

+ +

Tag-based and role-based access-control policies might include:

+ +

1.     Tag: salesRegion

+

a.    Allow users in role salesRegion.US to access resources tagged with salesRegion.value = "US"

+

b.    Allow users in role salesRegion.EMEA to access resources tagged with salesRegion.value = "EMEA"

+

 

+ +

+ Fig 7. Apache Ranger™ tag attribute-based access policy for sales region +

+ +

 

+

Fig 7. Apache Ranger™ tag attribute-based access policy for sales region

+

 

+

 

+ +

2.     Tag: sensitivityLevel

+

a.    Allow users in role sensitivityLevel.sensitive to access resources tagged with sensitivityLevel.value = "sensitive", OR empty.

+

b.    Allow users in role sensitivityLevel.highlySensitive to access resources tagged with sensitivityLevel.value = "sensitive" OR "highlySensitive", OR empty.

+

 

+ +

+ Fig 8. Apache Ranger™ tag attribute-based access policy for sensitivity level +

+ +

 

+

Fig 8. Apache Ranger™ tag attribute-based access policy for sensitivity level

+

 

+

 

+ +

Note that the knowledge needed for access control is now explicit: the columns’ metadata is tagged with an explicit sensitivity level, and the users are explicitly members of the appropriate salesRegion role.

+

 

+ +

Note that these tag policies can be used to handle an EMEASales table as well as the USSales table, depending on whether the value of the tag is ‘US’ or ‘EMEA’.

+

 

+ +

Tag-based and Role-based Access Control: GlobalSales

+ +

In this section we return to the GlobalSales table. In this case we can’t use a simple salesRegion tag on the table, since the table contains data from multiple regions including US and EMEA. A row-filter is needed, as in the resource-based policy above.

+ +

 

+ +

As before, let’s use the following tags to capture metadata relevant to sensitivity access control, and have them assigned to columns as shown below:

+ +

 

+ + + + + + + + + + + + + + + + + +

Columns

Tag

Tag Attribute

c2, c3

sensitivityLevel

value="sensitive"

c11, c12, c13, c14

sensitivityLevel

value="highlySensitive"

+ +

 

+ +

Also, let’s use the same roles listed in the previous use case, Fig. 6

+ +

 

+ +

Tag-based and role-based access-control policies might include:

+ +

1.     Tag: sensitivityLevel: same policy as the previous use case, Fig. 8

+

2.     Row filter Policy:

+

a.    Users in the salesRegion.US role have access to rows where salesRegion = "US"

+

b.    Users in the salesRegion.EMEA role have access to rows where salesRegion = "EMEA"

+

 

+ +

+ Fig 9. Apache Ranger™ row-filter policy to restrict access to data in table GlobalSales based on sales region and user roles +

+ +

 

+

Fig 9. Apache Ranger™ row-filter policy to restrict access to data in table GlobalSales based on sales region and user roles

+

 

+

 

+ +

This policy controls access by any user who has been assigned to a salesRegion role, not simply bob or celestine.

+

 

+ +

Beyond Tag-based and Role-based Access Control: GlobalSalesPartners

+

As our final level of complexity, in this section we will extend access control to a GlobalSalesPartners table that includes info on which business partner ("ABC" or "XYZ") produced the data.

+ +

 

+

+ Table GlobalSalesPartners +

+ +

 

+ +

The following additional conditions must be enforced for accessing data in this table:

+

1.     Bob can see only data from partner "ABC"

+

2.     Celestine can see data from both partners.

+

 

+ +

As before, let’s use the following tags to capture metadata relevant to sensitivity level, and have them assigned to columns as shown below:

+

 

+ + + + + + + + + + + + + + + + + +

Columns

Tag

Tag Attribute

c2, c3

sensitivityLevel

value="sensitive"

c11, c12, c13, c14

sensitivityLevel

value="highlySensitive"

+ +

 

+ +

Let’s use the following roles to capture the users’ access scope by sensitivity level, region, and sales partner, and assign our users as members of the appropriate roles:

+ +

 

+ +

+ Fig 10. Apache Ranger™ roles to capture sensitivity level, sales region and sales partners for users +

+ +

 

+

Fig 10. Apache Ranger™ roles to capture sensitivity level, sales region and sales partners for users

+

 

+

 

+ +

Tag-based and role-based access-control policies might include:

+

1.     Tag: sensitivityLevel: same policy as earlier use case, Fig. 8

+

2.     Row filter Policy:

+

a.    Users in salesRegion.US role have access to rows where salesRegion = "US"

+

b.    Users in salesRegion.EMEA role have access to rows where salesRegion = "EMEA"

+

c.    Users in salesPartner.ABC role have access to rows where salesPartner = "ABC"

+

d.    Users in salesPartner.XYZ role has access to rows where salesPartner = "XYZ"

+ +

 

+

+ Fig 11. Apache Ranger™ row-filter policy to restrict access to data in table GlobalSalesPartners based on sales region and sales partner +

+

 

+

Fig 11. Apache Ranger™ row-filter policy to restrict access to data in table GlobalSalesPartners based on sales region and sales partner

+

 

+

 

+ +

It is easy to see that as the numbers of salesRegions and salesPartners rise, the number of roles and row filter conditions increases combinatorially, and rapidly becomes difficult to manage.

+

 

+ +

As I said before, built-in Apache Ranger™ TBAC, RBAC, and row-filter based access policies are powerful, but they are not sufficient for complex access control constraints, like above. Join in to part 2 of this blog series to see how ABAC can answer these and other more complex constraint requirements, and do it…well, elegantly!

+

 

+
+ +
+
+
+
+

[1]Specifying expiration dates for access control policies where relevant. This is not specific to ABAC and so we won’t discuss it further in this blog series.

+

 

+
+ +
+

[2]We will cover details of masking policies in a subsequent blog.

+

 

+
+
+
+ + + + diff --git a/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig1-policy_globalsalespartners_row_filter_sr_sp.jpg b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig1-policy_globalsalespartners_row_filter_sr_sp.jpg new file mode 100644 index 0000000000..622f1ac218 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig1-policy_globalsalespartners_row_filter_sr_sp.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig2-policy_globalsalespartners_row_filter_abac.jpg b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig2-policy_globalsalespartners_row_filter_abac.jpg new file mode 100644 index 0000000000..52593e0e8b Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig2-policy_globalsalespartners_row_filter_abac.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig3-policy_tag_based_on_user_role.jpg b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig3-policy_tag_based_on_user_role.jpg new file mode 100644 index 0000000000..2a5ef6f1b2 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig3-policy_tag_based_on_user_role.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig4-policy_tag_policy_abac.jpg b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig4-policy_tag_policy_abac.jpg new file mode 100644 index 0000000000..3026e6d282 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_2.files/fig4-policy_tag_policy_abac.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_2.files/table_globalsalespartners.jpg b/docs/src/site/resources/blogs/adventures_in_abac_2.files/table_globalsalespartners.jpg new file mode 100644 index 0000000000..c7bb989074 Binary files /dev/null and b/docs/src/site/resources/blogs/adventures_in_abac_2.files/table_globalsalespartners.jpg differ diff --git a/docs/src/site/resources/blogs/adventures_in_abac_2.html b/docs/src/site/resources/blogs/adventures_in_abac_2.html new file mode 100644 index 0000000000..928e84098e --- /dev/null +++ b/docs/src/site/resources/blogs/adventures_in_abac_2.html @@ -0,0 +1,312 @@ + + + + + + + Adventures in ABAC - Part 2 + + + + +
+

Adventures in attribute-based access control (ABAC) - Part 2

+

 

+
+

Barbara Eckman, Ph.D., Distinguished Architect, Comcast

+

Oct 15, 2023

+
+

 

+ +
+

Introduction

+ +

+ Previously in Part 1 of this series we examined an increasingly + complex series of use cases involving role membership and row filtering. While built-in Apache Ranger™ + TBAC, RBAC, and row-filter based access policies are powerful, they may not be sufficient for complex access + control constraints. As the numbers of row filters that must be simultaneously enforced rises, the number of + roles and row filter conditions increases combinatorially and rapidly becomes difficult to manage. +

+ +

 

+ +

+ This post introduces the principles of Attribute-based Access Control (ABAC) and shows how they enable us to + avoid this potentially mushrooming complexity. Let’s recall the final use case from part 1. +

+ +

 

+ +

Recap: GlobalSalesPartners table row filters

+

+ The GlobalSalesPartners table includes info on which business partner (“ABC” or “XYZ”) produced the data, as + well as the salesRegion where the sale occurred. +

+

+ Table USSales +

+

 

+

Recall our favorite users:

+ + + + + + + + + + + + + + + + + +

User

Region

Partner

Bob

US

ABC

Celestine

EMEA

ABC, XYZ

+

 

+ +

Row-filter policies from part 1:

+ +

1.      Users in salesRegion.US role have access to rows where salesRegion = “US”

+

2.      Users in salesRegion.EMEA role have access to rows where salesRegion = “EMEA”

+

3.      Users in salesPartner.ABC role have access to rows where salesPartner = “ABC”

+

4.      Users in salesPartner.XYZ role has access to rows where salesPartner = “XYZ”

+

 

+

+ Fig 1. Apache Ranger™ Table GlobalSalesPartners: row-filter policy to restrict access based on sales region and sales partner +

+

 

+

Fig 1. Apache Ranger™ Table GlobalSalesPartners: row-filter policy to restrict access based on sales region and sales partner

+

 

+

+ We noted previously that as the numbers of salesRegions and salesPartners rise, the number of + roles and row filter conditions increases combinatorially, and rapidly becomes difficult to manage. +

+ +

 

+ +

ABAC Principles

+

+ But what if Ranger policy engine had direct access to Bob‘s sales partners, and Bob‘s sales region, without + reference to any roles he might be a member of? Then a row filter could be expressed this way, assuming Bob + has access to data from only one sales region and sales partner +

+ +

 

+ +

     <partner attribute value in row> == <Bob’s partner> AND <sales region value in row> == <Bob’s region>

+ +

 

+ +

Now assume this can be generalized to work for all users:

+ +

 

+ +

     <partner attribute value in row> == $USER.partner AND <sales region value in row> == $USER.region

+ +

Welcome to Attribute Based Access Control (ABAC)!

+

+ In RBAC, the role to which a user is assigned membership is the central method of expressing what the user + should be allowed access to. Bob is in the role partner.ABC, along with, say, Sarah and Thomas and Srikanth + and Joon. Thus, all these users have access to partner ABC’s data. +

+

 

+

+ In ABAC, the central method of expressing what the user should be allowed to have access to is the value of + the user’s attributes. Bob’s partner attribute value equals “ABC”, along with the partner attribute of Sarah + and Thomas and Srikanth and Joon. Just as in the RBAC case, all these users have access to partner ABC’s data. +

+

 

+

+ But how do we program this in Apache Ranger? Apache Ranger uses a user-store, populated with users and their + attributes typically loaded from LDAP, SCIM, Azure Active Directory (AAD), Okta, etc., by Apache Ranger + usersync. If an attribute named partner is added to a user’s record on the identity provider, then that + information will be gathered as part of the usersync and can be referenced within a Ranger policy condition + as $USER.partner. +

+

 

+

GlobalSalesPartners Row-Filters Using ABAC

+

+ Assume that the UserStore has been populated with salesRegion and salesPartner attributes for Bob and + Celestine as follows: +

+

 

+ + + + + + + + + + + + + + + + + +

User

salesRegion

salesPartner

Bob

US

ABC

Celestine

EMEA

XYZ

+ +

 

+

 

+

+ With ABAC, the 8 row filter conditions from the previous blog post become a single condition, matching the + users’ partners and regions with the value in the salesPartner and salesRegion columns: +

+

 

+ +

+ Fig 2. Apache Ranger™ Table GlobalSalesPartners: ABAC-based row-filter policy to restrict access based on sales region and sales partner +

+ +

 

+

Fig 2. Apache Ranger™ Table GlobalSalesPartners: ABAC-based row-filter policy to restrict access based on sales region and sales partner

+

 

+ +

 

+

+ This policy works for all users, not just those with salesRegion or salesPartner access like Bob’s or Celestine’s. +

+ +

ABAC in Tag-Based Policies

+

+ Next, consider tables representing a single sales region, like USSales from blog post Part 1. +

+

 

+ + + + + + + + + + + + +

Resource

Tag

Tag Attribute

Table: USSales

salesRegion

value=”US”

+ +

 

+ +

+ Allow only users in the salesRegion.US role to access resources tagged with salesRegion.value = ”US”. Create + a role-based (RBAC) tag policy that allows access to tagged tables based on the role membership of the user: +

+

 

+ +

+ Fig 3. Apache Ranger™ Tag-based policy allowing access to tagged tables based on the user’s role membership +

+ +

 

+

Fig 3. Apache Ranger™ Tag-based policy allowing access to tagged tables based on the user’s role membership

+

 

+ +

 

+ +

+ Using the RBAC method, we need to create a policy condition for each of the salesRegion.* roles. Depending on + how many salesRegions a company defines, this could get large. And if they are continually being added or + subtracted, the policy has to be commensurately updated. +

+ +

 

+

+ Let’s use ABAC to greatly simplify this process, by creating a tag policy that allows table access to any user + whose salesRegion attribute matches the attribute of the salesRegion tag: +

+ +

+ Fig 3. Apache Ranger™ ABAC-based tag policy to allow access to tables based on the value of the user’s attribute. +

+ +

 

+

Fig 4. Apache Ranger™ ABAC-based tag policy to allow access to tables based on the value of the user’s attribute.

+

 

+ +

 

+

+ As we have seen, ABAC makes policy creation and maintenance much easier! But what if the custodians of your + Identity Provider are too busy to keep up with managing the additional attributes you need for ABAC? Is there + another way to populate the UserStore? Yes! Check out Part 3 of this blog series to find out how you can + retrieve UserStore entries from a variety of alternative sources! +

+ +

 

+
+
+ + + + diff --git a/docs/src/site/resources/blogs/dynamic_expressions.html b/docs/src/site/resources/blogs/dynamic_expressions.html new file mode 100644 index 0000000000..e82d278a19 --- /dev/null +++ b/docs/src/site/resources/blogs/dynamic_expressions.html @@ -0,0 +1,665 @@ + + + + + + Apache Ranger Policy Model + + + + +
+

Apache Ranger - Dynamic Expressions

+

 

+
+

Madhan Neethiraj, Apache Ranger committer

+

Dec 12, 2023

+
+

 

+ +
+

Introduction

+ +

+ Apache Ranger policy model offers a rich set of features that help security administrators handle various + access + and governance requirements with ease. These features include: +

+ +

 

+ + +

1. Consistent model to authorize access data in large number of services

+

 

+

2. Ability to dynamically apply data masking and row-filtering

+

 

+

3. Delegated access control administration

+

 

+

4. Ability to explicitly deny access

+

 

+

5. Use of wildcards in resource names in access policies

+

 

+

6. Role-based access control (RBAC)

+

 

+

7. Tag-based access control (TBAC), based on tags associated with resources

+

 

+

8. Attribute-based access control (ABAC), based on attributes of users, groups and tags

+

 

+
+ +

 

+ +

+ In addition to above, Apache Ranger policies can use various attributes available in the access context to + authorize the access - attributes including resource owner, time of access, tags associated with the + accessed + resource, attributes of user/groups/tags, groups/roles the user belongs to. This document explores use cases + that can leverage such attributes in policies using dynamic expressions. +

+ +

 

+ +

Dynamic expressions

+ +

+ Apache Ranger policy engine evaluates dynamic expressions specified in policies using the script engine + included in the JVM, in a sandboxed environment. Dynamic expressions can be used in Apache Ranger policies + in + following contexts: +

+ +

Policy conditions

+

+ Expressions can used in policy conditions to decide whether to evaluate the policy or a policy-item. These + expressions should evaluate to a boolean value i.e., true + or false. Examples: +

+

 

+ +

Condition for highly sensitive data (level >= 10)

+

 

+

TAG.sensitiveLevel >= 10

+

 

+ +

Condition to check if the user has appropriate level of clearance to access sensitive data

+

 

+

USER.allowedSensitiveLevel >= TAG.sensitiveLevel

+

 

+ +

Condition to check if the user belongs to group finance and is in role analyst

+

 

+

IS_IN_GROUP('finance') AND IS_IN_ROLE('analyst')

+

 

+ +

Row filters

+

+ Expressions can be used to set up row-filters with dynamic values. To distinguish expressions from the rest + of the row-filter text, they should be enclosed within delimiters ${{ + and }}. Examples: +

+

 

+ +

Row-filter expression to restrict users to access only rows belonging to their department:

+

 

+

dept_code == ${{USER.department}}

+

 

+ +

Row-filter expression to restrict users to access only rows from data sources specified in user attribute named allowedSources:

+

 

+

data_source in (${{USER.allowedSources}})

+

 

+ +

Resource names

+

+ Use of expressions in resource names can help reduce the number of policies, which in + turn makes it easier to manage policies. Examples: +

+

 

+ +

Policy resource for home directory of the user:

+

 

+

/home/${{REQ.user}}

+

 

+ +

Policy resource for directory of user's department:

+

 

+

/data/dept/${{USER.dept}}

+

 

+ +

Policy resource for database of user's department:

+

 

+

db_${{USER.dept}}

+

 

+ +

Supported expressions

+

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Variable/Function name

+
+

Description

+
+

Example values

+
+

GET_TAG_NAMES()

+
+

Names of tags associated with the resource, as a CSV (comma separated values) string

+
+

PII,FINANCE

+
+

GET_TAG_ATTR_NAMES()

+
+

Names of attributes in all tags associated with the resource, as a CSV string

+
+

piiType,sensitiveLevel

+
+

GET_TAG_ATTR(attrName)

+
+

Value of the given attribute in tags associated with the resource, as a CSV string

+
+

email

+
+

GET_UG_NAMES()

+
+

Names of groups the user belongs to, as a CSV string

+
+

managers,finance-admins

+
+

GET_UG_ATTR_NAMES()

+
+

Names of all attributes in groups the user belongs to, as a CSV string

+
+

attr1,attr2

+
+

GET_UG_ATTR(attrName)

+
+

Value of the given attribute in groups the user belongs to, as a CSV string

+
+

val1

+
+

GET_UR_NAMES()

+
+

Names of roles assigned to the user,  as a CSV string

+
+

analyst,dba

+
+

GET_USER_ATTR_NAMES()

+
+

Names of all attributes of the user, as a CSV string

+
+

allowedSensitiveLevel, allowedSources

+
+

GET_USER_ATTR(attrName)

+
+

Value of the given attribute associated with the user

+
+

10

+
+

HAS_TAG(tagName)

+
+

Is the given tag associated with the resource?

+
+

true

+

false

+
+

HAS_ANY_TAG

+
+

Is any tag associated with the resource?

+
+

true

+

false

+
+

HAS_NO_TAG

+
+

Are not tags associated with the resource?

+
+

true

+

false

+
+

HAS_USER_ATTR(attrName)

+
+

Does the user have the given attribute?

+
+

true

+

false

+
+

HAS_UG_ATTR(attrName)

+
+

Does any group associated with the user have the specified attribute?

+
+

true

+

false

+
+

HAS_TAG_ATTR(attrName)

+
+

Does any tag associated with the resource have the specified attribute?

+
+

true

+

false

+
+

IS_IN_GROUP(groupName)

+
+

Does the user belong to the specified group?

+
+

true

+

false

+
+

IS_IN_ROLE(roleName)

+
+

Is the user assigned to the specified role?

+
+

true

+

false

+
+

IS_IN_ANY_GROUP

+
+

Does the user belong to any group?

+
+

true

+

false

+
+

IS_IN_ANY_ROLE

+
+

Is any role assigned to the user?

+
+

true

+

false

+
+

IS_NOT_IN_ANY_GROUP

+
+

Does the user belong to no group?

+
+

true

+

false

+
+

IS_NOT_IN_ANY_ROLE

+
+

Is the user associated with no roles?

+
+

true

+

false

+
+

REQ

+
+

Request details, as a map

+
+

{

+

 "accessType":  "select",

+

 "clientIPAddress": "10.120.27.49",

+

 "clusterType": "etl",

+

 "clusterName": "etl-e1",

+

 "accessType":  "select",

+

 "user":        "scott",

+

 "userGroups":  [ "g1" ],

+

 "userRoles":   [ "r1" ],

+

}

+
+

RES

+
+

Resource details, as a map

+
+

{

+

  "database":   "db1",

+

  "table":      "tbl1",

+

  "Column":     "col1",

+

  "_ownerUser": "jane"

+

}

+
+

TAG

+
+

Current tag, as a map.

+

This is available only in tag-based policies.

+
+

{

+

 "_type": "SENSITIVE",

+

 "sensitiveLevel": 10

+

}

+
+

TAGNAMES

+
+

Names of tags associated with the resource, as a list

+
+

[ "PII", "SENSITIVE" ]

+
+

TAGS

+
+

All tags associated with the resource, as a map

+
+

{

+

 "SENSITIVE": {

+

  "_type", "SENSITIVE",

+

  "level": 10

+

 },

+

 "PII": {

+

  "_type",   "PII",

+

  "piiType": "email"

+

 }

+

}

+
+

UGNAMES

+
+

Names of groups the user belongs to, as a list

+
+

[ "g1" ]

+
+

URNAMES

+
+

Names of roles the user is assigned to, as a list

+
+

[ "r1" ]

+
+

USER

+
+

Name of the user

+
+

"scott"

+
+
+

 

+ +

+ Most functions listed in the table above take optional parameters, to make it easier to handle use cases that require special handling. +

+ +

 

+ +

Default value

+

+ A function call can include a default value as an optional parameter, which will be returned when there is no + value available. For example, consider the following expression: +

+ +

 

+ + +

USER.allowedSensitiveLevel >= TAG.sensitiveLevel

+

 

+
+ +

+ When the user doesn’t have an attribute named allowedSensitiveLevel, the expression will always evaluate to + false since USER.allowedSensitiveLevel would evaluate to null. To handle such cases, consider the following + alternate expression which would use 0 as the value instead of null: +

+ +

 

+ + +

GET_USER_ATTR('allowedSensitiveLevel', 0) >= TAG.sensitiveLevel

+

 

+
+ +

+ Here is another example of using default value in function calls: +

+ +

 

+ + +

dept_code in (${{GET_UG_ATTR('deptCode', -1)}})

+

 

+
+ +

 

+ +

Separator

+

+ Functions that return a CSV string, like GET_TAG_NAMES(), can include following optional parameters: +

+ + +

 

+

optional #1. default value: value to return when no value is available

+

 

+

optional #2. separator: string to use as the separator between values

+

 

+
+ +

 

+

+ Here is an example of using optional parameters: +

+ + +

 

+

GET_TAG_NAMES('', '|') == 'tag1|tag2|tag3'

+

 

+
+ +

Quotes

+

+ Each function that returns a CSV string has another version with _Q appended to the function name; this version + surrounds each value within quotes. For example, consider the following row-filter expression: +

+ + +

 

+

location_state IN (${{GET_UG_ATTR_Q('state')}})

+

 

+
+ +

 

+

+ The expression can evaluate to the following, if the user belongs to groups having an attribute named state: +

+ + +

 

+

location_state IN ('CA','OR','WA')

+

 

+
+ +
+ + + + diff --git a/docs/src/site/resources/blogs/policy_model.html b/docs/src/site/resources/blogs/policy_model.html new file mode 100644 index 0000000000..cfc5eaa825 --- /dev/null +++ b/docs/src/site/resources/blogs/policy_model.html @@ -0,0 +1,634 @@ + + + + + + + Apache Ranger Policy Model + + + + +
+

Apache Ranger Policy Model

+

 

+
+

Madhan Neethiraj, Apache Ranger committer

+

Mar 08, 2022

+
+

 

+ +
+

Introduction

+ +

+ Apache Ranger is an extensible framework that enables enterprises to adopt a consistent approach to authorize + access to their resources across multiple services/applications/cloud. Apache Ranger framework also enables + enterprises to collect audit logs of access to their resources, to help meet various compliance requirements. +

+ +

 

+ +

+ Apache Ranger is a central part of security in many large deployments in enterprises across various domains + like finance, retail, insurance, healthcare, services. Apache Ranger has out-of-the box support for a large + number of popular services and many more services are supported by commercial vendors. Apache Ranger is highly + optimized for performance, adds negligible overhead in authorizing access to resources. It has been very well + proven in very high throughput services like Apache Kafka, Apache HBase which perform thousands of + authorizations per second. +

+ +

 

+ +

+ Apache Ranger provides an intuitive web user interface to manage authorization policies and audit logs for + access to resources across a large number of services. Apache Ranger also provides REST, Python, Java APIs for + programmatic integration with tools used by enterprises. Open framework provided by Apache Ranger enables + enterprises to extend Apache Ranger authorization to their own applications and services as well. +

+ +

 

+ +

+ Here are few key points that make Apache Ranger a compelling option for enterprises looking to standardize + authorization of access to their resources: +

+ +

1.    out-of-the-box support for more than a dozen popular services like Apache Hive, Apache HBase, Apache Kafka, Apache Solr, Elasticsearch, Apache NiFi and Presto.

+ +

2.    support for services like Amazon EMR, AWS S3, ADLS-Gen2, GCS, Snowflake, Google BigQuery, Trino, Dremio, Starburst, Apache Impala, Postgres, MS-SQL and Amazon Redshift by commercial vendors.

+ +

3.    policies for access authorization, row-filters, data masking.

+ +

4.    resource-based, classification-based policies, role-based, attribute-based policies.

+ +

5.    delegated administration, deny and exceptions in policies, custom conditions.

+ +

6.    centralized audit logs of accesses to enterprise resources across multiple services, interactive user interface to view audit logs of accesses.

+ +

7.    intuitive policy management UI.

+ +

8.    Java, Python, REST APIs for programmatic integration for policy management.

+ +

9.    open framework which enables enterprises to extend Apache Ranger authorization to their own applications and services.

+ +

Policy Model

+ +

+ At the core of Apache Ranger authorization is its policy model. We will go through key aspects of the Apache + Ranger policy model in this section. +

+ +

Resources

+

+ A resource is a fundamental element in the Apache Ranger policy model. Apache Ranger enables policies to + authorize access to resources. In this context, a resource is anything whose access needs to be authorized, + like a file/path, database, table, column, topic; but can also be a service – like Apache Knox topology. + Apache Ranger policy model captures details of resources of a service in a declarative way – details like + hierarchy, case-sensitivity, supports row-filter/data-masking, etc. +

+ +

 

+ +

+ Type of resources vary across services/applications, as seen in the table below: +

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Service

Resources

Apache Hive

databases, tables, columns, udfs

Apache Kafka

topics

Apache Solr

collections

AWS S3

buckets, objects

ADLS-Gen2

storage-accounts, containers, objects

Azure PowerBI

workspaces

Google BigQuery

projects, datasets, tables, columns

Snowflake

databases, schemas, tables, columns, warehouses

Trino

catalogs, schemas, tables, columns, procedures

...

+ +

Permissions

+

+ A permission is another fundamental element in the Apache Ranger policy model. A permission is an action + performed on a resource, like reading a file, creating a directory, querying a table, or publishing a message + to a topic. Apache Ranger policy model captures details of permissions of a service in a declarative way – + details like which permissions are applicable to specific resource types, implied permissions, etc. +

+ +

 

+ +

+ Like resources, list of permissions varies across services/applications, as seen in the table below: +

+ +

 

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Service

Permissions

Apache Hive

create, alter, drop, select, insert, ..

Apache Kafka

publish, consume, create, delete, describe, configure, ..

Apache Solr

query, update, others, Solr admin

AWS S3

read, write, delete, ..

ADLS-Gen2

read, write, delete, ..

Azure PowerBI

contributor, member, admin, none

Google BigQuery

project-list, dataset-create, table-create, table-list, query, ..

Snowflake

CreateSchema, CreateTable, Select, Insert, Update, ..

Trino

create, alter, drop, select, insert, ..

...

+ +

Users, Groups, Roles

+

+ Apache Ranger enables authorization policies to be set up to allow/deny permissions to users, groups, and + roles. Users and groups are typically obtained from an enterprise directory like AD/LDAP. Apache Ranger + user-sync module handles details of bringing users and groups from sources like LDAP/AD/OS, and keeping up + with the changes in the sources - like addition of users and groups, addition/removal of a user from a group. +

+ +

 

+ +

+ Apache Ranger user-sync supports retrieving attributes of users and groups as well. Such attributes, like + dept/location/site-id, can be used in authorization policies to allow/deny access to resources, and set up + row-filters that restrict users to access relevant subset of data. More on this later in this document. +

+ +

 

+ +

+ In addition to users and groups, Apache Ranger supports roles to be used in authorization policies. A role in + Apache Ranger is a grouping of users, groups, and other roles. Roles can be managed using Apache Ranger UI and + REST APIs by authorized users. Role based authorization is widely used in enterprises and having support for + roles in Apache Ranger makes it possible to use well established enterprise security practices in Apache Ranger + authorization policies. +

+ +

Delegated Admin

+

+ Apache Ranger enables decentralization of authorization policies management with support for delegated-admin + feature. A set of users, groups and roles can be granted permission, via an Apache Ranger policy (what else!), + to manage authorization policies for a subset of resources and permissions. For example, users in + finance-admin group can be granted permissions to manage authorization policies for contents of + Snowflake database named finance, and AWS S3 objects under s3://mybucket/dept/finance. + This offers a scalable approach to manage authorization in large deployments. +

+ +

Security Zone

+

+ Apache Ranger supports security zones to enable multi-tenancy within an organization where admins from + different lines of businesses can manage security policies for their own resources. For example, data that + belongs to the sales team can be managed by administrators of the sales team, similarly data of marketing, + sales, operations teams can be managed by respective administrators. +

+

 

+

+ Also, security zones can be used to isolate resources based on purpose. For example, it is common for a data + lake to have distinct areas and authorization policies for test data, unprocessed/raw data, semi-processed + data, and production data. Apache Ranger makes it easier to manage security policies in such deployments with + use of security zones like: +

+

-    Test zone

+

-    Landing zone

+

-    Staging zone

+

-    Production zone

+

+ A security zone can contain resources from multiple services/applications, like AWS S3, ADLS-Gen2, GCS, + Snowflake, Amazon Redshift, Postgres, Apache Hadoop, Apache Hive, Apache HBase, Apache Kafka. This makes it + easier to set up consistent authorization policies across multiple services by a set of administrators + designated for each security zone. +

+ +

Allow, Deny, Exceptions

+

+ In addition to authorization policies that can grant access to resources, Apache Ranger also enables policies + to be setup to: +

+

-    deny access to users/groups/roles on resources

+

-    exclude a subset of users from accesses allowed/denied above

+

-    deny all access to specific resources other than the ones allowed in the policy

+

+ This makes it easier to set up policies to protect sensitive resources. +

+ +

Wildcards, macros, variables in resource names

+

+ Apache Ranger policies support use of wildcards, macros, and variables in resource names. This makes it + possible to use small number of policies for a large number of resources, as shown below: +

+ +

 

+ + + + + + + + + + + + + + + + + +

Policy Resource

Description

test_*

matches all resources having name that start with test_

/home/{USER}

a path under /home having name of current user

/dept/${{USER.dept}}

a path under /dept having name of current user’s department

+

+ +

Policy validity schedule

+

+ Apache Ranger enables policies to be effective only for specific time schedules. This feature can be used to + create policies that need to be effective at a future time, for example to allow access to revenue reports for + a wider audience only after a specific time. This feature can also be used to allow temporary access to + specific users/groups/roles, with a specific start and end times. +

+

 

+ +

Attribute based access control

+

+ Apache Ranger enables use of user, group, resource, classification, and the environment attributes in + authorization policies. ABAC makes it possible to express authorization policies without prior knowledge of + specific resources, specific users – which helps avoid the need for new policies as new resources or users are + introduced. +

+
+

+ For example: +

+

-    allow each user to access all tables owned by them, using {OWNER} macro:

+ + + + + + + + + + + + + +

resource

database=*, table=*

users

{OWNER}

permissions

all

+

+

-    allow users to access their department data in AWS S3, by using user attribute ${{USER.dept}}:

+ + + + + + + + + + + + + +

resource

bucket=mycompany, object=/data/${{USER.dept}}/*

users

{USER}

permissions

read,write

+

+

-    allow users in mktg group to access PII data of email type, by using tag attribute TAG.piiType:

+ + + + + + + + + + + + + + + + + +

resource

tag=PII

groups

mktg

condition

TAG.piiType == 'email'

permissions

select

+

+

-    tables with SENSITIVE classification should be accessible only by users having privileges for that sensitive level

+ + + + + + + + + + + + + + + + + +

resource

tag=SENSITIVE

groups

public

condition

TAG.sensitiveLevel < USER.allowedSensitiveLevel

permissions

select

+ +

Resource based access control

+

+ Apache Ranger enables setting up policies to grant or deny permissions to users/group/roles based on specific + resource names, like: +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Service

Resource

Permission

Apache Hive

+ + + + + + + + + + + + + +
databasesales
tableorder_data
columnorder_amount
+

select

Apache Kafka

+ + + + + +
topicfinance
+

publish, consume

AWS S3

+ + + + + + + + + +
bucketmycompany
path/home/{USER}
+

read, write, delete

ADLS-Gen2

+ + + + + + + + + + + + + +
storage-accountmycompany
containerhome
path/{USER}
+

read, write, delete

...

+ +

Tag based access control

+

+ In addition to authorization policies on resources, Apache Ranger enables policies to be set up on + classifications (tags) associated with resources. This feature enables enterprises to separate responsibility + of classification of resources (PII, PCI, PHI, credit card number, etc.) from setting up access-control + policies. Classifications created, by a team of data stewards and tools that scan data for sensitive + information, can be leveraged to drive authorization to access the resources. +

+ +

 

+ +

+ Authorization policies on the classifications themselves, instead of directly on the resources, will ensure + that appropriate policies will automatically be applied as classifications are added, removed, and updated on + resources. Also, a single tag-based policy (for example on PII) can be used to authorize access to resources + across multiple services like AWS S3, ADLS-Gen2, Snowflake, Databricks SQL, Apache Hive, Apache HBase, Apache + Kafka. This can significantly reduce the complexity in managing authorization policies. +

+ +

Data masking

+

+ Apache Ranger data-masking policies enable enterprises to allow access to sensitive data suitably masked + depending on the context in which a user accesses the data. Some users will need the data without masking, + while some other users can only be allowed to see partial or masked or transformed value. While authorization + policies can be used to either allow or deny access to certain data, data-masking policies enable dynamically + mask sensitive data as users access the data, for example to ensure that: +

+

-    analysts have access to only specific part of birthday (year or month or day)

+

-    only last 4 digits of a national id are available to customer service representatives

+

-    only salary ranges of employees (i.e., not the salary) are available to analysts

+

 

+

+ In addition to supporting data-masking policies on resources, like columns in Apache Hive/Snowflake/Databricks + SQL/Presto, Apache Ranger enables setting up data-masking policies based on classifications (tags) associated + with resources. This can significantly reduce the complexity in managing masking policies. In addition, + tag-based masking policies leverage classifications added to resources by data stewards and tools that scan + data for sensitive information. +

+ +

Row-filter

+

+ Apache Ranger row-filter policies enable enterprises to allow users to access only a subset of data depending + upon the context in which a user accesses the data. When a table having a row-filter is accessed by the user, + only a subset of rows will be visible to the user – depending upon the filter setup in row-filter policy. + Row-filters can be used for example to ensure that: +

+

-    data of customers residing in a country is available only to analysts authorized to access the country’s data

+

-    a store manager has access to only data relevant to the store she/he works in

+

-    analysts don’t have access to sensitive records

+ +

Access audit logs

+

+ Apache Ranger generates audit logs of accesses to resources protected by Apache Ranger authorization. Apache + Ranger can be configured to store audit logs in multiple destinations, including Solr, HDFS, AWS S3, AWS + CloudWatch, ADLS-Gen2, Elasticsearch. Audit logs generated by Apache Ranger include following details, which + can help enterprises to satisfy various compliance requirements: +

+

+

-    resource accessed; action performed; was access allowed

+

-    time of access, tags associated with the resource (PII, PCI, PHI, ..)

+

-    who performed the access, IP address from which the access was performed

+

-    ID of Apache Ranger policy that allowed or denied the access

+

 

+

+ Apache Ranger provides an interactive user interface to view audit logs stored in Solr, Elasticsearch or AWS + CloudWatch, with search capabilities to look for access audits for specific resources, specific users, client + IP addresses, within a given time frame, specific classifications. Apache Ranger audit logs can be stored in + ORC or JSON formats, which can then be loaded into various tools for analysis. +

+

 

+ +

References

+

-    Apache Ranger: tag-based policies

+

-    Apache Ranger: row-filter and data-masking policies

+

-    Apache Ranger: security zones

+

-    Apache Ranger: Python

+

-    Apache Ranger: Java

+

-    Apache Ranger: REST

+
+
+ + + + diff --git a/docs/src/site/resources/css/custom.css b/docs/src/site/resources/css/custom.css new file mode 100644 index 0000000000..e71b2f9f71 --- /dev/null +++ b/docs/src/site/resources/css/custom.css @@ -0,0 +1,286 @@ +/* You can override this file with your own styles */ +@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@300;400;600&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;1,300;1,400&display=swap'); + +.project-ranger{ + overflow-x: hidden; +} + +.nav { + margin-top: 20px !important; +} + +.navbar .nav.pull-right { + margin-top: 18px !important; +} + +/* Css for Navbar */ +.navbar .nav>li>a { + color: #17425a; +} + +.navbar-inner { + background-image: none; + background-color: #67BE9C; + border-bottom: none; +} + +.navbar .nav li.dropdown.open>.dropdown-toggle, .navbar .nav li.dropdown.active>.dropdown-toggle, .navbar .nav li.dropdown.open.active>.dropdown-toggle { + background-color: #17425a26; + color: #17425a; + border: 1px solid transparent; +} +.navbar .nav>li>a:hover { + color: #ffffff; + text-decoration: none; + background-color: transparent; +} + +.dropdown-menu{ + background-color: #ffffff; +} + +.dropdown-menu>li>a{ + color: #17425a; +} + +.dropdown-menu>li>a:hover, .dropdown-menu>li>a:focus, .dropdown-submenu:hover>a, .dropdown-submenu:focus>a, +.dropdown-menu>.active>a, .dropdown-menu>.active>a:hover, .dropdown-menu>.active>a:focus{ + background-image: none; + background-color: #e9ecef; + color: #17425a; +} + +.navbar .nav li.dropdown.open>.dropdown-toggle .caret, .navbar .nav li.dropdown.active>.dropdown-toggle .caret, .navbar .nav li.dropdown.open.active>.dropdown-toggle .caret { + border-top-color: #17425a; + border-bottom-color: #17425a; +} +.navbar .nav li.dropdown>.dropdown-toggle .caret { + border-top-color: #17425a; + border-bottom-color: #17425a; +} + +.navbar .pull-right>li>.dropdown-menu:after, .navbar .nav>li>.dropdown-menu.pull-right:after { + right: 13px; + left: auto; +} +.navbar .nav>li>.dropdown-menu:after { + border-bottom: 6px solid #67BE9C; +} + +.navbar .nav li.dropdown>a:hover .caret, .navbar .nav li.dropdown>a:focus .caret { + border-top-color: #ffffff; + border-bottom-color: #ffffff; +} + +.navbar .nav>li>a:focus, .navbar .nav>li>a:hover { + color: #ffffff; + text-decoration: none; + background-color: transparent; +} + + +/* Css for custom header */ +#banner .pull-left{ + float: none; +} + +body +{ + margin: 0; + padding: 0; +} + +.header-section +{ + position: relative; + width: 100%; + top: 70px; + display: flex; + height: 350px; + padding-top: 3%; + padding-bottom: 3%; +} + +.header-section:after { + content: ' '; + z-index: -1; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: #67BE9C; + border-radius: 0 0 50% 75%/0 0 100% 100%; + transform: scaleX(1.5); + background-image: url(/background_design.png); + background-repeat: no-repeat; + background-position: 300px -94px; + background-size: 80%; +} + +.header-section .content +{ + position: relative; + z-index: 1; + margin: 0 auto; + max-width: 900px; + text-align: center; +} + +.header-section .content p +{ + color: #fff; + font-size: 35px; +} + +.header-section .left-side-text { + font-size: 34px; + color: #17425a; + font-weight:400; + margin-bottom: 10px; +} + +.button-container a { + font-size: 16px; + color: #ffffff !important; + margin: 4px 2px; + padding: 15px 45px; + background-color: #17425a; + border-radius: 4px; +} +.button-container a:hover { + background-color: #406377; /* Darker green background color */ +} + +/* link highlights */ +a { + color: #0B9ACE; + text-decoration: none; +} + +a:hover, a:focus { + color: #157ab5; + text-decoration: underline; +} + +footer.well { + color: #4b565a; +} + +.hide { + display: none; +} + +.main-body { + padding-top: 100px; +} + +/* Css for tablet and mobile phone view */ +#banner > .span12 { + width:100%; +} + +@media (min-width: 768px) and (max-width: 979px) { + .container{ + width: 100%; + } + .main-body { + padding-top: 0px !important; + } +} + +@media (max-width: 979px){ + .navbar-fixed-top { + position: relative; + } + .header-section { + top: 0; + padding: 10px 20px; + overflow: hidden; + } + .body-content{ + padding: 10px 20px; + } + .subfooter, + footer.well { + padding: 10px 20px; + } + + #bannerLeft > h1 { + margin:0 + } + .header-section:after{ + overflow: hidden; + } + .row { + overflow: hidden; + } + .navbar-fixed-top { + margin-bottom: 0 !important; + } + .navbar-fixed-top ~ .container { + width:auto; + } + +} + +@media (max-width: 768px) { + .row-fluid .span6 { + width: 70%; + } + .navbar .nav-collapse .nav>li>a { + color: #17425a; + } + .navbar .nav li.dropdown.active>.dropdown-toggle{ + color: #ffffff; + } + .dropdown.open .dropdown-menu{ + background-color: #ffffff; + } +} + +@media (max-width: 767px){ + .row-fluid .span6 { + width: 90%; + } + .navbar-fixed-top{ + position: fixed; + } + .navbar-fixed-top .brand { + margin-left: 30px; + } + .btn.btn-navbar { + position: relative; + top: 15px; + right: 15px; + } + .header-section{ + top: 90px; + } + .header-section:after { + background-image: none; + } + .nav-collapse, .nav-collapse.collapse{ + padding: 0 15px; + } + .header-section, + .subfooter, + footer.well, + .body-content { + padding: 10px 30px; + } +} + +table.bodyTable.table.table-striped.table-hover { + display: block; + width: 100%; + overflow-x: auto; +} + +#Contributors ~ table.bodyTable.table.table-striped.table-hover { + display:table; +} + +.dropdown-backdrop{ + display: none; +} \ No newline at end of file diff --git a/docs/src/site/resources/index.js b/docs/src/site/resources/index.js new file mode 100644 index 0000000000..bb876f28d7 --- /dev/null +++ b/docs/src/site/resources/index.js @@ -0,0 +1,74 @@ +/** + * 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. + */ + +var gatewayUrl; +var apiBaseUrl = "/service"; + +window.onload = function() { + const ui = SwaggerUIBundle({ + url: getSwaggerBaseUrl(window.location.pathname) + "/swagger.json", + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout", + requestInterceptor: function(request) { + if (!request.url.includes("swagger.json")) { + request.url = getAPIUrl(request.url); + } + if (request.method != "GET") { + request.headers['X-XSRF-HEADER'] = localStorage.csrfToken; + } + + return request; + }, + docExpansion: 'none' + }) + window.ui = ui; + setLogo() + if(document.getElementById("swagger-ui").getElementsByClassName("float-right").length > 0) { + document.getElementById("swagger-ui").getElementsByClassName("float-right")[0].querySelector("a").remove() + } + +} + +function setLogo() { + if( document.getElementById("swagger-ui").getElementsByClassName("topbar-wrapper").length > 0){ + document.getElementById("swagger-ui").getElementsByClassName("topbar-wrapper")[0].getElementsByTagName("img")[0].src = gatewayUrl + "/images/ranger_logo.png"; + } +} + +function getSwaggerBaseUrl(url) { + var path = url.replace(/\/[\w-]+.(jsp|html)|\/+$/ig, ''); + splitPath = path.split("/"); + splitPath.pop(); + gatewayUrl = splitPath.join("/"); + + return window.location.origin + path; +}; + +function getAPIUrl(url) { + url = new URL(url); + var path = url.origin + apiBaseUrl + url.pathname + url.search; + return path; +}; diff --git a/docs/src/site/resources/override-banner.js b/docs/src/site/resources/override-banner.js new file mode 100644 index 0000000000..f5613a01a6 --- /dev/null +++ b/docs/src/site/resources/override-banner.js @@ -0,0 +1,15 @@ +// Using URL +const currentPageUrl = window.location.href; + // Check if the current page is the introduction page +const isIntroductionPage = currentPageUrl.includes("index") || currentPageUrl.endsWith("/"); + +// Get the banner, main-body and hide element +const bannerLeft = document.getElementById("bannerLeft"); +const mainBody = document.getElementsByClassName("main-body")[0]; +const hide = document.getElementsByClassName("hide")[0]; + +// Hide the banner element based on the introduction page +if (isIntroductionPage) { + hide.classList.remove("hide"); + mainBody.style.paddingTop = "0"; +} diff --git a/docs/src/site/resources/ranger-logo.svg b/docs/src/site/resources/ranger-logo.svg new file mode 100644 index 0000000000..814255694d --- /dev/null +++ b/docs/src/site/resources/ranger-logo.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/docs/src/site/resources/smooth-scroll.js b/docs/src/site/resources/smooth-scroll.js new file mode 100644 index 0000000000..a2d5db61bd --- /dev/null +++ b/docs/src/site/resources/smooth-scroll.js @@ -0,0 +1,18 @@ +$(document).ready(function() { + // Smooth scrolling for internal links + $('a[href^="#"]').not('[data-toggle="dropdown"]').on('click', function(e) { + e.preventDefault(); + + var target = $(this.hash); + if (target.length === 0) { + target = $('a[name="' + this.hash.substr(1) + '"]'); + } + if (target.length === 0) { + target = $('html'); + } + + $('html, body').animate({ + scrollTop: target.offset().top + }, 500); + }); + }); \ No newline at end of file diff --git a/docs/src/site/resources/swagger-ui-bundle.js b/docs/src/site/resources/swagger-ui-bundle.js new file mode 100644 index 0000000000..f4e8e3cb5a --- /dev/null +++ b/docs/src/site/resources/swagger-ui-bundle.js @@ -0,0 +1,20 @@ +/** + * 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. + */ + +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.SwaggerUIBundle=t():e.SwaggerUIBundle=t()}(this,(()=>(()=>{var e={17967:(e,t)=>{"use strict";t.Nm=t.Rq=void 0;var n=/^([^\w]*)(javascript|data|vbscript)/im,r=/&#(\w+)(^\w|;)?/g,o=/&(newline|tab);/gi,s=/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim,i=/^.+(:|:)/gim,a=[".","/"];t.Rq="about:blank",t.Nm=function(e){if(!e)return t.Rq;var l,c=(l=e,l.replace(s,"").replace(r,(function(e,t){return String.fromCharCode(t)}))).replace(o,"").replace(s,"").trim();if(!c)return t.Rq;if(function(e){return a.indexOf(e[0])>-1}(c))return c;var u=c.match(i);if(!u)return c;var p=u[0];return n.test(p)?t.Rq:c}},53795:(e,t,n)=>{"use strict";n.d(t,{Z:()=>P});var r=n(23101),o=n.n(r),s=n(61125),i=n.n(s),a=n(11882),l=n.n(a),c=n(97606),u=n.n(c),p=n(67294),h=n(43393);function f(e){return f="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},f(e)}function d(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=function(e,t){return function(n){if("string"==typeof n)return(0,h.is)(t[n],e[n]);if(Array.isArray(n))return(0,h.is)(x(t,n),x(e,n));throw new TypeError("Invalid key: expected Array or string: "+n)}}(t,n),o=e||Object.keys(function(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:{};return!S(this.updateOnProps,this.props,e,"updateOnProps")||!S(this.updateOnStates,this.state,t,"updateOnStates")}}],r&&d(n.prototype,r),o&&d(n,o),t}(p.Component);var j=n(23930),O=n.n(j),k=n(45697),A=n.n(k);const C=e=>{const t=e.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(t)}catch{return t}};class P extends _{constructor(){super(...arguments),i()(this,"getModelName",(e=>-1!==l()(e).call(e,"#/definitions/")?C(e.replace(/^.*#\/definitions\//,"")):-1!==l()(e).call(e,"#/components/schemas/")?C(e.replace(/^.*#\/components\/schemas\//,"")):void 0)),i()(this,"getRefSchema",(e=>{let{specSelectors:t}=this.props;return t.findDefinition(e)}))}render(){let{getComponent:e,getConfigs:t,specSelectors:r,schema:s,required:i,name:a,isRef:l,specPath:c,displayName:u,includeReadOnly:h,includeWriteOnly:f}=this.props;const d=e("ObjectModel"),m=e("ArrayModel"),g=e("PrimitiveModel");let y="object",v=s&&s.get("$$ref");if(!a&&v&&(a=this.getModelName(v)),!s&&v&&(s=this.getRefSchema(a)),!s)return p.createElement("span",{className:"model model-title"},p.createElement("span",{className:"model-title__text"},u||a),p.createElement("img",{src:n(2517),height:"20px",width:"20px"}));const b=r.isOAS3()&&s.get("deprecated");switch(l=void 0!==l?l:!!v,y=s&&s.get("type")||y,y){case"object":return p.createElement(d,o()({className:"object"},this.props,{specPath:c,getConfigs:t,schema:s,name:a,deprecated:b,isRef:l,includeReadOnly:h,includeWriteOnly:f}));case"array":return p.createElement(m,o()({className:"array"},this.props,{getConfigs:t,schema:s,name:a,deprecated:b,required:i,includeReadOnly:h,includeWriteOnly:f}));default:return p.createElement(g,o()({},this.props,{getComponent:e,getConfigs:t,schema:s,name:a,deprecated:b,required:i}))}}}i()(P,"propTypes",{schema:u()(O()).isRequired,getComponent:A().func.isRequired,getConfigs:A().func.isRequired,specSelectors:A().object.isRequired,name:A().string,displayName:A().string,isRef:A().bool,required:A().bool,expandDepth:A().number,depth:A().number,specPath:O().list.isRequired,includeReadOnly:A().bool,includeWriteOnly:A().bool})},5623:(e,t,n)=>{"use strict";n.d(t,{Z:()=>h});var r=n(61125),o=n.n(r),s=n(28222),i=n.n(s),a=n(67294),l=n(84564),c=n.n(l),u=n(90242),p=n(27504);class h extends a.Component{constructor(e,t){super(e,t),o()(this,"getDefinitionUrl",(()=>{let{specSelectors:e}=this.props;return new(c())(e.url(),p.Z.location).toString()}));let{getConfigs:n}=e,{validatorUrl:r}=n();this.state={url:this.getDefinitionUrl(),validatorUrl:void 0===r?"https://validator.swagger.io/validator":r}}UNSAFE_componentWillReceiveProps(e){let{getConfigs:t}=e,{validatorUrl:n}=t();this.setState({url:this.getDefinitionUrl(),validatorUrl:void 0===n?"https://validator.swagger.io/validator":n})}render(){let{getConfigs:e}=this.props,{spec:t}=e(),n=(0,u.Nm)(this.state.validatorUrl);return"object"==typeof t&&i()(t).length?null:this.state.url&&(0,u.hW)(this.state.validatorUrl)&&(0,u.hW)(this.state.url)?a.createElement("span",{className:"float-right"},a.createElement("a",{target:"_blank",rel:"noopener noreferrer",href:`${n}/debug?url=${encodeURIComponent(this.state.url)}`},a.createElement(f,{src:`${n}?url=${encodeURIComponent(this.state.url)}`,alt:"Online validator badge"}))):null}}class f extends a.Component{constructor(e){super(e),this.state={loaded:!1,error:!1}}componentDidMount(){const e=new Image;e.onload=()=>{this.setState({loaded:!0})},e.onerror=()=>{this.setState({error:!0})},e.src=this.props.src}UNSAFE_componentWillReceiveProps(e){if(e.src!==this.props.src){const t=new Image;t.onload=()=>{this.setState({loaded:!0})},t.onerror=()=>{this.setState({error:!0})},t.src=e.src}}render(){return this.state.error?a.createElement("img",{alt:"Error"}):this.state.loaded?a.createElement("img",{src:this.props.src,alt:this.props.alt}):null}}},4599:(e,t,n)=>{"use strict";n.d(t,{Z:()=>ye,s:()=>ve});var r=n(67294),o=n(89927);function s(e,t){if(Array.prototype.indexOf)return e.indexOf(t);for(var n=0,r=e.length;n=0;n--)!0===t(e[n])&&e.splice(n,1)}function a(e){throw new Error("Unhandled case for value: '".concat(e,"'"))}var l=function(){function e(e){void 0===e&&(e={}),this.tagName="",this.attrs={},this.innerHTML="",this.whitespaceRegex=/\s+/,this.tagName=e.tagName||"",this.attrs=e.attrs||{},this.innerHTML=e.innerHtml||e.innerHTML||""}return e.prototype.setTagName=function(e){return this.tagName=e,this},e.prototype.getTagName=function(){return this.tagName||""},e.prototype.setAttr=function(e,t){return this.getAttrs()[e]=t,this},e.prototype.getAttr=function(e){return this.getAttrs()[e]},e.prototype.setAttrs=function(e){return Object.assign(this.getAttrs(),e),this},e.prototype.getAttrs=function(){return this.attrs||(this.attrs={})},e.prototype.setClass=function(e){return this.setAttr("class",e)},e.prototype.addClass=function(e){for(var t,n=this.getClass(),r=this.whitespaceRegex,o=n?n.split(r):[],i=e.split(r);t=i.shift();)-1===s(o,t)&&o.push(t);return this.getAttrs().class=o.join(" "),this},e.prototype.removeClass=function(e){for(var t,n=this.getClass(),r=this.whitespaceRegex,o=n?n.split(r):[],i=e.split(r);o.length&&(t=i.shift());){var a=s(o,t);-1!==a&&o.splice(a,1)}return this.getAttrs().class=o.join(" "),this},e.prototype.getClass=function(){return this.getAttrs().class||""},e.prototype.hasClass=function(e){return-1!==(" "+this.getClass()+" ").indexOf(" "+e+" ")},e.prototype.setInnerHTML=function(e){return this.innerHTML=e,this},e.prototype.setInnerHtml=function(e){return this.setInnerHTML(e)},e.prototype.getInnerHTML=function(){return this.innerHTML||""},e.prototype.getInnerHtml=function(){return this.getInnerHTML()},e.prototype.toAnchorString=function(){var e=this.getTagName(),t=this.buildAttrsStr();return["<",e,t=t?" "+t:"",">",this.getInnerHtml(),""].join("")},e.prototype.buildAttrsStr=function(){if(!this.attrs)return"";var e=this.getAttrs(),t=[];for(var n in e)e.hasOwnProperty(n)&&t.push(n+'="'+e[n]+'"');return t.join(" ")},e}();var c=function(){function e(e){void 0===e&&(e={}),this.newWindow=!1,this.truncate={},this.className="",this.newWindow=e.newWindow||!1,this.truncate=e.truncate||{},this.className=e.className||""}return e.prototype.build=function(e){return new l({tagName:"a",attrs:this.createAttrs(e),innerHtml:this.processAnchorText(e.getAnchorText())})},e.prototype.createAttrs=function(e){var t={href:e.getAnchorHref()},n=this.createCssClass(e);return n&&(t.class=n),this.newWindow&&(t.target="_blank",t.rel="noopener noreferrer"),this.truncate&&this.truncate.length&&this.truncate.length=a)return l.host.length==t?(l.host.substr(0,t-o)+n).substr(0,a+r):i(u,a).substr(0,a+r);var p="";if(l.path&&(p+="/"+l.path),l.query&&(p+="?"+l.query),p){if((u+p).length>=a)return(u+p).length==t?(u+p).substr(0,t):(u+i(p,a-u.length)).substr(0,a+r);u+=p}if(l.fragment){var h="#"+l.fragment;if((u+h).length>=a)return(u+h).length==t?(u+h).substr(0,t):(u+i(h,a-u.length)).substr(0,a+r);u+=h}if(l.scheme&&l.host){var f=l.scheme+"://";if((u+f).length0&&(d=u.substr(-1*Math.floor(a/2))),(u.substr(0,Math.ceil(a/2))+n+d).substr(0,a+r)}(e,n):"middle"===r?function(e,t,n){if(e.length<=t)return e;var r,o;null==n?(n="…",r=8,o=3):(r=n.length,o=n.length);var s=t-o,i="";return s>0&&(i=e.substr(-1*Math.floor(s/2))),(e.substr(0,Math.ceil(s/2))+n+i).substr(0,s+r)}(e,n):function(e,t,n){return function(e,t,n){var r;return e.length>t&&(null==n?(n="…",r=3):r=n.length,e=e.substring(0,t-r)+n),e}(e,t,n)}(e,n)},e}(),u=function(){function e(e){this.__jsduckDummyDocProp=null,this.matchedText="",this.offset=0,this.tagBuilder=e.tagBuilder,this.matchedText=e.matchedText,this.offset=e.offset}return e.prototype.getMatchedText=function(){return this.matchedText},e.prototype.setOffset=function(e){this.offset=e},e.prototype.getOffset=function(){return this.offset},e.prototype.getCssClassSuffixes=function(){return[this.getType()]},e.prototype.buildTag=function(){return this.tagBuilder.build(this)},e}(),p=function(e,t){return p=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(e[n]=t[n])},p(e,t)};function h(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function n(){this.constructor=e}p(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}var f=function(){return f=Object.assign||function(e){for(var t,n=1,r=arguments.length;n-1},e.isValidUriScheme=function(e){var t=e.match(this.uriSchemeRegex),n=t&&t[0].toLowerCase();return"javascript:"!==n&&"vbscript:"!==n},e.urlMatchDoesNotHaveProtocolOrDot=function(e,t){return!(!e||t&&this.hasFullProtocolRegex.test(t)||-1!==e.indexOf("."))},e.urlMatchDoesNotHaveAtLeastOneWordChar=function(e,t){return!(!e||!t)&&(!this.hasFullProtocolRegex.test(t)&&!this.hasWordCharAfterProtocolRegex.test(e))},e.hasFullProtocolRegex=/^[A-Za-z][-.+A-Za-z0-9]*:\/\//,e.uriSchemeRegex=/^[A-Za-z][-.+A-Za-z0-9]*:/,e.hasWordCharAfterProtocolRegex=new RegExp(":[^\\s]*?["+k+"]"),e.ipRegex=/[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/,e}(),V=(d=new RegExp("[/?#](?:["+N+"\\-+&@#/%=~_()|'$*\\[\\]{}?!:,.;^✓]*["+N+"\\-+&@#/%=~_()|'$*\\[\\]{}✓])?"),new RegExp(["(?:","(",/(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/.source,D(2),")","|","(","(//)?",/(?:www\.)/.source,D(6),")","|","(","(//)?",D(10)+"\\.",L.source,"(?![-"+P+"])",")",")","(?::[0-9]+)?","(?:"+d.source+")?"].join(""),"gi")),W=new RegExp("["+N+"]"),J=function(e){function t(t){var n=e.call(this,t)||this;return n.stripPrefix={scheme:!0,www:!0},n.stripTrailingSlash=!0,n.decodePercentEncoding=!0,n.matcherRegex=V,n.wordCharRegExp=W,n.stripPrefix=t.stripPrefix,n.stripTrailingSlash=t.stripTrailingSlash,n.decodePercentEncoding=t.decodePercentEncoding,n}return h(t,e),t.prototype.parseMatches=function(e){for(var t,n=this.matcherRegex,r=this.stripPrefix,o=this.stripTrailingSlash,s=this.decodePercentEncoding,i=this.tagBuilder,a=[],l=function(){var n=t[0],l=t[1],u=t[4],p=t[5],h=t[9],f=t.index,d=p||h,m=e.charAt(f-1);if(!z.isValid(n,l))return"continue";if(f>0&&"@"===m)return"continue";if(f>0&&d&&c.wordCharRegExp.test(m))return"continue";if(/\?$/.test(n)&&(n=n.substr(0,n.length-1)),c.matchHasUnbalancedClosingParen(n))n=n.substr(0,n.length-1);else{var g=c.matchHasInvalidCharAfterTld(n,l);g>-1&&(n=n.substr(0,g))}var y=["http://","https://"].find((function(e){return!!l&&-1!==l.indexOf(e)}));if(y){var v=n.indexOf(y);n=n.substr(v),l=l.substr(v),f+=v}var w=l?"scheme":u?"www":"tld",E=!!l;a.push(new b({tagBuilder:i,matchedText:n,offset:f,urlMatchType:w,url:n,protocolUrlMatch:E,protocolRelativeMatch:!!d,stripPrefix:r,stripTrailingSlash:o,decodePercentEncoding:s}))},c=this;null!==(t=n.exec(e));)l();return a},t.prototype.matchHasUnbalancedClosingParen=function(e){var t,n=e.charAt(e.length-1);if(")"===n)t="(";else if("]"===n)t="[";else{if("}"!==n)return!1;t="{"}for(var r=0,o=0,s=e.length-1;o-1&&s-i<=140){var o=e.slice(i,s),a=new g({tagBuilder:t,matchedText:o,offset:i,serviceName:n,hashtag:o.slice(1)});r.push(a)}}},t}(w),G=["twitter","facebook","instagram","tiktok"],Z=new RegExp("".concat(/(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/.source,"|").concat(/(0([1-9]{1}-?[1-9]\d{3}|[1-9]{2}-?\d{3}|[1-9]{2}\d{1}-?\d{2}|[1-9]{2}\d{2}-?\d{1})-?\d{4}|0[789]0-?\d{4}-?\d{4}|050-?\d{4}-?\d{4})/.source),"g"),Y=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.matcherRegex=Z,t}return h(t,e),t.prototype.parseMatches=function(e){for(var t,n=this.matcherRegex,r=this.tagBuilder,o=[];null!==(t=n.exec(e));){var s=t[0],i=s.replace(/[^0-9,;#]/g,""),a=!(!t[1]&&!t[2]),l=0==t.index?"":e.substr(t.index-1,1),c=e.substr(t.index+s.length,1),u=!l.match(/\d/)&&!c.match(/\d/);this.testMatch(t[3])&&this.testMatch(s)&&u&&o.push(new v({tagBuilder:r,matchedText:s,offset:t.index,number:i,plusSign:a}))}return o},t.prototype.testMatch=function(e){return S.test(e)},t}(w),X=new RegExp("@[_".concat(N,"]{1,50}(?![_").concat(N,"])"),"g"),Q=new RegExp("@[_.".concat(N,"]{1,30}(?![_").concat(N,"])"),"g"),ee=new RegExp("@[-_.".concat(N,"]{1,50}(?![-_").concat(N,"])"),"g"),te=new RegExp("@[_.".concat(N,"]{1,23}[_").concat(N,"](?![_").concat(N,"])"),"g"),ne=new RegExp("[^"+N+"]"),re=function(e){function t(t){var n=e.call(this,t)||this;return n.serviceName="twitter",n.matcherRegexes={twitter:X,instagram:Q,soundcloud:ee,tiktok:te},n.nonWordCharRegex=ne,n.serviceName=t.serviceName,n}return h(t,e),t.prototype.parseMatches=function(e){var t,n=this.serviceName,r=this.matcherRegexes[this.serviceName],o=this.nonWordCharRegex,s=this.tagBuilder,i=[];if(!r)return i;for(;null!==(t=r.exec(e));){var a=t.index,l=e.charAt(a-1);if(0===a||o.test(l)){var c=t[0].replace(/\.+$/g,""),u=c.slice(1);i.push(new y({tagBuilder:s,matchedText:c,offset:a,serviceName:n,mention:u}))}}return i},t}(w);function oe(e,t){for(var n,r=t.onOpenTag,o=t.onCloseTag,s=t.onText,i=t.onComment,l=t.onDoctype,c=new se,u=0,p=e.length,h=0,d=0,m=c;u"===e?(m=new se(f(f({},m),{name:J()})),W()):E.test(e)||x.test(e)||":"===e||z()}function w(e){">"===e?z():E.test(e)?h=3:z()}function S(e){_.test(e)||("/"===e?h=12:">"===e?W():"<"===e?V():"="===e||j.test(e)||O.test(e)?z():h=5)}function k(e){_.test(e)?h=6:"/"===e?h=12:"="===e?h=7:">"===e?W():"<"===e?V():j.test(e)&&z()}function A(e){_.test(e)||("/"===e?h=12:"="===e?h=7:">"===e?W():"<"===e?V():j.test(e)?z():h=5)}function C(e){_.test(e)||('"'===e?h=8:"'"===e?h=9:/[>=`]/.test(e)?z():"<"===e?V():h=10)}function P(e){'"'===e&&(h=11)}function N(e){"'"===e&&(h=11)}function I(e){_.test(e)?h=4:">"===e?W():"<"===e&&V()}function T(e){_.test(e)?h=4:"/"===e?h=12:">"===e?W():"<"===e?V():(h=4,u--)}function R(e){">"===e?(m=new se(f(f({},m),{isClosing:!0})),W()):h=4}function M(t){"--"===e.substr(u,2)?(u+=2,m=new se(f(f({},m),{type:"comment"})),h=14):"DOCTYPE"===e.substr(u,7).toUpperCase()?(u+=7,m=new se(f(f({},m),{type:"doctype"})),h=20):z()}function D(e){"-"===e?h=15:">"===e?z():h=16}function F(e){"-"===e?h=18:">"===e?z():h=16}function L(e){"-"===e&&(h=17)}function B(e){h="-"===e?18:16}function $(e){">"===e?W():"!"===e?h=19:"-"===e||(h=16)}function q(e){"-"===e?h=17:">"===e?W():h=16}function U(e){">"===e?W():"<"===e&&V()}function z(){h=0,m=c}function V(){h=1,m=new se({idx:u})}function W(){var t=e.slice(d,m.idx);t&&s(t,d),"comment"===m.type?i(m.idx):"doctype"===m.type?l(m.idx):(m.isOpening&&r(m.name,m.idx),m.isClosing&&o(m.name,m.idx)),z(),d=u+1}function J(){var t=m.idx+(m.isClosing?2:1);return e.slice(t,u).toLowerCase()}d=0&&r++},onText:function(e,n){if(0===r){var s=function(e,t){if(!t.global)throw new Error("`splitRegex` must have the 'g' flag set");for(var n,r=[],o=0;n=t.exec(e);)r.push(e.substring(o,n.index)),r.push(n[0]),o=n.index+n[0].length;return r.push(e.substring(o)),r}(e,/( | |<|<|>|>|"|"|')/gi),i=n;s.forEach((function(e,n){if(n%2==0){var r=t.parseText(e,i);o.push.apply(o,r)}i+=e.length}))}},onCloseTag:function(e){n.indexOf(e)>=0&&(r=Math.max(r-1,0))},onComment:function(e){},onDoctype:function(e){}}),o=this.compactMatches(o),o=this.removeUnwantedMatches(o)},e.prototype.compactMatches=function(e){e.sort((function(e,t){return e.getOffset()-t.getOffset()}));for(var t=0;to?t:t+1;e.splice(i,1);continue}if(e[t+1].getOffset()/g,">"));for(var t=this.parse(e),n=[],r=0,o=0,s=t.length;o/i.test(e)}function ce(){var e=[],t=new ie({stripPrefix:!1,url:!0,email:!0,replaceFn:function(t){switch(t.getType()){case"url":e.push({text:t.matchedText,url:t.getUrl()});break;case"email":e.push({text:t.matchedText,url:"mailto:"+t.getEmail().replace(/^mailto:/i,"")})}return!1}});return{links:e,autolinker:t}}function ue(e){var t,n,r,o,s,i,a,l,c,u,p,h,f,d,m=e.tokens,g=null;for(n=0,r=m.length;n=0;t--)if("link_close"!==(s=o[t]).type){if("htmltag"===s.type&&(d=s.content,/^\s]/i.test(d)&&p>0&&p--,le(s.content)&&p++),!(p>0)&&"text"===s.type&&ae.test(s.content)){if(g||(h=(g=ce()).links,f=g.autolinker),i=s.content,h.length=0,f.link(i),!h.length)continue;for(a=[],u=s.level,l=0;l({useUnsafeMarkdown:!1})};const ye=ge;function ve(e){let{useUnsafeMarkdown:t=!1}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n=t,r=t?[]:["style","class"];return t&&!ve.hasWarnedAboutDeprecation&&(console.warn("useUnsafeMarkdown display configuration parameter is deprecated since >3.26.0 and will be removed in v4.0.0."),ve.hasWarnedAboutDeprecation=!0),fe().sanitize(e,{ADD_ATTR:["target"],FORBID_TAGS:["style","form"],ALLOW_DATA_ATTR:n,FORBID_ATTR:r})}ve.hasWarnedAboutDeprecation=!1},45308:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r,o=n(86),s=n.n(o),i=n(8712),a=n.n(i),l=n(90242),c=n(27621);const u=n(95102),p={},h=p;s()(r=a()(u).call(u)).call(r,(function(e){if("./index.js"===e)return;let t=u(e);p[(0,l.Zl)(e)]=t.default?t.default:t})),p.SafeRender=c.default},55812:(e,t,n)=>{"use strict";n.r(t),n.d(t,{AUTHORIZE:()=>h,AUTHORIZE_OAUTH2:()=>m,CONFIGURE_AUTH:()=>y,LOGOUT:()=>f,PRE_AUTHORIZE_OAUTH2:()=>d,RESTORE_AUTHORIZATION:()=>v,SHOW_AUTH_POPUP:()=>p,VALIDATE:()=>g,authPopup:()=>M,authorize:()=>w,authorizeAccessCodeWithBasicAuthentication:()=>P,authorizeAccessCodeWithFormParams:()=>C,authorizeApplication:()=>A,authorizeOauth2:()=>j,authorizeOauth2WithPersistOption:()=>O,authorizePassword:()=>k,authorizeRequest:()=>N,authorizeWithPersistOption:()=>E,configureAuth:()=>I,logout:()=>x,logoutWithPersistOption:()=>S,persistAuthorizationIfNeeded:()=>R,preAuthorizeImplicit:()=>_,restoreAuthorization:()=>T,showDefinitions:()=>b});var r=n(35627),o=n.n(r),s=n(76986),i=n.n(s),a=n(84564),l=n.n(a),c=n(27504),u=n(90242);const p="show_popup",h="authorize",f="logout",d="pre_authorize_oauth2",m="authorize_oauth2",g="validate",y="configure_auth",v="restore_authorization";function b(e){return{type:p,payload:e}}function w(e){return{type:h,payload:e}}const E=e=>t=>{let{authActions:n}=t;n.authorize(e),n.persistAuthorizationIfNeeded()};function x(e){return{type:f,payload:e}}const S=e=>t=>{let{authActions:n}=t;n.logout(e),n.persistAuthorizationIfNeeded()},_=e=>t=>{let{authActions:n,errActions:r}=t,{auth:s,token:i,isValid:a}=e,{schema:l,name:u}=s,p=l.get("flow");delete c.Z.swaggerUIRedirectOauth2,"accessCode"===p||a||r.newAuthErr({authId:u,source:"auth",level:"warning",message:"Authorization may be unsafe, passed state was changed in server Passed state wasn't returned from auth server"}),i.error?r.newAuthErr({authId:u,source:"auth",level:"error",message:o()(i)}):n.authorizeOauth2WithPersistOption({auth:s,token:i})};function j(e){return{type:m,payload:e}}const O=e=>t=>{let{authActions:n}=t;n.authorizeOauth2(e),n.persistAuthorizationIfNeeded()},k=e=>t=>{let{authActions:n}=t,{schema:r,name:o,username:s,password:a,passwordType:l,clientId:c,clientSecret:p}=e,h={grant_type:"password",scope:e.scopes.join(" "),username:s,password:a},f={};switch(l){case"request-body":!function(e,t,n){t&&i()(e,{client_id:t});n&&i()(e,{client_secret:n})}(h,c,p);break;case"basic":f.Authorization="Basic "+(0,u.r3)(c+":"+p);break;default:console.warn(`Warning: invalid passwordType ${l} was passed, not including client id and secret`)}return n.authorizeRequest({body:(0,u.GZ)(h),url:r.get("tokenUrl"),name:o,headers:f,query:{},auth:e})};const A=e=>t=>{let{authActions:n}=t,{schema:r,scopes:o,name:s,clientId:i,clientSecret:a}=e,l={Authorization:"Basic "+(0,u.r3)(i+":"+a)},c={grant_type:"client_credentials",scope:o.join(" ")};return n.authorizeRequest({body:(0,u.GZ)(c),name:s,url:r.get("tokenUrl"),auth:e,headers:l})},C=e=>{let{auth:t,redirectUrl:n}=e;return e=>{let{authActions:r}=e,{schema:o,name:s,clientId:i,clientSecret:a,codeVerifier:l}=t,c={grant_type:"authorization_code",code:t.code,client_id:i,client_secret:a,redirect_uri:n,code_verifier:l};return r.authorizeRequest({body:(0,u.GZ)(c),name:s,url:o.get("tokenUrl"),auth:t})}},P=e=>{let{auth:t,redirectUrl:n}=e;return e=>{let{authActions:r}=e,{schema:o,name:s,clientId:i,clientSecret:a,codeVerifier:l}=t,c={Authorization:"Basic "+(0,u.r3)(i+":"+a)},p={grant_type:"authorization_code",code:t.code,client_id:i,redirect_uri:n,code_verifier:l};return r.authorizeRequest({body:(0,u.GZ)(p),name:s,url:o.get("tokenUrl"),auth:t,headers:c})}},N=e=>t=>{let n,{fn:r,getConfigs:s,authActions:a,errActions:c,oas3Selectors:u,specSelectors:p,authSelectors:h}=t,{body:f,query:d={},headers:m={},name:g,url:y,auth:v}=e,{additionalQueryStringParams:b}=h.getConfigs()||{};if(p.isOAS3()){let e=u.serverEffectiveValue(u.selectedServer());n=l()(y,e,!0)}else n=l()(y,p.url(),!0);"object"==typeof b&&(n.query=i()({},n.query,b));const w=n.toString();let E=i()({Accept:"application/json, text/plain, */*","Content-Type":"application/x-www-form-urlencoded","X-Requested-With":"XMLHttpRequest"},m);r.fetch({url:w,method:"post",headers:E,query:d,body:f,requestInterceptor:s().requestInterceptor,responseInterceptor:s().responseInterceptor}).then((function(e){let t=JSON.parse(e.data),n=t&&(t.error||""),r=t&&(t.parseError||"");e.ok?n||r?c.newAuthErr({authId:g,level:"error",source:"auth",message:o()(t)}):a.authorizeOauth2WithPersistOption({auth:v,token:t}):c.newAuthErr({authId:g,level:"error",source:"auth",message:e.statusText})})).catch((e=>{let t=new Error(e).message;if(e.response&&e.response.data){const n=e.response.data;try{const e="string"==typeof n?JSON.parse(n):n;e.error&&(t+=`, error: ${e.error}`),e.error_description&&(t+=`, description: ${e.error_description}`)}catch(e){}}c.newAuthErr({authId:g,level:"error",source:"auth",message:t})}))};function I(e){return{type:y,payload:e}}function T(e){return{type:v,payload:e}}const R=()=>e=>{let{authSelectors:t,getConfigs:n}=e;if(!n().persistAuthorization)return;const r=t.authorized().toJS();localStorage.setItem("authorized",o()(r))},M=(e,t)=>()=>{c.Z.swaggerUIRedirectOauth2=t,c.Z.open(e)}},87105:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(28222),o=n.n(r),s=n(67294),i=n(57557),a=n.n(i);class l extends s.Component{mapStateToProps(e,t){return{state:e,ownProps:a()(t,o()(t.getSystem()))}}render(){const{getComponent:e,ownProps:t}=this.props,n=e("LockIcon");return s.createElement(n,t)}}const c=l},53219:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(28222),o=n.n(r),s=n(67294),i=n(57557),a=n.n(i);class l extends s.Component{mapStateToProps(e,t){return{state:e,ownProps:a()(t,o()(t.getSystem()))}}render(){const{getComponent:e,ownProps:t}=this.props,n=e("UnlockIcon");return s.createElement(n,t)}}const c=l},53779:(e,t,n)=>{"use strict";n.r(t),n.d(t,{loaded:()=>r});const r=(e,t)=>n=>{const{getConfigs:r,authActions:o}=t,s=r();if(e(n),s.persistAuthorization){const e=localStorage.getItem("authorized");e&&o.restoreAuthorization({authorized:JSON.parse(e)})}}},93705:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>f,preauthorizeApiKey:()=>m,preauthorizeBasic:()=>d});var r=n(11189),o=n.n(r),s=n(43962),i=n(55812),a=n(60035),l=n(60489),c=n(53779),u=n(22849),p=n(87105),h=n(53219);function f(){return{afterLoad(e){this.rootInjects=this.rootInjects||{},this.rootInjects.initOAuth=e.authActions.configureAuth,this.rootInjects.preauthorizeApiKey=o()(m).call(m,null,e),this.rootInjects.preauthorizeBasic=o()(d).call(d,null,e)},components:{LockAuthIcon:p.default,UnlockAuthIcon:h.default,LockAuthOperationIcon:p.default,UnlockAuthOperationIcon:h.default},statePlugins:{auth:{reducers:s.default,actions:i,selectors:a,wrapActions:{authorize:u.authorize,logout:u.logout}},configs:{wrapActions:{loaded:c.loaded}},spec:{wrapActions:{execute:l.execute}}}}}function d(e,t,n,r){const{authActions:{authorize:o},specSelectors:{specJson:s,isOAS3:i}}=e,a=i()?["components","securitySchemes"]:["securityDefinitions"],l=s().getIn([...a,t]);return l?o({[t]:{value:{username:n,password:r},schema:l.toJS()}}):null}function m(e,t,n){const{authActions:{authorize:r},specSelectors:{specJson:o,isOAS3:s}}=e,i=s()?["components","securitySchemes"]:["securityDefinitions"],a=o().getIn([...i,t]);return a?r({[t]:{value:n,schema:a.toJS()}}):null}},43962:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(86),o=n.n(r),s=n(76986),i=n.n(s),a=n(43393),l=n(90242),c=n(55812);const u={[c.SHOW_AUTH_POPUP]:(e,t)=>{let{payload:n}=t;return e.set("showDefinitions",n)},[c.AUTHORIZE]:(e,t)=>{var n;let{payload:r}=t,s=(0,a.fromJS)(r),i=e.get("authorized")||(0,a.Map)();return o()(n=s.entrySeq()).call(n,(t=>{let[n,r]=t;if(!(0,l.Wl)(r.getIn))return e.set("authorized",i);let o=r.getIn(["schema","type"]);if("apiKey"===o||"http"===o)i=i.set(n,r);else if("basic"===o){let e=r.getIn(["value","username"]),t=r.getIn(["value","password"]);i=i.setIn([n,"value"],{username:e,header:"Basic "+(0,l.r3)(e+":"+t)}),i=i.setIn([n,"schema"],r.get("schema"))}})),e.set("authorized",i)},[c.AUTHORIZE_OAUTH2]:(e,t)=>{let n,{payload:r}=t,{auth:o,token:s}=r;o.token=i()({},s),n=(0,a.fromJS)(o);let l=e.get("authorized")||(0,a.Map)();return l=l.set(n.get("name"),n),e.set("authorized",l)},[c.LOGOUT]:(e,t)=>{let{payload:n}=t,r=e.get("authorized").withMutations((e=>{o()(n).call(n,(t=>{e.delete(t)}))}));return e.set("authorized",r)},[c.CONFIGURE_AUTH]:(e,t)=>{let{payload:n}=t;return e.set("configs",n)},[c.RESTORE_AUTHORIZATION]:(e,t)=>{let{payload:n}=t;return e.set("authorized",(0,a.fromJS)(n.authorized))}}},60035:(e,t,n)=>{"use strict";n.r(t),n.d(t,{authorized:()=>x,definitionsForRequirements:()=>E,definitionsToAuthorize:()=>b,getConfigs:()=>_,getDefinitionsByNames:()=>w,isAuthorized:()=>S,shownDefinitions:()=>v});var r=n(86),o=n.n(r),s=n(51679),i=n.n(s),a=n(14418),l=n.n(a),c=n(11882),u=n.n(c),p=n(97606),h=n.n(p),f=n(28222),d=n.n(f),m=n(20573),g=n(43393);const y=e=>e,v=(0,m.P1)(y,(e=>e.get("showDefinitions"))),b=(0,m.P1)(y,(()=>e=>{var t;let{specSelectors:n}=e,r=n.securityDefinitions()||(0,g.Map)({}),s=(0,g.List)();return o()(t=r.entrySeq()).call(t,(e=>{let[t,n]=e,r=(0,g.Map)();r=r.set(t,n),s=s.push(r)})),s})),w=(e,t)=>e=>{var n;let{specSelectors:r}=e;console.warn("WARNING: getDefinitionsByNames is deprecated and will be removed in the next major version.");let s=r.securityDefinitions(),i=(0,g.List)();return o()(n=t.valueSeq()).call(n,(e=>{var t;let n=(0,g.Map)();o()(t=e.entrySeq()).call(t,(e=>{let t,[r,i]=e,a=s.get(r);var l;"oauth2"===a.get("type")&&i.size&&(t=a.get("scopes"),o()(l=t.keySeq()).call(l,(e=>{i.contains(e)||(t=t.delete(e))})),a=a.set("allowedScopes",t));n=n.set(r,a)})),i=i.push(n)})),i},E=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:(0,g.List)();return e=>{let{authSelectors:n}=e;const r=n.definitionsToAuthorize()||(0,g.List)();let s=(0,g.List)();return o()(r).call(r,(e=>{let n=i()(t).call(t,(t=>t.get(e.keySeq().first())));n&&(o()(e).call(e,((t,r)=>{if("oauth2"===t.get("type")){const i=n.get(r);let a=t.get("scopes");var s;if(g.List.isList(i)&&g.Map.isMap(a))o()(s=a.keySeq()).call(s,(e=>{i.contains(e)||(a=a.delete(e))})),e=e.set(r,t.set("scopes",a))}})),s=s.push(e))})),s}},x=(0,m.P1)(y,(e=>e.get("authorized")||(0,g.Map)())),S=(e,t)=>e=>{var n;let{authSelectors:r}=e,o=r.authorized();return g.List.isList(t)?!!l()(n=t.toJS()).call(n,(e=>{var t,n;return-1===u()(t=h()(n=d()(e)).call(n,(e=>!!o.get(e)))).call(t,!1)})).length:null},_=(0,m.P1)(y,(e=>e.get("configs")))},60489:(e,t,n)=>{"use strict";n.r(t),n.d(t,{execute:()=>r});const r=(e,t)=>{let{authSelectors:n,specSelectors:r}=t;return t=>{let{path:o,method:s,operation:i,extras:a}=t,l={authorized:n.authorized()&&n.authorized().toJS(),definitions:r.securityDefinitions()&&r.securityDefinitions().toJS(),specSecurity:r.security()&&r.security().toJS()};return e({path:o,method:s,operation:i,securities:l,...a})}}},22849:(e,t,n)=>{"use strict";n.r(t),n.d(t,{authorize:()=>c,logout:()=>u});var r=n(3665),o=n.n(r),s=n(58309),i=n.n(s),a=n(86),l=n.n(a);const c=(e,t)=>n=>{e(n);if(t.getConfigs().persistAuthorization)try{const[{schema:e,value:t}]=o()(n),r="apiKey"===e.get("type"),s="cookie"===e.get("in");r&&s&&(document.cookie=`${e.get("name")}=${t}; SameSite=None; Secure`)}catch(e){console.error("Error persisting cookie based apiKey in document.cookie.",e)}},u=(e,t)=>n=>{const r=t.getConfigs(),o=t.authSelectors.authorized();try{r.persistAuthorization&&i()(n)&&l()(n).call(n,(e=>{const t=o.get(e,{}),n="apiKey"===t.getIn(["schema","type"]),r="cookie"===t.getIn(["schema","in"]);if(n&&r){const e=t.getIn(["schema","name"]);document.cookie=`${e}=; Max-Age=-99999999`}}))}catch(e){console.error("Error deleting cookie based apiKey from document.cookie.",e)}e(n)}},70714:(e,t,n)=>{"use strict";n.r(t),n.d(t,{TOGGLE_CONFIGS:()=>o,UPDATE_CONFIGS:()=>r,loaded:()=>a,toggle:()=>i,update:()=>s});const r="configs_update",o="configs_toggle";function s(e,t){return{type:r,payload:{[e]:t}}}function i(e){return{type:o,payload:e}}const a=()=>()=>{}},92256:(e,t,n)=>{"use strict";n.r(t),n.d(t,{parseYamlConfig:()=>o});var r=n(1272);const o=(e,t)=>{try{return r.ZP.load(e)}catch(e){return t&&t.errActions.newThrownErr(new Error(e)),{}}}},46709:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(92256),o=n(70714),s=n(22698),i=n(69018),a=n(37743);const l={getLocalConfig:()=>(0,r.parseYamlConfig)('---\nurl: "https://petstore.swagger.io/v2/swagger.json"\ndom_id: "#swagger-ui"\nvalidatorUrl: "https://validator.swagger.io/validator"\n')};function c(){return{statePlugins:{spec:{actions:s,selectors:l},configs:{reducers:a.default,actions:o,selectors:i}}}}},37743:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(43393),o=n(70714);const s={[o.UPDATE_CONFIGS]:(e,t)=>e.merge((0,r.fromJS)(t.payload)),[o.TOGGLE_CONFIGS]:(e,t)=>{const n=t.payload,r=e.get(n);return e.set(n,!r)}}},69018:(e,t,n)=>{"use strict";n.r(t),n.d(t,{get:()=>s});var r=n(58309),o=n.n(r);const s=(e,t)=>e.getIn(o()(t)?t:[t])},22698:(e,t,n)=>{"use strict";n.r(t),n.d(t,{downloadConfig:()=>o,getConfigByUrl:()=>s});var r=n(92256);const o=e=>t=>{const{fn:{fetch:n}}=t;return n(e)},s=(e,t)=>n=>{let{specActions:o}=n;if(e)return o.downloadConfig(e).then(s,s);function s(n){n instanceof Error||n.status>=400?(o.updateLoadingStatus("failedConfig"),o.updateLoadingStatus("failedConfig"),o.updateUrl(""),console.error(n.statusText+" "+e.url),t(null)):t((0,r.parseYamlConfig)(n.text))}}},31970:(e,t,n)=>{"use strict";n.r(t),n.d(t,{setHash:()=>r});const r=e=>e?history.pushState(null,null,`#${e}`):window.location.hash=""},34980:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(41599),o=n(60877),s=n(34584);function i(){return[r.default,{statePlugins:{configs:{wrapActions:{loaded:(e,t)=>function(){e(...arguments);const n=decodeURIComponent(window.location.hash);t.layoutActions.parseDeepLinkHash(n)}}}},wrapComponents:{operation:o.default,OperationTag:s.default}}]}},41599:(e,t,n)=>{"use strict";n.r(t),n.d(t,{clearScrollTo:()=>_,default:()=>j,parseDeepLinkHash:()=>E,readyToScroll:()=>x,scrollTo:()=>w,scrollToElement:()=>S,show:()=>b});var r=n(58309),o=n.n(r),s=n(24278),i=n.n(s),a=n(97606),l=n.n(a),c=n(11882),u=n.n(c),p=n(31970),h=n(45172),f=n.n(h),d=n(90242),m=n(43393),g=n.n(m);const y="layout_scroll_to",v="layout_clear_scroll",b=(e,t)=>{let{getConfigs:n,layoutSelectors:r}=t;return function(){for(var t=arguments.length,s=new Array(t),i=0;i({type:y,payload:o()(e)?e:[e]}),E=e=>t=>{let{layoutActions:n,layoutSelectors:r,getConfigs:o}=t;if(o().deepLinking&&e){var s;let t=i()(e).call(e,1);"!"===t[0]&&(t=i()(t).call(t,1)),"/"===t[0]&&(t=i()(t).call(t,1));const o=l()(s=t.split("/")).call(s,(e=>e||"")),a=r.isShownKeyFromUrlHashArray(o),[c,p="",h=""]=a;if("operations"===c){const e=r.isShownKeyFromUrlHashArray([p]);u()(p).call(p,"_")>-1&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),n.show(l()(e).call(e,(e=>e.replace(/_/g," "))),!0)),n.show(e,!0)}(u()(p).call(p,"_")>-1||u()(h).call(h,"_")>-1)&&(console.warn("Warning: escaping deep link whitespace with `_` will be unsupported in v4.0, use `%20` instead."),n.show(l()(a).call(a,(e=>e.replace(/_/g," "))),!0)),n.show(a,!0),n.scrollTo(a)}},x=(e,t)=>n=>{const r=n.layoutSelectors.getScrollToKey();g().is(r,(0,m.fromJS)(e))&&(n.layoutActions.scrollToElement(t),n.layoutActions.clearScrollTo())},S=(e,t)=>n=>{try{t=t||n.fn.getScrollParent(e),f().createScroller(t).to(e)}catch(e){console.error(e)}},_=()=>({type:v});const j={fn:{getScrollParent:function(e,t){const n=document.documentElement;let r=getComputedStyle(e);const o="absolute"===r.position,s=t?/(auto|scroll|hidden)/:/(auto|scroll)/;if("fixed"===r.position)return n;for(let t=e;t=t.parentElement;)if(r=getComputedStyle(t),(!o||"static"!==r.position)&&s.test(r.overflow+r.overflowY+r.overflowX))return t;return n}},statePlugins:{layout:{actions:{scrollToElement:S,scrollTo:w,clearScrollTo:_,readyToScroll:x,parseDeepLinkHash:E},selectors:{getScrollToKey:e=>e.get("scrollToKey"),isShownKeyFromUrlHashArray(e,t){const[n,r]=t;return r?["operations",n,r]:n?["operations-tag",n]:[]},urlHashArrayFromIsShownKey(e,t){let[n,r,o]=t;return"operations"==n?[r,o]:"operations-tag"==n?[r]:[]}},reducers:{[y]:(e,t)=>e.set("scrollToKey",g().fromJS(t.payload)),[v]:e=>e.delete("scrollToKey")},wrapActions:{show:b}}}}},34584:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(61125),o=n.n(r),s=n(67294);const i=(e,t)=>class extends s.Component{constructor(){super(...arguments),o()(this,"onLoad",(e=>{const{tag:n}=this.props,r=["operations-tag",n];t.layoutActions.readyToScroll(r,e)}))}render(){return s.createElement("span",{ref:this.onLoad},s.createElement(e,this.props))}}},60877:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(61125),o=n.n(r),s=n(67294);n(23930);const i=(e,t)=>class extends s.Component{constructor(){super(...arguments),o()(this,"onLoad",(e=>{const{operation:n}=this.props,{tag:r,operationId:o}=n.toObject();let{isShownKey:s}=n.toObject();s=s||["operations",r,o],t.layoutActions.readyToScroll(s,e)}))}render(){return s.createElement("span",{ref:this.onLoad},s.createElement(e,this.props))}}},48011:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d});var r=n(76986),o=n.n(r),s=n(63460),i=n.n(s),a=n(11882),l=n.n(a),c=n(35627),u=n.n(c),p=n(20573),h=n(43393),f=n(27504);function d(e){let{fn:t}=e;return{statePlugins:{spec:{actions:{download:e=>n=>{let{errActions:r,specSelectors:s,specActions:a,getConfigs:l}=n,{fetch:c}=t;const u=l();function p(t){if(t instanceof Error||t.status>=400)return a.updateLoadingStatus("failed"),r.newThrownErr(o()(new Error((t.message||t.statusText)+" "+e),{source:"fetch"})),void(!t.status&&t instanceof Error&&function(){try{let t;if("URL"in f.Z?t=new(i())(e):(t=document.createElement("a"),t.href=e),"https:"!==t.protocol&&"https:"===f.Z.location.protocol){const e=o()(new Error(`Possible mixed-content issue? The page was loaded over https:// but a ${t.protocol}// URL was specified. Check that you are not attempting to load mixed content.`),{source:"fetch"});return void r.newThrownErr(e)}if(t.origin!==f.Z.location.origin){const e=o()(new Error(`Possible cross-origin (CORS) issue? The URL origin (${t.origin}) does not match the page (${f.Z.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.`),{source:"fetch"});r.newThrownErr(e)}}catch(e){return}}());a.updateLoadingStatus("success"),a.updateSpec(t.text),s.url()!==e&&a.updateUrl(e)}e=e||s.url(),a.updateLoadingStatus("loading"),r.clear({source:"fetch"}),c({url:e,loadSpec:!0,requestInterceptor:u.requestInterceptor||(e=>e),responseInterceptor:u.responseInterceptor||(e=>e),credentials:"same-origin",headers:{Accept:"application/json,*/*"}}).then(p,p)},updateLoadingStatus:e=>{let t=[null,"loading","failed","success","failedConfig"];return-1===l()(t).call(t,e)&&console.error(`Error: ${e} is not one of ${u()(t)}`),{type:"spec_update_loading_status",payload:e}}},reducers:{spec_update_loading_status:(e,t)=>"string"==typeof t.payload?e.set("loadingStatus",t.payload):e},selectors:{loadingStatus:(0,p.P1)((e=>e||(0,h.Map)()),(e=>e.get("loadingStatus")||null))}}}}}},34966:(e,t,n)=>{"use strict";n.r(t),n.d(t,{CLEAR:()=>c,CLEAR_BY:()=>u,NEW_AUTH_ERR:()=>l,NEW_SPEC_ERR:()=>i,NEW_SPEC_ERR_BATCH:()=>a,NEW_THROWN_ERR:()=>o,NEW_THROWN_ERR_BATCH:()=>s,clear:()=>g,clearBy:()=>y,newAuthErr:()=>m,newSpecErr:()=>f,newSpecErrBatch:()=>d,newThrownErr:()=>p,newThrownErrBatch:()=>h});var r=n(7710);const o="err_new_thrown_err",s="err_new_thrown_err_batch",i="err_new_spec_err",a="err_new_spec_err_batch",l="err_new_auth_err",c="err_clear",u="err_clear_by";function p(e){return{type:o,payload:(0,r.serializeError)(e)}}function h(e){return{type:s,payload:e}}function f(e){return{type:i,payload:e}}function d(e){return{type:a,payload:e}}function m(e){return{type:l,payload:e}}function g(){return{type:c,payload:arguments.length>0&&void 0!==arguments[0]?arguments[0]:{}}}function y(){return{type:u,payload:arguments.length>0&&void 0!==arguments[0]?arguments[0]:()=>!0}}},56982:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(14418),o=n.n(r),s=n(97606),i=n.n(s),a=n(54061),l=n.n(a);const c=[n(2392),n(21835)];function u(e){var t;let n={jsSpec:{}},r=l()(c,((e,t)=>{try{let r=t.transform(e,n);return o()(r).call(r,(e=>!!e))}catch(t){return console.error("Transformer error:",t),e}}),e);return i()(t=o()(r).call(r,(e=>!!e))).call(t,(e=>(!e.get("line")&&e.get("path"),e)))}},2392:(e,t,n)=>{"use strict";n.r(t),n.d(t,{transform:()=>p});var r=n(97606),o=n.n(r),s=n(11882),i=n.n(s),a=n(24278),l=n.n(a),c=n(24282),u=n.n(c);function p(e){return o()(e).call(e,(e=>{var t;let n="is not of a type(s)",r=i()(t=e.get("message")).call(t,n);if(r>-1){var o,s;let t=l()(o=e.get("message")).call(o,r+19).split(",");return e.set("message",l()(s=e.get("message")).call(s,0,r)+function(e){return u()(e).call(e,((e,t,n,r)=>n===r.length-1&&r.length>1?e+"or "+t:r[n+1]&&r.length>2?e+t+", ":r[n+1]?e+t+" ":e+t),"should be a")}(t))}return e}))}},21835:(e,t,n)=>{"use strict";n.r(t),n.d(t,{transform:()=>r});n(97606),n(11882),n(27361),n(43393);function r(e,t){let{jsSpec:n}=t;return e}},77793:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(93527),o=n(34966),s=n(87667);function i(e){return{statePlugins:{err:{reducers:(0,r.default)(e),actions:o,selectors:s}}}}},93527:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>y});var r=n(76986),o=n.n(r),s=n(97606),i=n.n(s),a=n(39022),l=n.n(a),c=n(14418),u=n.n(c),p=n(2250),h=n.n(p),f=n(34966),d=n(43393),m=n(56982);let g={line:0,level:"error",message:"Unknown error"};function y(){return{[f.NEW_THROWN_ERR]:(e,t)=>{let{payload:n}=t,r=o()(g,n,{type:"thrown"});return e.update("errors",(e=>(e||(0,d.List)()).push((0,d.fromJS)(r)))).update("errors",(e=>(0,m.default)(e)))},[f.NEW_THROWN_ERR_BATCH]:(e,t)=>{let{payload:n}=t;return n=i()(n).call(n,(e=>(0,d.fromJS)(o()(g,e,{type:"thrown"})))),e.update("errors",(e=>{var t;return l()(t=e||(0,d.List)()).call(t,(0,d.fromJS)(n))})).update("errors",(e=>(0,m.default)(e)))},[f.NEW_SPEC_ERR]:(e,t)=>{let{payload:n}=t,r=(0,d.fromJS)(n);return r=r.set("type","spec"),e.update("errors",(e=>(e||(0,d.List)()).push((0,d.fromJS)(r)).sortBy((e=>e.get("line"))))).update("errors",(e=>(0,m.default)(e)))},[f.NEW_SPEC_ERR_BATCH]:(e,t)=>{let{payload:n}=t;return n=i()(n).call(n,(e=>(0,d.fromJS)(o()(g,e,{type:"spec"})))),e.update("errors",(e=>{var t;return l()(t=e||(0,d.List)()).call(t,(0,d.fromJS)(n))})).update("errors",(e=>(0,m.default)(e)))},[f.NEW_AUTH_ERR]:(e,t)=>{let{payload:n}=t,r=(0,d.fromJS)(o()({},n));return r=r.set("type","auth"),e.update("errors",(e=>(e||(0,d.List)()).push((0,d.fromJS)(r)))).update("errors",(e=>(0,m.default)(e)))},[f.CLEAR]:(e,t)=>{var n;let{payload:r}=t;if(!r||!e.get("errors"))return e;let o=u()(n=e.get("errors")).call(n,(e=>{var t;return h()(t=e.keySeq()).call(t,(t=>{const n=e.get(t),o=r[t];return!o||n!==o}))}));return e.merge({errors:o})},[f.CLEAR_BY]:(e,t)=>{var n;let{payload:r}=t;if(!r||"function"!=typeof r)return e;let o=u()(n=e.get("errors")).call(n,(e=>r(e)));return e.merge({errors:o})}}}},87667:(e,t,n)=>{"use strict";n.r(t),n.d(t,{allErrors:()=>s,lastError:()=>i});var r=n(43393),o=n(20573);const s=(0,o.P1)((e=>e),(e=>e.get("errors",(0,r.List)()))),i=(0,o.P1)(s,(e=>e.last()))},49978:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(4309);function o(){return{fn:{opsFilter:r.default}}}},4309:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(14418),o=n.n(r),s=n(11882),i=n.n(s);function a(e,t){return o()(e).call(e,((e,n)=>-1!==i()(n).call(n,t)))}},26395:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("path",{d:"M17.418 6.109c.272-.268.709-.268.979 0s.271.701 0 .969l-7.908 7.83c-.27.268-.707.268-.979 0l-7.908-7.83c-.27-.268-.27-.701 0-.969.271-.268.709-.268.979 0L10 13.25l7.418-7.141z"}))};i.defaultProps={className:null,width:20,height:20};const a=i},39689:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("path",{d:"M 17.418 14.908 C 17.69 15.176 18.127 15.176 18.397 14.908 C 18.667 14.64 18.668 14.207 18.397 13.939 L 10.489 6.109 C 10.219 5.841 9.782 5.841 9.51 6.109 L 1.602 13.939 C 1.332 14.207 1.332 14.64 1.602 14.908 C 1.873 15.176 2.311 15.176 2.581 14.908 L 10 7.767 L 17.418 14.908 Z"}))};i.defaultProps={className:null,width:20,height:20};const a=i},86984:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("path",{d:"M13.25 10L6.109 2.58c-.268-.27-.268-.707 0-.979.268-.27.701-.27.969 0l7.83 7.908c.268.271.268.709 0 .979l-7.83 7.908c-.268.271-.701.27-.969 0-.268-.269-.268-.707 0-.979L13.25 10z"}))};i.defaultProps={className:null,width:20,height:20};const a=i},22478:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("path",{d:"M14.348 14.849c-.469.469-1.229.469-1.697 0L10 11.819l-2.651 3.029c-.469.469-1.229.469-1.697 0-.469-.469-.469-1.229 0-1.697l2.758-3.15-2.759-3.152c-.469-.469-.469-1.228 0-1.697.469-.469 1.228-.469 1.697 0L10 8.183l2.651-3.031c.469-.469 1.228-.469 1.697 0 .469.469.469 1.229 0 1.697l-2.758 3.152 2.758 3.15c.469.469.469 1.229 0 1.698z"}))};i.defaultProps={className:null,width:20,height:20};const a=i},93388:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 15 16",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("g",{transform:"translate(2, -1)"},s.createElement("path",{fill:"#ffffff",fillRule:"evenodd",d:"M2 13h4v1H2v-1zm5-6H2v1h5V7zm2 3V8l-3 3 3 3v-2h5v-2H9zM4.5 9H2v1h2.5V9zM2 12h2.5v-1H2v1zm9 1h1v2c-.02.28-.11.52-.3.7-.19.18-.42.28-.7.3H1c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h3c0-1.11.89-2 2-2 1.11 0 2 .89 2 2h3c.55 0 1 .45 1 1v5h-1V6H1v9h10v-2zM2 5h8c0-.55-.45-1-1-1H8c-.55 0-1-.45-1-1s-.45-1-1-1-1 .45-1 1-.45 1-1 1H3c-.55 0-1 .45-1 1z"})))};i.defaultProps={className:null,width:15,height:16};const a=i},56945:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("path",{d:"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8zM12 8H8V5.199C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8z"}))};i.defaultProps={className:null,width:20,height:20};const a=i},22568:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=e=>{let{className:t,width:n,height:r,...i}=e;return s.createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 20 20",className:t,width:n,height:r,"aria-hidden":"true",focusable:"false"},i),s.createElement("path",{d:"M15.8 8H14V5.6C14 2.703 12.665 1 10 1 7.334 1 6 2.703 6 5.6V6h2v-.801C8 3.754 8.797 3 10 3c1.203 0 2 .754 2 2.199V8H4c-.553 0-1 .646-1 1.199V17c0 .549.428 1.139.951 1.307l1.197.387C5.672 18.861 6.55 19 7.1 19h5.8c.549 0 1.428-.139 1.951-.307l1.196-.387c.524-.167.953-.757.953-1.306V9.199C17 8.646 16.352 8 15.8 8z"}))};i.defaultProps={className:null,width:20,height:20};const a=i},10070:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(39689),o=n(26395),s=n(86984),i=n(22478),a=n(93388),l=n(56945),c=n(22568);const u=()=>({components:{ArrowUpIcon:r.default,ArrowDownIcon:o.default,ArrowIcon:s.default,CloseIcon:i.default,CopyIcon:a.default,LockIcon:l.default,UnlockIcon:c.default}})},47349:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(67294),o=n(94184),s=n.n(o),i=n(12603);const a=e=>{let{expanded:t,children:n,onChange:o}=e;const a=(0,i.useComponent)("ChevronRightIcon"),l=(0,r.useCallback)((e=>{o(e,!t)}),[t,o]);return r.createElement("button",{type:"button",className:"json-schema-2020-12-accordion",onClick:l},r.createElement("div",{className:"json-schema-2020-12-accordion__children"},n),r.createElement("span",{className:s()("json-schema-2020-12-accordion__icon",{"json-schema-2020-12-accordion__icon--expanded":t,"json-schema-2020-12-accordion__icon--collapsed":!t})},r.createElement(a,null)))};a.defaultProps={expanded:!1};const l=a},36867:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=e=>{let{expanded:t,onClick:n}=e;const o=(0,r.useCallback)((e=>{n(e,!t)}),[t,n]);return r.createElement("button",{type:"button",className:"json-schema-2020-12-expand-deep-button",onClick:o},t?"Collapse all":"Expand all")}},22675:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(97606),o=n.n(r),s=n(67294),i=n(94184),a=n.n(i),l=(n(16648),n(12603)),c=n(69006);const u=(0,s.forwardRef)(((e,t)=>{let{schema:n,name:r,dependentRequired:i,onExpand:u}=e;const p=(0,l.useFn)(),h=(0,l.useIsExpanded)(),f=(0,l.useIsExpandedDeeply)(),[d,m]=(0,s.useState)(h||f),[g,y]=(0,s.useState)(f),[v,b]=(0,l.useLevel)(),w=(0,l.useIsEmbedded)(),E=p.isExpandable(n)||i.length>0,x=(0,l.useIsCircular)(n),S=(0,l.useRenderedSchemas)(n),_=p.stringifyConstraints(n),j=(0,l.useComponent)("Accordion"),O=(0,l.useComponent)("Keyword$schema"),k=(0,l.useComponent)("Keyword$vocabulary"),A=(0,l.useComponent)("Keyword$id"),C=(0,l.useComponent)("Keyword$anchor"),P=(0,l.useComponent)("Keyword$dynamicAnchor"),N=(0,l.useComponent)("Keyword$ref"),I=(0,l.useComponent)("Keyword$dynamicRef"),T=(0,l.useComponent)("Keyword$defs"),R=(0,l.useComponent)("Keyword$comment"),M=(0,l.useComponent)("KeywordAllOf"),D=(0,l.useComponent)("KeywordAnyOf"),F=(0,l.useComponent)("KeywordOneOf"),L=(0,l.useComponent)("KeywordNot"),B=(0,l.useComponent)("KeywordIf"),$=(0,l.useComponent)("KeywordThen"),q=(0,l.useComponent)("KeywordElse"),U=(0,l.useComponent)("KeywordDependentSchemas"),z=(0,l.useComponent)("KeywordPrefixItems"),V=(0,l.useComponent)("KeywordItems"),W=(0,l.useComponent)("KeywordContains"),J=(0,l.useComponent)("KeywordProperties"),K=(0,l.useComponent)("KeywordPatternProperties"),H=(0,l.useComponent)("KeywordAdditionalProperties"),G=(0,l.useComponent)("KeywordPropertyNames"),Z=(0,l.useComponent)("KeywordUnevaluatedItems"),Y=(0,l.useComponent)("KeywordUnevaluatedProperties"),X=(0,l.useComponent)("KeywordType"),Q=(0,l.useComponent)("KeywordEnum"),ee=(0,l.useComponent)("KeywordConst"),te=(0,l.useComponent)("KeywordConstraint"),ne=(0,l.useComponent)("KeywordDependentRequired"),re=(0,l.useComponent)("KeywordContentSchema"),oe=(0,l.useComponent)("KeywordTitle"),se=(0,l.useComponent)("KeywordDescription"),ie=(0,l.useComponent)("KeywordDefault"),ae=(0,l.useComponent)("KeywordDeprecated"),le=(0,l.useComponent)("KeywordReadOnly"),ce=(0,l.useComponent)("KeywordWriteOnly"),ue=(0,l.useComponent)("ExpandDeepButton");(0,s.useEffect)((()=>{y(f)}),[f]),(0,s.useEffect)((()=>{y(g)}),[g]);const pe=(0,s.useCallback)(((e,t)=>{m(t),!t&&y(!1),u(e,t,!1)}),[u]),he=(0,s.useCallback)(((e,t)=>{m(t),y(t),u(e,t,!0)}),[u]);return s.createElement(c.JSONSchemaLevelContext.Provider,{value:b},s.createElement(c.JSONSchemaDeepExpansionContext.Provider,{value:g},s.createElement(c.JSONSchemaCyclesContext.Provider,{value:S},s.createElement("article",{ref:t,"data-json-schema-level":v,className:a()("json-schema-2020-12",{"json-schema-2020-12--embedded":w,"json-schema-2020-12--circular":x})},s.createElement("div",{className:"json-schema-2020-12-head"},E&&!x?s.createElement(s.Fragment,null,s.createElement(j,{expanded:d,onChange:pe},s.createElement(oe,{title:r,schema:n})),s.createElement(ue,{expanded:d,onClick:he})):s.createElement(oe,{title:r,schema:n}),s.createElement(ae,{schema:n}),s.createElement(le,{schema:n}),s.createElement(ce,{schema:n}),s.createElement(X,{schema:n,isCircular:x}),_.length>0&&o()(_).call(_,(e=>s.createElement(te,{key:`${e.scope}-${e.value}`,constraint:e})))),s.createElement("div",{className:a()("json-schema-2020-12-body",{"json-schema-2020-12-body--collapsed":!d})},d&&s.createElement(s.Fragment,null,s.createElement(se,{schema:n}),!x&&E&&s.createElement(s.Fragment,null,s.createElement(J,{schema:n}),s.createElement(K,{schema:n}),s.createElement(H,{schema:n}),s.createElement(Y,{schema:n}),s.createElement(G,{schema:n}),s.createElement(M,{schema:n}),s.createElement(D,{schema:n}),s.createElement(F,{schema:n}),s.createElement(L,{schema:n}),s.createElement(B,{schema:n}),s.createElement($,{schema:n}),s.createElement(q,{schema:n}),s.createElement(U,{schema:n}),s.createElement(z,{schema:n}),s.createElement(V,{schema:n}),s.createElement(Z,{schema:n}),s.createElement(W,{schema:n}),s.createElement(re,{schema:n})),s.createElement(Q,{schema:n}),s.createElement(ee,{schema:n}),s.createElement(ne,{schema:n,dependentRequired:i}),s.createElement(ie,{schema:n}),s.createElement(O,{schema:n}),s.createElement(k,{schema:n}),s.createElement(A,{schema:n}),s.createElement(C,{schema:n}),s.createElement(P,{schema:n}),s.createElement(N,{schema:n}),!x&&E&&s.createElement(T,{schema:n}),s.createElement(I,{schema:n}),s.createElement(R,{schema:n})))))))}));u.defaultProps={name:"",dependentRequired:[],onExpand:()=>{}};const p=u},12260:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=()=>r.createElement("svg",{xmlns:"http://www.w3.org/2000/svg",width:"24",height:"24",viewBox:"0 0 24 24"},r.createElement("path",{d:"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"}))},64922:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$anchor?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$anchor"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$anchor"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$anchor)):null}},4685:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$comment?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$comment"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$comment"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$comment)):null}},36418:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(2018),l=n.n(a),c=n(67294),u=n(94184),p=n.n(u),h=(n(16648),n(12603)),f=n(69006);const d=e=>{var t;let{schema:n}=e;const r=(null==n?void 0:n.$defs)||{},s=(0,h.useIsExpandedDeeply)(),[a,u]=(0,c.useState)(s),[d,m]=(0,c.useState)(!1),g=(0,h.useComponent)("Accordion"),y=(0,h.useComponent)("ExpandDeepButton"),v=(0,h.useComponent)("JSONSchema"),b=(0,c.useCallback)((()=>{u((e=>!e))}),[]),w=(0,c.useCallback)(((e,t)=>{u(t),m(t)}),[]);return 0===o()(r).length?null:c.createElement(f.JSONSchemaDeepExpansionContext.Provider,{value:d},c.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$defs"},c.createElement(g,{expanded:a,onChange:b},c.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$defs")),c.createElement(y,{expanded:a,onClick:w}),c.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),c.createElement("ul",{className:p()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!a})},a&&c.createElement(c.Fragment,null,i()(t=l()(r)).call(t,(e=>{let[t,n]=e;return c.createElement("li",{key:t,className:"json-schema-2020-12-property"},c.createElement(v,{name:t,schema:n}))}))))))}},51338:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$dynamicAnchor?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$dynamicAnchor"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$dynamicAnchor"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$dynamicAnchor)):null}},27655:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$dynamicRef?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$dynamicRef"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$dynamicRef"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$dynamicRef)):null}},93460:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$id?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$id"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$id"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$id)):null}},72348:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$ref?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$ref"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$ref"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$ref)):null}},69359:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.$schema?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$schema"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$schema"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t.$schema)):null}},7568:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(97606),o=n.n(r),s=n(2018),i=n.n(s),a=n(67294),l=n(94184),c=n.n(l),u=(n(16648),n(12603));const p=e=>{var t;let{schema:n}=e;const r=(0,u.useIsExpandedDeeply)(),[s,l]=(0,a.useState)(r),p=(0,u.useComponent)("Accordion"),h=(0,a.useCallback)((()=>{l((e=>!e))}),[]);return null!=n&&n.$vocabulary?"object"!=typeof n.$vocabulary?null:a.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--$vocabulary"},a.createElement(p,{expanded:s,onChange:h},a.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"$vocabulary")),a.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),a.createElement("ul",null,s&&o()(t=i()(n.$vocabulary)).call(t,(e=>{let[t,n]=e;return a.createElement("li",{key:t,className:c()("json-schema-2020-12-$vocabulary-uri",{"json-schema-2020-12-$vocabulary-uri--disabled":!n})},a.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},t))})))):null}},65253:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),{additionalProperties:s}=t,i=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"additionalProperties"))return null;const a=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Additional properties");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--additionalProperties"},!0===s?r.createElement(r.Fragment,null,a,r.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"allowed")):!1===s?r.createElement(r.Fragment,null,a,r.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"forbidden")):r.createElement(i,{name:a,schema:s}))}},46457:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294),l=n(94184),c=n.n(l),u=(n(16648),n(12603)),p=n(69006);const h=e=>{let{schema:t}=e;const n=(null==t?void 0:t.allOf)||[],r=(0,u.useFn)(),s=(0,u.useIsExpandedDeeply)(),[l,h]=(0,a.useState)(s),[f,d]=(0,a.useState)(!1),m=(0,u.useComponent)("Accordion"),g=(0,u.useComponent)("ExpandDeepButton"),y=(0,u.useComponent)("JSONSchema"),v=(0,u.useComponent)("KeywordType"),b=(0,a.useCallback)((()=>{h((e=>!e))}),[]),w=(0,a.useCallback)(((e,t)=>{h(t),d(t)}),[]);return o()(n)&&0!==n.length?a.createElement(p.JSONSchemaDeepExpansionContext.Provider,{value:f},a.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--allOf"},a.createElement(m,{expanded:l,onChange:b},a.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"All of")),a.createElement(g,{expanded:l,onClick:w}),a.createElement(v,{schema:{allOf:n}}),a.createElement("ul",{className:c()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!l})},l&&a.createElement(a.Fragment,null,i()(n).call(n,((e,t)=>a.createElement("li",{key:`#${t}`,className:"json-schema-2020-12-property"},a.createElement(y,{name:`#${t} ${r.getTitle(e)}`,schema:e})))))))):null}},8776:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294),l=n(94184),c=n.n(l),u=(n(16648),n(12603)),p=n(69006);const h=e=>{let{schema:t}=e;const n=(null==t?void 0:t.anyOf)||[],r=(0,u.useFn)(),s=(0,u.useIsExpandedDeeply)(),[l,h]=(0,a.useState)(s),[f,d]=(0,a.useState)(!1),m=(0,u.useComponent)("Accordion"),g=(0,u.useComponent)("ExpandDeepButton"),y=(0,u.useComponent)("JSONSchema"),v=(0,u.useComponent)("KeywordType"),b=(0,a.useCallback)((()=>{h((e=>!e))}),[]),w=(0,a.useCallback)(((e,t)=>{h(t),d(t)}),[]);return o()(n)&&0!==n.length?a.createElement(p.JSONSchemaDeepExpansionContext.Provider,{value:f},a.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--anyOf"},a.createElement(m,{expanded:l,onChange:b},a.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Any of")),a.createElement(g,{expanded:l,onClick:w}),a.createElement(v,{schema:{anyOf:n}}),a.createElement("ul",{className:c()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!l})},l&&a.createElement(a.Fragment,null,i()(n).call(n,((e,t)=>a.createElement("li",{key:`#${t}`,className:"json-schema-2020-12-property"},a.createElement(y,{name:`#${t} ${r.getTitle(e)}`,schema:e})))))))):null}},27308:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)();return n.hasKeyword(t,"const")?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--const"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Const"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--const"},n.stringify(t.const))):null}},69956:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294);const o=e=>{let{constraint:t}=e;return r.createElement("span",{className:`json-schema-2020-12__constraint json-schema-2020-12__constraint--${t.scope}`},t.value)},s=r.memo(o)},38993:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"contains"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Contains");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--contains"},r.createElement(s,{name:i,schema:t.contains}))}},3484:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"contentSchema"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Content schema");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--contentSchema"},r.createElement(s,{name:i,schema:t.contentSchema}))}},55148:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)();return n.hasKeyword(t,"default")?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--default"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Default"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--const"},n.stringify(t.default))):null}},24539:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(97606),o=n.n(r),s=n(67294);n(16648);const i=e=>{let{dependentRequired:t}=e;return 0===t.length?null:s.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--dependentRequired"},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Required when defined"),s.createElement("ul",null,o()(t).call(t,(e=>s.createElement("li",{key:e},s.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--warning"},e))))))}},26076:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(2018),l=n.n(a),c=n(67294),u=n(94184),p=n.n(u),h=(n(16648),n(12603)),f=n(69006);const d=e=>{var t;let{schema:n}=e;const r=(null==n?void 0:n.dependentSchemas)||[],s=(0,h.useIsExpandedDeeply)(),[a,u]=(0,c.useState)(s),[d,m]=(0,c.useState)(!1),g=(0,h.useComponent)("Accordion"),y=(0,h.useComponent)("ExpandDeepButton"),v=(0,h.useComponent)("JSONSchema"),b=(0,c.useCallback)((()=>{u((e=>!e))}),[]),w=(0,c.useCallback)(((e,t)=>{u(t),m(t)}),[]);return"object"!=typeof r||0===o()(r).length?null:c.createElement(f.JSONSchemaDeepExpansionContext.Provider,{value:d},c.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--dependentSchemas"},c.createElement(g,{expanded:a,onChange:b},c.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Dependent schemas")),c.createElement(y,{expanded:a,onClick:w}),c.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),c.createElement("ul",{className:p()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!a})},a&&c.createElement(c.Fragment,null,i()(t=l()(r)).call(t,(e=>{let[t,n]=e;return c.createElement("li",{key:t,className:"json-schema-2020-12-property"},c.createElement(v,{name:t,schema:n}))}))))))}},26661:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return!0!==(null==t?void 0:t.deprecated)?null:r.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--warning"},"deprecated")}},79446:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return null!=t&&t.description?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},r.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},t.description)):null}},67207:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"else"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Else");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--if"},r.createElement(s,{name:i,schema:t.else}))}},91805:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294),l=(n(16648),n(12603));const c=e=>{var t;let{schema:n}=e;const r=(0,l.useFn)();return o()(null==n?void 0:n.enum)?a.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--enum"},a.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Allowed values"),a.createElement("ul",null,i()(t=n.enum).call(t,(e=>{const t=r.stringify(e);return a.createElement("li",{key:t},a.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--const"},t))})))):null}},40487:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"if"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"If");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--if"},r.createElement(s,{name:i,schema:t.if}))}},89206:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"items"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Items");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--items"},r.createElement(s,{name:i,schema:t.items}))}},65174:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"not"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Not");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--not"},r.createElement(s,{name:i,schema:t.not}))}},13834:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294),l=n(94184),c=n.n(l),u=(n(16648),n(12603)),p=n(69006);const h=e=>{let{schema:t}=e;const n=(null==t?void 0:t.oneOf)||[],r=(0,u.useFn)(),s=(0,u.useIsExpandedDeeply)(),[l,h]=(0,a.useState)(s),[f,d]=(0,a.useState)(!1),m=(0,u.useComponent)("Accordion"),g=(0,u.useComponent)("ExpandDeepButton"),y=(0,u.useComponent)("JSONSchema"),v=(0,u.useComponent)("KeywordType"),b=(0,a.useCallback)((()=>{h((e=>!e))}),[]),w=(0,a.useCallback)(((e,t)=>{h(t),d(t)}),[]);return o()(n)&&0!==n.length?a.createElement(p.JSONSchemaDeepExpansionContext.Provider,{value:f},a.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--oneOf"},a.createElement(m,{expanded:l,onChange:b},a.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"One of")),a.createElement(g,{expanded:l,onClick:w}),a.createElement(v,{schema:{oneOf:n}}),a.createElement("ul",{className:c()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!l})},l&&a.createElement(a.Fragment,null,i()(n).call(n,((e,t)=>a.createElement("li",{key:`#${t}`,className:"json-schema-2020-12-property"},a.createElement(y,{name:`#${t} ${r.getTitle(e)}`,schema:e})))))))):null}},36746:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(2018),l=n.n(a),c=n(67294),u=(n(16648),n(12603));const p=e=>{var t;let{schema:n}=e;const r=(null==n?void 0:n.patternProperties)||{},s=(0,u.useComponent)("JSONSchema");return 0===o()(r).length?null:c.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--patternProperties"},c.createElement("ul",null,i()(t=l()(r)).call(t,(e=>{let[t,n]=e;return c.createElement("li",{key:t,className:"json-schema-2020-12-property"},c.createElement(s,{name:t,schema:n}))}))))}},93971:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294),l=n(94184),c=n.n(l),u=(n(16648),n(12603)),p=n(69006);const h=e=>{let{schema:t}=e;const n=(null==t?void 0:t.prefixItems)||[],r=(0,u.useFn)(),s=(0,u.useIsExpandedDeeply)(),[l,h]=(0,a.useState)(s),[f,d]=(0,a.useState)(!1),m=(0,u.useComponent)("Accordion"),g=(0,u.useComponent)("ExpandDeepButton"),y=(0,u.useComponent)("JSONSchema"),v=(0,u.useComponent)("KeywordType"),b=(0,a.useCallback)((()=>{h((e=>!e))}),[]),w=(0,a.useCallback)(((e,t)=>{h(t),d(t)}),[]);return o()(n)&&0!==n.length?a.createElement(p.JSONSchemaDeepExpansionContext.Provider,{value:f},a.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--prefixItems"},a.createElement(m,{expanded:l,onChange:b},a.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Prefix items")),a.createElement(g,{expanded:l,onClick:w}),a.createElement(v,{schema:{prefixItems:n}}),a.createElement("ul",{className:c()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!l})},l&&a.createElement(a.Fragment,null,i()(n).call(n,((e,t)=>a.createElement("li",{key:`#${t}`,className:"json-schema-2020-12-property"},a.createElement(y,{name:`#${t} ${r.getTitle(e)}`,schema:e})))))))):null}},25472:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>y});var r=n(58309),o=n.n(r),s=n(28222),i=n.n(s),a=n(97606),l=n.n(a),c=n(2018),u=n.n(c),p=n(58118),h=n.n(p),f=n(67294),d=n(94184),m=n.n(d),g=(n(16648),n(12603));const y=e=>{var t;let{schema:n}=e;const r=(0,g.useFn)(),s=(null==n?void 0:n.properties)||{},a=o()(null==n?void 0:n.required)?n.required:[],c=(0,g.useComponent)("JSONSchema");return 0===i()(s).length?null:f.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},f.createElement("ul",null,l()(t=u()(s)).call(t,(e=>{let[t,o]=e;const s=h()(a).call(a,t),i=r.getDependentRequired(t,n);return f.createElement("li",{key:t,className:m()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":s})},f.createElement(c,{name:t,schema:o,dependentRequired:i}))}))))}},42338:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),{propertyNames:s}=t,i=(0,o.useComponent)("JSONSchema"),a=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Property names");return n.hasKeyword(t,"propertyNames")?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--propertyNames"},r.createElement(i,{name:a,schema:s})):null}},16456:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return!0!==(null==t?void 0:t.readOnly)?null:r.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"read-only")}},67401:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),s=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"then"))return null;const i=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Then");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--then"},r.createElement(s,{name:i,schema:t.then}))}},78137:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{title:t,schema:n}=e;const s=(0,o.useFn)();return t||s.getTitle(n)?r.createElement("div",{className:"json-schema-2020-12__title"},t||s.getTitle(n)):null};s.defaultProps={title:""};const i=s},22285:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t,isCircular:n}=e;const s=(0,o.useFn)().getType(t),i=n?" [circular]":"";return r.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},`${s}${i}`)};s.defaultProps={isCircular:!1};const i=s},85828:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),{unevaluatedItems:s}=t,i=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"unevaluatedItems"))return null;const a=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Unevaluated items");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--unevaluatedItems"},r.createElement(i,{name:a,schema:s}))}},6907:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=(n(16648),n(12603));const s=e=>{let{schema:t}=e;const n=(0,o.useFn)(),{unevaluatedProperties:s}=t,i=(0,o.useComponent)("JSONSchema");if(!n.hasKeyword(t,"unevaluatedProperties"))return null;const a=r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--primary"},"Unevaluated properties");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--unevaluatedProperties"},r.createElement(i,{name:a,schema:s}))}},15789:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);n(16648);const o=e=>{let{schema:t}=e;return!0!==(null==t?void 0:t.writeOnly)?null:r.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"write-only")}},69006:(e,t,n)=>{"use strict";n.r(t),n.d(t,{JSONSchemaContext:()=>i,JSONSchemaCyclesContext:()=>c,JSONSchemaDeepExpansionContext:()=>l,JSONSchemaLevelContext:()=>a});var r=n(82737),o=n.n(r),s=n(67294);const i=(0,s.createContext)(null);i.displayName="JSONSchemaContext";const a=(0,s.createContext)(0);a.displayName="JSONSchemaLevelContext";const l=(0,s.createContext)(!1);l.displayName="JSONSchemaDeepExpansionContext";const c=(0,s.createContext)(new(o()))},33499:(e,t,n)=>{"use strict";n.r(t),n.d(t,{getDependentRequired:()=>F,getTitle:()=>C,getType:()=>P,hasKeyword:()=>I,isBooleanJSONSchema:()=>N,isExpandable:()=>T,stringify:()=>R,stringifyConstraints:()=>D,upperFirst:()=>A});var r=n(24278),o=n.n(r),s=n(19030),i=n.n(s),a=n(58309),l=n.n(a),c=n(97606),u=n.n(c),p=n(58118),h=n.n(p),f=n(91086),d=n.n(f),m=n(14418),g=n.n(m),y=n(35627),v=n.n(y),b=n(25110),w=n.n(b),E=n(24282),x=n.n(E),S=n(2018),_=n.n(S),j=n(82737),O=n.n(j),k=n(12603);const A=e=>"string"==typeof e?`${e.charAt(0).toUpperCase()}${o()(e).call(e,1)}`:e,C=e=>{const t=(0,k.useFn)();return null!=e&&e.title?t.upperFirst(e.title):null!=e&&e.$anchor?t.upperFirst(e.$anchor):null!=e&&e.$id?e.$id:""},P=function(e){var t,n;let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:new(i());const o=(0,k.useFn)();if(null==e)return"any";if(o.isBooleanJSONSchema(e))return e?"any":"never";if("object"!=typeof e)return"any";if(r.has(e))return"any";r.add(e);const{type:s,prefixItems:a,items:c}=e,p=()=>{if(l()(a)){const e=u()(a).call(a,(e=>P(e,r))),t=c?P(c,r):"any";return`array<[${e.join(", ")}], ${t}>`}if(c){return`array<${P(c,r)}>`}return"array"};if(e.not&&"any"===P(e.not))return"never";const f=l()(s)?u()(s).call(s,(e=>"array"===e?p():e)).join(" | "):"array"===s?p():h()(t=["null","boolean","object","array","number","integer","string"]).call(t,s)?s:(()=>{var t,n;if(Object.hasOwn(e,"prefixItems")||Object.hasOwn(e,"items")||Object.hasOwn(e,"contains"))return p();if(Object.hasOwn(e,"properties")||Object.hasOwn(e,"additionalProperties")||Object.hasOwn(e,"patternProperties"))return"object";if(h()(t=["int32","int64"]).call(t,e.format))return"integer";if(h()(n=["float","double"]).call(n,e.format))return"number";if(Object.hasOwn(e,"minimum")||Object.hasOwn(e,"maximum")||Object.hasOwn(e,"exclusiveMinimum")||Object.hasOwn(e,"exclusiveMaximum")||Object.hasOwn(e,"multipleOf"))return"number | integer";if(Object.hasOwn(e,"pattern")||Object.hasOwn(e,"format")||Object.hasOwn(e,"minLength")||Object.hasOwn(e,"maxLength"))return"string";if(void 0!==e.const){if(null===e.const)return"null";if("boolean"==typeof e.const)return"boolean";if("number"==typeof e.const)return d()(e.const)?"integer":"number";if("string"==typeof e.const)return"string";if(l()(e.const))return"array";if("object"==typeof e.const)return"object"}return null})(),m=(t,n)=>{if(l()(e[t])){var o;return`(${u()(o=e[t]).call(o,(e=>P(e,r))).join(n)})`}return null},y=m("oneOf"," | "),v=m("anyOf"," | "),b=m("allOf"," & "),w=g()(n=[f,y,v,b]).call(n,Boolean).join(" | ");return r.delete(e),w||"any"},N=e=>"boolean"==typeof e,I=(e,t)=>null!==e&&"object"==typeof e&&Object.hasOwn(e,t),T=e=>{const t=(0,k.useFn)();return(null==e?void 0:e.$schema)||(null==e?void 0:e.$vocabulary)||(null==e?void 0:e.$id)||(null==e?void 0:e.$anchor)||(null==e?void 0:e.$dynamicAnchor)||(null==e?void 0:e.$ref)||(null==e?void 0:e.$dynamicRef)||(null==e?void 0:e.$defs)||(null==e?void 0:e.$comment)||(null==e?void 0:e.allOf)||(null==e?void 0:e.anyOf)||(null==e?void 0:e.oneOf)||t.hasKeyword(e,"not")||t.hasKeyword(e,"if")||t.hasKeyword(e,"then")||t.hasKeyword(e,"else")||(null==e?void 0:e.dependentSchemas)||(null==e?void 0:e.prefixItems)||t.hasKeyword(e,"items")||t.hasKeyword(e,"contains")||(null==e?void 0:e.properties)||(null==e?void 0:e.patternProperties)||t.hasKeyword(e,"additionalProperties")||t.hasKeyword(e,"propertyNames")||t.hasKeyword(e,"unevaluatedItems")||t.hasKeyword(e,"unevaluatedProperties")||(null==e?void 0:e.description)||(null==e?void 0:e.enum)||t.hasKeyword(e,"const")||t.hasKeyword(e,"contentSchema")||t.hasKeyword(e,"default")},R=e=>{var t;return null===e||h()(t=["number","bigint","boolean"]).call(t,typeof e)?String(e):l()(e)?`[${u()(e).call(e,R).join(", ")}]`:v()(e)},M=(e,t,n)=>{const r="number"==typeof t,o="number"==typeof n;return r&&o?t===n?`${t} ${e}`:`[${t}, ${n}] ${e}`:r?`>= ${t} ${e}`:o?`<= ${n} ${e}`:null},D=e=>{const t=[],n=(e=>{if("number"!=typeof(null==e?void 0:e.multipleOf))return null;if(e.multipleOf<=0)return null;if(1===e.multipleOf)return null;const{multipleOf:t}=e;if(d()(t))return`multiple of ${t}`;const n=10**t.toString().split(".")[1].length;return`multiple of ${t*n}/${n}`})(e);null!==n&&t.push({scope:"number",value:n});const r=(e=>{const t=null==e?void 0:e.minimum,n=null==e?void 0:e.maximum,r=null==e?void 0:e.exclusiveMinimum,o=null==e?void 0:e.exclusiveMaximum,s="number"==typeof t,i="number"==typeof n,a="number"==typeof r,l="number"==typeof o,c=a&&(!s||to);if((s||a)&&(i||l))return`${c?"(":"["}${c?r:t}, ${u?o:n}${u?")":"]"}`;if(s||a)return`${c?">":"≥"} ${c?r:t}`;if(i||l)return`${u?"<":"≤"} ${u?o:n}`;return null})(e);null!==r&&t.push({scope:"number",value:r}),null!=e&&e.format&&t.push({scope:"string",value:e.format});const o=M("characters",null==e?void 0:e.minLength,null==e?void 0:e.maxLength);null!==o&&t.push({scope:"string",value:o}),null!=e&&e.pattern&&t.push({scope:"string",value:`matches ${null==e?void 0:e.pattern}`}),null!=e&&e.contentMediaType&&t.push({scope:"string",value:`media type: ${e.contentMediaType}`}),null!=e&&e.contentEncoding&&t.push({scope:"string",value:`encoding: ${e.contentEncoding}`});const s=M(null!=e&&e.hasUniqueItems?"unique items":"items",null==e?void 0:e.minItems,null==e?void 0:e.maxItems);null!==s&&t.push({scope:"array",value:s});const i=M("contained items",null==e?void 0:e.minContains,null==e?void 0:e.maxContains);null!==i&&t.push({scope:"array",value:i});const a=M("properties",null==e?void 0:e.minProperties,null==e?void 0:e.maxProperties);return null!==a&&t.push({scope:"object",value:a}),t},F=(e,t)=>{var n;return null!=t&&t.dependentRequired?w()(x()(n=_()(t.dependentRequired)).call(n,((t,n)=>{let[r,o]=n;return l()(o)&&h()(o).call(o,e)?(t.add(r),t):t}),new(O()))):[]}},65077:(e,t,n)=>{"use strict";n.r(t),n.d(t,{withJSONSchemaContext:()=>H});var r=n(67294),o=n(22675),s=n(69359),i=n(7568),a=n(93460),l=n(64922),c=n(51338),u=n(72348),p=n(27655),h=n(36418),f=n(4685),d=n(46457),m=n(8776),g=n(13834),y=n(65174),v=n(40487),b=n(67401),w=n(67207),E=n(26076),x=n(93971),S=n(89206),_=n(38993),j=n(25472),O=n(36746),k=n(65253),A=n(42338),C=n(85828),P=n(6907),N=n(22285),I=n(91805),T=n(27308),R=n(69956),M=n(24539),D=n(3484),F=n(78137),L=n(79446),B=n(55148),$=n(26661),q=n(16456),U=n(15789),z=n(47349),V=n(36867),W=n(12260),J=n(69006),K=n(33499);const H=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n={components:{JSONSchema:o.default,Keyword$schema:s.default,Keyword$vocabulary:i.default,Keyword$id:a.default,Keyword$anchor:l.default,Keyword$dynamicAnchor:c.default,Keyword$ref:u.default,Keyword$dynamicRef:p.default,Keyword$defs:h.default,Keyword$comment:f.default,KeywordAllOf:d.default,KeywordAnyOf:m.default,KeywordOneOf:g.default,KeywordNot:y.default,KeywordIf:v.default,KeywordThen:b.default,KeywordElse:w.default,KeywordDependentSchemas:E.default,KeywordPrefixItems:x.default,KeywordItems:S.default,KeywordContains:_.default,KeywordProperties:j.default,KeywordPatternProperties:O.default,KeywordAdditionalProperties:k.default,KeywordPropertyNames:A.default,KeywordUnevaluatedItems:C.default,KeywordUnevaluatedProperties:P.default,KeywordType:N.default,KeywordEnum:I.default,KeywordConst:T.default,KeywordConstraint:R.default,KeywordDependentRequired:M.default,KeywordContentSchema:D.default,KeywordTitle:F.default,KeywordDescription:L.default,KeywordDefault:B.default,KeywordDeprecated:$.default,KeywordReadOnly:q.default,KeywordWriteOnly:U.default,Accordion:z.default,ExpandDeepButton:V.default,ChevronRightIcon:W.default,...t.components},config:{default$schema:"https://json-schema.org/draft/2020-12/schema",defaultExpandedLevels:0,...t.config},fn:{upperFirst:K.upperFirst,getTitle:K.getTitle,getType:K.getType,isBooleanJSONSchema:K.isBooleanJSONSchema,hasKeyword:K.hasKeyword,isExpandable:K.isExpandable,stringify:K.stringify,stringifyConstraints:K.stringifyConstraints,getDependentRequired:K.getDependentRequired,...t.fn}},H=t=>r.createElement(J.JSONSchemaContext.Provider,{value:n},r.createElement(e,t));return H.contexts={JSONSchemaContext:J.JSONSchemaContext},H.displayName=e.displayName,H}},12603:(e,t,n)=>{"use strict";n.r(t),n.d(t,{useComponent:()=>l,useConfig:()=>a,useFn:()=>c,useIsCircular:()=>m,useIsEmbedded:()=>p,useIsExpanded:()=>h,useIsExpandedDeeply:()=>f,useLevel:()=>u,useRenderedSchemas:()=>d});var r=n(82737),o=n.n(r),s=n(67294),i=n(69006);const a=()=>{const{config:e}=(0,s.useContext)(i.JSONSchemaContext);return e},l=e=>{const{components:t}=(0,s.useContext)(i.JSONSchemaContext);return t[e]||null},c=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;const{fn:t}=(0,s.useContext)(i.JSONSchemaContext);return void 0!==e?t[e]:t},u=()=>{const e=(0,s.useContext)(i.JSONSchemaLevelContext);return[e,e+1]},p=()=>{const[e]=u();return e>0},h=()=>{const[e]=u(),{defaultExpandedLevels:t}=a();return t-e>0},f=()=>(0,s.useContext)(i.JSONSchemaDeepExpansionContext),d=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:void 0;if(void 0===e)return(0,s.useContext)(i.JSONSchemaCyclesContext);const t=(0,s.useContext)(i.JSONSchemaCyclesContext);return new(o())([...t,e])},m=e=>d().has(e)},97139:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>Z});var r=n(22675),o=n(69359),s=n(7568),i=n(93460),a=n(64922),l=n(51338),c=n(72348),u=n(27655),p=n(36418),h=n(4685),f=n(46457),d=n(8776),m=n(13834),g=n(65174),y=n(40487),v=n(67401),b=n(67207),w=n(26076),E=n(93971),x=n(89206),S=n(38993),_=n(25472),j=n(36746),O=n(65253),k=n(42338),A=n(85828),C=n(6907),P=n(22285),N=n(91805),I=n(27308),T=n(69956),R=n(24539),M=n(3484),D=n(78137),F=n(79446),L=n(55148),B=n(26661),$=n(16456),q=n(15789),U=n(47349),z=n(36867),V=n(12260),W=n(33499),J=n(78591),K=n(69006),H=n(12603),G=n(65077);const Z=()=>({components:{JSONSchema202012:r.default,JSONSchema202012Keyword$schema:o.default,JSONSchema202012Keyword$vocabulary:s.default,JSONSchema202012Keyword$id:i.default,JSONSchema202012Keyword$anchor:a.default,JSONSchema202012Keyword$dynamicAnchor:l.default,JSONSchema202012Keyword$ref:c.default,JSONSchema202012Keyword$dynamicRef:u.default,JSONSchema202012Keyword$defs:p.default,JSONSchema202012Keyword$comment:h.default,JSONSchema202012KeywordAllOf:f.default,JSONSchema202012KeywordAnyOf:d.default,JSONSchema202012KeywordOneOf:m.default,JSONSchema202012KeywordNot:g.default,JSONSchema202012KeywordIf:y.default,JSONSchema202012KeywordThen:v.default,JSONSchema202012KeywordElse:b.default,JSONSchema202012KeywordDependentSchemas:w.default,JSONSchema202012KeywordPrefixItems:E.default,JSONSchema202012KeywordItems:x.default,JSONSchema202012KeywordContains:S.default,JSONSchema202012KeywordProperties:_.default,JSONSchema202012KeywordPatternProperties:j.default,JSONSchema202012KeywordAdditionalProperties:O.default,JSONSchema202012KeywordPropertyNames:k.default,JSONSchema202012KeywordUnevaluatedItems:A.default,JSONSchema202012KeywordUnevaluatedProperties:C.default,JSONSchema202012KeywordType:P.default,JSONSchema202012KeywordEnum:N.default,JSONSchema202012KeywordConst:I.default,JSONSchema202012KeywordConstraint:T.default,JSONSchema202012KeywordDependentRequired:R.default,JSONSchema202012KeywordContentSchema:M.default,JSONSchema202012KeywordTitle:D.default,JSONSchema202012KeywordDescription:F.default,JSONSchema202012KeywordDefault:L.default,JSONSchema202012KeywordDeprecated:B.default,JSONSchema202012KeywordReadOnly:$.default,JSONSchema202012KeywordWriteOnly:q.default,JSONSchema202012Accordion:U.default,JSONSchema202012ExpandDeepButton:z.default,JSONSchema202012ChevronRightIcon:V.default,withJSONSchema202012Context:G.withJSONSchemaContext,JSONSchema202012DeepExpansionContext:()=>K.JSONSchemaDeepExpansionContext},fn:{upperFirst:W.upperFirst,jsonSchema202012:{isExpandable:W.isExpandable,hasKeyword:W.hasKeyword,useFn:H.useFn,useConfig:H.useConfig,useComponent:H.useComponent,useIsExpandedDeeply:H.useIsExpandedDeeply,sampleFromSchema:J.sampleFromSchema,sampleFromSchemaGeneric:J.sampleFromSchemaGeneric,sampleEncoderAPI:J.encoderAPI,sampleFormatAPI:J.formatAPI,sampleMediaTypeAPI:J.mediaTypeAPI,createXMLExample:J.createXMLExample,memoizedSampleFromSchema:J.memoizedSampleFromSchema,memoizedCreateXMLExample:J.memoizedCreateXMLExample}}})},16648:(e,t,n)=>{"use strict";n.r(t),n.d(t,{booleanSchema:()=>i,objectSchema:()=>s,schema:()=>a});var r=n(45697),o=n.n(r);const s=o().object,i=o().bool,a=o().oneOfType([s,i])},9507:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});const r=new(n(70674).default),o=(e,t)=>"function"==typeof t?r.register(e,t):null===t?r.unregister(e):r.get(e);o.getDefaults=()=>r.defaults;const s=o},22906:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});const r=new(n(14215).default),o=(e,t)=>"function"==typeof t?r.register(e,t):null===t?r.unregister(e):r.get(e)},90537:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});const r=new(n(43782).default),o=(e,t)=>{if("function"==typeof t)return r.register(e,t);if(null===t)return r.unregister(e);const n=e.split(";").at(0),o=`${n.split("/").at(0)}/*`;return r.get(e)||r.get(n)||r.get(o)};o.getDefaults=()=>r.defaults;const s=o},70674:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>w});var r=n(61125),o=n.n(r),s=n(47667),i=n.n(s),a=n(28886),l=n.n(a),c=n(14215),u=n(41433),p=n(58509),h=n(44366),f=n(65037),d=n(5709),m=n(54180),g=n(91967);function y(e,t,n){!function(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")}(e,t),t.set(e,n)}var v=new(l());class b extends c.default{constructor(){super(...arguments),y(this,v,{writable:!0,value:{"7bit":u.default,"8bit":p.default,binary:h.default,"quoted-printable":f.default,base16:d.default,base32:m.default,base64:g.default}}),o()(this,"data",{...i()(this,v)})}get defaults(){return{...i()(this,v)}}}const w=b},43782:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>v});var r=n(61125),o=n.n(r),s=n(47667),i=n.n(s),a=n(28886),l=n.n(a),c=n(14215),u=n(65378),p=n(46724),h=n(54342),f=n(92974),d=n(2672);function m(e,t,n){!function(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")}(e,t),t.set(e,n)}var g=new(l());class y extends c.default{constructor(){super(...arguments),m(this,g,{writable:!0,value:{...u.default,...p.default,...h.default,...f.default,...d.default}}),o()(this,"data",{...i()(this,g)})}get defaults(){return{...i()(this,g)}}}const v=y},14215:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(61125),o=n.n(r);const s=class{constructor(){o()(this,"data",{})}register(e,t){this.data[e]=t}unregister(e){void 0===e?this.data={}:delete this.data[e]}get(e){return this.data[e]}}},84539:(e,t,n)=>{"use strict";n.r(t),n.d(t,{ALL_TYPES:()=>o,SCALAR_TYPES:()=>r});const r=["number","integer","string","boolean","null"],o=["array","object",...r]},13783:(e,t,n)=>{"use strict";n.r(t),n.d(t,{extractExample:()=>a,hasExample:()=>i});var r=n(58309),o=n.n(r),s=n(23084);const i=e=>{if(!(0,s.isJSONSchemaObject)(e))return!1;const{examples:t,example:n,default:r}=e;return!!(o()(t)&&t.length>=1)||(void 0!==r||void 0!==n)},a=e=>{if(!(0,s.isJSONSchemaObject)(e))return null;const{examples:t,example:n,default:r}=e;return o()(t)&&t.length>=1?t.at(0):void 0!==r?r:void 0!==n?n:void 0}},37078:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>v});var r=n(58309),o=n.n(r),s=n(39022),i=n.n(s),a=n(25110),l=n.n(a),c=n(82737),u=n.n(c),p=n(28222),h=n.n(p),f=n(14418),d=n.n(f),m=n(90242),g=n(23084);const y=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if((0,g.isBooleanJSONSchema)(e)&&!0===e)return!0;if((0,g.isBooleanJSONSchema)(e)&&!1===e)return!1;if((0,g.isBooleanJSONSchema)(t)&&!0===t)return!0;if((0,g.isBooleanJSONSchema)(t)&&!1===t)return!1;if(!(0,g.isJSONSchema)(e))return t;if(!(0,g.isJSONSchema)(t))return e;const r={...t,...e};if(t.type&&e.type&&o()(t.type)&&"string"==typeof t.type){var s;const n=i()(s=(0,m.AF)(t.type)).call(s,e.type);r.type=l()(new(u())(n))}if(o()(t.required)&&o()(e.required)&&(r.required=[...new(u())([...e.required,...t.required])]),t.properties&&e.properties){const o=new(u())([...h()(t.properties),...h()(e.properties)]);r.properties={};for(const s of o){const o=t.properties[s]||{},i=e.properties[s]||{};var a;if(o.readOnly&&!n.includeReadOnly||o.writeOnly&&!n.includeWriteOnly)r.required=d()(a=r.required||[]).call(a,(e=>e!==s));else r.properties[s]=y(i,o,n)}}return(0,g.isJSONSchema)(t.items)&&(0,g.isJSONSchema)(e.items)&&(r.items=y(e.items,t.items,n)),(0,g.isJSONSchema)(t.contains)&&(0,g.isJSONSchema)(e.contains)&&(r.contains=y(e.contains,t.contains,n)),(0,g.isJSONSchema)(t.contentSchema)&&(0,g.isJSONSchema)(e.contentSchema)&&(r.contentSchema=y(e.contentSchema,t.contentSchema,n)),r},v=y},23084:(e,t,n)=>{"use strict";n.r(t),n.d(t,{isBooleanJSONSchema:()=>s,isJSONSchema:()=>a,isJSONSchemaObject:()=>i});var r=n(68630),o=n.n(r);const s=e=>"boolean"==typeof e,i=e=>o()(e),a=e=>s(e)||i(e)},35202:(e,t,n)=>{"use strict";n.r(t),n.d(t,{bytes:()=>a,integer:()=>h,number:()=>p,pick:()=>c,randexp:()=>l,string:()=>u});var r=n(92282),o=n.n(r),s=n(14419),i=n.n(s);const a=e=>o()(e),l=e=>{try{return new(i())(e).gen()}catch{return"string"}},c=e=>e.at(0),u=()=>"string",p=()=>0,h=()=>0},96276:(e,t,n)=>{"use strict";n.r(t),n.d(t,{foldType:()=>_,getType:()=>O,inferType:()=>j});var r=n(58309),o=n.n(r),s=n(91086),i=n.n(s),a=n(58118),l=n.n(a),c=n(19030),u=n.n(c),p=n(28222),h=n.n(p),f=n(97606),d=n.n(f),m=n(14418),g=n.n(m),y=n(84539),v=n(23084),b=n(35202),w=n(13783);const E={array:["items","prefixItems","contains","maxContains","minContains","maxItems","minItems","uniqueItems","unevaluatedItems"],object:["properties","additionalProperties","patternProperties","propertyNames","minProperties","maxProperties","required","dependentSchemas","dependentRequired","unevaluatedProperties"],string:["pattern","format","minLength","maxLength","contentEncoding","contentMediaType","contentSchema"],integer:["minimum","maximum","exclusiveMinimum","exclusiveMaximum","multipleOf"]};E.number=E.integer;const x="string",S=e=>void 0===e?null:null===e?"null":o()(e)?"array":i()(e)?"integer":typeof e,_=e=>{if(o()(e)&&e.length>=1){if(l()(e).call(e,"array"))return"array";if(l()(e).call(e,"object"))return"object";{const t=(0,b.pick)(e);if(l()(y.ALL_TYPES).call(y.ALL_TYPES,t))return t}}return l()(y.ALL_TYPES).call(y.ALL_TYPES,e)?e:null},j=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:new(u());if(!(0,v.isJSONSchemaObject)(e))return x;if(t.has(e))return x;t.add(e);let{type:n,const:r}=e;if(n=_(n),"string"!=typeof n){const t=h()(E);e:for(let r=0;r{if(o()(e[n])){var r;const o=d()(r=e[n]).call(r,(e=>j(e,t)));return _(o)}return null},i=r("allOf"),a=r("anyOf"),l=r("oneOf"),c=e.not?j(e.not,t):null;var s;if(i||a||l||c)n=_(g()(s=[i,a,l,c]).call(s,Boolean))}if("string"!=typeof n&&(0,w.hasExample)(e)){const t=(0,w.extractExample)(e),r=S(t);n="string"==typeof r?r:n}return t.delete(e),n||x},O=e=>j(e)},99346:(e,t,n)=>{"use strict";n.r(t),n.d(t,{fromJSONBooleanSchema:()=>o,typeCast:()=>s});var r=n(23084);const o=e=>!1===e?{not:{}}:{},s=e=>(0,r.isBooleanJSONSchema)(e)?o(e):(0,r.isJSONSchemaObject)(e)?e:{}},41433:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(48764).Buffer;const o=e=>r.from(e).toString("ascii")},58509:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(48764).Buffer;const o=e=>r.from(e).toString("utf8")},5709:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(48764).Buffer;const o=e=>r.from(e).toString("hex")},54180:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(48764).Buffer;const o=e=>{const t=r.from(e).toString("utf8"),n="ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";let o=0,s="",i=0,a=0;for(let e=0;e=5;)s+=n.charAt(i>>>a-5&31),a-=5;a>0&&(s+=n.charAt(i<<5-a&31),o=(8-8*t.length%5)%5);for(let e=0;e{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(48764).Buffer;const o=e=>r.from(e).toString("base64")},44366:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(48764).Buffer;const o=e=>r.from(e).toString("binary")},65037:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(24278),o=n.n(r);const s=e=>{let t="";for(let s=0;s=33&&i<=60||i>=62&&i<=126||9===i||32===i)t+=e.charAt(s);else if(13===i||10===i)t+="\r\n";else if(i>126){const r=unescape(encodeURIComponent(e.charAt(s)));for(let e=0;e{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>(new Date).toISOString()},81456:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>(new Date).toISOString().substring(0,10)},560:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>.1},64299:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"P3D"},3981:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"user@example.com"},51890:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>.1},69375:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"example.com"},94518:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"실례@example.com"},70273:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"실례.com"},57864:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>2**30>>>0},21726:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>2**53-1},28793:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"198.51.100.42"},98269:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"2001:0db8:5b96:0000:0000:426f:8e17:642a"},45693:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"path/실례.html"},13080:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"https://실례.com/"},37856:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"/a/b/c"},2672:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(57740),o=n.n(r),s=n(35202);const i={"application/json":()=>'{"key":"value"}',"application/ld+json":()=>'{"name": "John Doe"}',"application/x-httpd-php":()=>"Hello World!

'; ?>","application/rtf":()=>o()`{\rtf1\adeflang1025\ansi\ansicpg1252\uc1`,"application/x-sh":()=>'echo "Hello World!"',"application/xhtml+xml":()=>"

content

","application/*":()=>(0,s.bytes)(25).toString("binary")}},54342:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(35202);const o={"audio/*":()=>(0,r.bytes)(25).toString("binary")}},46724:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(35202);const o={"image/*":()=>(0,r.bytes)(25).toString("binary")}},65378:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r={"text/plain":()=>"string","text/css":()=>".selector { border: 1px solid red }","text/csv":()=>"value1,value2,value3","text/html":()=>"

content

","text/calendar":()=>"BEGIN:VCALENDAR","text/javascript":()=>"console.dir('Hello world!');","text/xml":()=>'John Doe',"text/*":()=>"string"}},92974:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(35202);const o={"video/*":()=>(0,r.bytes)(25).toString("binary")}},93393:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"********"},4335:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"^[a-z]+$"},80375:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"1/0"},65243:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>(new Date).toISOString().substring(11)},94692:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"path/index.html"},83829:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"https://example.com/dictionary/{term:1}/{term}"},52978:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"https://example.com/"},38859:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>"3fa85f64-5717-4562-b3fc-2c963f66afa6"},78591:(e,t,n)=>{"use strict";n.r(t),n.d(t,{createXMLExample:()=>r.createXMLExample,encoderAPI:()=>o.default,formatAPI:()=>s.default,mediaTypeAPI:()=>i.default,memoizedCreateXMLExample:()=>r.memoizedCreateXMLExample,memoizedSampleFromSchema:()=>r.memoizedSampleFromSchema,sampleFromSchema:()=>r.sampleFromSchema,sampleFromSchemaGeneric:()=>r.sampleFromSchemaGeneric});var r=n(94277),o=n(9507),s=n(22906),i=n(90537)},94277:(e,t,n)=>{"use strict";n.r(t),n.d(t,{createXMLExample:()=>M,memoizedCreateXMLExample:()=>L,memoizedSampleFromSchema:()=>B,sampleFromSchema:()=>D,sampleFromSchemaGeneric:()=>R});var r=n(58309),o=n.n(r),s=n(91086),i=n.n(s),a=n(86),l=n.n(a),c=n(51679),u=n.n(c),p=n(58118),h=n.n(p),f=n(39022),d=n.n(f),m=n(97606),g=n.n(m),y=n(35627),v=n.n(y),b=n(53479),w=n.n(b),E=n(41609),x=n.n(E),S=n(68630),_=n.n(S),j=n(90242),O=n(60314),k=n(63273),A=n(96276),C=n(99346),P=n(13783),N=n(35202),I=n(37078),T=n(23084);const R=function(e){var t;let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,s=arguments.length>3&&void 0!==arguments[3]&&arguments[3];"function"==typeof(null===(t=e)||void 0===t?void 0:t.toJS)&&(e=e.toJS()),e=(0,C.typeCast)(e);let a=void 0!==r||(0,P.hasExample)(e);const c=!a&&o()(e.oneOf)&&e.oneOf.length>0,p=!a&&o()(e.anyOf)&&e.anyOf.length>0;if(!a&&(c||p)){const t=(0,C.typeCast)(c?(0,N.pick)(e.oneOf):(0,N.pick)(e.anyOf));!(e=(0,I.default)(e,t,n)).xml&&t.xml&&(e.xml=t.xml),(0,P.hasExample)(e)&&(0,P.hasExample)(t)&&(a=!0)}const f={};let{xml:m,properties:y,additionalProperties:v,items:b,contains:w}=e||{},E=(0,A.getType)(e),{includeReadOnly:S,includeWriteOnly:O}=n;m=m||{};let M,{name:D,prefix:F,namespace:L}=m,B={};if(Object.hasOwn(e,"type")||(e.type=E),s&&(D=D||"notagname",M=(F?`${F}:`:"")+D,L)){f[F?`xmlns:${F}`:"xmlns"]=L}s&&(B[M]=[]);const $=(0,j.mz)(y);let q,U=0;const z=()=>i()(e.maxProperties)&&e.maxProperties>0&&U>=e.maxProperties,V=t=>!(i()(e.maxProperties)&&e.maxProperties>0)||!z()&&(!(t=>{var n;return!o()(e.required)||0===e.required.length||!h()(n=e.required).call(n,t)})(t)||e.maxProperties-U-(()=>{if(!o()(e.required)||0===e.required.length)return 0;let t=0;var n,r;return s?l()(n=e.required).call(n,(e=>t+=void 0===B[e]?0:1)):l()(r=e.required).call(r,(e=>{var n;t+=void 0===(null===(n=B[M])||void 0===n?void 0:u()(n).call(n,(t=>void 0!==t[e])))?0:1})),e.required.length-t})()>0);if(q=s?function(t){let r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;if(e&&$[t]){if($[t].xml=$[t].xml||{},$[t].xml.attribute){const e=o()($[t].enum)?(0,N.pick)($[t].enum):void 0;if((0,P.hasExample)($[t]))f[$[t].xml.name||t]=(0,P.extractExample)($[t]);else if(void 0!==e)f[$[t].xml.name||t]=e;else{const e=(0,C.typeCast)($[t]),n=(0,A.getType)(e),r=$[t].xml.name||t;f[r]=k.default[n](e)}return}$[t].xml.name=$[t].xml.name||t}else $[t]||!1===v||($[t]={xml:{name:t}});let i=R($[t],n,r,s);var a;V(t)&&(U++,o()(i)?B[M]=d()(a=B[M]).call(a,i):B[M].push(i))}:(t,r)=>{var o;if(V(t)){if(_()(null===(o=e.discriminator)||void 0===o?void 0:o.mapping)&&e.discriminator.propertyName===t&&"string"==typeof e.$$ref){for(const n in e.discriminator.mapping)if(-1!==e.$$ref.search(e.discriminator.mapping[n])){B[t]=n;break}}else B[t]=R($[t],n,r,s);U++}},a){let t;if(t=void 0!==r?r:(0,P.extractExample)(e),!s){if("number"==typeof t&&"string"===E)return`${t}`;if("string"!=typeof t||"string"===E)return t;try{return JSON.parse(t)}catch{return t}}if("array"===E){if(!o()(t)){if("string"==typeof t)return t;t=[t]}let r=[];return(0,T.isJSONSchemaObject)(b)&&(b.xml=b.xml||m||{},b.xml.name=b.xml.name||m.name,r=g()(t).call(t,(e=>R(b,n,e,s)))),(0,T.isJSONSchemaObject)(w)&&(w.xml=w.xml||m||{},w.xml.name=w.xml.name||m.name,r=[R(w,n,void 0,s),...r]),r=k.default.array(e,{sample:r}),m.wrapped?(B[M]=r,x()(f)||B[M].push({_attr:f})):B=r,B}if("object"===E){if("string"==typeof t)return t;for(const e in t){var W,J,K,H;Object.hasOwn(t,e)&&(null!==(W=$[e])&&void 0!==W&&W.readOnly&&!S||null!==(J=$[e])&&void 0!==J&&J.writeOnly&&!O||(null!==(K=$[e])&&void 0!==K&&null!==(H=K.xml)&&void 0!==H&&H.attribute?f[$[e].xml.name||e]=t[e]:q(e,t[e])))}return x()(f)||B[M].push({_attr:f}),B}return B[M]=x()(f)?t:[{_attr:f},t],B}if("array"===E){let t=[];var G,Z;if((0,T.isJSONSchemaObject)(w))if(s&&(w.xml=w.xml||e.xml||{},w.xml.name=w.xml.name||m.name),o()(w.anyOf))t.push(...g()(G=w.anyOf).call(G,(e=>R((0,I.default)(e,w,n),n,void 0,s))));else if(o()(w.oneOf)){var Y;t.push(...g()(Y=w.oneOf).call(Y,(e=>R((0,I.default)(e,w,n),n,void 0,s))))}else{if(!(!s||s&&m.wrapped))return R(w,n,void 0,s);t.push(R(w,n,void 0,s))}if((0,T.isJSONSchemaObject)(b))if(s&&(b.xml=b.xml||e.xml||{},b.xml.name=b.xml.name||m.name),o()(b.anyOf))t.push(...g()(Z=b.anyOf).call(Z,(e=>R((0,I.default)(e,b,n),n,void 0,s))));else if(o()(b.oneOf)){var X;t.push(...g()(X=b.oneOf).call(X,(e=>R((0,I.default)(e,b,n),n,void 0,s))))}else{if(!(!s||s&&m.wrapped))return R(b,n,void 0,s);t.push(R(b,n,void 0,s))}return t=k.default.array(e,{sample:t}),s&&m.wrapped?(B[M]=t,x()(f)||B[M].push({_attr:f}),B):t}if("object"===E){for(let e in $){var Q,ee,te;Object.hasOwn($,e)&&(null!==(Q=$[e])&&void 0!==Q&&Q.deprecated||null!==(ee=$[e])&&void 0!==ee&&ee.readOnly&&!S||null!==(te=$[e])&&void 0!==te&&te.writeOnly&&!O||q(e))}if(s&&f&&B[M].push({_attr:f}),z())return B;if((0,T.isBooleanJSONSchema)(v)&&v)s?B[M].push({additionalProp:"Anything can be here"}):B.additionalProp1={},U++;else if((0,T.isJSONSchemaObject)(v)){var ne,re;const t=v,r=R(t,n,void 0,s);if(s&&"string"==typeof(null==t||null===(ne=t.xml)||void 0===ne?void 0:ne.name)&&"notagname"!==(null==t||null===(re=t.xml)||void 0===re?void 0:re.name))B[M].push(r);else{const t=i()(e.minProperties)&&e.minProperties>0&&U{const r=R(e,t,n,!0);if(r)return"string"==typeof r?r:w()(r,{declaration:!0,indent:"\t"})},D=(e,t,n)=>R(e,t,n,!1),F=(e,t,n)=>[e,v()(t),v()(n)],L=(0,O.Z)(M,F),B=(0,O.Z)(D,F)},83982:(e,t,n)=>{"use strict";n.r(t),n.d(t,{applyArrayConstraints:()=>p,default:()=>h});var r=n(91086),o=n.n(r),s=n(24278),i=n.n(s),a=n(25110),l=n.n(a),c=n(82737),u=n.n(c);const p=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{minItems:n,maxItems:r,uniqueItems:s}=t,{contains:a,minContains:c,maxContains:p}=t;let h=[...e];if(null!=a&&"object"==typeof a){if(o()(c)&&c>1){const e=h.at(0);for(let t=1;t0&&(h=i()(e).call(e,0,r)),o()(n)&&n>0)for(let e=0;h.length{let{sample:n}=t;return p(n,e)}},34108:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=e=>"boolean"!=typeof e.default||e.default},63273:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(83982),o=n(46852),s=n(74522),i=n(83455),a=n(58864),l=n(34108),c=n(90853);const u={array:r.default,object:o.default,string:s.default,number:i.default,integer:a.default,boolean:l.default,null:c.default},p=new Proxy(u,{get:(e,t)=>"string"==typeof t&&Object.hasOwn(e,t)?e[t]:()=>`Unknown Type: ${t}`})},58864:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(35202),o=n(22906),s=n(57864),i=n(21726);const a=e=>{const{format:t}=e;return"string"==typeof t?(e=>{const{format:t}=e,n=(0,o.default)(t);if("function"==typeof n)return n(e);switch(t){case"int32":return(0,s.default)();case"int64":return(0,i.default)()}return(0,r.integer)()})(e):(0,r.integer)()}},90853:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>null},83455:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(91086),o=n.n(r),s=n(44081),i=n.n(s),a=n(35202),l=n(22906),c=n(51890),u=n(560);const p=e=>{const{format:t}=e;let n;return n="string"==typeof t?(e=>{const{format:t}=e,n=(0,l.default)(t);if("function"==typeof n)return n(e);switch(t){case"float":return(0,c.default)();case"double":return(0,u.default)()}return(0,a.number)()})(e):(0,a.number)(),function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{minimum:n,maximum:r,exclusiveMinimum:s,exclusiveMaximum:a}=t,{multipleOf:l}=t,c=o()(e)?1:i();let u="number"==typeof n?n:null,p="number"==typeof r?r:null,h=e;if("number"==typeof s&&(u=null!==u?Math.max(u,s+c):s+c),"number"==typeof a&&(p=null!==p?Math.min(p,a-c):a-c),h=u>p&&e||u||p||h,"number"==typeof l&&l>0){const e=h%l;h=0===e?h:h+l-e}return h}(n,e)}},46852:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=()=>{throw new Error("Not implemented")}},74522:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>L});var r=n(91086),o=n.n(r),s=n(24278),i=n.n(s),a=n(58309),l=n.n(a),c=n(35627),u=n.n(c),p=n(6557),h=n.n(p),f=n(35202),d=n(23084),m=n(3981),g=n(94518),y=n(69375),v=n(70273),b=n(28793),w=n(98269),E=n(52978),x=n(94692),S=n(13080),_=n(45693),j=n(38859),O=n(83829),k=n(37856),A=n(80375),C=n(74045),P=n(81456),N=n(65243),I=n(64299),T=n(93393),R=n(4335),M=n(22906),D=n(9507),F=n(90537);const L=function(e){let{sample:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{contentEncoding:n,contentMediaType:r,contentSchema:s}=e,{pattern:a,format:c}=e,p=(0,D.default)(n)||h();let L;if("string"==typeof a)L=(0,f.randexp)(a);else if("string"==typeof c)L=(e=>{const{format:t}=e,n=(0,M.default)(t);if("function"==typeof n)return n(e);switch(t){case"email":return(0,m.default)();case"idn-email":return(0,g.default)();case"hostname":return(0,y.default)();case"idn-hostname":return(0,v.default)();case"ipv4":return(0,b.default)();case"ipv6":return(0,w.default)();case"uri":return(0,E.default)();case"uri-reference":return(0,x.default)();case"iri":return(0,S.default)();case"iri-reference":return(0,_.default)();case"uuid":return(0,j.default)();case"uri-template":return(0,O.default)();case"json-pointer":return(0,k.default)();case"relative-json-pointer":return(0,A.default)();case"date-time":return(0,C.default)();case"date":return(0,P.default)();case"time":return(0,N.default)();case"duration":return(0,I.default)();case"password":return(0,T.default)();case"regex":return(0,R.default)()}return(0,f.string)()})(e);else if((0,d.isJSONSchema)(s)&&"string"==typeof r&&void 0!==t)L=l()(t)||"object"==typeof t?u()(t):String(t);else if("string"==typeof r){const t=(0,F.default)(r);"function"==typeof t&&(L=t(e))}else L=(0,f.string)();return p(function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{maxLength:n,minLength:r}=t;let s=e;if(o()(n)&&n>0&&(s=i()(s).call(s,0,n)),o()(r)&&r>0){let e=0;for(;s.length{"use strict";n.r(t),n.d(t,{SHOW:()=>a,UPDATE_FILTER:()=>s,UPDATE_LAYOUT:()=>o,UPDATE_MODE:()=>i,changeMode:()=>p,show:()=>u,updateFilter:()=>c,updateLayout:()=>l});var r=n(90242);const o="layout_update_layout",s="layout_update_filter",i="layout_update_mode",a="layout_show";function l(e){return{type:o,payload:e}}function c(e){return{type:s,payload:e}}function u(e){let t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return e=(0,r.AF)(e),{type:a,payload:{thing:e,shown:t}}}function p(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return e=(0,r.AF)(e),{type:i,payload:{thing:e,mode:t}}}},26821:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(5672),o=n(25474),s=n(4400),i=n(28989);function a(){return{statePlugins:{layout:{reducers:r.default,actions:o,selectors:s},spec:{wrapSelectors:i}}}}},5672:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(39022),o=n.n(r),s=n(43393),i=n(25474);const a={[i.UPDATE_LAYOUT]:(e,t)=>e.set("layout",t.payload),[i.UPDATE_FILTER]:(e,t)=>e.set("filter",t.payload),[i.SHOW]:(e,t)=>{const n=t.payload.shown,r=(0,s.fromJS)(t.payload.thing);return e.update("shown",(0,s.fromJS)({}),(e=>e.set(r,n)))},[i.UPDATE_MODE]:(e,t)=>{var n;let r=t.payload.thing,s=t.payload.mode;return e.setIn(o()(n=["modes"]).call(n,r),(s||"")+"")}}},4400:(e,t,n)=>{"use strict";n.r(t),n.d(t,{current:()=>i,currentFilter:()=>a,isShown:()=>l,showSummary:()=>u,whatMode:()=>c});var r=n(20573),o=n(90242),s=n(43393);const i=e=>e.get("layout"),a=e=>e.get("filter"),l=(e,t,n)=>(t=(0,o.AF)(t),e.get("shown",(0,s.fromJS)({})).get((0,s.fromJS)(t),n)),c=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";return t=(0,o.AF)(t),e.getIn(["modes",...t],n)},u=(0,r.P1)((e=>e),(e=>!l(e,"editor")))},28989:(e,t,n)=>{"use strict";n.r(t),n.d(t,{taggedOperations:()=>s});var r=n(24278),o=n.n(r);const s=(e,t)=>function(n){for(var r=arguments.length,s=new Array(r>1?r-1:0),i=1;i=0&&(a=o()(a).call(a,0,h)),a}},9150:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(11189),o=n.n(r);function s(e){let{configs:t}=e;const n={debug:0,info:1,log:2,warn:3,error:4},r=e=>n[e]||-1;let{logLevel:s}=t,i=r(s);function a(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),o=1;o=i&&console[e](...n)}return a.warn=o()(a).call(a,null,"warn"),a.error=o()(a).call(a,null,"error"),a.info=o()(a).call(a,null,"info"),a.debug=o()(a).call(a,null,"debug"),{rootInjects:{log:a}}}},67002:(e,t,n)=>{"use strict";n.r(t),n.d(t,{CLEAR_REQUEST_BODY_VALIDATE_ERROR:()=>h,CLEAR_REQUEST_BODY_VALUE:()=>f,SET_REQUEST_BODY_VALIDATE_ERROR:()=>p,UPDATE_ACTIVE_EXAMPLES_MEMBER:()=>a,UPDATE_REQUEST_BODY_INCLUSION:()=>i,UPDATE_REQUEST_BODY_VALUE:()=>o,UPDATE_REQUEST_BODY_VALUE_RETAIN_FLAG:()=>s,UPDATE_REQUEST_CONTENT_TYPE:()=>l,UPDATE_RESPONSE_CONTENT_TYPE:()=>c,UPDATE_SELECTED_SERVER:()=>r,UPDATE_SERVER_VARIABLE_VALUE:()=>u,clearRequestBodyValidateError:()=>S,clearRequestBodyValue:()=>j,initRequestBodyValidateError:()=>_,setActiveExamplesMember:()=>v,setRequestBodyInclusion:()=>y,setRequestBodyValidateError:()=>x,setRequestBodyValue:()=>m,setRequestContentType:()=>b,setResponseContentType:()=>w,setRetainRequestBodyValueFlag:()=>g,setSelectedServer:()=>d,setServerVariableValue:()=>E});const r="oas3_set_servers",o="oas3_set_request_body_value",s="oas3_set_request_body_retain_flag",i="oas3_set_request_body_inclusion",a="oas3_set_active_examples_member",l="oas3_set_request_content_type",c="oas3_set_response_content_type",u="oas3_set_server_variable_value",p="oas3_set_request_body_validate_error",h="oas3_clear_request_body_validate_error",f="oas3_clear_request_body_value";function d(e,t){return{type:r,payload:{selectedServerUrl:e,namespace:t}}}function m(e){let{value:t,pathMethod:n}=e;return{type:o,payload:{value:t,pathMethod:n}}}const g=e=>{let{value:t,pathMethod:n}=e;return{type:s,payload:{value:t,pathMethod:n}}};function y(e){let{value:t,pathMethod:n,name:r}=e;return{type:i,payload:{value:t,pathMethod:n,name:r}}}function v(e){let{name:t,pathMethod:n,contextType:r,contextName:o}=e;return{type:a,payload:{name:t,pathMethod:n,contextType:r,contextName:o}}}function b(e){let{value:t,pathMethod:n}=e;return{type:l,payload:{value:t,pathMethod:n}}}function w(e){let{value:t,path:n,method:r}=e;return{type:c,payload:{value:t,path:n,method:r}}}function E(e){let{server:t,namespace:n,key:r,val:o}=e;return{type:u,payload:{server:t,namespace:n,key:r,val:o}}}const x=e=>{let{path:t,method:n,validationErrors:r}=e;return{type:p,payload:{path:t,method:n,validationErrors:r}}},S=e=>{let{path:t,method:n}=e;return{type:h,payload:{path:t,method:n}}},_=e=>{let{pathMethod:t}=e;return{type:h,payload:{path:t[0],method:t[1]}}},j=e=>{let{pathMethod:t}=e;return{type:f,payload:{pathMethod:t}}}},73723:(e,t,n)=>{"use strict";n.r(t),n.d(t,{definitionsToAuthorize:()=>p});var r=n(86),o=n.n(r),s=n(14418),i=n.n(s),a=n(24282),l=n.n(a),c=n(20573),u=n(43393);const p=(h=(0,c.P1)((e=>e),(e=>{let{specSelectors:t}=e;return t.securityDefinitions()}),((e,t)=>{var n;let r=(0,u.List)();return t?(o()(n=t.entrySeq()).call(n,(e=>{let[t,n]=e;const s=n.get("type");var a;if("oauth2"===s&&o()(a=n.get("flows").entrySeq()).call(a,(e=>{let[o,s]=e,a=(0,u.fromJS)({flow:o,authorizationUrl:s.get("authorizationUrl"),tokenUrl:s.get("tokenUrl"),scopes:s.get("scopes"),type:n.get("type"),description:n.get("description")});r=r.push(new u.Map({[t]:i()(a).call(a,(e=>void 0!==e))}))})),"http"!==s&&"apiKey"!==s||(r=r.push(new u.Map({[t]:n}))),"openIdConnect"===s&&n.get("openIdConnectData")){let e=n.get("openIdConnectData"),s=e.get("grant_types_supported")||["authorization_code","implicit"];o()(s).call(s,(o=>{var s;let a=e.get("scopes_supported")&&l()(s=e.get("scopes_supported")).call(s,((e,t)=>e.set(t,"")),new u.Map),c=(0,u.fromJS)({flow:o,authorizationUrl:e.get("authorization_endpoint"),tokenUrl:e.get("token_endpoint"),scopes:a,type:"oauth2",openIdConnectUrl:n.get("openIdConnectUrl")});r=r.push(new u.Map({[t]:i()(c).call(c,(e=>void 0!==e))}))}))}})),r):r})),(e,t)=>function(){for(var n=arguments.length,r=new Array(n),o=0;o{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294);n(23930);const l=e=>{let{callbacks:t,specPath:n,specSelectors:r,getComponent:s}=e;const l=r.callbacksOperations({callbacks:t,specPath:n}),c=o()(l),u=s("OperationContainer",!0);return 0===c.length?a.createElement("span",null,"No callbacks"):a.createElement("div",null,i()(c).call(c,(e=>{var t;return a.createElement("div",{key:`${e}`},a.createElement("h2",null,e),i()(t=l[e]).call(t,(t=>a.createElement(u,{key:`${e}-${t.path}-${t.method}`,op:t.operation,tag:"callbacks",method:t.method,path:t.path,specPath:t.specPath,allowTryItOut:!1}))))})))}},86775:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r=n(61125),o=n.n(r),s=n(76986),i=n.n(s),a=n(14418),l=n.n(a),c=n(97606),u=n.n(c),p=n(67294);class h extends p.Component{constructor(e,t){super(e,t),o()(this,"onChange",(e=>{let{onChange:t}=this.props,{value:n,name:r}=e.target,o=i()({},this.state.value);r?o[r]=n:o=n,this.setState({value:o},(()=>t(this.state)))}));let{name:n,schema:r}=this.props,s=this.getValue();this.state={name:n,schema:r,value:s}}getValue(){let{name:e,authorized:t}=this.props;return t&&t.getIn([e,"value"])}render(){var e;let{schema:t,getComponent:n,errSelectors:r,name:o}=this.props;const s=n("Input"),i=n("Row"),a=n("Col"),c=n("authError"),h=n("Markdown",!0),f=n("JumpToPath",!0),d=(t.get("scheme")||"").toLowerCase();let m=this.getValue(),g=l()(e=r.allErrors()).call(e,(e=>e.get("authId")===o));if("basic"===d){var y;let e=m?m.get("username"):null;return p.createElement("div",null,p.createElement("h4",null,p.createElement("code",null,o||t.get("name")),"  (http, Basic)",p.createElement(f,{path:["securityDefinitions",o]})),e&&p.createElement("h6",null,"Authorized"),p.createElement(i,null,p.createElement(h,{source:t.get("description")})),p.createElement(i,null,p.createElement("label",null,"Username:"),e?p.createElement("code",null," ",e," "):p.createElement(a,null,p.createElement(s,{type:"text",required:"required",name:"username","aria-label":"auth-basic-username",onChange:this.onChange,autoFocus:!0}))),p.createElement(i,null,p.createElement("label",null,"Password:"),e?p.createElement("code",null," ****** "):p.createElement(a,null,p.createElement(s,{autoComplete:"new-password",name:"password",type:"password","aria-label":"auth-basic-password",onChange:this.onChange}))),u()(y=g.valueSeq()).call(y,((e,t)=>p.createElement(c,{error:e,key:t}))))}var v;return"bearer"===d?p.createElement("div",null,p.createElement("h4",null,p.createElement("code",null,o||t.get("name")),"  (http, Bearer)",p.createElement(f,{path:["securityDefinitions",o]})),m&&p.createElement("h6",null,"Authorized"),p.createElement(i,null,p.createElement(h,{source:t.get("description")})),p.createElement(i,null,p.createElement("label",null,"Value:"),m?p.createElement("code",null," ****** "):p.createElement(a,null,p.createElement(s,{type:"text","aria-label":"auth-bearer-value",onChange:this.onChange,autoFocus:!0}))),u()(v=g.valueSeq()).call(v,((e,t)=>p.createElement(c,{error:e,key:t})))):p.createElement("div",null,p.createElement("em",null,p.createElement("b",null,o)," HTTP authentication: unsupported scheme ",`'${d}'`))}}},76467:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(33427),o=n(42458),s=n(15757),i=n(56617),a=n(9928),l=n(45327),c=n(86775),u=n(96796);const p={Callbacks:r.default,HttpAuth:c.default,RequestBody:o.default,Servers:i.default,ServersContainer:a.default,RequestBodyEditor:l.default,OperationServers:u.default,operationLink:s.default}},15757:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(35627),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294);n(23930);class l extends a.Component{render(){const{link:e,name:t,getComponent:n}=this.props,r=n("Markdown",!0);let s=e.get("operationId")||e.get("operationRef"),l=e.get("parameters")&&e.get("parameters").toJS(),c=e.get("description");return a.createElement("div",{className:"operation-link"},a.createElement("div",{className:"description"},a.createElement("b",null,a.createElement("code",null,t)),c?a.createElement(r,{source:c}):null),a.createElement("pre",null,"Operation `",s,"`",a.createElement("br",null),a.createElement("br",null),"Parameters ",function(e,t){var n;if("string"!=typeof t)return"";return i()(n=t.split("\n")).call(n,((t,n)=>n>0?Array(e+1).join(" ")+t:t)).join("\n")}(0,o()(l,null,2))||"{}",a.createElement("br",null)))}}const c=l},96796:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(61125),o=n.n(r),s=n(67294);n(23930);class i extends s.Component{constructor(){super(...arguments),o()(this,"setSelectedServer",(e=>{const{path:t,method:n}=this.props;return this.forceUpdate(),this.props.setSelectedServer(e,`${t}:${n}`)})),o()(this,"setServerVariableValue",(e=>{const{path:t,method:n}=this.props;return this.forceUpdate(),this.props.setServerVariableValue({...e,namespace:`${t}:${n}`})})),o()(this,"getSelectedServer",(()=>{const{path:e,method:t}=this.props;return this.props.getSelectedServer(`${e}:${t}`)})),o()(this,"getServerVariable",((e,t)=>{const{path:n,method:r}=this.props;return this.props.getServerVariable({namespace:`${n}:${r}`,server:e},t)})),o()(this,"getEffectiveServerValue",(e=>{const{path:t,method:n}=this.props;return this.props.getEffectiveServerValue({server:e,namespace:`${t}:${n}`})}))}render(){const{operationServers:e,pathServers:t,getComponent:n}=this.props;if(!e&&!t)return null;const r=n("Servers"),o=e||t,i=e?"operation":"path";return s.createElement("div",{className:"opblock-section operation-servers"},s.createElement("div",{className:"opblock-section-header"},s.createElement("div",{className:"tab-header"},s.createElement("h4",{className:"opblock-title"},"Servers"))),s.createElement("div",{className:"opblock-description-wrapper"},s.createElement("h4",{className:"message"},"These ",i,"-level options override the global server options."),s.createElement(r,{servers:o,currentServer:this.getSelectedServer(),setSelectedServer:this.setSelectedServer,setServerVariableValue:this.setServerVariableValue,getServerVariable:this.getServerVariable,getEffectiveServerValue:this.getEffectiveServerValue})))}}},45327:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(61125),o=n.n(r),s=n(67294),i=n(94184),a=n.n(i),l=n(90242);const c=Function.prototype;class u extends s.PureComponent{constructor(e,t){super(e,t),o()(this,"applyDefaultValue",(e=>{const{onChange:t,defaultValue:n}=e||this.props;return this.setState({value:n}),t(n)})),o()(this,"onChange",(e=>{this.props.onChange((0,l.Pz)(e))})),o()(this,"onDomChange",(e=>{const t=e.target.value;this.setState({value:t},(()=>this.onChange(t)))})),this.state={value:(0,l.Pz)(e.value)||e.defaultValue},e.onChange(e.value)}UNSAFE_componentWillReceiveProps(e){this.props.value!==e.value&&e.value!==this.state.value&&this.setState({value:(0,l.Pz)(e.value)}),!e.value&&e.defaultValue&&this.state.value&&this.applyDefaultValue(e)}render(){let{getComponent:e,errors:t}=this.props,{value:n}=this.state,r=t.size>0;const o=e("TextArea");return s.createElement("div",{className:"body-param"},s.createElement(o,{className:a()("body-param__text",{invalid:r}),title:t.size?t.join(", "):"",value:n,onChange:this.onDomChange}))}}o()(u,"defaultProps",{onChange:c,userHasEditedBody:!1})},42458:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>g,getDefaultRequestBodyValue:()=>m});var r=n(97606),o=n.n(r),s=n(11882),i=n.n(s),a=n(58118),l=n.n(a),c=n(58309),u=n.n(c),p=n(67294),h=(n(23930),n(43393)),f=n(90242),d=n(2518);const m=(e,t,n,r)=>{const o=e.getIn(["content",t])??(0,h.OrderedMap)(),s=o.get("schema",(0,h.OrderedMap)()).toJS(),i=void 0!==o.get("examples"),a=o.get("example"),l=i?o.getIn(["examples",n,"value"]):a,c=r.getSampleSchema(s,t,{includeWriteOnly:!0},l);return(0,f.Pz)(c)},g=e=>{let{userHasEditedBody:t,requestBody:n,requestBodyValue:r,requestBodyInclusionSetting:s,requestBodyErrors:a,getComponent:c,getConfigs:g,specSelectors:y,fn:v,contentType:b,isExecute:w,specPath:E,onChange:x,onChangeIncludeEmpty:S,activeExamplesKey:_,updateActiveExamplesKey:j,setRetainRequestBodyValueFlag:O}=e;const k=e=>{x(e.target.files[0])},A=e=>{let t={key:e,shouldDispatchInit:!1,defaultValue:!0};return"no value"===s.get(e,"no value")&&(t.shouldDispatchInit=!0),t},C=c("Markdown",!0),P=c("modelExample"),N=c("RequestBodyEditor"),I=c("highlightCode"),T=c("ExamplesSelectValueRetainer"),R=c("Example"),M=c("ParameterIncludeEmpty"),{showCommonExtensions:D}=g(),F=(null==n?void 0:n.get("description"))??null,L=(null==n?void 0:n.get("content"))??new h.OrderedMap;b=b||L.keySeq().first()||"";const B=L.get(b)??(0,h.OrderedMap)(),$=B.get("schema",(0,h.OrderedMap)()),q=B.get("examples",null),U=null==q?void 0:o()(q).call(q,((e,t)=>{var r;const o=null===(r=e)||void 0===r?void 0:r.get("value",null);return o&&(e=e.set("value",m(n,b,t,v),o)),e}));if(a=h.List.isList(a)?a:(0,h.List)(),!B.size)return null;const z="object"===B.getIn(["schema","type"]),V="binary"===B.getIn(["schema","format"]),W="base64"===B.getIn(["schema","format"]);if("application/octet-stream"===b||0===i()(b).call(b,"image/")||0===i()(b).call(b,"audio/")||0===i()(b).call(b,"video/")||V||W){const e=c("Input");return w?p.createElement(e,{type:"file",onChange:k}):p.createElement("i",null,"Example values are not available for ",p.createElement("code",null,b)," media types.")}if(z&&("application/x-www-form-urlencoded"===b||0===i()(b).call(b,"multipart/"))&&$.get("properties",(0,h.OrderedMap)()).size>0){var J;const e=c("JsonSchemaForm"),t=c("ParameterExt"),n=$.get("properties",(0,h.OrderedMap)());return r=h.Map.isMap(r)?r:(0,h.OrderedMap)(),p.createElement("div",{className:"table-container"},F&&p.createElement(C,{source:F}),p.createElement("table",null,p.createElement("tbody",null,h.Map.isMap(n)&&o()(J=n.entrySeq()).call(J,(n=>{var i,d;let[m,g]=n;if(g.get("readOnly"))return;let y=D?(0,f.po)(g):null;const b=l()(i=$.get("required",(0,h.List)())).call(i,m),E=g.get("type"),_=g.get("format"),j=g.get("description"),O=r.getIn([m,"value"]),k=r.getIn([m,"errors"])||a,P=s.get(m)||!1,N=g.has("default")||g.has("example")||g.hasIn(["items","example"])||g.hasIn(["items","default"]),I=g.has("enum")&&(1===g.get("enum").size||b),T=N||I;let R="";"array"!==E||T||(R=[]),("object"===E||T)&&(R=v.getSampleSchema(g,!1,{includeWriteOnly:!0})),"string"!=typeof R&&"object"===E&&(R=(0,f.Pz)(R)),"string"==typeof R&&"array"===E&&(R=JSON.parse(R));const F="string"===E&&("binary"===_||"base64"===_);return p.createElement("tr",{key:m,className:"parameters","data-property-name":m},p.createElement("td",{className:"parameters-col_name"},p.createElement("div",{className:b?"parameter__name required":"parameter__name"},m,b?p.createElement("span",null," *"):null),p.createElement("div",{className:"parameter__type"},E,_&&p.createElement("span",{className:"prop-format"},"($",_,")"),D&&y.size?o()(d=y.entrySeq()).call(d,(e=>{let[n,r]=e;return p.createElement(t,{key:`${n}-${r}`,xKey:n,xVal:r})})):null),p.createElement("div",{className:"parameter__deprecated"},g.get("deprecated")?"deprecated":null)),p.createElement("td",{className:"parameters-col_description"},p.createElement(C,{source:j}),w?p.createElement("div",null,p.createElement(e,{fn:v,dispatchInitialValue:!F,schema:g,description:m,getComponent:c,value:void 0===O?R:O,required:b,errors:k,onChange:e=>{x(e,[m])}}),b?null:p.createElement(M,{onChange:e=>S(m,e),isIncluded:P,isIncludedOptions:A(m),isDisabled:u()(O)?0!==O.length:!(0,f.O2)(O)})):null))})))))}const K=m(n,b,_,v);let H=null;return(0,d.O)(K)&&(H="json"),p.createElement("div",null,F&&p.createElement(C,{source:F}),U?p.createElement(T,{userHasEditedBody:t,examples:U,currentKey:_,currentUserInputValue:r,onSelect:e=>{j(e)},updateValue:x,defaultToFirstExample:!0,getComponent:c,setRetainRequestBodyValueFlag:O}):null,w?p.createElement("div",null,p.createElement(N,{value:r,errors:a,defaultValue:K,onChange:x,getComponent:c})):p.createElement(P,{getComponent:c,getConfigs:g,specSelectors:y,expandDepth:1,isExecute:w,schema:B.get("schema"),specPath:E.push("content",b),example:p.createElement(I,{className:"body-param__example",getConfigs:g,language:H,value:(0,f.Pz)(r)||K}),includeWriteOnly:!0}),U?p.createElement(R,{example:U.get(_),getComponent:c,getConfigs:g}):null)}},9928:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);class o extends r.Component{render(){const{specSelectors:e,oas3Selectors:t,oas3Actions:n,getComponent:o}=this.props,s=e.servers(),i=o("Servers");return s&&s.size?r.createElement("div",null,r.createElement("span",{className:"servers-title"},"Servers"),r.createElement(i,{servers:s,currentServer:t.selectedServer(),setSelectedServer:n.setSelectedServer,setServerVariableValue:n.setServerVariableValue,getServerVariable:t.serverVariableValue,getEffectiveServerValue:t.serverEffectiveValue})):null}}},56617:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(61125),o=n.n(r),s=n(51679),i=n.n(s),a=n(97606),l=n.n(a),c=n(67294),u=n(43393);n(23930);class p extends c.Component{constructor(){super(...arguments),o()(this,"onServerChange",(e=>{this.setServer(e.target.value)})),o()(this,"onServerVariableValueChange",(e=>{let{setServerVariableValue:t,currentServer:n}=this.props,r=e.target.getAttribute("data-variable"),o=e.target.value;"function"==typeof t&&t({server:n,key:r,val:o})})),o()(this,"setServer",(e=>{let{setSelectedServer:t}=this.props;t(e)}))}componentDidMount(){var e;let{servers:t,currentServer:n}=this.props;n||this.setServer(null===(e=t.first())||void 0===e?void 0:e.get("url"))}UNSAFE_componentWillReceiveProps(e){let{servers:t,setServerVariableValue:n,getServerVariable:r}=e;if(this.props.currentServer!==e.currentServer||this.props.servers!==e.servers){var o;let s=i()(t).call(t,(t=>t.get("url")===e.currentServer)),a=i()(o=this.props.servers).call(o,(e=>e.get("url")===this.props.currentServer))||(0,u.OrderedMap)();if(!s)return this.setServer(t.first().get("url"));let c=a.get("variables")||(0,u.OrderedMap)(),p=(i()(c).call(c,(e=>e.get("default")))||(0,u.OrderedMap)()).get("default"),h=s.get("variables")||(0,u.OrderedMap)(),f=(i()(h).call(h,(e=>e.get("default")))||(0,u.OrderedMap)()).get("default");l()(h).call(h,((t,o)=>{r(e.currentServer,o)&&p===f||n({server:e.currentServer,key:o,val:t.get("default")||""})}))}}render(){var e,t;let{servers:n,currentServer:r,getServerVariable:o,getEffectiveServerValue:s}=this.props,a=(i()(n).call(n,(e=>e.get("url")===r))||(0,u.OrderedMap)()).get("variables")||(0,u.OrderedMap)(),p=0!==a.size;return c.createElement("div",{className:"servers"},c.createElement("label",{htmlFor:"servers"},c.createElement("select",{onChange:this.onServerChange,value:r},l()(e=n.valueSeq()).call(e,(e=>c.createElement("option",{value:e.get("url"),key:e.get("url")},e.get("url"),e.get("description")&&` - ${e.get("description")}`))).toArray())),p?c.createElement("div",null,c.createElement("div",{className:"computed-url"},"Computed URL:",c.createElement("code",null,s(r))),c.createElement("h4",null,"Server variables"),c.createElement("table",null,c.createElement("tbody",null,l()(t=a.entrySeq()).call(t,(e=>{var t;let[n,s]=e;return c.createElement("tr",{key:n},c.createElement("td",null,n),c.createElement("td",null,s.get("enum")?c.createElement("select",{"data-variable":n,onChange:this.onServerVariableValueChange},l()(t=s.get("enum")).call(t,(e=>c.createElement("option",{selected:e===o(r,n),key:e,value:e},e)))):c.createElement("input",{type:"text",value:o(r,n)||"",onChange:this.onServerVariableValueChange,"data-variable":n})))}))))):null)}}},7779:(e,t,n)=>{"use strict";n.r(t),n.d(t,{OAS30ComponentWrapFactory:()=>c,OAS3ComponentWrapFactory:()=>l,isOAS30:()=>i,isSwagger2:()=>a});var r=n(23101),o=n.n(r),s=n(67294);function i(e){const t=e.get("openapi");return"string"==typeof t&&/^3\.0\.([0123])(?:-rc[012])?$/.test(t)}function a(e){const t=e.get("swagger");return"string"==typeof t&&"2.0"===t}function l(e){return(t,n)=>r=>{var i;return"function"==typeof(null===(i=n.specSelectors)||void 0===i?void 0:i.isOAS3)?n.specSelectors.isOAS3()?s.createElement(e,o()({},r,n,{Ori:t})):s.createElement(t,r):(console.warn("OAS3 wrapper: couldn't get spec"),null)}}function c(e){return(t,n)=>r=>{var i;return"function"==typeof(null===(i=n.specSelectors)||void 0===i?void 0:i.isOAS30)?n.specSelectors.isOAS30()?s.createElement(e,o()({},r,n,{Ori:t})):s.createElement(t,r):(console.warn("OAS30 wrapper: couldn't get spec"),null)}}},97451:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(92044),o=n(73723),s=n(91741),i=n(76467),a=n(37761),l=n(67002),c=n(5065),u=n(62109);function p(){return{components:i.default,wrapComponents:a.default,statePlugins:{spec:{wrapSelectors:r,selectors:s},auth:{wrapSelectors:o},oas3:{actions:l,reducers:u.default,selectors:c}}}}},62109:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(8712),o=n.n(r),s=n(86),i=n.n(s),a=n(24282),l=n.n(a),c=n(43393),u=n(67002);const p={[u.UPDATE_SELECTED_SERVER]:(e,t)=>{let{payload:{selectedServerUrl:n,namespace:r}}=t;const o=r?[r,"selectedServer"]:["selectedServer"];return e.setIn(o,n)},[u.UPDATE_REQUEST_BODY_VALUE]:(e,t)=>{let{payload:{value:n,pathMethod:r}}=t,[s,a]=r;if(!c.Map.isMap(n))return e.setIn(["requestData",s,a,"bodyValue"],n);let l,u=e.getIn(["requestData",s,a,"bodyValue"])||(0,c.Map)();c.Map.isMap(u)||(u=(0,c.Map)());const[...p]=o()(n).call(n);return i()(p).call(p,(e=>{let t=n.getIn([e]);u.has(e)&&c.Map.isMap(t)||(l=u.setIn([e,"value"],t))})),e.setIn(["requestData",s,a,"bodyValue"],l)},[u.UPDATE_REQUEST_BODY_VALUE_RETAIN_FLAG]:(e,t)=>{let{payload:{value:n,pathMethod:r}}=t,[o,s]=r;return e.setIn(["requestData",o,s,"retainBodyValue"],n)},[u.UPDATE_REQUEST_BODY_INCLUSION]:(e,t)=>{let{payload:{value:n,pathMethod:r,name:o}}=t,[s,i]=r;return e.setIn(["requestData",s,i,"bodyInclusion",o],n)},[u.UPDATE_ACTIVE_EXAMPLES_MEMBER]:(e,t)=>{let{payload:{name:n,pathMethod:r,contextType:o,contextName:s}}=t,[i,a]=r;return e.setIn(["examples",i,a,o,s,"activeExample"],n)},[u.UPDATE_REQUEST_CONTENT_TYPE]:(e,t)=>{let{payload:{value:n,pathMethod:r}}=t,[o,s]=r;return e.setIn(["requestData",o,s,"requestContentType"],n)},[u.UPDATE_RESPONSE_CONTENT_TYPE]:(e,t)=>{let{payload:{value:n,path:r,method:o}}=t;return e.setIn(["requestData",r,o,"responseContentType"],n)},[u.UPDATE_SERVER_VARIABLE_VALUE]:(e,t)=>{let{payload:{server:n,namespace:r,key:o,val:s}}=t;const i=r?[r,"serverVariableValues",n,o]:["serverVariableValues",n,o];return e.setIn(i,s)},[u.SET_REQUEST_BODY_VALIDATE_ERROR]:(e,t)=>{let{payload:{path:n,method:r,validationErrors:o}}=t,s=[];if(s.push("Required field is not provided"),o.missingBodyValue)return e.setIn(["requestData",n,r,"errors"],(0,c.fromJS)(s));if(o.missingRequiredKeys&&o.missingRequiredKeys.length>0){const{missingRequiredKeys:t}=o;return e.updateIn(["requestData",n,r,"bodyValue"],(0,c.fromJS)({}),(e=>l()(t).call(t,((e,t)=>e.setIn([t,"errors"],(0,c.fromJS)(s))),e)))}return console.warn("unexpected result: SET_REQUEST_BODY_VALIDATE_ERROR"),e},[u.CLEAR_REQUEST_BODY_VALIDATE_ERROR]:(e,t)=>{let{payload:{path:n,method:r}}=t;const s=e.getIn(["requestData",n,r,"bodyValue"]);if(!c.Map.isMap(s))return e.setIn(["requestData",n,r,"errors"],(0,c.fromJS)([]));const[...i]=o()(s).call(s);return i?e.updateIn(["requestData",n,r,"bodyValue"],(0,c.fromJS)({}),(e=>l()(i).call(i,((e,t)=>e.setIn([t,"errors"],(0,c.fromJS)([]))),e))):e},[u.CLEAR_REQUEST_BODY_VALUE]:(e,t)=>{let{payload:{pathMethod:n}}=t,[r,o]=n;const s=e.getIn(["requestData",r,o,"bodyValue"]);return s?c.Map.isMap(s)?e.setIn(["requestData",r,o,"bodyValue"],(0,c.Map)()):e.setIn(["requestData",r,o,"bodyValue"],""):e}}},5065:(e,t,n)=>{"use strict";n.r(t),n.d(t,{activeExamplesMember:()=>S,hasUserEditedBody:()=>w,requestBodyErrors:()=>x,requestBodyInclusionSetting:()=>E,requestBodyValue:()=>y,requestContentType:()=>_,responseContentType:()=>j,selectDefaultRequestBodyValue:()=>b,selectedServer:()=>g,serverEffectiveValue:()=>A,serverVariableValue:()=>O,serverVariables:()=>k,shouldRetainRequestBodyValue:()=>v,validOperationMethods:()=>I,validateBeforeExecute:()=>C,validateShallowRequired:()=>N});var r=n(97606),o=n.n(r),s=n(86),i=n.n(s),a=n(28222),l=n.n(a),c=n(11882),u=n.n(c),p=n(43393),h=n(20573),f=n(42458),d=n(90242);const m=e=>function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o{if(n.getSystem().specSelectors.isOAS3()){const o=e(t,...r);return"function"==typeof o?o(n):o}return null}};const g=m(((e,t)=>{const n=t?[t,"selectedServer"]:["selectedServer"];return e.getIn(n)||""})),y=m(((e,t,n)=>e.getIn(["requestData",t,n,"bodyValue"])||null)),v=m(((e,t,n)=>e.getIn(["requestData",t,n,"retainBodyValue"])||!1)),b=(e,t,n)=>e=>{const{oas3Selectors:r,specSelectors:o,fn:s}=e.getSystem();if(o.isOAS3()){const e=r.requestContentType(t,n);if(e)return(0,f.getDefaultRequestBodyValue)(o.specResolvedSubtree(["paths",t,n,"requestBody"]),e,r.activeExamplesMember(t,n,"requestBody","requestBody"),s)}return null},w=m(((e,t,n)=>e=>{const{oas3Selectors:r,specSelectors:o,fn:s}=e;let i=!1;const a=r.requestContentType(t,n);let l=r.requestBodyValue(t,n);const c=o.specResolvedSubtree(["paths",t,n,"requestBody"]);if(!c)return!1;if(p.Map.isMap(l)&&(l=(0,d.Pz)(l.mapEntries((e=>p.Map.isMap(e[1])?[e[0],e[1].get("value")]:e)).toJS())),p.List.isList(l)&&(l=(0,d.Pz)(l)),a){const e=(0,f.getDefaultRequestBodyValue)(c,a,r.activeExamplesMember(t,n,"requestBody","requestBody"),s);i=!!l&&l!==e}return i})),E=m(((e,t,n)=>e.getIn(["requestData",t,n,"bodyInclusion"])||(0,p.Map)())),x=m(((e,t,n)=>e.getIn(["requestData",t,n,"errors"])||null)),S=m(((e,t,n,r,o)=>e.getIn(["examples",t,n,r,o,"activeExample"])||null)),_=m(((e,t,n)=>e.getIn(["requestData",t,n,"requestContentType"])||null)),j=m(((e,t,n)=>e.getIn(["requestData",t,n,"responseContentType"])||null)),O=m(((e,t,n)=>{let r;if("string"!=typeof t){const{server:e,namespace:o}=t;r=o?[o,"serverVariableValues",e,n]:["serverVariableValues",e,n]}else{r=["serverVariableValues",t,n]}return e.getIn(r)||null})),k=m(((e,t)=>{let n;if("string"!=typeof t){const{server:e,namespace:r}=t;n=r?[r,"serverVariableValues",e]:["serverVariableValues",e]}else{n=["serverVariableValues",t]}return e.getIn(n)||(0,p.OrderedMap)()})),A=m(((e,t)=>{var n,r;if("string"!=typeof t){const{server:o,namespace:s}=t;r=o,n=s?e.getIn([s,"serverVariableValues",r]):e.getIn(["serverVariableValues",r])}else r=t,n=e.getIn(["serverVariableValues",r]);n=n||(0,p.OrderedMap)();let s=r;return o()(n).call(n,((e,t)=>{s=s.replace(new RegExp(`{${t}}`,"g"),e)})),s})),C=(P=(e,t)=>((e,t)=>(t=t||[],!!e.getIn(["requestData",...t,"bodyValue"])))(e,t),function(){for(var e=arguments.length,t=new Array(e),n=0;n{const n=e.getSystem().specSelectors.specJson();let r=[...t][1]||[];return!n.getIn(["paths",...r,"requestBody","required"])||P(...t)}});var P;const N=(e,t)=>{var n;let{oas3RequiredRequestBodyContentType:r,oas3RequestContentType:o,oas3RequestBodyValue:s}=t,a=[];if(!p.Map.isMap(s))return a;let c=[];return i()(n=l()(r.requestContentType)).call(n,(e=>{if(e===o){let t=r.requestContentType[e];i()(t).call(t,(e=>{u()(c).call(c,e)<0&&c.push(e)}))}})),i()(c).call(c,(e=>{s.getIn([e,"value"])||a.push(e)})),a},I=(0,h.P1)((()=>["get","put","post","delete","options","head","patch","trace"]))},91741:(e,t,n)=>{"use strict";n.r(t),n.d(t,{callbacksOperations:()=>E,isOAS3:()=>v,isOAS30:()=>y,isSwagger2:()=>g,servers:()=>w});var r=n(97606),o=n.n(r),s=n(24282),i=n.n(s),a=n(14418),l=n.n(a),c=n(58118),u=n.n(c),p=n(39022),h=n.n(p),f=n(43393),d=n(7779);const m=(0,f.Map)(),g=()=>e=>{const t=e.getSystem().specSelectors.specJson();return(0,d.isSwagger2)(t)},y=()=>e=>{const t=e.getSystem().specSelectors.specJson();return(0,d.isOAS30)(t)},v=()=>e=>e.getSystem().specSelectors.isOAS30();function b(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o{if(n.specSelectors.isOAS3()){const o=e(t,...r);return"function"==typeof o?o(n):o}return null}}}const w=b((()=>e=>e.specSelectors.specJson().get("servers",m))),E=b(((e,t)=>{let{callbacks:n,specPath:r}=t;return e=>{var t;const s=e.specSelectors.validOperationMethods();return f.Map.isMap(n)?o()(t=i()(n).call(n,((e,t,n)=>f.Map.isMap(t)?i()(t).call(t,((e,t,i)=>{var a,c;if(!f.Map.isMap(t))return e;const p=o()(a=l()(c=t.entrySeq()).call(c,(e=>{let[t]=e;return u()(s).call(s,t)}))).call(a,(e=>{let[t,o]=e;return{operation:(0,f.Map)({operation:o}),method:t,path:i,callbackName:n,specPath:h()(r).call(r,[n,i,t])}}));return h()(e).call(e,p)}),(0,f.List)()):e),(0,f.List)()).groupBy((e=>e.callbackName))).call(t,(e=>e.toArray())).toObject():{}}}))},92044:(e,t,n)=>{"use strict";n.r(t),n.d(t,{basePath:()=>d,consumes:()=>m,definitions:()=>c,hasHost:()=>u,host:()=>f,produces:()=>g,schemes:()=>y,securityDefinitions:()=>p,validOperationMethods:()=>h});var r=n(20573),o=n(33881),s=n(43393);const i=(0,s.Map)();function a(e){return(t,n)=>function(){if(n.getSystem().specSelectors.isOAS3()){const t=e(...arguments);return"function"==typeof t?t(n):t}return t(...arguments)}}const l=a((0,r.P1)((()=>null))),c=a((()=>e=>{const t=e.getSystem().specSelectors.specJson().getIn(["components","schemas"]);return s.Map.isMap(t)?t:i})),u=a((()=>e=>e.getSystem().specSelectors.specJson().hasIn(["servers",0]))),p=a((0,r.P1)(o.specJsonWithResolvedSubtrees,(e=>e.getIn(["components","securitySchemes"])||null))),h=(e,t)=>function(n){if(t.specSelectors.isOAS3())return t.oas3Selectors.validOperationMethods();for(var r=arguments.length,o=new Array(r>1?r-1:0),s=1;s{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(7779).OAS3ComponentWrapFactory)((e=>{let{Ori:t,...n}=e;const{schema:o,getComponent:s,errSelectors:i,authorized:a,onAuthChange:l,name:c}=n,u=s("HttpAuth");return"http"===o.get("type")?r.createElement(u,{key:c,schema:o,name:c,errSelectors:i,authorized:a,getComponent:s,onChange:l}):r.createElement(t,n)}))},37761:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(22460),o=n(70356),s=n(69487),i=n(50058),a=n(53499),l=n(90287);const c={Markdown:r.default,AuthItem:o.default,JsonSchema_string:l.default,VersionStamp:s.default,model:a.default,onlineValidatorBadge:i.default}},90287:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(7779).OAS3ComponentWrapFactory)((e=>{let{Ori:t,...n}=e;const{schema:o,getComponent:s,errors:i,onChange:a}=n,l=o&&o.get?o.get("format"):null,c=o&&o.get?o.get("type"):null,u=s("Input");return c&&"string"===c&&l&&("binary"===l||"base64"===l)?r.createElement(u,{type:"file",className:i.length?"invalid":"",title:i.length?i:"",onChange:e=>{a(e.target.files[0])},disabled:t.isDisabled}):r.createElement(t,n)}))},22460:(e,t,n)=>{"use strict";n.r(t),n.d(t,{Markdown:()=>h,default:()=>f});var r=n(81607),o=n.n(r),s=n(67294),i=n(94184),a=n.n(i),l=n(89927),c=n(7779),u=n(4599);const p=new l._("commonmark");p.block.ruler.enable(["table"]),p.set({linkTarget:"_blank"});const h=e=>{let{source:t,className:n="",getConfigs:r}=e;if("string"!=typeof t)return null;if(t){const{useUnsafeMarkdown:e}=r(),i=p.render(t),l=(0,u.s)(i,{useUnsafeMarkdown:e});let c;return"string"==typeof l&&(c=o()(l).call(l)),s.createElement("div",{dangerouslySetInnerHTML:{__html:c},className:a()(n,"renderedMarkdown")})}return null};h.defaultProps={getConfigs:()=>({useUnsafeMarkdown:!1})};const f=(0,c.OAS3ComponentWrapFactory)(h)},53499:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(23101),o=n.n(r),s=n(67294),i=n(7779),a=n(53795);class l extends s.Component{render(){let{getConfigs:e,schema:t}=this.props,n=["model-box"],r=null;return!0===t.get("deprecated")&&(n.push("deprecated"),r=s.createElement("span",{className:"model-deprecated-warning"},"Deprecated:")),s.createElement("div",{className:n.join(" ")},r,s.createElement(a.Z,o()({},this.props,{getConfigs:e,depth:1,expandDepth:this.props.expandDepth||0})))}}const c=(0,i.OAS3ComponentWrapFactory)(l)},50058:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(7779),o=n(5623);const s=(0,r.OAS3ComponentWrapFactory)(o.Z)},69487:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(7779).OAS30ComponentWrapFactory)((e=>{const{Ori:t}=e;return r.createElement("span",null,r.createElement(t,e),r.createElement("small",{className:"version-stamp"},r.createElement("pre",{className:"version"},"OAS 3.0")))}))},92372:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(76986),o=n.n(r),s=n(25800),i=n(84380);const a=function(e){let{fn:t,getSystem:n}=e;if(t.jsonSchema202012){const e=(0,s.makeIsExpandable)(t.jsonSchema202012.isExpandable,n);o()(this.fn.jsonSchema202012,{isExpandable:e,getProperties:s.getProperties})}if("function"==typeof t.sampleFromSchema&&t.jsonSchema202012){const e=(0,i.wrapOAS31Fn)({sampleFromSchema:t.jsonSchema202012.sampleFromSchema,sampleFromSchemaGeneric:t.jsonSchema202012.sampleFromSchemaGeneric,createXMLExample:t.jsonSchema202012.createXMLExample,memoizedSampleFromSchema:t.jsonSchema202012.memoizedSampleFromSchema,memoizedCreateXMLExample:t.jsonSchema202012.memoizedCreateXMLExample},n());o()(this.fn,e)}}},89503:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=n(90242);const s=e=>{let{getComponent:t,specSelectors:n}=e;const s=n.selectContactNameField(),i=n.selectContactUrl(),a=n.selectContactEmailField(),l=t("Link");return r.createElement("div",{className:"info__contact"},i&&r.createElement("div",null,r.createElement(l,{href:(0,o.Nm)(i),target:"_blank"},s," - Website")),a&&r.createElement(l,{href:(0,o.Nm)(`mailto:${a}`)},i?`Send email to ${s}`:`Contact ${s}`))}},16133:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=n(90242);const s=e=>{let{getComponent:t,specSelectors:n}=e;const s=n.version(),i=n.url(),a=n.basePath(),l=n.host(),c=n.selectInfoSummaryField(),u=n.selectInfoDescriptionField(),p=n.selectInfoTitleField(),h=n.selectInfoTermsOfServiceUrl(),f=n.selectExternalDocsUrl(),d=n.selectExternalDocsDescriptionField(),m=n.contact(),g=n.license(),y=t("Markdown",!0),v=t("Link"),b=t("VersionStamp"),w=t("InfoUrl"),E=t("InfoBasePath"),x=t("License",!0),S=t("Contact",!0),_=t("JsonSchemaDialect",!0);return r.createElement("div",{className:"info"},r.createElement("hgroup",{className:"main"},r.createElement("h2",{className:"title"},p,s&&r.createElement(b,{version:s})),(l||a)&&r.createElement(E,{host:l,basePath:a}),i&&r.createElement(w,{getComponent:t,url:i})),c&&r.createElement("p",{className:"info__summary"},c),r.createElement("div",{className:"info__description description"},r.createElement(y,{source:u})),h&&r.createElement("div",{className:"info__tos"},r.createElement(v,{target:"_blank",href:(0,o.Nm)(h)},"Terms of service")),m.size>0&&r.createElement(S,null),g.size>0&&r.createElement(x,null),f&&r.createElement(v,{className:"info__extdocs",target:"_blank",href:(0,o.Nm)(f)},d||f),r.createElement(_,null))}},92562:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=n(90242);const s=e=>{let{getComponent:t,specSelectors:n}=e;const s=n.selectJsonSchemaDialectField(),i=n.selectJsonSchemaDialectDefault(),a=t("Link");return r.createElement(r.Fragment,null,s&&s===i&&r.createElement("p",{className:"info__jsonschemadialect"},"JSON Schema dialect:"," ",r.createElement(a,{target:"_blank",href:(0,o.Nm)(s)},s)),s&&s!==i&&r.createElement("div",{className:"error-wrapper"},r.createElement("div",{className:"no-margin"},r.createElement("div",{className:"errors"},r.createElement("div",{className:"errors-wrapper"},r.createElement("h4",{className:"center"},"Warning"),r.createElement("p",{className:"message"},r.createElement("strong",null,"OpenAPI.jsonSchemaDialect")," field contains a value different from the default value of"," ",r.createElement(a,{target:"_blank",href:i},i),". Values different from the default one are currently not supported. Please either omit the field or provide it with the default value."))))))}},51876:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294),o=n(90242);const s=e=>{let{getComponent:t,specSelectors:n}=e;const s=n.selectLicenseNameField(),i=n.selectLicenseUrl(),a=t("Link");return r.createElement("div",{className:"info__license"},i?r.createElement("div",{className:"info__license__url"},r.createElement(a,{target:"_blank",href:(0,o.Nm)(i)},s)):r.createElement("span",null,s))}},92718:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(58118),o=n.n(r),s=n(67294);n(23930);const i=e=>"string"==typeof e&&o()(e).call(e,"#/components/schemas/")?(e=>{const t=e.replace(/~1/g,"/").replace(/~0/g,"~");try{return decodeURIComponent(t)}catch{return t}})(e.replace(/^.*#\/components\/schemas\//,"")):null,a=(0,s.forwardRef)(((e,t)=>{let{schema:n,getComponent:r,onToggle:o}=e;const a=r("JSONSchema202012"),l=i(n.get("$$ref")),c=(0,s.useCallback)(((e,t)=>{o(l,t)}),[l,o]);return s.createElement(a,{name:l,schema:n.toJS(),ref:t,onExpand:c})}));a.defaultProps={name:"",displayName:"",isRef:!1,required:!1,expandDepth:0,depth:1,includeReadOnly:!1,includeWriteOnly:!1,onToggle:()=>{}};const l=a},20263:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>h});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(2018),l=n.n(a),c=n(67294),u=n(94184),p=n.n(u);const h=e=>{var t;let{specActions:n,specSelectors:r,layoutSelectors:s,layoutActions:a,getComponent:u,getConfigs:h}=e;const f=r.selectSchemas(),d=o()(f).length>0,m=["components","schemas"],{docExpansion:g,defaultModelsExpandDepth:y}=h(),v=y>0&&"none"!==g,b=s.isShown(m,v),w=u("Collapse"),E=u("JSONSchema202012"),x=u("ArrowUpIcon"),S=u("ArrowDownIcon");(0,c.useEffect)((()=>{const e=b&&y>1,t=null!=r.specResolvedSubtree(m);e&&!t&&n.requestResolvedSubtree(m)}),[b,y]);const _=(0,c.useCallback)((()=>{a.show(m,!b)}),[b]),j=(0,c.useCallback)((e=>{null!==e&&a.readyToScroll(m,e)}),[]),O=e=>t=>{null!==t&&a.readyToScroll([...m,e],t)},k=e=>(t,o)=>{if(o){const t=[...m,e];null!=r.specResolvedSubtree(t)||n.requestResolvedSubtree([...m,e])}};return!d||y<0?null:c.createElement("section",{className:p()("models",{"is-open":b}),ref:j},c.createElement("h4",null,c.createElement("button",{"aria-expanded":b,className:"models-control",onClick:_},c.createElement("span",null,"Schemas"),b?c.createElement(x,null):c.createElement(S,null))),c.createElement(w,{isOpened:b},i()(t=l()(f)).call(t,(e=>{let[t,n]=e;return c.createElement(E,{key:t,ref:O(t),schema:n,name:t,onExpand:k(t)})}))))}},33429:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=e=>{let{bypass:t,isSwagger2:n,isOAS3:o,isOAS31:s,alsoShow:i,children:a}=e;return t?r.createElement("div",null,a):n&&(o||s)?r.createElement("div",{className:"version-pragma"},i,r.createElement("div",{className:"version-pragma__message version-pragma__message--ambiguous"},r.createElement("div",null,r.createElement("h3",null,"Unable to render this definition"),r.createElement("p",null,r.createElement("code",null,"swagger")," and ",r.createElement("code",null,"openapi")," fields cannot be present in the same Swagger or OpenAPI definition. Please remove one of the fields."),r.createElement("p",null,"Supported version fields are ",r.createElement("code",null,'swagger: "2.0"')," and those that match ",r.createElement("code",null,"openapi: 3.x.y")," (for example,"," ",r.createElement("code",null,"openapi: 3.1.0"),").")))):n||o||s?r.createElement("div",null,a):r.createElement("div",{className:"version-pragma"},i,r.createElement("div",{className:"version-pragma__message version-pragma__message--missing"},r.createElement("div",null,r.createElement("h3",null,"Unable to render this definition"),r.createElement("p",null,"The provided definition does not specify a valid version field."),r.createElement("p",null,"Please indicate a valid Swagger or OpenAPI version field. Supported version fields are ",r.createElement("code",null,'swagger: "2.0"')," and those that match ",r.createElement("code",null,"openapi: 3.x.y")," (for example,"," ",r.createElement("code",null,"openapi: 3.1.0"),")."))))}},39508:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(67294);const l=e=>{let{specSelectors:t,getComponent:n}=e;const r=t.selectWebhooksOperations(),s=o()(r),l=n("OperationContainer",!0);return 0===s.length?null:a.createElement("div",{className:"webhooks"},a.createElement("h2",null,"Webhooks"),i()(s).call(s,(e=>{var t;return a.createElement("div",{key:`${e}-webhook`},i()(t=r[e]).call(t,(t=>a.createElement(l,{key:`${e}-${t.method}-webhook`,op:t.operation,tag:"webhooks",method:t.method,path:e,specPath:t.specPath,allowTryItOut:!1}))))})))}},84380:(e,t,n)=>{"use strict";n.r(t),n.d(t,{createOnlyOAS31ComponentWrapper:()=>g,createOnlyOAS31Selector:()=>f,createOnlyOAS31SelectorWrapper:()=>d,createSystemSelector:()=>m,isOAS31:()=>h,wrapOAS31Fn:()=>y});var r=n(23101),o=n.n(r),s=n(82865),i=n.n(s),a=n(97606),l=n.n(a),c=n(2018),u=n.n(c),p=n(67294);const h=e=>{const t=e.get("openapi");return"string"==typeof t&&/^3\.1\.(?:[1-9]\d*|0)$/.test(t)},f=e=>function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o{if(n.getSystem().specSelectors.isOAS31()){const o=e(t,...r);return"function"==typeof o?o(n):o}return null}},d=e=>(t,n)=>function(r){for(var o=arguments.length,s=new Array(o>1?o-1:0),i=1;ifunction(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o{const o=e(t,n,...r);return"function"==typeof o?o(n):o}},g=e=>(t,n)=>r=>n.specSelectors.isOAS31()?p.createElement(e,o()({},r,{originalComponent:t,getSystem:n.getSystem})):p.createElement(t,r),y=(e,t)=>{var n;const{fn:r,specSelectors:o}=t;return i()(l()(n=u()(e)).call(n,(e=>{let[t,n]=e;const s=r[t];return[t,function(){return o.isOAS31()?n(...arguments):"function"==typeof s?s(...arguments):void 0}]})))}},29806:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>P});var r=n(39508),o=n(51876),s=n(89503),i=n(16133),a=n(92562),l=n(33429),c=n(92718),u=n(20263),p=n(6608),h=n(77423),f=n(284),d=n(17042),m=n(22914),g=n(41434),y=n(1122),v=n(84380),b=n(9305),w=n(32884),E=n(64280),x=n(59450),S=n(36617),_=n(19525),j=n(25324),O=n(80809),k=n(14951),A=n(77536),C=n(92372);const P=e=>{let{fn:t}=e;const n=t.createSystemSelector||v.createSystemSelector,P=t.createOnlyOAS31Selector||v.createOnlyOAS31Selector;return{afterLoad:C.default,fn:{isOAS31:v.isOAS31,createSystemSelector:v.createSystemSelector,createOnlyOAS31Selector:v.createOnlyOAS31Selector},components:{Webhooks:r.default,JsonSchemaDialect:a.default,OAS31Info:i.default,OAS31License:o.default,OAS31Contact:s.default,OAS31VersionPragmaFilter:l.default,OAS31Model:c.default,OAS31Models:u.default,JSONSchema202012KeywordExample:x.default,JSONSchema202012KeywordXml:S.default,JSONSchema202012KeywordDiscriminator:_.default,JSONSchema202012KeywordExternalDocs:j.default},wrapComponents:{InfoContainer:f.default,License:p.default,Contact:h.default,VersionPragmaFilter:g.default,VersionStamp:y.default,Model:d.default,Models:m.default,JSONSchema202012KeywordDescription:O.default,JSONSchema202012KeywordDefault:k.default,JSONSchema202012KeywordProperties:A.default},statePlugins:{spec:{selectors:{isOAS31:n(b.isOAS31),license:b.license,selectLicenseNameField:b.selectLicenseNameField,selectLicenseUrlField:b.selectLicenseUrlField,selectLicenseIdentifierField:P(b.selectLicenseIdentifierField),selectLicenseUrl:n(b.selectLicenseUrl),contact:b.contact,selectContactNameField:b.selectContactNameField,selectContactEmailField:b.selectContactEmailField,selectContactUrlField:b.selectContactUrlField,selectContactUrl:n(b.selectContactUrl),selectInfoTitleField:b.selectInfoTitleField,selectInfoSummaryField:P(b.selectInfoSummaryField),selectInfoDescriptionField:b.selectInfoDescriptionField,selectInfoTermsOfServiceField:b.selectInfoTermsOfServiceField,selectInfoTermsOfServiceUrl:n(b.selectInfoTermsOfServiceUrl),selectExternalDocsDescriptionField:b.selectExternalDocsDescriptionField,selectExternalDocsUrlField:b.selectExternalDocsUrlField,selectExternalDocsUrl:n(b.selectExternalDocsUrl),webhooks:P(b.webhooks),selectWebhooksOperations:P(n(b.selectWebhooksOperations)),selectJsonSchemaDialectField:b.selectJsonSchemaDialectField,selectJsonSchemaDialectDefault:b.selectJsonSchemaDialectDefault,selectSchemas:n(b.selectSchemas)},wrapSelectors:{isOAS3:w.isOAS3,selectLicenseUrl:w.selectLicenseUrl}},oas31:{selectors:{selectLicenseUrl:P(n(E.selectLicenseUrl))}}}}}},45989:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=e=>{let{schema:t,getSystem:n}=e;if(null==t||!t.description)return null;const{getComponent:o}=n(),s=o("Markdown");return r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--description"},r.createElement("div",{className:"json-schema-2020-12-core-keyword__value json-schema-2020-12-core-keyword__value--secondary"},r.createElement(s,{source:t.description})))}},19525:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(28222),o=n.n(r),s=n(67294),i=n(94184),a=n.n(i),l=n(7749);const c=e=>{let{schema:t,getSystem:n}=e;const r=(null==t?void 0:t.discriminator)||{},{fn:i,getComponent:c}=n(),{useIsExpandedDeeply:u,useComponent:p}=i.jsonSchema202012,h=u(),f=!!r.mapping,[d,m]=(0,s.useState)(h),[g,y]=(0,s.useState)(!1),v=p("Accordion"),b=p("ExpandDeepButton"),w=c("JSONSchema202012DeepExpansionContext")(),E=(0,s.useCallback)((()=>{m((e=>!e))}),[]),x=(0,s.useCallback)(((e,t)=>{m(t),y(t)}),[]);return 0===o()(r).length?null:s.createElement(w.Provider,{value:g},s.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--discriminator"},f?s.createElement(s.Fragment,null,s.createElement(v,{expanded:d,onChange:E},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Discriminator")),s.createElement(b,{expanded:d,onClick:x})):s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Discriminator"),r.propertyName&&s.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},r.propertyName),s.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),s.createElement("ul",{className:a()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!d})},d&&s.createElement("li",{className:"json-schema-2020-12-property"},s.createElement(l.default,{discriminator:r})))))}},7749:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(28222),o=n.n(r),s=n(97606),i=n.n(s),a=n(2018),l=n.n(a),c=n(67294);const u=e=>{var t;let{discriminator:n}=e;const r=(null==n?void 0:n.mapping)||{};return 0===o()(r).length?null:i()(t=l()(r)).call(t,(e=>{let[t,n]=e;return c.createElement("div",{key:`${t}-${n}`,className:"json-schema-2020-12-keyword"},c.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},t),c.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},n))}))};u.defaultProps={mapping:void 0};const p=u},59450:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=e=>{let{schema:t,getSystem:n}=e;const{fn:o}=n(),{hasKeyword:s,stringify:i}=o.jsonSchema202012.useFn();return s(t,"example")?r.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--example"},r.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"Example"),r.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--const"},i(t.example))):null}},25324:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(28222),o=n.n(r),s=n(67294),i=n(94184),a=n.n(i),l=n(90242);const c=e=>{let{schema:t,getSystem:n}=e;const r=(null==t?void 0:t.externalDocs)||{},{fn:i,getComponent:c}=n(),{useIsExpandedDeeply:u,useComponent:p}=i.jsonSchema202012,h=u(),f=!(!r.description&&!r.url),[d,m]=(0,s.useState)(h),[g,y]=(0,s.useState)(!1),v=p("Accordion"),b=p("ExpandDeepButton"),w=c("JSONSchema202012KeywordDescription"),E=c("Link"),x=c("JSONSchema202012DeepExpansionContext")(),S=(0,s.useCallback)((()=>{m((e=>!e))}),[]),_=(0,s.useCallback)(((e,t)=>{m(t),y(t)}),[]);return 0===o()(r).length?null:s.createElement(x.Provider,{value:g},s.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--externalDocs"},f?s.createElement(s.Fragment,null,s.createElement(v,{expanded:d,onChange:S},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"External documentation")),s.createElement(b,{expanded:d,onClick:_})):s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"External documentation"),s.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),s.createElement("ul",{className:a()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!d})},d&&s.createElement(s.Fragment,null,r.description&&s.createElement("li",{className:"json-schema-2020-12-property"},s.createElement(w,{schema:r,getSystem:n})),r.url&&s.createElement("li",{className:"json-schema-2020-12-property"},s.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword"},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"url"),s.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},s.createElement(E,{target:"_blank",href:(0,l.Nm)(r.url)},r.url))))))))}},9023:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>g});var r=n(58309),o=n.n(r),s=n(28222),i=n.n(s),a=n(97606),l=n.n(a),c=n(2018),u=n.n(c),p=n(58118),h=n.n(p),f=n(67294),d=n(94184),m=n.n(d);const g=e=>{var t;let{schema:n,getSystem:r}=e;const{fn:s}=r(),{useComponent:a}=s.jsonSchema202012,{getDependentRequired:c,getProperties:p}=s.jsonSchema202012.useFn(),d=s.jsonSchema202012.useConfig(),g=o()(null==n?void 0:n.required)?n.required:[],y=a("JSONSchema"),v=p(n,d);return 0===i()(v).length?null:f.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--properties"},f.createElement("ul",null,l()(t=u()(v)).call(t,(e=>{let[t,r]=e;const o=h()(g).call(g,t),s=c(t,n);return f.createElement("li",{key:t,className:m()("json-schema-2020-12-property",{"json-schema-2020-12-property--required":o})},f.createElement(y,{name:t,schema:r,dependentRequired:s}))}))))}},36617:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(28222),o=n.n(r),s=n(67294),i=n(94184),a=n.n(i);const l=e=>{let{schema:t,getSystem:n}=e;const r=(null==t?void 0:t.xml)||{},{fn:i,getComponent:l}=n(),{useIsExpandedDeeply:c,useComponent:u}=i.jsonSchema202012,p=c(),h=!!(r.name||r.namespace||r.prefix),[f,d]=(0,s.useState)(p),[m,g]=(0,s.useState)(!1),y=u("Accordion"),v=u("ExpandDeepButton"),b=l("JSONSchema202012DeepExpansionContext")(),w=(0,s.useCallback)((()=>{d((e=>!e))}),[]),E=(0,s.useCallback)(((e,t)=>{d(t),g(t)}),[]);return 0===o()(r).length?null:s.createElement(b.Provider,{value:m},s.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword--xml"},h?s.createElement(s.Fragment,null,s.createElement(y,{expanded:f,onChange:w},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"XML")),s.createElement(v,{expanded:f,onClick:E})):s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"XML"),!0===r.attribute&&s.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"attribute"),!0===r.wrapped&&s.createElement("span",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--muted"},"wrapped"),s.createElement("strong",{className:"json-schema-2020-12__attribute json-schema-2020-12__attribute--primary"},"object"),s.createElement("ul",{className:a()("json-schema-2020-12-keyword__children",{"json-schema-2020-12-keyword__children--collapsed":!f})},f&&s.createElement(s.Fragment,null,r.name&&s.createElement("li",{className:"json-schema-2020-12-property"},s.createElement("div",{className:"json-schema-2020-12-keyword json-schema-2020-12-keyword"},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"name"),s.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},r.name))),r.namespace&&s.createElement("li",{className:"json-schema-2020-12-property"},s.createElement("div",{className:"json-schema-2020-12-keyword"},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"namespace"),s.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},r.namespace))),r.prefix&&s.createElement("li",{className:"json-schema-2020-12-property"},s.createElement("div",{className:"json-schema-2020-12-keyword"},s.createElement("span",{className:"json-schema-2020-12-keyword__name json-schema-2020-12-keyword__name--secondary"},"prefix"),s.createElement("span",{className:"json-schema-2020-12-keyword__value json-schema-2020-12-keyword__value--secondary"},r.prefix)))))))}},25800:(e,t,n)=>{"use strict";n.r(t),n.d(t,{getProperties:()=>u,makeIsExpandable:()=>c});var r=n(2018),o=n.n(r),s=n(14418),i=n.n(s),a=n(82865),l=n.n(a);const c=(e,t)=>{const{fn:n}=t();if("function"!=typeof e)return null;const{hasKeyword:r}=n.jsonSchema202012;return t=>e(t)||r(t,"example")||(null==t?void 0:t.xml)||(null==t?void 0:t.discriminator)||(null==t?void 0:t.externalDocs)},u=(e,t)=>{let{includeReadOnly:n,includeWriteOnly:r}=t;if(null==e||!e.properties)return{};const s=o()(e.properties),a=i()(s).call(s,(e=>{let[,t]=e;const o=!0===(null==t?void 0:t.readOnly),s=!0===(null==t?void 0:t.writeOnly);return(!o||n)&&(!s||r)}));return l()(a)}},14951:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)((e=>{let{schema:t,getSystem:n,originalComponent:o}=e;const{getComponent:s}=n(),i=s("JSONSchema202012KeywordDiscriminator"),a=s("JSONSchema202012KeywordXml"),l=s("JSONSchema202012KeywordExample"),c=s("JSONSchema202012KeywordExternalDocs");return r.createElement(r.Fragment,null,r.createElement(o,{schema:t}),r.createElement(i,{schema:t,getSystem:n}),r.createElement(a,{schema:t,getSystem:n}),r.createElement(c,{schema:t,getSystem:n}),r.createElement(l,{schema:t,getSystem:n}))}))},80809:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(45989);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)(r.default)},77536:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(9023);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)(r.default)},64280:(e,t,n)=>{"use strict";n.r(t),n.d(t,{selectLicenseUrl:()=>s});var r=n(20573),o=n(63543);const s=(0,r.P1)(((e,t)=>t.specSelectors.url()),((e,t)=>t.oas3Selectors.selectedServer()),((e,t)=>t.specSelectors.selectLicenseUrlField()),((e,t)=>t.specSelectors.selectLicenseIdentifierField()),((e,t,n,r)=>n?(0,o.mn)(n,e,{selectedServer:t}):r?`https://spdx.org/licenses/${r}.html`:void 0))},9305:(e,t,n)=>{"use strict";n.r(t),n.d(t,{contact:()=>A,isOAS31:()=>w,license:()=>S,selectContactEmailField:()=>P,selectContactNameField:()=>C,selectContactUrl:()=>I,selectContactUrlField:()=>N,selectExternalDocsDescriptionField:()=>L,selectExternalDocsUrl:()=>$,selectExternalDocsUrlField:()=>B,selectInfoDescriptionField:()=>M,selectInfoSummaryField:()=>R,selectInfoTermsOfServiceField:()=>D,selectInfoTermsOfServiceUrl:()=>F,selectInfoTitleField:()=>T,selectJsonSchemaDialectDefault:()=>U,selectJsonSchemaDialectField:()=>q,selectLicenseIdentifierField:()=>k,selectLicenseNameField:()=>_,selectLicenseUrl:()=>O,selectLicenseUrlField:()=>j,selectSchemas:()=>z,selectWebhooksOperations:()=>x,webhooks:()=>E});var r=n(97606),o=n.n(r),s=n(24282),i=n.n(s),a=n(14418),l=n.n(a),c=n(58118),u=n.n(c),p=n(39022),h=n.n(p),f=n(2018),d=n.n(f),m=n(43393),g=n(20573),y=n(63543),v=n(84380);const b=(0,m.Map)(),w=(0,g.P1)(((e,t)=>t.specSelectors.specJson()),v.isOAS31),E=()=>e=>e.specSelectors.specJson().get("webhooks",b),x=(0,g.P1)(((e,t)=>t.specSelectors.webhooks()),((e,t)=>t.specSelectors.validOperationMethods()),((e,t)=>t.specSelectors.specResolvedSubtree(["webhooks"])),((e,t)=>{var n;return m.Map.isMap(e)?o()(n=i()(e).call(e,((e,n,r)=>{var s,i;if(!m.Map.isMap(n))return e;const a=o()(s=l()(i=n.entrySeq()).call(i,(e=>{let[n]=e;return u()(t).call(t,n)}))).call(s,(e=>{let[t,n]=e;return{operation:(0,m.Map)({operation:n}),method:t,path:r,specPath:(0,m.List)(["webhooks",r,t])}}));return h()(e).call(e,a)}),(0,m.List)()).groupBy((e=>e.path))).call(n,(e=>e.toArray())).toObject():{}})),S=()=>e=>e.specSelectors.info().get("license",b),_=()=>e=>e.specSelectors.license().get("name","License"),j=()=>e=>e.specSelectors.license().get("url"),O=(0,g.P1)(((e,t)=>t.specSelectors.url()),((e,t)=>t.oas3Selectors.selectedServer()),((e,t)=>t.specSelectors.selectLicenseUrlField()),((e,t,n)=>{if(n)return(0,y.mn)(n,e,{selectedServer:t})})),k=()=>e=>e.specSelectors.license().get("identifier"),A=()=>e=>e.specSelectors.info().get("contact",b),C=()=>e=>e.specSelectors.contact().get("name","the developer"),P=()=>e=>e.specSelectors.contact().get("email"),N=()=>e=>e.specSelectors.contact().get("url"),I=(0,g.P1)(((e,t)=>t.specSelectors.url()),((e,t)=>t.oas3Selectors.selectedServer()),((e,t)=>t.specSelectors.selectContactUrlField()),((e,t,n)=>{if(n)return(0,y.mn)(n,e,{selectedServer:t})})),T=()=>e=>e.specSelectors.info().get("title"),R=()=>e=>e.specSelectors.info().get("summary"),M=()=>e=>e.specSelectors.info().get("description"),D=()=>e=>e.specSelectors.info().get("termsOfService"),F=(0,g.P1)(((e,t)=>t.specSelectors.url()),((e,t)=>t.oas3Selectors.selectedServer()),((e,t)=>t.specSelectors.selectInfoTermsOfServiceField()),((e,t,n)=>{if(n)return(0,y.mn)(n,e,{selectedServer:t})})),L=()=>e=>e.specSelectors.externalDocs().get("description"),B=()=>e=>e.specSelectors.externalDocs().get("url"),$=(0,g.P1)(((e,t)=>t.specSelectors.url()),((e,t)=>t.oas3Selectors.selectedServer()),((e,t)=>t.specSelectors.selectExternalDocsUrlField()),((e,t,n)=>{if(n)return(0,y.mn)(n,e,{selectedServer:t})})),q=()=>e=>e.specSelectors.specJson().get("jsonSchemaDialect"),U=()=>"https://spec.openapis.org/oas/3.1/dialect/base",z=(0,g.P1)(((e,t)=>t.specSelectors.definitions()),((e,t)=>t.specSelectors.specResolvedSubtree(["components","schemas"])),((e,t)=>{var n;return m.Map.isMap(e)?m.Map.isMap(t)?i()(n=d()(e.toJS())).call(n,((e,n)=>{let[r,o]=n;const s=t.get(r);return e[r]=(null==s?void 0:s.toJS())||o,e}),{}):e.toJS():{}}))},32884:(e,t,n)=>{"use strict";n.r(t),n.d(t,{isOAS3:()=>o,selectLicenseUrl:()=>s});var r=n(84380);const o=(e,t)=>function(n){const r=t.specSelectors.isOAS31();for(var o=arguments.length,s=new Array(o>1?o-1:0),i=1;i(e,t)=>t.oas31Selectors.selectLicenseUrl()))},77423:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)((e=>{let{getSystem:t}=e;const n=t().getComponent("OAS31Contact",!0);return r.createElement(n,null)}))},284:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)((e=>{let{getSystem:t}=e;const n=t().getComponent("OAS31Info",!0);return r.createElement(n,null)}))},6608:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)((e=>{let{getSystem:t}=e;const n=t().getComponent("OAS31License",!0);return r.createElement(n,null)}))},17042:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(67294),o=n(84380),s=n(25800);const i=(0,o.createOnlyOAS31ComponentWrapper)((e=>{let{getSystem:t,...n}=e;const o=t(),{getComponent:i,fn:a,getConfigs:l}=o,c=l(),u=i("OAS31Model"),p=i("JSONSchema202012"),h=i("JSONSchema202012Keyword$schema"),f=i("JSONSchema202012Keyword$vocabulary"),d=i("JSONSchema202012Keyword$id"),m=i("JSONSchema202012Keyword$anchor"),g=i("JSONSchema202012Keyword$dynamicAnchor"),y=i("JSONSchema202012Keyword$ref"),v=i("JSONSchema202012Keyword$dynamicRef"),b=i("JSONSchema202012Keyword$defs"),w=i("JSONSchema202012Keyword$comment"),E=i("JSONSchema202012KeywordAllOf"),x=i("JSONSchema202012KeywordAnyOf"),S=i("JSONSchema202012KeywordOneOf"),_=i("JSONSchema202012KeywordNot"),j=i("JSONSchema202012KeywordIf"),O=i("JSONSchema202012KeywordThen"),k=i("JSONSchema202012KeywordElse"),A=i("JSONSchema202012KeywordDependentSchemas"),C=i("JSONSchema202012KeywordPrefixItems"),P=i("JSONSchema202012KeywordItems"),N=i("JSONSchema202012KeywordContains"),I=i("JSONSchema202012KeywordProperties"),T=i("JSONSchema202012KeywordPatternProperties"),R=i("JSONSchema202012KeywordAdditionalProperties"),M=i("JSONSchema202012KeywordPropertyNames"),D=i("JSONSchema202012KeywordUnevaluatedItems"),F=i("JSONSchema202012KeywordUnevaluatedProperties"),L=i("JSONSchema202012KeywordType"),B=i("JSONSchema202012KeywordEnum"),$=i("JSONSchema202012KeywordConst"),q=i("JSONSchema202012KeywordConstraint"),U=i("JSONSchema202012KeywordDependentRequired"),z=i("JSONSchema202012KeywordContentSchema"),V=i("JSONSchema202012KeywordTitle"),W=i("JSONSchema202012KeywordDescription"),J=i("JSONSchema202012KeywordDefault"),K=i("JSONSchema202012KeywordDeprecated"),H=i("JSONSchema202012KeywordReadOnly"),G=i("JSONSchema202012KeywordWriteOnly"),Z=i("JSONSchema202012Accordion"),Y=i("JSONSchema202012ExpandDeepButton"),X=i("JSONSchema202012ChevronRightIcon"),Q=i("withJSONSchema202012Context")(u,{config:{default$schema:"https://spec.openapis.org/oas/3.1/dialect/base",defaultExpandedLevels:c.defaultModelExpandDepth,includeReadOnly:Boolean(n.includeReadOnly),includeWriteOnly:Boolean(n.includeWriteOnly)},components:{JSONSchema:p,Keyword$schema:h,Keyword$vocabulary:f,Keyword$id:d,Keyword$anchor:m,Keyword$dynamicAnchor:g,Keyword$ref:y,Keyword$dynamicRef:v,Keyword$defs:b,Keyword$comment:w,KeywordAllOf:E,KeywordAnyOf:x,KeywordOneOf:S,KeywordNot:_,KeywordIf:j,KeywordThen:O,KeywordElse:k,KeywordDependentSchemas:A,KeywordPrefixItems:C,KeywordItems:P,KeywordContains:N,KeywordProperties:I,KeywordPatternProperties:T,KeywordAdditionalProperties:R,KeywordPropertyNames:M,KeywordUnevaluatedItems:D,KeywordUnevaluatedProperties:F,KeywordType:L,KeywordEnum:B,KeywordConst:$,KeywordConstraint:q,KeywordDependentRequired:U,KeywordContentSchema:z,KeywordTitle:V,KeywordDescription:W,KeywordDefault:J,KeywordDeprecated:K,KeywordReadOnly:H,KeywordWriteOnly:G,Accordion:Z,ExpandDeepButton:Y,ChevronRightIcon:X},fn:{upperFirst:a.upperFirst,isExpandable:(0,s.makeIsExpandable)(a.jsonSchema202012.isExpandable,t),getProperties:s.getProperties}});return r.createElement(Q,n)}))},22914:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>s});var r=n(67294);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)((e=>{let{getSystem:t}=e;const{getComponent:n,fn:s,getConfigs:i}=t(),a=i();if(o.ModelsWithJSONSchemaContext)return r.createElement(o.ModelsWithJSONSchemaContext,null);const l=n("OAS31Models",!0),c=n("JSONSchema202012"),u=n("JSONSchema202012Keyword$schema"),p=n("JSONSchema202012Keyword$vocabulary"),h=n("JSONSchema202012Keyword$id"),f=n("JSONSchema202012Keyword$anchor"),d=n("JSONSchema202012Keyword$dynamicAnchor"),m=n("JSONSchema202012Keyword$ref"),g=n("JSONSchema202012Keyword$dynamicRef"),y=n("JSONSchema202012Keyword$defs"),v=n("JSONSchema202012Keyword$comment"),b=n("JSONSchema202012KeywordAllOf"),w=n("JSONSchema202012KeywordAnyOf"),E=n("JSONSchema202012KeywordOneOf"),x=n("JSONSchema202012KeywordNot"),S=n("JSONSchema202012KeywordIf"),_=n("JSONSchema202012KeywordThen"),j=n("JSONSchema202012KeywordElse"),O=n("JSONSchema202012KeywordDependentSchemas"),k=n("JSONSchema202012KeywordPrefixItems"),A=n("JSONSchema202012KeywordItems"),C=n("JSONSchema202012KeywordContains"),P=n("JSONSchema202012KeywordProperties"),N=n("JSONSchema202012KeywordPatternProperties"),I=n("JSONSchema202012KeywordAdditionalProperties"),T=n("JSONSchema202012KeywordPropertyNames"),R=n("JSONSchema202012KeywordUnevaluatedItems"),M=n("JSONSchema202012KeywordUnevaluatedProperties"),D=n("JSONSchema202012KeywordType"),F=n("JSONSchema202012KeywordEnum"),L=n("JSONSchema202012KeywordConst"),B=n("JSONSchema202012KeywordConstraint"),$=n("JSONSchema202012KeywordDependentRequired"),q=n("JSONSchema202012KeywordContentSchema"),U=n("JSONSchema202012KeywordTitle"),z=n("JSONSchema202012KeywordDescription"),V=n("JSONSchema202012KeywordDefault"),W=n("JSONSchema202012KeywordDeprecated"),J=n("JSONSchema202012KeywordReadOnly"),K=n("JSONSchema202012KeywordWriteOnly"),H=n("JSONSchema202012Accordion"),G=n("JSONSchema202012ExpandDeepButton"),Z=n("JSONSchema202012ChevronRightIcon"),Y=n("withJSONSchema202012Context");return o.ModelsWithJSONSchemaContext=Y(l,{config:{default$schema:"https://spec.openapis.org/oas/3.1/dialect/base",defaultExpandedLevels:a.defaultModelsExpandDepth-1,includeReadOnly:!0,includeWriteOnly:!0},components:{JSONSchema:c,Keyword$schema:u,Keyword$vocabulary:p,Keyword$id:h,Keyword$anchor:f,Keyword$dynamicAnchor:d,Keyword$ref:m,Keyword$dynamicRef:g,Keyword$defs:y,Keyword$comment:v,KeywordAllOf:b,KeywordAnyOf:w,KeywordOneOf:E,KeywordNot:x,KeywordIf:S,KeywordThen:_,KeywordElse:j,KeywordDependentSchemas:O,KeywordPrefixItems:k,KeywordItems:A,KeywordContains:C,KeywordProperties:P,KeywordPatternProperties:N,KeywordAdditionalProperties:I,KeywordPropertyNames:T,KeywordUnevaluatedItems:R,KeywordUnevaluatedProperties:M,KeywordType:D,KeywordEnum:F,KeywordConst:L,KeywordConstraint:B,KeywordDependentRequired:$,KeywordContentSchema:q,KeywordTitle:U,KeywordDescription:z,KeywordDefault:V,KeywordDeprecated:W,KeywordReadOnly:J,KeywordWriteOnly:K,Accordion:H,ExpandDeepButton:G,ChevronRightIcon:Z},fn:{upperFirst:s.upperFirst,isExpandable:s.jsonSchema202012.isExpandable,getProperties:s.jsonSchema202012.getProperties}}),r.createElement(o.ModelsWithJSONSchemaContext,null)}));o.ModelsWithJSONSchemaContext=null;const s=o},41434:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(23101),o=n.n(r),s=n(67294);const i=(e,t)=>e=>{const n=t.specSelectors.isOAS31(),r=t.getComponent("OAS31VersionPragmaFilter");return s.createElement(r,o()({isOAS31:n},e))}},1122:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=(0,n(84380).createOnlyOAS31ComponentWrapper)((e=>{let{originalComponent:t,...n}=e;return r.createElement("span",null,r.createElement(t,n),r.createElement("small",{className:"version-stamp"},r.createElement("pre",{className:"version"},"OAS 3.1")))}))},28560:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(87198),o=n.n(r);let s=!1;function i(){return{statePlugins:{spec:{wrapActions:{updateSpec:e=>function(){return s=!0,e(...arguments)},updateJsonSpec:(e,t)=>function(){const n=t.getConfigs().onComplete;return s&&"function"==typeof n&&(o()(n,0),s=!1),e(...arguments)}}}}}}},92135:(e,t,n)=>{"use strict";n.r(t),n.d(t,{requestSnippetGenerator_curl_bash:()=>j,requestSnippetGenerator_curl_cmd:()=>O,requestSnippetGenerator_curl_powershell:()=>_});var r=n(11882),o=n.n(r),s=n(81607),i=n.n(s),a=n(35627),l=n.n(a),c=n(97606),u=n.n(c),p=n(12196),h=n.n(p),f=n(74386),d=n.n(f),m=n(58118),g=n.n(m),y=n(27504),v=n(43393);const b=e=>{var t;const n="_**[]";return o()(e).call(e,n)<0?e:i()(t=e.split(n)[0]).call(t)},w=e=>"-d "===e||/^[_\/-]/g.test(e)?e:"'"+e.replace(/'/g,"'\\''")+"'",E=e=>"-d "===(e=e.replace(/\^/g,"^^").replace(/\\"/g,'\\\\"').replace(/"/g,'""').replace(/\n/g,"^\n"))?e.replace(/-d /g,"-d ^\n"):/^[_\/-]/g.test(e)?e:'"'+e+'"',x=e=>"-d "===e?e:/\n/.test(e)?'@"\n'+e.replace(/"/g,'\\"').replace(/`/g,"``").replace(/\$/,"`$")+'\n"@':/^[_\/-]/g.test(e)?e:"'"+e.replace(/"/g,'""').replace(/'/g,"''")+"'";const S=function(e,t,n){let r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"",o=!1,s="";const i=function(){for(var e=arguments.length,n=new Array(e),r=0;rs+=` ${n}`,p=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return s+=h()(" ").call(" ",e)};let f=e.get("headers");if(s+="curl"+r,e.has("curlOptions")&&i(...e.get("curlOptions")),i("-X",e.get("method")),c(),p(),a(`${e.get("url")}`),f&&f.size)for(let t of d()(m=e.get("headers")).call(m)){var m;c(),p();let[e,n]=t;a("-H",`${e}: ${n}`),o=o||/^content-type$/i.test(e)&&/^multipart\/form-data$/i.test(n)}const w=e.get("body");var E;if(w)if(o&&g()(E=["POST","PUT","PATCH"]).call(E,e.get("method")))for(let[e,t]of w.entrySeq()){let n=b(e);c(),p(),a("-F"),t instanceof y.Z.File&&"string"==typeof t.valueOf()?i(`${n}=${t.data}${t.type?`;type=${t.type}`:""}`):t instanceof y.Z.File?i(`${n}=@${t.name}${t.type?`;type=${t.type}`:""}`):i(`${n}=${t}`)}else if(w instanceof y.Z.File)c(),p(),a(`--data-binary '@${w.name}'`);else{c(),p(),a("-d ");let t=w;v.Map.isMap(t)?a(function(e){let t=[];for(let[n,r]of e.get("body").entrySeq()){let e=b(n);r instanceof y.Z.File?t.push(` "${e}": {\n "name": "${r.name}"${r.type?`,\n "type": "${r.type}"`:""}\n }`):t.push(` "${e}": ${l()(r,null,2).replace(/(\r\n|\r|\n)/g,"\n ")}`)}return`{\n${t.join(",\n")}\n}`}(e)):("string"!=typeof t&&(t=l()(t)),a(t))}else w||"POST"!==e.get("method")||(c(),p(),a("-d ''"));return s},_=e=>S(e,x,"`\n",".exe"),j=e=>S(e,w,"\\\n"),O=e=>S(e,E,"^\n")},86575:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(92135),o=n(4669),s=n(84206);const i=()=>({components:{RequestSnippets:s.default},fn:r,statePlugins:{requestSnippets:{selectors:o}}})},84206:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>w});var r=n(14418),o=n.n(r),s=n(25110),i=n.n(s),a=n(86),l=n.n(a),c=n(97606),u=n.n(c),p=n(67294),h=n(27361),f=n.n(h),d=n(23560),m=n.n(d),g=n(74855),y=n(96513);const v={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(250, 250, 250)",paddingBottom:"0",paddingTop:"0",border:"1px solid rgb(51, 51, 51)",borderRadius:"4px 4px 0 0",boxShadow:"none",borderBottom:"none"},b={cursor:"pointer",lineHeight:1,display:"inline-flex",backgroundColor:"rgb(51, 51, 51)",boxShadow:"none",border:"1px solid rgb(51, 51, 51)",paddingBottom:"0",paddingTop:"0",borderRadius:"4px 4px 0 0",marginTop:"-5px",marginRight:"-5px",marginLeft:"-5px",zIndex:"9999",borderBottom:"none"},w=e=>{var t,n;let{request:r,requestSnippetsSelectors:s,getConfigs:a,getComponent:c}=e;const h=m()(a)?a():null,d=!1!==f()(h,"syntaxHighlight")&&f()(h,"syntaxHighlight.activated",!0),w=(0,p.useRef)(null),E=c("ArrowUpIcon"),x=c("ArrowDownIcon"),[S,_]=(0,p.useState)(null===(t=s.getSnippetGenerators())||void 0===t?void 0:t.keySeq().first()),[j,O]=(0,p.useState)(null==s?void 0:s.getDefaultExpanded());(0,p.useEffect)((()=>{}),[]),(0,p.useEffect)((()=>{var e;const t=o()(e=i()(w.current.childNodes)).call(e,(e=>{var t;return!!e.nodeType&&(null===(t=e.classList)||void 0===t?void 0:t.contains("curl-command"))}));return l()(t).call(t,(e=>e.addEventListener("mousewheel",I,{passive:!1}))),()=>{l()(t).call(t,(e=>e.removeEventListener("mousewheel",I)))}}),[r]);const k=s.getSnippetGenerators(),A=k.get(S),C=A.get("fn")(r),P=()=>{O(!j)},N=e=>e===S?b:v,I=e=>{const{target:t,deltaY:n}=e,{scrollHeight:r,offsetHeight:o,scrollTop:s}=t;r>o&&(0===s&&n<0||o+s>=r&&n>0)&&e.preventDefault()},T=d?p.createElement(y.d3,{language:A.get("syntax"),className:"curl microlight",style:(0,y.C2)(f()(h,"syntaxHighlight.theme"))},C):p.createElement("textarea",{readOnly:!0,className:"curl",value:C});return p.createElement("div",{className:"request-snippets",ref:w},p.createElement("div",{style:{width:"100%",display:"flex",justifyContent:"flex-start",alignItems:"center",marginBottom:"15px"}},p.createElement("h4",{onClick:()=>P(),style:{cursor:"pointer"}},"Snippets"),p.createElement("button",{onClick:()=>P(),style:{border:"none",background:"none"},title:j?"Collapse operation":"Expand operation"},j?p.createElement(x,{className:"arrow",width:"10",height:"10"}):p.createElement(E,{className:"arrow",width:"10",height:"10"}))),j&&p.createElement("div",{className:"curl-command"},p.createElement("div",{style:{paddingLeft:"15px",paddingRight:"10px",width:"100%",display:"flex"}},u()(n=k.entrySeq()).call(n,(e=>{let[t,n]=e;return p.createElement("div",{style:N(t),className:"btn",key:t,onClick:()=>(e=>{S!==e&&_(e)})(t)},p.createElement("h4",{style:t===S?{color:"white"}:{}},n.get("title")))}))),p.createElement("div",{className:"copy-to-clipboard"},p.createElement(g.CopyToClipboard,{text:C},p.createElement("button",null))),p.createElement("div",null,T)))}},4669:(e,t,n)=>{"use strict";n.r(t),n.d(t,{getActiveLanguage:()=>d,getDefaultExpanded:()=>m,getGenerators:()=>h,getSnippetGenerators:()=>f});var r=n(14418),o=n.n(r),s=n(58118),i=n.n(s),a=n(97606),l=n.n(a),c=n(20573),u=n(43393);const p=e=>e||(0,u.Map)(),h=(0,c.P1)(p,(e=>{const t=e.get("languages"),n=e.get("generators",(0,u.Map)());return!t||t.isEmpty()?n:o()(n).call(n,((e,n)=>i()(t).call(t,n)))})),f=e=>t=>{var n,r;let{fn:s}=t;return o()(n=l()(r=h(e)).call(r,((e,t)=>{const n=(e=>s[`requestSnippetGenerator_${e}`])(t);return"function"!=typeof n?null:e.set("fn",n)}))).call(n,(e=>e))},d=(0,c.P1)(p,(e=>e.get("activeLanguage"))),m=(0,c.P1)(p,(e=>e.get("defaultExpanded")))},36195:(e,t,n)=>{"use strict";n.r(t),n.d(t,{ErrorBoundary:()=>i,default:()=>a});var r=n(67294),o=n(56189),s=n(29403);class i extends r.Component{static getDerivedStateFromError(e){return{hasError:!0,error:e}}constructor(){super(...arguments),this.state={hasError:!1,error:null}}componentDidCatch(e,t){this.props.fn.componentDidCatch(e,t)}render(){const{getComponent:e,targetName:t,children:n}=this.props;if(this.state.hasError){const n=e("Fallback");return r.createElement(n,{name:t})}return n}}i.defaultProps={targetName:"this component",getComponent:()=>s.default,fn:{componentDidCatch:o.componentDidCatch},children:null};const a=i},29403:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(67294);const o=e=>{let{name:t}=e;return r.createElement("div",{className:"fallback"},"😱 ",r.createElement("i",null,"Could not render ","t"===t?"this component":t,", see the console."))}},56189:(e,t,n)=>{"use strict";n.r(t),n.d(t,{componentDidCatch:()=>i,withErrorBoundary:()=>a});var r=n(23101),o=n.n(r),s=n(67294);const i=console.error,a=e=>t=>{const{getComponent:n,fn:r}=e(),i=n("ErrorBoundary"),a=r.getDisplayName(t);class l extends s.Component{render(){return s.createElement(i,{targetName:a,getComponent:n,fn:r},s.createElement(t,o()({},this.props,this.context)))}}var c;return l.displayName=`WithErrorBoundary(${a})`,(c=t).prototype&&c.prototype.isReactComponent&&(l.prototype.mapStateToProps=t.prototype.mapStateToProps),l}},27621:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>u});var r=n(47475),o=n.n(r),s=n(7287),i=n.n(s),a=n(36195),l=n(29403),c=n(56189);const u=function(){let{componentList:e=[],fullOverride:t=!1}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return n=>{var r;let{getSystem:s}=n;const u=t?e:["App","BaseLayout","VersionPragmaFilter","InfoContainer","ServersContainer","SchemesContainer","AuthorizeBtnContainer","FilterContainer","Operations","OperationContainer","parameters","responses","OperationServers","Models","ModelWrapper",...e],p=i()(u,o()(r=Array(u.length)).call(r,((e,t)=>{let{fn:n}=t;return n.withErrorBoundary(e)})));return{fn:{componentDidCatch:c.componentDidCatch,withErrorBoundary:(0,c.withErrorBoundary)(s)},components:{ErrorBoundary:a.default,Fallback:l.default},wrapComponents:p}}}},72846:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>p});var r=n(24282),o=n.n(r),s=n(35627),i=n.n(s),a=n(59704),l=n.n(a);const c=[{when:/json/,shouldStringifyTypes:["string"]}],u=["object"],p=e=>(t,n,r,s)=>{const{fn:a}=e(),p=a.memoizedSampleFromSchema(t,n,s),h=typeof p,f=o()(c).call(c,((e,t)=>t.when.test(r)?[...e,...t.shouldStringifyTypes]:e),u);return l()(f,(e=>e===h))?i()(p,null,2):p}},16132:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=e=>function(t){var n,r;let o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:void 0;const{fn:a}=e();return"function"==typeof(null===(n=t)||void 0===n?void 0:n.toJS)&&(t=t.toJS()),"function"==typeof(null===(r=i)||void 0===r?void 0:r.toJS)&&(i=i.toJS()),/xml/.test(o)?a.getXmlSampleSchema(t,s,i):/(yaml|yml)/.test(o)?a.getYamlSampleSchema(t,s,o,i):a.getJsonSampleSchema(t,s,o,i)}},81169:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});const r=e=>(t,n,r)=>{const{fn:o}=e();if(t&&!t.xml&&(t.xml={}),t&&!t.xml.name){if(!t.$$ref&&(t.type||t.items||t.properties||t.additionalProperties))return'\n\x3c!-- XML example cannot be generated; root element name is undefined --\x3e';if(t.$$ref){let e=t.$$ref.match(/\S*\/(\S+)$/);t.xml.name=e[1]}}return o.memoizedCreateXMLExample(t,n,r)}},79431:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>i});var r=n(24278),o=n.n(r),s=n(1272);const i=e=>(t,n,r,i)=>{const{fn:a}=e(),l=a.getJsonSampleSchema(t,n,r,i);let c;try{c=s.ZP.dump(s.ZP.load(l),{lineWidth:-1},{schema:s.A8}),"\n"===c[c.length-1]&&(c=o()(c).call(c,0,c.length-1))}catch(e){return console.error(e),"error: could not generate yaml example"}return c.replace(/\t/g," ")}},29812:(e,t,n)=>{"use strict";n.r(t),n.d(t,{createXMLExample:()=>q,inferSchema:()=>$,memoizedCreateXMLExample:()=>V,memoizedSampleFromSchema:()=>W,sampleFromSchema:()=>U,sampleFromSchemaGeneric:()=>B});var r=n(11882),o=n.n(r),s=n(86),i=n.n(s),a=n(58309),l=n.n(a),c=n(58118),u=n.n(c),p=n(92039),h=n.n(p),f=n(24278),d=n.n(f),m=n(51679),g=n.n(m),y=n(39022),v=n.n(y),b=n(97606),w=n.n(b),E=n(35627),x=n.n(E),S=n(53479),_=n.n(S),j=n(14419),O=n.n(j),k=n(41609),A=n.n(k),C=n(90242),P=n(60314);const N={string:e=>e.pattern?(e=>{try{return new(O())(e).gen()}catch(e){return"string"}})(e.pattern):"string",string_email:()=>"user@example.com","string_date-time":()=>(new Date).toISOString(),string_date:()=>(new Date).toISOString().substring(0,10),string_uuid:()=>"3fa85f64-5717-4562-b3fc-2c963f66afa6",string_hostname:()=>"example.com",string_ipv4:()=>"198.51.100.42",string_ipv6:()=>"2001:0db8:5b96:0000:0000:426f:8e17:642a",number:()=>0,number_float:()=>0,integer:()=>0,boolean:e=>"boolean"!=typeof e.default||e.default},I=e=>{e=(0,C.mz)(e);let{type:t,format:n}=e,r=N[`${t}_${n}`]||N[t];return(0,C.Wl)(r)?r(e):"Unknown Type: "+e.type},T=e=>(0,C.XV)(e,"$$ref",(e=>"string"==typeof e&&o()(e).call(e,"#")>-1)),R=["maxProperties","minProperties"],M=["minItems","maxItems"],D=["minimum","maximum","exclusiveMinimum","exclusiveMaximum"],F=["minLength","maxLength"],L=function(e,t){var n;let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};var s;(i()(n=["example","default","enum","xml","type",...R,...M,...D,...F]).call(n,(n=>(n=>{void 0===t[n]&&void 0!==e[n]&&(t[n]=e[n])})(n))),void 0!==e.required&&l()(e.required))&&(void 0!==t.required&&t.required.length||(t.required=[]),i()(s=e.required).call(s,(e=>{var n;u()(n=t.required).call(n,e)||t.required.push(e)})));if(e.properties){t.properties||(t.properties={});let n=(0,C.mz)(e.properties);for(let s in n){var a;if(Object.prototype.hasOwnProperty.call(n,s))if(!n[s]||!n[s].deprecated)if(!n[s]||!n[s].readOnly||r.includeReadOnly)if(!n[s]||!n[s].writeOnly||r.includeWriteOnly)if(!t.properties[s])t.properties[s]=n[s],!e.required&&l()(e.required)&&-1!==o()(a=e.required).call(a,s)&&(t.required?t.required.push(s):t.required=[s])}}return e.items&&(t.items||(t.items={}),t.items=L(e.items,t.items,r)),t},B=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0,r=arguments.length>3&&void 0!==arguments[3]&&arguments[3];e&&(0,C.Wl)(e.toJS)&&(e=e.toJS());let s=void 0!==n||e&&void 0!==e.example||e&&void 0!==e.default;const a=!s&&e&&e.oneOf&&e.oneOf.length>0,c=!s&&e&&e.anyOf&&e.anyOf.length>0;if(!s&&(a||c)){const n=(0,C.mz)(a?e.oneOf[0]:e.anyOf[0]);if(L(n,e,t),!e.xml&&n.xml&&(e.xml=n.xml),void 0!==e.example&&void 0!==n.example)s=!0;else if(n.properties){e.properties||(e.properties={});let r=(0,C.mz)(n.properties);for(let s in r){var p;if(Object.prototype.hasOwnProperty.call(r,s))if(!r[s]||!r[s].deprecated)if(!r[s]||!r[s].readOnly||t.includeReadOnly)if(!r[s]||!r[s].writeOnly||t.includeWriteOnly)if(!e.properties[s])e.properties[s]=r[s],!n.required&&l()(n.required)&&-1!==o()(p=n.required).call(p,s)&&(e.required?e.required.push(s):e.required=[s])}}}const f={};let{xml:m,type:y,example:b,properties:E,additionalProperties:x,items:S}=e||{},{includeReadOnly:_,includeWriteOnly:j}=t;m=m||{};let O,{name:k,prefix:P,namespace:N}=m,F={};if(r&&(k=k||"notagname",O=(P?P+":":"")+k,N)){f[P?"xmlns:"+P:"xmlns"]=N}r&&(F[O]=[]);const $=t=>h()(t).call(t,(t=>Object.prototype.hasOwnProperty.call(e,t)));e&&!y&&(E||x||$(R)?y="object":S||$(M)?y="array":$(D)?(y="number",e.type="number"):s||e.enum||(y="string",e.type="string"));const q=t=>{var n,r,o,s,i;null!==(null===(n=e)||void 0===n?void 0:n.maxItems)&&void 0!==(null===(r=e)||void 0===r?void 0:r.maxItems)&&(t=d()(t).call(t,0,null===(i=e)||void 0===i?void 0:i.maxItems));if(null!==(null===(o=e)||void 0===o?void 0:o.minItems)&&void 0!==(null===(s=e)||void 0===s?void 0:s.minItems)){let n=0;for(;t.length<(null===(a=e)||void 0===a?void 0:a.minItems);){var a;t.push(t[n++%t.length])}}return t},U=(0,C.mz)(E);let z,V=0;const W=()=>e&&null!==e.maxProperties&&void 0!==e.maxProperties&&V>=e.maxProperties,J=t=>!e||null===e.maxProperties||void 0===e.maxProperties||!W()&&(!(t=>{var n;return!(e&&e.required&&e.required.length&&u()(n=e.required).call(n,t))})(t)||e.maxProperties-V-(()=>{if(!e||!e.required)return 0;let t=0;var n,o;return r?i()(n=e.required).call(n,(e=>t+=void 0===F[e]?0:1)):i()(o=e.required).call(o,(e=>{var n;return t+=void 0===(null===(n=F[O])||void 0===n?void 0:g()(n).call(n,(t=>void 0!==t[e])))?0:1})),e.required.length-t})()>0);if(z=r?function(n){let o=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;if(e&&U[n]){if(U[n].xml=U[n].xml||{},U[n].xml.attribute){const e=l()(U[n].enum)?U[n].enum[0]:void 0,t=U[n].example,r=U[n].default;return void(f[U[n].xml.name||n]=void 0!==t?t:void 0!==r?r:void 0!==e?e:I(U[n]))}U[n].xml.name=U[n].xml.name||n}else U[n]||!1===x||(U[n]={xml:{name:n}});let s=B(e&&U[n]||void 0,t,o,r);var i;J(n)&&(V++,l()(s)?F[O]=v()(i=F[O]).call(i,s):F[O].push(s))}:(n,o)=>{if(J(n)){if(Object.prototype.hasOwnProperty.call(e,"discriminator")&&e.discriminator&&Object.prototype.hasOwnProperty.call(e.discriminator,"mapping")&&e.discriminator.mapping&&Object.prototype.hasOwnProperty.call(e,"$$ref")&&e.$$ref&&e.discriminator.propertyName===n){for(let t in e.discriminator.mapping)if(-1!==e.$$ref.search(e.discriminator.mapping[t])){F[n]=t;break}}else F[n]=B(U[n],t,o,r);V++}},s){let o;if(o=T(void 0!==n?n:void 0!==b?b:e.default),!r){if("number"==typeof o&&"string"===y)return`${o}`;if("string"!=typeof o||"string"===y)return o;try{return JSON.parse(o)}catch(e){return o}}if(e||(y=l()(o)?"array":typeof o),"array"===y){if(!l()(o)){if("string"==typeof o)return o;o=[o]}const n=e?e.items:void 0;n&&(n.xml=n.xml||m||{},n.xml.name=n.xml.name||m.name);let s=w()(o).call(o,(e=>B(n,t,e,r)));return s=q(s),m.wrapped?(F[O]=s,A()(f)||F[O].push({_attr:f})):F=s,F}if("object"===y){if("string"==typeof o)return o;for(let t in o)Object.prototype.hasOwnProperty.call(o,t)&&(e&&U[t]&&U[t].readOnly&&!_||e&&U[t]&&U[t].writeOnly&&!j||(e&&U[t]&&U[t].xml&&U[t].xml.attribute?f[U[t].xml.name||t]=o[t]:z(t,o[t])));return A()(f)||F[O].push({_attr:f}),F}return F[O]=A()(f)?o:[{_attr:f},o],F}if("object"===y){for(let e in U)Object.prototype.hasOwnProperty.call(U,e)&&(U[e]&&U[e].deprecated||U[e]&&U[e].readOnly&&!_||U[e]&&U[e].writeOnly&&!j||z(e));if(r&&f&&F[O].push({_attr:f}),W())return F;if(!0===x)r?F[O].push({additionalProp:"Anything can be here"}):F.additionalProp1={},V++;else if(x){const n=(0,C.mz)(x),o=B(n,t,void 0,r);if(r&&n.xml&&n.xml.name&&"notagname"!==n.xml.name)F[O].push(o);else{const t=null!==e.minProperties&&void 0!==e.minProperties&&VB(L(S,e,t),t,void 0,r)));else if(l()(S.oneOf)){var G;n=w()(G=S.oneOf).call(G,(e=>B(L(S,e,t),t,void 0,r)))}else{if(!(!r||r&&m.wrapped))return B(S,t,void 0,r);n=[B(S,t,void 0,r)]}return n=q(n),r&&m.wrapped?(F[O]=n,A()(f)||F[O].push({_attr:f}),F):n}let Z;if(e&&l()(e.enum))Z=(0,C.AF)(e.enum)[0];else{if(!e)return;if(Z=I(e),"number"==typeof Z){let t=e.minimum;null!=t&&(e.exclusiveMinimum&&t++,Z=t);let n=e.maximum;null!=n&&(e.exclusiveMaximum&&n--,Z=n)}if("string"==typeof Z&&(null!==e.maxLength&&void 0!==e.maxLength&&(Z=d()(Z).call(Z,0,e.maxLength)),null!==e.minLength&&void 0!==e.minLength)){let t=0;for(;Z.length(e.schema&&(e=e.schema),e.properties&&(e.type="object"),e),q=(e,t,n)=>{const r=B(e,t,n,!0);if(r)return"string"==typeof r?r:_()(r,{declaration:!0,indent:"\t"})},U=(e,t,n)=>B(e,t,n,!1),z=(e,t,n)=>[e,x()(t),x()(n)],V=(0,P.Z)(q,z),W=(0,P.Z)(U,z)},8883:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>l});var r=n(29812),o=n(72846),s=n(79431),i=n(81169),a=n(16132);const l=e=>{let{getSystem:t}=e;return{fn:{inferSchema:r.inferSchema,sampleFromSchema:r.sampleFromSchema,sampleFromSchemaGeneric:r.sampleFromSchemaGeneric,createXMLExample:r.createXMLExample,memoizedSampleFromSchema:r.memoizedSampleFromSchema,memoizedCreateXMLExample:r.memoizedCreateXMLExample,getJsonSampleSchema:(0,o.default)(t),getYamlSampleSchema:(0,s.default)(t),getXmlSampleSchema:(0,i.default)(t),getSampleSchema:(0,a.default)(t)}}}},51228:(e,t,n)=>{"use strict";n.r(t),n.d(t,{CLEAR_REQUEST:()=>oe,CLEAR_RESPONSE:()=>re,CLEAR_VALIDATE_PARAMS:()=>se,LOG_REQUEST:()=>ne,SET_MUTATED_REQUEST:()=>te,SET_REQUEST:()=>ee,SET_RESPONSE:()=>Q,SET_SCHEME:()=>ce,UPDATE_EMPTY_PARAM_INCLUSION:()=>Y,UPDATE_JSON:()=>G,UPDATE_OPERATION_META_VALUE:()=>ie,UPDATE_PARAM:()=>Z,UPDATE_RESOLVED:()=>ae,UPDATE_RESOLVED_SUBTREE:()=>le,UPDATE_SPEC:()=>K,UPDATE_URL:()=>H,VALIDATE_PARAMS:()=>X,changeConsumesValue:()=>Ae,changeParam:()=>Ee,changeParamByIdentity:()=>xe,changeProducesValue:()=>Ce,clearRequest:()=>Fe,clearResponse:()=>De,clearValidateParams:()=>ke,execute:()=>Me,executeRequest:()=>Re,invalidateResolvedSubtreeCache:()=>_e,logRequest:()=>Te,parseToJson:()=>me,requestResolvedSubtree:()=>we,resolveSpec:()=>ye,setMutatedRequest:()=>Ie,setRequest:()=>Ne,setResponse:()=>Pe,setScheme:()=>Le,updateEmptyParamInclusion:()=>Oe,updateJsonSpec:()=>de,updateResolved:()=>he,updateResolvedSubtree:()=>Se,updateSpec:()=>pe,updateUrl:()=>fe,validateParams:()=>je});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(96718),l=n.n(a),c=n(24282),u=n.n(c),p=n(18492),h=n.n(p),f=n(86),d=n.n(f),m=n(2250),g=n.n(m),y=n(6226),v=n.n(y),b=n(14418),w=n.n(b),E=n(3665),x=n.n(E),S=n(51679),_=n.n(S),j=n(28222),O=n.n(j),k=n(76986),A=n.n(k),C=n(70586),P=n.n(C),N=n(1272),I=n(43393),T=n(84564),R=n.n(T),M=n(7710),D=n(47037),F=n.n(D),L=n(23279),B=n.n(L),$=n(36968),q=n.n($),U=n(72700),z=n.n(U),V=n(75703),W=n.n(V),J=n(90242);const K="spec_update_spec",H="spec_update_url",G="spec_update_json",Z="spec_update_param",Y="spec_update_empty_param_inclusion",X="spec_validate_param",Q="spec_set_response",ee="spec_set_request",te="spec_set_mutated_request",ne="spec_log_request",re="spec_clear_response",oe="spec_clear_request",se="spec_clear_validate_param",ie="spec_update_operation_meta_value",ae="spec_update_resolved",le="spec_update_resolved_subtree",ce="set_scheme",ue=e=>F()(e)?e:"";function pe(e){const t=ue(e).replace(/\t/g," ");if("string"==typeof e)return{type:K,payload:t}}function he(e){return{type:ae,payload:e}}function fe(e){return{type:H,payload:e}}function de(e){return{type:G,payload:e}}const me=e=>t=>{let{specActions:n,specSelectors:r,errActions:o}=t,{specStr:s}=r,i=null;try{e=e||s(),o.clear({source:"parser"}),i=N.ZP.load(e,{schema:N.A8})}catch(e){return console.error(e),o.newSpecErr({source:"parser",level:"error",message:e.reason,line:e.mark&&e.mark.line?e.mark.line+1:void 0})}return i&&"object"==typeof i?n.updateJsonSpec(i):{}};let ge=!1;const ye=(e,t)=>n=>{let{specActions:r,specSelectors:s,errActions:a,fn:{fetch:c,resolve:u,AST:p={}},getConfigs:h}=n;ge||(console.warn("specActions.resolveSpec is deprecated since v3.10.0 and will be removed in v4.0.0; use requestResolvedSubtree instead!"),ge=!0);const{modelPropertyMacro:f,parameterMacro:d,requestInterceptor:m,responseInterceptor:g}=h();void 0===e&&(e=s.specJson()),void 0===t&&(t=s.url());let y=p.getLineNumberForPath?p.getLineNumberForPath:()=>{},v=s.specStr();return u({fetch:c,spec:e,baseDoc:t,modelPropertyMacro:f,parameterMacro:d,requestInterceptor:m,responseInterceptor:g}).then((e=>{let{spec:t,errors:n}=e;if(a.clear({type:"thrown"}),o()(n)&&n.length>0){let e=i()(n).call(n,(e=>(console.error(e),e.line=e.fullPath?y(v,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",l()(e,"message",{enumerable:!0,value:e.message}),e)));a.newThrownErrBatch(e)}return r.updateResolved(t)}))};let ve=[];const be=B()((()=>{const e=u()(ve).call(ve,((e,t)=>{let{path:n,system:r}=t;return e.has(r)||e.set(r,[]),e.get(r).push(n),e}),new(h()));ve=[],d()(e).call(e,(async(e,t)=>{if(!t)return void console.error("debResolveSubtrees: don't have a system to operate on, aborting.");if(!t.fn.resolveSubtree)return void console.error("Error: Swagger-Client did not provide a `resolveSubtree` method, doing nothing.");const{errActions:n,errSelectors:r,fn:{resolveSubtree:s,fetch:a,AST:c={}},specSelectors:p,specActions:h}=t,f=c.getLineNumberForPath??W()(void 0),d=p.specStr(),{modelPropertyMacro:m,parameterMacro:y,requestInterceptor:b,responseInterceptor:E}=t.getConfigs();try{const t=await u()(e).call(e,(async(e,t)=>{let{resultMap:c,specWithCurrentSubtrees:u}=await e;const{errors:h,spec:S}=await s(u,t,{baseDoc:p.url(),modelPropertyMacro:m,parameterMacro:y,requestInterceptor:b,responseInterceptor:E});if(r.allErrors().size&&n.clearBy((e=>{var n;return"thrown"!==e.get("type")||"resolver"!==e.get("source")||!g()(n=e.get("fullPath")).call(n,((e,n)=>e===t[n]||void 0===t[n]))})),o()(h)&&h.length>0){let e=i()(h).call(h,(e=>(e.line=e.fullPath?f(d,e.fullPath):null,e.path=e.fullPath?e.fullPath.join("."):null,e.level="error",e.type="thrown",e.source="resolver",l()(e,"message",{enumerable:!0,value:e.message}),e)));n.newThrownErrBatch(e)}var _,j;S&&p.isOAS3()&&"components"===t[0]&&"securitySchemes"===t[1]&&await v().all(i()(_=w()(j=x()(S)).call(j,(e=>"openIdConnect"===e.type))).call(_,(async e=>{const t={url:e.openIdConnectUrl,requestInterceptor:b,responseInterceptor:E};try{const n=await a(t);n instanceof Error||n.status>=400?console.error(n.statusText+" "+t.url):e.openIdConnectData=JSON.parse(n.text)}catch(e){console.error(e)}})));return q()(c,t,S),u=z()(t,S,u),{resultMap:c,specWithCurrentSubtrees:u}}),v().resolve({resultMap:(p.specResolvedSubtree([])||(0,I.Map)()).toJS(),specWithCurrentSubtrees:p.specJS()}));h.updateResolvedSubtree([],t.resultMap)}catch(e){console.error(e)}}))}),35),we=e=>t=>{_()(ve).call(ve,(n=>{let{path:r,system:o}=n;return o===t&&r.toString()===e.toString()}))||(ve.push({path:e,system:t}),be())};function Ee(e,t,n,r,o){return{type:Z,payload:{path:e,value:r,paramName:t,paramIn:n,isXml:o}}}function xe(e,t,n,r){return{type:Z,payload:{path:e,param:t,value:n,isXml:r}}}const Se=(e,t)=>({type:le,payload:{path:e,value:t}}),_e=()=>({type:le,payload:{path:[],value:(0,I.Map)()}}),je=(e,t)=>({type:X,payload:{pathMethod:e,isOAS3:t}}),Oe=(e,t,n,r)=>({type:Y,payload:{pathMethod:e,paramName:t,paramIn:n,includeEmptyValue:r}});function ke(e){return{type:se,payload:{pathMethod:e}}}function Ae(e,t){return{type:ie,payload:{path:e,value:t,key:"consumes_value"}}}function Ce(e,t){return{type:ie,payload:{path:e,value:t,key:"produces_value"}}}const Pe=(e,t,n)=>({payload:{path:e,method:t,res:n},type:Q}),Ne=(e,t,n)=>({payload:{path:e,method:t,req:n},type:ee}),Ie=(e,t,n)=>({payload:{path:e,method:t,req:n},type:te}),Te=e=>({payload:e,type:ne}),Re=e=>t=>{let{fn:n,specActions:r,specSelectors:s,getConfigs:a,oas3Selectors:l}=t,{pathName:c,method:u,operation:p}=e,{requestInterceptor:h,responseInterceptor:f}=a(),m=p.toJS();var g,y;p&&p.get("parameters")&&d()(g=w()(y=p.get("parameters")).call(y,(e=>e&&!0===e.get("allowEmptyValue")))).call(g,(t=>{if(s.parameterInclusionSettingFor([c,u],t.get("name"),t.get("in"))){e.parameters=e.parameters||{};const n=(0,J.cz)(t,e.parameters);(!n||n&&0===n.size)&&(e.parameters[t.get("name")]="")}}));if(e.contextUrl=R()(s.url()).toString(),m&&m.operationId?e.operationId=m.operationId:m&&c&&u&&(e.operationId=n.opId(m,c,u)),s.isOAS3()){const t=`${c}:${u}`;e.server=l.selectedServer(t)||l.selectedServer();const n=l.serverVariables({server:e.server,namespace:t}).toJS(),r=l.serverVariables({server:e.server}).toJS();e.serverVariables=O()(n).length?n:r,e.requestContentType=l.requestContentType(c,u),e.responseContentType=l.responseContentType(c,u)||"*/*";const s=l.requestBodyValue(c,u),a=l.requestBodyInclusionSetting(c,u);var v;if(s&&s.toJS)e.requestBody=w()(v=i()(s).call(s,(e=>I.Map.isMap(e)?e.get("value"):e))).call(v,((e,t)=>(o()(e)?0!==e.length:!(0,J.O2)(e))||a.get(t))).toJS();else e.requestBody=s}let b=A()({},e);b=n.buildRequest(b),r.setRequest(e.pathName,e.method,b);e.requestInterceptor=async t=>{let n=await h.apply(void 0,[t]),o=A()({},n);return r.setMutatedRequest(e.pathName,e.method,o),n},e.responseInterceptor=f;const E=P()();return n.execute(e).then((t=>{t.duration=P()()-E,r.setResponse(e.pathName,e.method,t)})).catch((t=>{"Failed to fetch"===t.message&&(t.name="",t.message='**Failed to fetch.** \n**Possible Reasons:** \n - CORS \n - Network Failure \n - URL scheme must be "http" or "https" for CORS request.'),r.setResponse(e.pathName,e.method,{error:!0,err:(0,M.serializeError)(t)})}))},Me=function(){let{path:e,method:t,...n}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return r=>{let{fn:{fetch:o},specSelectors:s,specActions:i}=r,a=s.specJsonWithResolvedSubtrees().toJS(),l=s.operationScheme(e,t),{requestContentType:c,responseContentType:u}=s.contentTypeValues([e,t]).toJS(),p=/xml/i.test(c),h=s.parameterValues([e,t],p).toJS();return i.executeRequest({...n,fetch:o,spec:a,pathName:e,method:t,parameters:h,requestContentType:c,scheme:l,responseContentType:u})}};function De(e,t){return{type:re,payload:{path:e,method:t}}}function Fe(e,t){return{type:oe,payload:{path:e,method:t}}}function Le(e,t,n){return{type:ce,payload:{scheme:e,path:t,method:n}}}},37038:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>a});var r=n(20032),o=n(51228),s=n(33881),i=n(77508);function a(){return{statePlugins:{spec:{wrapActions:i,reducers:r.default,actions:o,selectors:s}}}}},20032:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>d});var r=n(24282),o=n.n(r),s=n(97606),i=n.n(s),a=n(76986),l=n.n(a),c=n(43393),u=n(90242),p=n(27504),h=n(33881),f=n(51228);const d={[f.UPDATE_SPEC]:(e,t)=>"string"==typeof t.payload?e.set("spec",t.payload):e,[f.UPDATE_URL]:(e,t)=>e.set("url",t.payload+""),[f.UPDATE_JSON]:(e,t)=>e.set("json",(0,u.oG)(t.payload)),[f.UPDATE_RESOLVED]:(e,t)=>e.setIn(["resolved"],(0,u.oG)(t.payload)),[f.UPDATE_RESOLVED_SUBTREE]:(e,t)=>{const{value:n,path:r}=t.payload;return e.setIn(["resolvedSubtrees",...r],(0,u.oG)(n))},[f.UPDATE_PARAM]:(e,t)=>{let{payload:n}=t,{path:r,paramName:o,paramIn:s,param:i,value:a,isXml:l}=n,c=i?(0,u.V9)(i):`${s}.${o}`;const p=l?"value_xml":"value";return e.setIn(["meta","paths",...r,"parameters",c,p],a)},[f.UPDATE_EMPTY_PARAM_INCLUSION]:(e,t)=>{let{payload:n}=t,{pathMethod:r,paramName:o,paramIn:s,includeEmptyValue:i}=n;if(!o||!s)return console.warn("Warning: UPDATE_EMPTY_PARAM_INCLUSION could not generate a paramKey."),e;const a=`${s}.${o}`;return e.setIn(["meta","paths",...r,"parameter_inclusions",a],i)},[f.VALIDATE_PARAMS]:(e,t)=>{let{payload:{pathMethod:n,isOAS3:r}}=t;const s=(0,h.specJsonWithResolvedSubtrees)(e).getIn(["paths",...n]),i=(0,h.parameterValues)(e,n).toJS();return e.updateIn(["meta","paths",...n,"parameters"],(0,c.fromJS)({}),(t=>{var a;return o()(a=s.get("parameters",(0,c.List)())).call(a,((t,o)=>{const s=(0,u.cz)(o,i),a=(0,h.parameterInclusionSettingFor)(e,n,o.get("name"),o.get("in")),l=(0,u.Ik)(o,s,{bypassRequiredCheck:a,isOAS3:r});return t.setIn([(0,u.V9)(o),"errors"],(0,c.fromJS)(l))}),t)}))},[f.CLEAR_VALIDATE_PARAMS]:(e,t)=>{let{payload:{pathMethod:n}}=t;return e.updateIn(["meta","paths",...n,"parameters"],(0,c.fromJS)([]),(e=>i()(e).call(e,(e=>e.set("errors",(0,c.fromJS)([]))))))},[f.SET_RESPONSE]:(e,t)=>{let n,{payload:{res:r,path:o,method:s}}=t;n=r.error?l()({error:!0,name:r.err.name,message:r.err.message,statusCode:r.err.statusCode},r.err.response):r,n.headers=n.headers||{};let i=e.setIn(["responses",o,s],(0,u.oG)(n));return p.Z.Blob&&r.data instanceof p.Z.Blob&&(i=i.setIn(["responses",o,s,"text"],r.data)),i},[f.SET_REQUEST]:(e,t)=>{let{payload:{req:n,path:r,method:o}}=t;return e.setIn(["requests",r,o],(0,u.oG)(n))},[f.SET_MUTATED_REQUEST]:(e,t)=>{let{payload:{req:n,path:r,method:o}}=t;return e.setIn(["mutatedRequests",r,o],(0,u.oG)(n))},[f.UPDATE_OPERATION_META_VALUE]:(e,t)=>{let{payload:{path:n,value:r,key:o}}=t,s=["paths",...n],i=["meta","paths",...n];return e.getIn(["json",...s])||e.getIn(["resolved",...s])||e.getIn(["resolvedSubtrees",...s])?e.setIn([...i,o],(0,c.fromJS)(r)):e},[f.CLEAR_RESPONSE]:(e,t)=>{let{payload:{path:n,method:r}}=t;return e.deleteIn(["responses",n,r])},[f.CLEAR_REQUEST]:(e,t)=>{let{payload:{path:n,method:r}}=t;return e.deleteIn(["requests",n,r])},[f.SET_SCHEME]:(e,t)=>{let{payload:{scheme:n,path:r,method:o}}=t;return r&&o?e.setIn(["scheme",r,o],n):r||o?void 0:e.setIn(["scheme","_defaultScheme"],n)}}},33881:(e,t,n)=>{"use strict";n.r(t),n.d(t,{allowTryItOutFor:()=>fe,basePath:()=>Q,canExecuteScheme:()=>Ae,consumes:()=>K,consumesOptionsFor:()=>Oe,contentTypeValues:()=>Se,currentProducesFor:()=>_e,definitions:()=>X,externalDocs:()=>q,findDefinition:()=>Y,getOAS3RequiredRequestBodyContentType:()=>Ne,getParameter:()=>ve,hasHost:()=>be,host:()=>ee,info:()=>$,isMediaTypeSchemaPropertiesEqual:()=>Ie,isOAS3:()=>B,lastError:()=>A,mutatedRequestFor:()=>he,mutatedRequests:()=>ce,operationScheme:()=>ke,operationWithMeta:()=>ye,operations:()=>J,operationsWithRootInherited:()=>ne,operationsWithTags:()=>se,parameterInclusionSettingFor:()=>me,parameterValues:()=>we,parameterWithMeta:()=>ge,parameterWithMetaByIdentity:()=>de,parametersIncludeIn:()=>Ee,parametersIncludeType:()=>xe,paths:()=>V,produces:()=>H,producesOptionsFor:()=>je,requestFor:()=>pe,requests:()=>le,responseFor:()=>ue,responses:()=>ae,schemes:()=>te,security:()=>G,securityDefinitions:()=>Z,semver:()=>z,spec:()=>L,specJS:()=>T,specJson:()=>I,specJsonWithResolvedSubtrees:()=>F,specResolved:()=>R,specResolvedSubtree:()=>M,specSource:()=>N,specStr:()=>P,tagDetails:()=>oe,taggedOperations:()=>ie,tags:()=>re,url:()=>C,validOperationMethods:()=>W,validateBeforeExecute:()=>Pe,validationErrors:()=>Ce,version:()=>U});var r=n(24278),o=n.n(r),s=n(86),i=n.n(s),a=n(11882),l=n.n(a),c=n(97606),u=n.n(c),p=n(14418),h=n.n(p),f=n(51679),d=n.n(f),m=n(24282),g=n.n(m),y=n(2578),v=n.n(y),b=n(92039),w=n.n(b),E=n(58309),x=n.n(E),S=n(20573),_=n(90242),j=n(43393);const O=["get","put","post","delete","options","head","patch","trace"],k=e=>e||(0,j.Map)(),A=(0,S.P1)(k,(e=>e.get("lastError"))),C=(0,S.P1)(k,(e=>e.get("url"))),P=(0,S.P1)(k,(e=>e.get("spec")||"")),N=(0,S.P1)(k,(e=>e.get("specSource")||"not-editor")),I=(0,S.P1)(k,(e=>e.get("json",(0,j.Map)()))),T=(0,S.P1)(I,(e=>e.toJS())),R=(0,S.P1)(k,(e=>e.get("resolved",(0,j.Map)()))),M=(e,t)=>e.getIn(["resolvedSubtrees",...t],void 0),D=(e,t)=>j.Map.isMap(e)&&j.Map.isMap(t)?t.get("$$ref")?t:(0,j.OrderedMap)().mergeWith(D,e,t):t,F=(0,S.P1)(k,(e=>(0,j.OrderedMap)().mergeWith(D,e.get("json"),e.get("resolvedSubtrees")))),L=e=>I(e),B=(0,S.P1)(L,(()=>!1)),$=(0,S.P1)(L,(e=>Te(e&&e.get("info")))),q=(0,S.P1)(L,(e=>Te(e&&e.get("externalDocs")))),U=(0,S.P1)($,(e=>e&&e.get("version"))),z=(0,S.P1)(U,(e=>{var t;return o()(t=/v?([0-9]*)\.([0-9]*)\.([0-9]*)/i.exec(e)).call(t,1)})),V=(0,S.P1)(F,(e=>e.get("paths"))),W=(0,S.P1)((()=>["get","put","post","delete","options","head","patch"])),J=(0,S.P1)(V,(e=>{if(!e||e.size<1)return(0,j.List)();let t=(0,j.List)();return e&&i()(e)?(i()(e).call(e,((e,n)=>{if(!e||!i()(e))return{};i()(e).call(e,((e,r)=>{l()(O).call(O,r)<0||(t=t.push((0,j.fromJS)({path:n,method:r,operation:e,id:`${r}-${n}`})))}))})),t):(0,j.List)()})),K=(0,S.P1)(L,(e=>(0,j.Set)(e.get("consumes")))),H=(0,S.P1)(L,(e=>(0,j.Set)(e.get("produces")))),G=(0,S.P1)(L,(e=>e.get("security",(0,j.List)()))),Z=(0,S.P1)(L,(e=>e.get("securityDefinitions"))),Y=(e,t)=>{const n=e.getIn(["resolvedSubtrees","definitions",t],null),r=e.getIn(["json","definitions",t],null);return n||r||null},X=(0,S.P1)(L,(e=>{const t=e.get("definitions");return j.Map.isMap(t)?t:(0,j.Map)()})),Q=(0,S.P1)(L,(e=>e.get("basePath"))),ee=(0,S.P1)(L,(e=>e.get("host"))),te=(0,S.P1)(L,(e=>e.get("schemes",(0,j.Map)()))),ne=(0,S.P1)(J,K,H,((e,t,n)=>u()(e).call(e,(e=>e.update("operation",(e=>{if(e){if(!j.Map.isMap(e))return;return e.withMutations((e=>(e.get("consumes")||e.update("consumes",(e=>(0,j.Set)(e).merge(t))),e.get("produces")||e.update("produces",(e=>(0,j.Set)(e).merge(n))),e)))}return(0,j.Map)()})))))),re=(0,S.P1)(L,(e=>{const t=e.get("tags",(0,j.List)());return j.List.isList(t)?h()(t).call(t,(e=>j.Map.isMap(e))):(0,j.List)()})),oe=(e,t)=>{var n;let r=re(e)||(0,j.List)();return d()(n=h()(r).call(r,j.Map.isMap)).call(n,(e=>e.get("name")===t),(0,j.Map)())},se=(0,S.P1)(ne,re,((e,t)=>g()(e).call(e,((e,t)=>{let n=(0,j.Set)(t.getIn(["operation","tags"]));return n.count()<1?e.update("default",(0,j.List)(),(e=>e.push(t))):g()(n).call(n,((e,n)=>e.update(n,(0,j.List)(),(e=>e.push(t)))),e)}),g()(t).call(t,((e,t)=>e.set(t.get("name"),(0,j.List)())),(0,j.OrderedMap)())))),ie=e=>t=>{var n;let{getConfigs:r}=t,{tagsSorter:o,operationsSorter:s}=r();return u()(n=se(e).sortBy(((e,t)=>t),((e,t)=>{let n="function"==typeof o?o:_.wh.tagsSorter[o];return n?n(e,t):null}))).call(n,((t,n)=>{let r="function"==typeof s?s:_.wh.operationsSorter[s],o=r?v()(t).call(t,r):t;return(0,j.Map)({tagDetails:oe(e,n),operations:o})}))},ae=(0,S.P1)(k,(e=>e.get("responses",(0,j.Map)()))),le=(0,S.P1)(k,(e=>e.get("requests",(0,j.Map)()))),ce=(0,S.P1)(k,(e=>e.get("mutatedRequests",(0,j.Map)()))),ue=(e,t,n)=>ae(e).getIn([t,n],null),pe=(e,t,n)=>le(e).getIn([t,n],null),he=(e,t,n)=>ce(e).getIn([t,n],null),fe=()=>!0,de=(e,t,n)=>{const r=F(e).getIn(["paths",...t,"parameters"],(0,j.OrderedMap)()),o=e.getIn(["meta","paths",...t,"parameters"],(0,j.OrderedMap)()),s=u()(r).call(r,(e=>{const t=o.get(`${n.get("in")}.${n.get("name")}`),r=o.get(`${n.get("in")}.${n.get("name")}.hash-${n.hashCode()}`);return(0,j.OrderedMap)().merge(e,t,r)}));return d()(s).call(s,(e=>e.get("in")===n.get("in")&&e.get("name")===n.get("name")),(0,j.OrderedMap)())},me=(e,t,n,r)=>{const o=`${r}.${n}`;return e.getIn(["meta","paths",...t,"parameter_inclusions",o],!1)},ge=(e,t,n,r)=>{const o=F(e).getIn(["paths",...t,"parameters"],(0,j.OrderedMap)()),s=d()(o).call(o,(e=>e.get("in")===r&&e.get("name")===n),(0,j.OrderedMap)());return de(e,t,s)},ye=(e,t,n)=>{var r;const o=F(e).getIn(["paths",t,n],(0,j.OrderedMap)()),s=e.getIn(["meta","paths",t,n],(0,j.OrderedMap)()),i=u()(r=o.get("parameters",(0,j.List)())).call(r,(r=>de(e,[t,n],r)));return(0,j.OrderedMap)().merge(o,s).set("parameters",i)};function ve(e,t,n,r){t=t||[];let o=e.getIn(["meta","paths",...t,"parameters"],(0,j.fromJS)([]));return d()(o).call(o,(e=>j.Map.isMap(e)&&e.get("name")===n&&e.get("in")===r))||(0,j.Map)()}const be=(0,S.P1)(L,(e=>{const t=e.get("host");return"string"==typeof t&&t.length>0&&"/"!==t[0]}));function we(e,t,n){t=t||[];let r=ye(e,...t).get("parameters",(0,j.List)());return g()(r).call(r,((e,t)=>{let r=n&&"body"===t.get("in")?t.get("value_xml"):t.get("value");return e.set((0,_.V9)(t,{allowHashes:!1}),r)}),(0,j.fromJS)({}))}function Ee(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(j.List.isList(e))return w()(e).call(e,(e=>j.Map.isMap(e)&&e.get("in")===t))}function xe(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if(j.List.isList(e))return w()(e).call(e,(e=>j.Map.isMap(e)&&e.get("type")===t))}function Se(e,t){t=t||[];let n=F(e).getIn(["paths",...t],(0,j.fromJS)({})),r=e.getIn(["meta","paths",...t],(0,j.fromJS)({})),o=_e(e,t);const s=n.get("parameters")||new j.List,i=r.get("consumes_value")?r.get("consumes_value"):xe(s,"file")?"multipart/form-data":xe(s,"formData")?"application/x-www-form-urlencoded":void 0;return(0,j.fromJS)({requestContentType:i,responseContentType:o})}function _e(e,t){t=t||[];const n=F(e).getIn(["paths",...t],null);if(null===n)return;const r=e.getIn(["meta","paths",...t,"produces_value"],null),o=n.getIn(["produces",0],null);return r||o||"application/json"}function je(e,t){t=t||[];const n=F(e),r=n.getIn(["paths",...t],null);if(null===r)return;const[o]=t,s=r.get("produces",null),i=n.getIn(["paths",o,"produces"],null),a=n.getIn(["produces"],null);return s||i||a}function Oe(e,t){t=t||[];const n=F(e),r=n.getIn(["paths",...t],null);if(null===r)return;const[o]=t,s=r.get("consumes",null),i=n.getIn(["paths",o,"consumes"],null),a=n.getIn(["consumes"],null);return s||i||a}const ke=(e,t,n)=>{let r=e.get("url").match(/^([a-z][a-z0-9+\-.]*):/),o=x()(r)?r[1]:null;return e.getIn(["scheme",t,n])||e.getIn(["scheme","_defaultScheme"])||o||""},Ae=(e,t,n)=>{var r;return l()(r=["http","https"]).call(r,ke(e,t,n))>-1},Ce=(e,t)=>{t=t||[];let n=e.getIn(["meta","paths",...t,"parameters"],(0,j.fromJS)([]));const r=[];return i()(n).call(n,(e=>{let t=e.get("errors");t&&t.count()&&i()(t).call(t,(e=>r.push(e)))})),r},Pe=(e,t)=>0===Ce(e,t).length,Ne=(e,t)=>{var n;let r={requestBody:!1,requestContentType:{}},o=e.getIn(["resolvedSubtrees","paths",...t,"requestBody"],(0,j.fromJS)([]));return o.size<1||(o.getIn(["required"])&&(r.requestBody=o.getIn(["required"])),i()(n=o.getIn(["content"]).entrySeq()).call(n,(e=>{const t=e[0];if(e[1].getIn(["schema","required"])){const n=e[1].getIn(["schema","required"]).toJS();r.requestContentType[t]=n}}))),r},Ie=(e,t,n,r)=>{if((n||r)&&n===r)return!0;let o=e.getIn(["resolvedSubtrees","paths",...t,"requestBody","content"],(0,j.fromJS)([]));if(o.size<2||!n||!r)return!1;let s=o.getIn([n,"schema","properties"],(0,j.fromJS)([])),i=o.getIn([r,"schema","properties"],(0,j.fromJS)([]));return!!s.equals(i)};function Te(e){return j.Map.isMap(e)?e:new j.Map}},77508:(e,t,n)=>{"use strict";n.r(t),n.d(t,{executeRequest:()=>p,updateJsonSpec:()=>u,updateSpec:()=>c,validateParams:()=>h});var r=n(28222),o=n.n(r),s=n(86),i=n.n(s),a=n(27361),l=n.n(a);const c=(e,t)=>{let{specActions:n}=t;return function(){e(...arguments),n.parseToJson(...arguments)}},u=(e,t)=>{let{specActions:n}=t;return function(){for(var t=arguments.length,r=new Array(t),s=0;s{l()(c,[e]).$ref&&n.requestResolvedSubtree(["paths",e])})),n.requestResolvedSubtree(["components","securitySchemes"])}},p=(e,t)=>{let{specActions:n}=t;return t=>(n.logRequest(t),e(t))},h=(e,t)=>{let{specSelectors:n}=t;return t=>e(t,n.isOAS3())}},34852:(e,t,n)=>{"use strict";n.r(t),n.d(t,{loaded:()=>r});const r=(e,t)=>function(){e(...arguments);const n=t.getConfigs().withCredentials;void 0!==n&&(t.fn.fetch.withCredentials="string"==typeof n?"true"===n:!!n)}},94001:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>XE});var r={};n.r(r),n.d(r,{JsonPatchError:()=>j,_areEquals:()=>M,applyOperation:()=>P,applyPatch:()=>N,applyReducer:()=>I,deepClone:()=>O,getValueByPointer:()=>C,validate:()=>R,validator:()=>T});var o={};n.r(o),n.d(o,{compare:()=>z,generate:()=>q,observe:()=>$,unobserve:()=>B});var s={};n.r(s),n.d(s,{hasElementSourceMap:()=>Ps,includesClasses:()=>Is,includesSymbols:()=>Ns,isAnnotationElement:()=>js,isArrayElement:()=>Es,isBooleanElement:()=>bs,isCommentElement:()=>Os,isElement:()=>ms,isLinkElement:()=>Ss,isMemberElement:()=>xs,isNullElement:()=>vs,isNumberElement:()=>ys,isObjectElement:()=>ws,isParseResultElement:()=>ks,isPrimitiveElement:()=>Cs,isRefElement:()=>_s,isSourceMapElement:()=>As,isStringElement:()=>gs});var i={};n.r(i),n.d(i,{isJSONReferenceElement:()=>uc,isJSONSchemaElement:()=>cc,isLinkDescriptionElement:()=>hc,isMediaElement:()=>pc});var a={};n.r(a),n.d(a,{isOpenApi3_0LikeElement:()=>qc,isOpenApiExtension:()=>Hc,isParameterLikeElement:()=>Uc,isReferenceLikeElement:()=>zc,isRequestBodyLikeElement:()=>Vc,isResponseLikeElement:()=>Wc,isServerLikeElement:()=>Jc,isTagLikeElement:()=>Kc});var l={};n.r(l),n.d(l,{isBooleanJsonSchemaElement:()=>lp,isCallbackElement:()=>Bu,isComponentsElement:()=>$u,isContactElement:()=>qu,isExampleElement:()=>Uu,isExternalDocumentationElement:()=>zu,isHeaderElement:()=>Vu,isInfoElement:()=>Wu,isLicenseElement:()=>Ju,isLinkElement:()=>Ku,isLinkElementExternal:()=>Hu,isMediaTypeElement:()=>hp,isOpenApi3_0Element:()=>Zu,isOpenapiElement:()=>Gu,isOperationElement:()=>Yu,isParameterElement:()=>Xu,isPathItemElement:()=>Qu,isPathItemElementExternal:()=>ep,isPathsElement:()=>tp,isReferenceElement:()=>np,isReferenceElementExternal:()=>rp,isRequestBodyElement:()=>op,isResponseElement:()=>sp,isResponsesElement:()=>ip,isSchemaElement:()=>ap,isSecurityRequirementElement:()=>cp,isServerElement:()=>up,isServerVariableElement:()=>pp});var c={};n.r(c),n.d(c,{isBooleanJsonSchemaElement:()=>iy,isCallbackElement:()=>Dg,isComponentsElement:()=>Fg,isContactElement:()=>Lg,isExampleElement:()=>Bg,isExternalDocumentationElement:()=>$g,isHeaderElement:()=>qg,isInfoElement:()=>Ug,isJsonSchemaDialectElement:()=>zg,isLicenseElement:()=>Vg,isLinkElement:()=>Wg,isLinkElementExternal:()=>Jg,isMediaTypeElement:()=>uy,isOpenApi3_1Element:()=>Hg,isOpenapiElement:()=>Kg,isOperationElement:()=>Gg,isParameterElement:()=>Zg,isPathItemElement:()=>Yg,isPathItemElementExternal:()=>Xg,isPathsElement:()=>Qg,isReferenceElement:()=>ey,isReferenceElementExternal:()=>ty,isRequestBodyElement:()=>ny,isResponseElement:()=>ry,isResponsesElement:()=>oy,isSchemaElement:()=>sy,isSecurityRequirementElement:()=>ay,isServerElement:()=>ly,isServerVariableElement:()=>cy});var u={};n.r(u),n.d(u,{cookie:()=>RE,header:()=>TE,path:()=>PE,query:()=>NE});var p,h=n(58826),f=n.n(h),d=(p=function(e,t){return p=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n])},p(e,t)},function(e,t){function n(){this.constructor=e}p(e,t),e.prototype=null===t?Object.create(t):(n.prototype=t.prototype,new n)}),m=Object.prototype.hasOwnProperty;function g(e,t){return m.call(e,t)}function y(e){if(Array.isArray(e)){for(var t=new Array(e.length),n=0;n=48&&t<=57))return!1;n++}return!0}function w(e){return-1===e.indexOf("/")&&-1===e.indexOf("~")?e:e.replace(/~/g,"~0").replace(/\//g,"~1")}function E(e){return e.replace(/~1/g,"/").replace(/~0/g,"~")}function x(e){if(void 0===e)return!0;if(e)if(Array.isArray(e)){for(var t=0,n=e.length;t0&&"constructor"==a[c-1]))throw new TypeError("JSON-Patch: modifying `__proto__` or `constructor/prototype` prop is banned for security reasons, if this was on purpose, please set `banPrototypeModifications` flag false and pass it to this function. More info in fast-json-patch README");if(n&&void 0===p&&(void 0===l[h]?p=a.slice(0,c).join("/"):c==u-1&&(p=t.path),void 0!==p&&f(t,0,e,p)),c++,Array.isArray(l)){if("-"===h)h=l.length;else{if(n&&!b(h))throw new j("Expected an unsigned base-10 integer value, making the new referenced value the array element with the zero-based index","OPERATION_PATH_ILLEGAL_ARRAY_INDEX",s,t,e);b(h)&&(h=~~h)}if(c>=u){if(n&&"add"===t.op&&h>l.length)throw new j("The specified index MUST NOT be greater than the number of elements in the array","OPERATION_VALUE_OUT_OF_BOUNDS",s,t,e);if(!1===(i=A[t.op].call(t,l,h,e)).test)throw new j("Test operation failed","TEST_OPERATION_FAILED",s,t,e);return i}}else if(c>=u){if(!1===(i=k[t.op].call(t,l,h,e)).test)throw new j("Test operation failed","TEST_OPERATION_FAILED",s,t,e);return i}if(l=l[h],n&&c0)throw new j('Operation `path` property must start with "/"',"OPERATION_PATH_INVALID",t,e,n);if(("move"===e.op||"copy"===e.op)&&"string"!=typeof e.from)throw new j("Operation `from` property is not present (applicable in `move` and `copy` operations)","OPERATION_FROM_REQUIRED",t,e,n);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&void 0===e.value)throw new j("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_REQUIRED",t,e,n);if(("add"===e.op||"replace"===e.op||"test"===e.op)&&x(e.value))throw new j("Operation `value` property is not present (applicable in `add`, `replace` and `test` operations)","OPERATION_VALUE_CANNOT_CONTAIN_UNDEFINED",t,e,n);if(n)if("add"==e.op){var o=e.path.split("/").length,s=r.split("/").length;if(o!==s+1&&o!==s)throw new j("Cannot perform an `add` operation at the desired path","OPERATION_PATH_CANNOT_ADD",t,e,n)}else if("replace"===e.op||"remove"===e.op||"_get"===e.op){if(e.path!==r)throw new j("Cannot perform the operation at a path that does not exist","OPERATION_PATH_UNRESOLVABLE",t,e,n)}else if("move"===e.op||"copy"===e.op){var i=R([{op:"_get",path:e.from,value:void 0}],n);if(i&&"OPERATION_PATH_UNRESOLVABLE"===i.name)throw new j("Cannot perform the operation from a path that does not exist","OPERATION_FROM_UNRESOLVABLE",t,e,n)}}function R(e,t,n){try{if(!Array.isArray(e))throw new j("Patch sequence must be an array","SEQUENCE_NOT_AN_ARRAY");if(t)N(v(t),v(e),n||!0);else{n=n||T;for(var r=0;r0&&(e.patches=[],e.callback&&e.callback(r)),r}function U(e,t,n,r,o){if(t!==e){"function"==typeof t.toJSON&&(t=t.toJSON());for(var s=y(t),i=y(e),a=!1,l=i.length-1;l>=0;l--){var c=e[p=i[l]];if(!g(t,p)||void 0===t[p]&&void 0!==c&&!1===Array.isArray(t))Array.isArray(e)===Array.isArray(t)?(o&&n.push({op:"test",path:r+"/"+w(p),value:v(c)}),n.push({op:"remove",path:r+"/"+w(p)}),a=!0):(o&&n.push({op:"test",path:r,value:e}),n.push({op:"replace",path:r,value:t}),!0);else{var u=t[p];"object"==typeof c&&null!=c&&"object"==typeof u&&null!=u&&Array.isArray(c)===Array.isArray(u)?U(c,u,n,r+"/"+w(p),o):c!==u&&(!0,o&&n.push({op:"test",path:r+"/"+w(p),value:v(c)}),n.push({op:"replace",path:r+"/"+w(p),value:v(u)}))}}if(a||s.length!=i.length)for(l=0;lvoid 0!==t&&e?e[t]:e),e)},applyPatch:function(e,t,n){if(n=n||{},"merge"===(t=f()(f()({},t),{},{path:t.path&&K(t.path)})).op){const n=ae(e,t.path);Object.assign(n,t.value),N(e,[H(t.path,n)])}else if("mergeDeep"===t.op){const n=ae(e,t.path),r=W()(n,t.value);e=N(e,[H(t.path,r)]).newDocument}else if("add"===t.op&&""===t.path&&te(t.value)){N(e,Object.keys(t.value).reduce(((e,n)=>(e.push({op:"add",path:`/${K(n)}`,value:t.value[n]}),e)),[]))}else if("replace"===t.op&&""===t.path){let{value:r}=t;n.allowMetaPatches&&t.meta&&se(t)&&(Array.isArray(t.value)||te(t.value))&&(r=f()(f()({},r),t.meta)),e=r}else if(N(e,[t]),n.allowMetaPatches&&t.meta&&se(t)&&(Array.isArray(t.value)||te(t.value))){const n=ae(e,t.path),r=f()(f()({},n),t.meta);N(e,[H(t.path,r)])}return e},parentPathMatch:function(e,t){if(!Array.isArray(t))return!1;for(let n=0,r=t.length;n(e+"").replace(/~/g,"~0").replace(/\//g,"~1"))).join("/")}`:e}function H(e,t,n){return{op:"replace",path:e,value:t,meta:n}}function G(e,t,n){return ee(Q(e.filter(se).map((e=>t(e.value,n,e.path)))||[]))}function Z(e,t,n){return n=n||[],Array.isArray(e)?e.map(((e,r)=>Z(e,t,n.concat(r)))):te(e)?Object.keys(e).map((r=>Z(e[r],t,n.concat(r)))):t(e,n[n.length-1],n)}function Y(e,t,n){let r=[];if((n=n||[]).length>0){const o=t(e,n[n.length-1],n);o&&(r=r.concat(o))}if(Array.isArray(e)){const o=e.map(((e,r)=>Y(e,t,n.concat(r))));o&&(r=r.concat(o))}else if(te(e)){const o=Object.keys(e).map((r=>Y(e[r],t,n.concat(r))));o&&(r=r.concat(o))}return r=Q(r),r}function X(e){return Array.isArray(e)?e:[e]}function Q(e){return[].concat(...e.map((e=>Array.isArray(e)?Q(e):e)))}function ee(e){return e.filter((e=>void 0!==e))}function te(e){return e&&"object"==typeof e}function ne(e){return e&&"function"==typeof e}function re(e){if(ie(e)){const{op:t}=e;return"add"===t||"remove"===t||"replace"===t}return!1}function oe(e){return re(e)||ie(e)&&"mutation"===e.type}function se(e){return oe(e)&&("add"===e.op||"replace"===e.op||"merge"===e.op||"mergeDeep"===e.op)}function ie(e){return e&&"object"==typeof e}function ae(e,t){try{return C(e,t)}catch(e){return console.error(e),{}}}n(31905);var le=n(1272),ce=n(8575);function ue(e,t){function n(){Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack;for(var e=arguments.length,n=new Array(e),r=0;r-1&&-1===de.indexOf(n)||me.indexOf(r)>-1||ge.some((e=>r.indexOf(e)>-1))}function ve(e,t){const[n,r]=e.split("#"),o=ce.resolve(n||"",t||"");return r?`${o}#${r}`:o}const be="application/json, application/yaml",we=/^([a-z]+:\/\/|\/\/)/i,Ee=ue("JSONRefError",(function(e,t,n){this.originalError=n,Object.assign(this,t||{})})),xe={},Se=new WeakMap,_e=[e=>"paths"===e[0]&&"responses"===e[3]&&"examples"===e[5],e=>"paths"===e[0]&&"responses"===e[3]&&"content"===e[5]&&"example"===e[7],e=>"paths"===e[0]&&"responses"===e[3]&&"content"===e[5]&&"examples"===e[7]&&"value"===e[9],e=>"paths"===e[0]&&"requestBody"===e[3]&&"content"===e[4]&&"example"===e[6],e=>"paths"===e[0]&&"requestBody"===e[3]&&"content"===e[4]&&"examples"===e[6]&&"value"===e[8],e=>"paths"===e[0]&&"parameters"===e[2]&&"example"===e[4],e=>"paths"===e[0]&&"parameters"===e[3]&&"example"===e[5],e=>"paths"===e[0]&&"parameters"===e[2]&&"examples"===e[4]&&"value"===e[6],e=>"paths"===e[0]&&"parameters"===e[3]&&"examples"===e[5]&&"value"===e[7],e=>"paths"===e[0]&&"parameters"===e[2]&&"content"===e[4]&&"example"===e[6],e=>"paths"===e[0]&&"parameters"===e[2]&&"content"===e[4]&&"examples"===e[6]&&"value"===e[8],e=>"paths"===e[0]&&"parameters"===e[3]&&"content"===e[4]&&"example"===e[7],e=>"paths"===e[0]&&"parameters"===e[3]&&"content"===e[5]&&"examples"===e[7]&&"value"===e[9]],je={key:"$ref",plugin:(e,t,n,r)=>{const o=r.getInstance(),s=n.slice(0,-1);if(ye(s)||(e=>_e.some((t=>t(e))))(s))return;const{baseDoc:i}=r.getContext(n);if("string"!=typeof e)return new Ee("$ref: must be a string (JSON-Ref)",{$ref:e,baseDoc:i,fullPath:n});const a=Pe(e),l=a[0],c=a[1]||"";let u,p,h;try{u=i||l?Ae(l,i):null}catch(t){return Ce(t,{pointer:c,$ref:e,basePath:u,fullPath:n})}if(function(e,t,n,r){let o=Se.get(r);o||(o={},Se.set(r,o));const s=function(e){if(0===e.length)return"";return`/${e.map(De).join("/")}`}(n),i=`${t||""}#${e}`,a=s.replace(/allOf\/\d+\/?/g,""),l=r.contextTree.get([]).baseDoc;if(t===l&&Le(a,e))return!0;let c="";const u=n.some((e=>(c=`${c}/${De(e)}`,o[c]&&o[c].some((e=>Le(e,i)||Le(i,e))))));if(u)return!0;return void(o[a]=(o[a]||[]).concat(i))}(c,u,s,r)&&!o.useCircularStructures){const t=ve(e,u);return e===t?null:J.replace(n,t)}if(null==u?(h=Re(c),p=r.get(h),void 0===p&&(p=new Ee(`Could not resolve reference: ${e}`,{pointer:c,$ref:e,baseDoc:i,fullPath:n}))):(p=Ne(u,c),p=null!=p.__value?p.__value:p.catch((t=>{throw Ce(t,{pointer:c,$ref:e,baseDoc:i,fullPath:n})}))),p instanceof Error)return[J.remove(n),p];const f=ve(e,u),d=J.replace(s,p,{$$ref:f});if(u&&u!==i)return[d,J.context(s,{baseDoc:u})];try{if(!function(e,t){const n=[e];return t.path.reduce(((e,t)=>(n.push(e[t]),e[t])),e),r(t.value);function r(e){return J.isObject(e)&&(n.indexOf(e)>=0||Object.keys(e).some((t=>r(e[t]))))}}(r.state,d)||o.useCircularStructures)return d}catch(e){return null}}},Oe=Object.assign(je,{docCache:xe,absoluteify:Ae,clearCache:function(e){void 0!==e?delete xe[e]:Object.keys(xe).forEach((e=>{delete xe[e]}))},JSONRefError:Ee,wrapError:Ce,getDoc:Ie,split:Pe,extractFromDoc:Ne,fetchJSON:function(e){return fetch(e,{headers:{Accept:be},loadSpec:!0}).then((e=>e.text())).then((e=>le.ZP.load(e)))},extract:Te,jsonPointerToArray:Re,unescapeJsonPointerToken:Me}),ke=Oe;function Ae(e,t){if(!we.test(e)){if(!t)throw new Ee(`Tried to resolve a relative URL, without having a basePath. path: '${e}' basePath: '${t}'`);return ce.resolve(t,e)}return e}function Ce(e,t){let n;return n=e&&e.response&&e.response.body?`${e.response.body.code} ${e.response.body.message}`:e.message,new Ee(`Could not resolve reference: ${n}`,t,e)}function Pe(e){return(e+"").split("#")}function Ne(e,t){const n=xe[e];if(n&&!J.isPromise(n))try{const e=Te(t,n);return Object.assign(Promise.resolve(e),{__value:e})}catch(e){return Promise.reject(e)}return Ie(e).then((e=>Te(t,e)))}function Ie(e){const t=xe[e];return t?J.isPromise(t)?t:Promise.resolve(t):(xe[e]=Oe.fetchJSON(e).then((t=>(xe[e]=t,t))),xe[e])}function Te(e,t){const n=Re(e);if(n.length<1)return t;const r=J.getIn(t,n);if(void 0===r)throw new Ee(`Could not resolve pointer: ${e} does not exist in document`,{pointer:e});return r}function Re(e){if("string"!=typeof e)throw new TypeError("Expected a string, got a "+typeof e);return"/"===e[0]&&(e=e.substr(1)),""===e?[]:e.split("/").map(Me)}function Me(e){if("string"!=typeof e)return e;return new URLSearchParams(`=${e.replace(/~1/g,"/").replace(/~0/g,"~")}`).get("")}function De(e){return new URLSearchParams([["",e.replace(/~/g,"~0").replace(/\//g,"~1")]]).toString().slice(1)}const Fe=e=>!e||"/"===e||"#"===e;function Le(e,t){if(Fe(t))return!0;const n=e.charAt(t.length),r=t.slice(-1);return 0===e.indexOf(t)&&(!n||"/"===n||"#"===n)&&"#"!==r}const Be={key:"allOf",plugin:(e,t,n,r,o)=>{if(o.meta&&o.meta.$$ref)return;const s=n.slice(0,-1);if(ye(s))return;if(!Array.isArray(e)){const e=new TypeError("allOf must be an array");return e.fullPath=n,e}let i=!1,a=o.value;if(s.forEach((e=>{a&&(a=a[e])})),a=f()({},a),0===Object.keys(a).length)return;delete a.allOf;const l=[];return l.push(r.replace(s,{})),e.forEach(((e,t)=>{if(!r.isObject(e)){if(i)return null;i=!0;const e=new TypeError("Elements in allOf must be objects");return e.fullPath=n,l.push(e)}l.push(r.mergeDeep(s,e));const o=function(e,t){let{specmap:n,getBaseUrlForNodePath:r=(e=>n.getContext([...t,...e]).baseDoc),targetKeys:o=["$ref","$$ref"]}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const s=[];return he()(e).forEach((function(){if(o.includes(this.key)&&"string"==typeof this.node){const e=this.path,o=t.concat(this.path),i=ve(this.node,r(e));s.push(n.replace(o,i))}})),s}(e,n.slice(0,-1),{getBaseUrlForNodePath:e=>r.getContext([...n,t,...e]).baseDoc,specmap:r});l.push(...o)})),a.example&&l.push(r.remove([].concat(s,"example"))),l.push(r.mergeDeep(s,a)),a.$$ref||l.push(r.remove([].concat(s,"$$ref"))),l}},$e={key:"parameters",plugin:(e,t,n,r)=>{if(Array.isArray(e)&&e.length){const t=Object.assign([],e),o=n.slice(0,-1),s=f()({},J.getIn(r.spec,o));for(let o=0;o{const o=f()({},e);for(const t in e)try{o[t].default=r.modelPropertyMacro(o[t])}catch(e){const t=new Error(e);return t.fullPath=n,t}return J.replace(n,o)}};class Ue{constructor(e){this.root=ze(e||{})}set(e,t){const n=this.getParent(e,!0);if(!n)return void Ve(this.root,t,null);const r=e[e.length-1],{children:o}=n;o[r]?Ve(o[r],t,n):o[r]=ze(t,n)}get(e){if((e=e||[]).length<1)return this.root.value;let t,n,r=this.root;for(let o=0;o{if(!e)return e;const{children:r}=e;return!r[n]&&t&&(r[n]=ze(null,e)),r[n]}),this.root)}}function ze(e,t){return Ve({children:{}},e,t)}function Ve(e,t,n){return e.value=t||{},e.protoValue=n?f()(f()({},n.protoValue),e.value):e.value,Object.keys(e.children).forEach((t=>{const n=e.children[t];e.children[t]=Ve(n,n.value,e)})),e}const We=()=>{};class Je{static getPluginName(e){return e.pluginName}static getPatchesOfType(e,t){return e.filter(t)}constructor(e){Object.assign(this,{spec:"",debugLevel:"info",plugins:[],pluginHistory:{},errors:[],mutations:[],promisedPatches:[],state:{},patches:[],context:{},contextTree:new Ue,showDebug:!1,allPatches:[],pluginProp:"specMap",libMethods:Object.assign(Object.create(this),J,{getInstance:()=>this}),allowMetaPatches:!1},e),this.get=this._get.bind(this),this.getContext=this._getContext.bind(this),this.hasRun=this._hasRun.bind(this),this.wrappedPlugins=this.plugins.map(this.wrapPlugin.bind(this)).filter(J.isFunction),this.patches.push(J.add([],this.spec)),this.patches.push(J.context([],this.context)),this.updatePatches(this.patches)}debug(e){if(this.debugLevel===e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r1?t-1:0),r=1;r!Array.isArray(e)||e.every(((e,n)=>e===t[n]));return function*(r,o){const s={};for(const e of r.filter(J.isAdditiveMutation))yield*i(e.value,e.path,e);function*i(r,a,l){if(J.isObject(r)){const c=a.length-1,u=a[c],p=a.indexOf("properties"),h="properties"===u&&c===p,f=o.allowMetaPatches&&s[r.$$ref];for(const c of Object.keys(r)){const u=r[c],p=a.concat(c),d=J.isObject(u),m=r.$$ref;if(f||d&&(o.allowMetaPatches&&m&&(s[m]=!0),yield*i(u,p,l)),!h&&c===e.key){const r=t(n,a);n&&!r||(yield e.plugin(u,c,p,o,l))}}}else e.key===a[a.length-1]&&(yield e.plugin(r,e.key,a,o))}}}(e)),Object.assign(r.bind(o),{pluginName:e.name||t,isGenerator:J.isGenerator(r)})}nextPlugin(){return this.wrappedPlugins.find((e=>this.getMutationsForPlugin(e).length>0))}nextPromisedPatch(){if(this.promisedPatches.length>0)return Promise.race(this.promisedPatches.map((e=>e.value)))}getPluginHistory(e){const t=this.constructor.getPluginName(e);return this.pluginHistory[t]||[]}getPluginRunCount(e){return this.getPluginHistory(e).length}getPluginHistoryTip(e){const t=this.getPluginHistory(e);return t&&t[t.length-1]||{}}getPluginMutationIndex(e){const t=this.getPluginHistoryTip(e).mutationIndex;return"number"!=typeof t?-1:t}updatePluginHistory(e,t){const n=this.constructor.getPluginName(e);this.pluginHistory[n]=this.pluginHistory[n]||[],this.pluginHistory[n].push(t)}updatePatches(e){J.normalizeArray(e).forEach((e=>{if(e instanceof Error)this.errors.push(e);else try{if(!J.isObject(e))return void this.debug("updatePatches","Got a non-object patch",e);if(this.showDebug&&this.allPatches.push(e),J.isPromise(e.value))return this.promisedPatches.push(e),void this.promisedPatchThen(e);if(J.isContextPatch(e))return void this.setContext(e.path,e.value);J.isMutation(e)&&this.updateMutations(e)}catch(e){console.error(e),this.errors.push(e)}}))}updateMutations(e){"object"==typeof e.value&&!Array.isArray(e.value)&&this.allowMetaPatches&&(e.value=f()({},e.value));const t=J.applyPatch(this.state,e,{allowMetaPatches:this.allowMetaPatches});t&&(this.mutations.push(e),this.state=t)}removePromisedPatch(e){const t=this.promisedPatches.indexOf(e);t<0?this.debug("Tried to remove a promisedPatch that isn't there!"):this.promisedPatches.splice(t,1)}promisedPatchThen(e){return e.value=e.value.then((t=>{const n=f()(f()({},e),{},{value:t});this.removePromisedPatch(e),this.updatePatches(n)})).catch((t=>{this.removePromisedPatch(e),this.updatePatches(t)})),e.value}getMutations(e,t){return e=e||0,"number"!=typeof t&&(t=this.mutations.length),this.mutations.slice(e,t)}getCurrentMutations(){return this.getMutationsForPlugin(this.getCurrentPlugin())}getMutationsForPlugin(e){const t=this.getPluginMutationIndex(e);return this.getMutations(t+1)}getCurrentPlugin(){return this.currentPlugin}getLib(){return this.libMethods}_get(e){return J.getIn(this.state,e)}_getContext(e){return this.contextTree.get(e)}setContext(e,t){return this.contextTree.set(e,t)}_hasRun(e){return this.getPluginRunCount(this.getCurrentPlugin())>(e||0)}dispatch(){const e=this,t=this.nextPlugin();if(!t){const e=this.nextPromisedPatch();if(e)return e.then((()=>this.dispatch())).catch((()=>this.dispatch()));const t={spec:this.state,errors:this.errors};return this.showDebug&&(t.patches=this.allPatches),Promise.resolve(t)}if(e.pluginCount=e.pluginCount||{},e.pluginCount[t]=(e.pluginCount[t]||0)+1,e.pluginCount[t]>100)return Promise.resolve({spec:e.state,errors:e.errors.concat(new Error("We've reached a hard limit of 100 plugin runs"))});if(t!==this.currentPlugin&&this.promisedPatches.length){const e=this.promisedPatches.map((e=>e.value));return Promise.all(e.map((e=>e.then(We,We)))).then((()=>this.dispatch()))}return function(){e.currentPlugin=t;const r=e.getCurrentMutations(),o=e.mutations.length-1;try{if(t.isGenerator)for(const o of t(r,e.getLib()))n(o);else{n(t(r,e.getLib()))}}catch(e){console.error(e),n([Object.assign(Object.create(e),{plugin:t})])}finally{e.updatePluginHistory(t,{mutationIndex:o})}return e.dispatch()}();function n(n){n&&(n=J.fullyNormalizeArray(n),e.updatePatches(n,t))}}}const Ke={refs:ke,allOf:Be,parameters:$e,properties:qe};var He=n(32454);function Ge(e){const{spec:t}=e,{paths:n}=t,r={};if(!n||t.$$normalized)return e;for(const e in n){const o=n[e];if(null==o||!["object","function"].includes(typeof o))continue;const s=o.parameters;for(const n in o){const i=o[n];if(null==i||!["object","function"].includes(typeof i))continue;const a=(0,He.Z)(i,e,n);if(a){r[a]?r[a].push(i):r[a]=[i];const e=r[a];if(e.length>1)e.forEach(((e,t)=>{e.__originalOperationId=e.__originalOperationId||e.operationId,e.operationId=`${a}${t+1}`}));else if(void 0!==i.operationId){const t=e[0];t.__originalOperationId=t.__originalOperationId||i.operationId,t.operationId=a}}if("parameters"!==n){const e=[],n={};for(const r in t)"produces"!==r&&"consumes"!==r&&"security"!==r||(n[r]=t[r],e.push(n));if(s&&(n.parameters=s,e.push(n)),e.length)for(const t of e)for(const e in t)if(i[e]){if("parameters"===e)for(const n of t[e]){i[e].some((e=>e.name&&e.name===n.name||e.$ref&&e.$ref===n.$ref||e.$$ref&&e.$$ref===n.$$ref||e===n))||i[e].push(n)}}else i[e]=t[e]}}}return t.$$normalized=!0,e}function Ze(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const{requestInterceptor:n,responseInterceptor:r}=t,o=e.withCredentials?"include":"same-origin";return t=>e({url:t,loadSpec:!0,requestInterceptor:n,responseInterceptor:r,headers:{Accept:be},credentials:o}).then((e=>e.body))}var Ye=n(80129),Xe=n.n(Ye);const Qe="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:window,{FormData:et,Blob:tt,File:nt}=Qe,rt=e=>":/?#[]@!$&'()*+,;=".indexOf(e)>-1,ot=e=>/^[a-z0-9\-._~]+$/i.test(e);function st(e){let{escape:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return"number"==typeof e&&(e=e.toString()),"string"==typeof e&&e.length&&t?n?JSON.parse(e):[...e].map((e=>{if(ot(e))return e;if(rt(e)&&"unsafe"===t)return e;const n=new TextEncoder;return Array.from(n.encode(e)).map((e=>`0${e.toString(16).toUpperCase()}`.slice(-2))).map((e=>`%${e}`)).join("")})).join(""):e}function it(e){const{value:t}=e;return Array.isArray(t)?function(e){let{key:t,value:n,style:r,explode:o,escape:s}=e;const i=e=>st(e,{escape:s});if("simple"===r)return n.map((e=>i(e))).join(",");if("label"===r)return`.${n.map((e=>i(e))).join(".")}`;if("matrix"===r)return n.map((e=>i(e))).reduce(((e,n)=>!e||o?`${e||""};${t}=${n}`:`${e},${n}`),"");if("form"===r){const e=o?`&${t}=`:",";return n.map((e=>i(e))).join(e)}if("spaceDelimited"===r){const e=o?`${t}=`:"";return n.map((e=>i(e))).join(` ${e}`)}if("pipeDelimited"===r){const e=o?`${t}=`:"";return n.map((e=>i(e))).join(`|${e}`)}return}(e):"object"==typeof t?function(e){let{key:t,value:n,style:r,explode:o,escape:s}=e;const i=e=>st(e,{escape:s}),a=Object.keys(n);if("simple"===r)return a.reduce(((e,t)=>{const r=i(n[t]);return`${e?`${e},`:""}${t}${o?"=":","}${r}`}),"");if("label"===r)return a.reduce(((e,t)=>{const r=i(n[t]);return`${e?`${e}.`:"."}${t}${o?"=":"."}${r}`}),"");if("matrix"===r&&o)return a.reduce(((e,t)=>`${e?`${e};`:";"}${t}=${i(n[t])}`),"");if("matrix"===r)return a.reduce(((e,r)=>{const o=i(n[r]);return`${e?`${e},`:`;${t}=`}${r},${o}`}),"");if("form"===r)return a.reduce(((e,t)=>{const r=i(n[t]);return`${e?`${e}${o?"&":","}`:""}${t}${o?"=":","}${r}`}),"");return}(e):function(e){let{key:t,value:n,style:r,escape:o}=e;const s=e=>st(e,{escape:o});if("simple"===r)return s(n);if("label"===r)return`.${s(n)}`;if("matrix"===r)return`;${t}=${s(n)}`;if("form"===r)return s(n);if("deepObject"===r)return s(n,{},!0);return}(e)}const at=(e,t)=>{t.body=e},lt={serializeRes:pt,mergeInQueryOrForm:Et};async function ct(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};"object"==typeof e&&(t=e,e=t.url),t.headers=t.headers||{},lt.mergeInQueryOrForm(t),t.headers&&Object.keys(t.headers).forEach((e=>{const n=t.headers[e];"string"==typeof n&&(t.headers[e]=n.replace(/\n+/g," "))})),t.requestInterceptor&&(t=await t.requestInterceptor(t)||t);const n=t.headers["content-type"]||t.headers["Content-Type"];let r;/multipart\/form-data/i.test(n)&&t.body instanceof et&&(delete t.headers["content-type"],delete t.headers["Content-Type"]);try{r=await(t.userFetch||fetch)(t.url,t),r=await lt.serializeRes(r,e,t),t.responseInterceptor&&(r=await t.responseInterceptor(r)||r)}catch(e){if(!r)throw e;const t=new Error(r.statusText||`response status is ${r.status}`);throw t.status=r.status,t.statusCode=r.status,t.responseError=e,t}if(!r.ok){const e=new Error(r.statusText||`response status is ${r.status}`);throw e.status=r.status,e.statusCode=r.status,e.response=r,e}return r}const ut=function(){return/(json|xml|yaml|text)\b/.test(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"")};function pt(e,t){let{loadSpec:n=!1}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const r={ok:e.ok,url:e.url||t,status:e.status,statusText:e.statusText,headers:ht(e.headers)},o=r.headers["content-type"],s=n||ut(o);return(s?e.text:e.blob||e.buffer).call(e).then((e=>{if(r.text=e,r.data=e,s)try{const t=function(e,t){return t&&(0===t.indexOf("application/json")||t.indexOf("+json")>0)?JSON.parse(e):le.ZP.load(e)}(e,o);r.body=t,r.obj=t}catch(e){r.parseError=e}return r}))}function ht(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return"function"!=typeof e.entries?{}:Array.from(e.entries()).reduce(((e,t)=>{let[n,r]=t;return e[n]=function(e){return e.includes(", ")?e.split(", "):e}(r),e}),{})}function ft(e,t){return t||"undefined"==typeof navigator||(t=navigator),t&&"ReactNative"===t.product?!(!e||"object"!=typeof e||"string"!=typeof e.uri):void 0!==nt&&e instanceof nt||(void 0!==tt&&e instanceof tt||(!!ArrayBuffer.isView(e)||null!==e&&"object"==typeof e&&"function"==typeof e.pipe))}function dt(e,t){return Array.isArray(e)&&e.some((e=>ft(e,t)))}const mt={form:",",spaceDelimited:"%20",pipeDelimited:"|"},gt={csv:",",ssv:"%20",tsv:"%09",pipes:"|"};class yt extends nt{constructor(e){super([e],arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",arguments.length>2&&void 0!==arguments[2]?arguments[2]:{}),this.data=e}valueOf(){return this.data}toString(){return this.valueOf()}}function vt(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];const{collectionFormat:r,allowEmptyValue:o,serializationOption:s,encoding:i}=t,a="object"!=typeof t||Array.isArray(t)?t:t.value,l=n?e=>e.toString():e=>encodeURIComponent(e),c=l(e);if(void 0===a&&o)return[[c,""]];if(ft(a)||dt(a))return[[c,a]];if(s)return bt(e,a,n,s);if(i){if([typeof i.style,typeof i.explode,typeof i.allowReserved].some((e=>"undefined"!==e))){const{style:t,explode:r,allowReserved:o}=i;return bt(e,a,n,{style:t,explode:r,allowReserved:o})}if("string"==typeof i.contentType){if(i.contentType.startsWith("application/json")){const e=l("string"==typeof a?a:JSON.stringify(a));return[[c,new yt(e,"blob",{type:i.contentType})]]}const e=l(String(a));return[[c,new yt(e,"blob",{type:i.contentType})]]}return"object"!=typeof a?[[c,l(a)]]:Array.isArray(a)&&a.every((e=>"object"!=typeof e))?[[c,a.map(l).join(",")]]:[[c,l(JSON.stringify(a))]]}return"object"!=typeof a?[[c,l(a)]]:Array.isArray(a)?"multi"===r?[[c,a.map(l)]]:[[c,a.map(l).join(gt[r||"csv"])]]:[[c,""]]}function bt(e,t,n,r){const o=r.style||"form",s=void 0===r.explode?"form"===o:r.explode,i=!n&&(r&&r.allowReserved?"unsafe":"reserved"),a=e=>st(e,{escape:i}),l=n?e=>e:e=>st(e,{escape:i});return"object"!=typeof t?[[l(e),a(t)]]:Array.isArray(t)?s?[[l(e),t.map(a)]]:[[l(e),t.map(a).join(mt[o])]]:"deepObject"===o?Object.keys(t).map((n=>[l(`${e}[${n}]`),a(t[n])])):s?Object.keys(t).map((e=>[l(e),a(t[e])])):[[l(e),Object.keys(t).map((e=>[`${l(e)},${a(t[e])}`])).join(",")]]}function wt(e){const t=Object.keys(e).reduce(((t,n)=>{for(const[r,o]of vt(n,e[n]))t[r]=o instanceof yt?o.valueOf():o;return t}),{});return Xe().stringify(t,{encode:!1,indices:!1})||""}function Et(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const{url:t="",query:n,form:r}=e;if(r){const t=Object.keys(r).some((e=>{const{value:t}=r[e];return ft(t)||dt(t)})),n=e.headers["content-type"]||e.headers["Content-Type"];if(t||/multipart\/form-data/i.test(n)){const t=(o=e.form,Object.entries(o).reduce(((e,t)=>{let[n,r]=t;for(const[t,o]of vt(n,r,!0))if(Array.isArray(o))for(const n of o)if(ArrayBuffer.isView(n)){const r=new tt([n]);e.append(t,r)}else e.append(t,n);else if(ArrayBuffer.isView(o)){const n=new tt([o]);e.append(t,n)}else e.append(t,o);return e}),new et));at(t,e)}else e.body=wt(r);delete e.form}var o;if(n){const[r,o]=t.split("?");let s="";if(o){const e=Xe().parse(o);Object.keys(n).forEach((t=>delete e[t])),s=Xe().stringify(e,{encode:!0})}const i=function(){for(var e=arguments.length,t=new Array(e),n=0;ne)).join("&");return r?`?${r}`:""}(s,wt(n));e.url=r+i,delete e.query}return e}const xt=e=>{const{baseDoc:t,url:n}=e;return t||n||""},St=e=>{const{fetch:t,http:n}=e;return t||n||ct};async function _t(e){const{spec:t,mode:n,allowMetaPatches:r=!0,pathDiscriminator:o,modelPropertyMacro:s,parameterMacro:i,requestInterceptor:a,responseInterceptor:l,skipNormalization:c,useCircularStructures:u}=e,p=xt(e),h=St(e);return function(e){p&&(Ke.refs.docCache[p]=e);Ke.refs.fetchJSON=Ze(h,{requestInterceptor:a,responseInterceptor:l});const t=[Ke.refs];"function"==typeof i&&t.push(Ke.parameters);"function"==typeof s&&t.push(Ke.properties);"strict"!==n&&t.push(Ke.allOf);return(f={spec:e,context:{baseDoc:p},plugins:t,allowMetaPatches:r,pathDiscriminator:o,parameterMacro:i,modelPropertyMacro:s,useCircularStructures:u},new Je(f).dispatch()).then(c?async e=>e:Ge);var f}(t)}const jt={name:"generic",match:()=>!0,normalize(e){let{spec:t}=e;const{spec:n}=Ge({spec:t});return n},resolve:async e=>_t(e)};const Ot=e=>{try{const{openapi:t}=e;return"string"==typeof t&&/^3\.0\.([0123])(?:-rc[012])?$/.test(t)}catch{return!1}},kt=e=>{try{const{openapi:t}=e;return"string"==typeof t&&/^3\.1\.(?:[1-9]\d*|0)$/.test(t)}catch{return!1}},At=e=>Ot(e)||kt(e),Ct={name:"openapi-2",match(e){let{spec:t}=e;return(e=>{try{const{swagger:t}=e;return"2.0"===t}catch{return!1}})(t)},normalize(e){let{spec:t}=e;const{spec:n}=Ge({spec:t});return n},resolve:async e=>async function(e){return _t(e)}(e)};const Pt={name:"openapi-3-0",match(e){let{spec:t}=e;return Ot(t)},normalize(e){let{spec:t}=e;const{spec:n}=Ge({spec:t});return n},resolve:async e=>async function(e){return _t(e)}(e)};var Nt=n(43500);class It extends Nt.RP{constructor(e,t,n){super(e,t,n),this.element="annotation"}get code(){return this.attributes.get("code")}set code(e){this.attributes.set("code",e)}}const Tt=It;class Rt extends Nt.RP{constructor(e,t,n){super(e,t,n),this.element="comment"}}const Mt=Rt;const Dt=function(){return!1};const Ft=function(){return!0};function Lt(e){return null!=e&&"object"==typeof e&&!0===e["@@functional/placeholder"]}function Bt(e){return function t(n){return 0===arguments.length||Lt(n)?t:e.apply(this,arguments)}}function $t(e){return function t(n,r){switch(arguments.length){case 0:return t;case 1:return Lt(n)?t:Bt((function(t){return e(n,t)}));default:return Lt(n)&&Lt(r)?t:Lt(n)?Bt((function(t){return e(t,r)})):Lt(r)?Bt((function(t){return e(n,t)})):e(n,r)}}}const qt=Array.isArray||function(e){return null!=e&&e.length>=0&&"[object Array]"===Object.prototype.toString.call(e)};function Ut(e,t,n){return function(){if(0===arguments.length)return n();var r=arguments[arguments.length-1];if(!qt(r)){for(var o=0;o=arguments.length)?a=t[i]:(a=arguments[o],o+=1),r[i]=a,Lt(a)||(s-=1),i+=1}return s<=0?n.apply(this,r):Gt(s,Zt(e,r,n))}}const Yt=$t((function(e,t){return 1===e?Bt(t):Gt(e,Zt(e,[],t))}));function Xt(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}function Qt(e,t,n){for(var r=0,o=n.length;r=0;)en(t=sn[n],e)&&!ln(r,t)&&(r[r.length]=t),n-=1;return r})):Bt((function(e){return Object(e)!==e?[]:Object.keys(e)}));const un=Bt((function(e){return null===e?"Null":void 0===e?"Undefined":Object.prototype.toString.call(e).slice(8,-1)}));function pn(e,t,n,r){var o=Xt(e);function s(e,t){return hn(e,t,n.slice(),r.slice())}return!Qt((function(e,t){return!Qt(s,t,e)}),Xt(t),o)}function hn(e,t,n,r){if(tn(e,t))return!0;var o,s,i=un(e);if(i!==un(t))return!1;if("function"==typeof e["fantasy-land/equals"]||"function"==typeof t["fantasy-land/equals"])return"function"==typeof e["fantasy-land/equals"]&&e["fantasy-land/equals"](t)&&"function"==typeof t["fantasy-land/equals"]&&t["fantasy-land/equals"](e);if("function"==typeof e.equals||"function"==typeof t.equals)return"function"==typeof e.equals&&e.equals(t)&&"function"==typeof t.equals&&t.equals(e);switch(i){case"Arguments":case"Array":case"Object":if("function"==typeof e.constructor&&"Promise"===(o=e.constructor,null==(s=String(o).match(/^function (\w*)/))?"":s[1]))return e===t;break;case"Boolean":case"Number":case"String":if(typeof e!=typeof t||!tn(e.valueOf(),t.valueOf()))return!1;break;case"Date":if(!tn(e.valueOf(),t.valueOf()))return!1;break;case"Error":return e.name===t.name&&e.message===t.message;case"RegExp":if(e.source!==t.source||e.global!==t.global||e.ignoreCase!==t.ignoreCase||e.multiline!==t.multiline||e.sticky!==t.sticky||e.unicode!==t.unicode)return!1}for(var a=n.length-1;a>=0;){if(n[a]===e)return r[a]===t;a-=1}switch(i){case"Map":return e.size===t.size&&pn(e.entries(),t.entries(),n.concat([e]),r.concat([t]));case"Set":return e.size===t.size&&pn(e.values(),t.values(),n.concat([e]),r.concat([t]));case"Arguments":case"Array":case"Object":case"Boolean":case"Number":case"String":case"Date":case"Error":case"RegExp":case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"ArrayBuffer":break;default:return!1}var l=cn(e);if(l.length!==cn(t).length)return!1;var c=n.concat([e]),u=r.concat([t]);for(a=l.length-1;a>=0;){var p=l[a];if(!en(p,t)||!hn(t[p],e[p],c,u))return!1;a-=1}return!0}const fn=$t((function(e,t){return hn(e,t,[],[])}));function dn(e,t){return function(e,t,n){var r,o;if("function"==typeof e.indexOf)switch(typeof t){case"number":if(0===t){for(r=1/t;n=0}function mn(e,t){for(var n=0,r=t.length,o=Array(r);n":On(n,r)},r=function(e,t){return mn((function(t){return gn(t)+": "+n(e[t])}),t.slice().sort())};switch(Object.prototype.toString.call(e)){case"[object Arguments]":return"(function() { return arguments; }("+mn(n,e).join(", ")+"))";case"[object Array]":return"["+mn(n,e).concat(r(e,jn((function(e){return/^\d+$/.test(e)}),cn(e)))).join(", ")+"]";case"[object Boolean]":return"object"==typeof e?"new Boolean("+n(e.valueOf())+")":e.toString();case"[object Date]":return"new Date("+(isNaN(e.valueOf())?n(NaN):gn(vn(e)))+")";case"[object Map]":return"new Map("+n(Array.from(e))+")";case"[object Null]":return"null";case"[object Number]":return"object"==typeof e?"new Number("+n(e.valueOf())+")":1/e==-1/0?"-0":e.toString(10);case"[object Set]":return"new Set("+n(Array.from(e).sort())+")";case"[object String]":return"object"==typeof e?"new String("+n(e.valueOf())+")":gn(e);case"[object Undefined]":return"undefined";default:if("function"==typeof e.toString){var o=e.toString();if("[object Object]"!==o)return o}return"{"+r(e,cn(e)).join(", ")+"}"}}const kn=Bt((function(e){return On(e,[])}));const An=$t((function(e,t){if(e===t)return t;function n(e,t){if(e>t!=t>e)return t>e?t:e}var r=n(e,t);if(void 0!==r)return r;var o=n(typeof e,typeof t);if(void 0!==o)return o===typeof e?e:t;var s=kn(e),i=n(s,kn(t));return void 0!==i&&i===s?e:t}));var Cn=function(){function e(e,t){this.xf=t,this.f=e}return e.prototype["@@transducer/init"]=Vt,e.prototype["@@transducer/result"]=Wt,e.prototype["@@transducer/step"]=function(e,t){return this.xf["@@transducer/step"](e,this.f(t))},e}();const Pn=$t(Ut(["fantasy-land/map","map"],(function(e){return function(t){return new Cn(e,t)}}),(function(e,t){switch(Object.prototype.toString.call(t)){case"[object Function]":return Yt(t.length,(function(){return e.call(this,t.apply(this,arguments))}));case"[object Object]":return wn((function(n,r){return n[r]=e(t[r]),n}),{},cn(t));default:return mn(e,t)}}))),Nn=Number.isInteger||function(e){return e<<0===e};function In(e){return"[object String]"===Object.prototype.toString.call(e)}const Tn=$t((function(e,t){var n=e<0?t.length+e:e;return In(t)?t.charAt(n):t[n]}));const Rn=$t((function(e,t){if(null!=t)return Nn(e)?Tn(e,t):t[e]}));const Mn=$t((function(e,t){return Pn(Rn(e),t)}));function Dn(e){return function t(n,r,o){switch(arguments.length){case 0:return t;case 1:return Lt(n)?t:$t((function(t,r){return e(n,t,r)}));case 2:return Lt(n)&&Lt(r)?t:Lt(n)?$t((function(t,n){return e(t,r,n)})):Lt(r)?$t((function(t,r){return e(n,t,r)})):Bt((function(t){return e(n,r,t)}));default:return Lt(n)&&Lt(r)&&Lt(o)?t:Lt(n)&&Lt(r)?$t((function(t,n){return e(t,n,o)})):Lt(n)&&Lt(o)?$t((function(t,n){return e(t,r,n)})):Lt(r)&&Lt(o)?$t((function(t,r){return e(n,t,r)})):Lt(n)?Bt((function(t){return e(t,r,o)})):Lt(r)?Bt((function(t){return e(n,t,o)})):Lt(o)?Bt((function(t){return e(n,r,t)})):e(n,r,o)}}}const Fn=Bt((function(e){return!!qt(e)||!!e&&("object"==typeof e&&(!In(e)&&(0===e.length||e.length>0&&(e.hasOwnProperty(0)&&e.hasOwnProperty(e.length-1)))))}));var Ln="undefined"!=typeof Symbol?Symbol.iterator:"@@iterator";function Bn(e,t,n){return function(r,o,s){if(Fn(s))return e(r,o,s);if(null==s)return o;if("function"==typeof s["fantasy-land/reduce"])return t(r,o,s,"fantasy-land/reduce");if(null!=s[Ln])return n(r,o,s[Ln]());if("function"==typeof s.next)return n(r,o,s);if("function"==typeof s.reduce)return t(r,o,s,"reduce");throw new TypeError("reduce: list must be array or iterable")}}function $n(e,t,n){for(var r=0,o=n.length;r1){var s=!or(r)&&en(o,r)&&"object"==typeof r[o]?r[o]:Nn(t[1])?[]:{};n=e(Array.prototype.slice.call(t,1),n,s)}return function(e,t,n){if(Nn(e)&&qt(n)){var r=[].concat(n);return r[e]=t,r}var o={};for(var s in n)o[s]=n[s];return o[e]=t,o}(o,n,r)}));function ir(e){var t=Object.prototype.toString.call(e);return"[object Function]"===t||"[object AsyncFunction]"===t||"[object GeneratorFunction]"===t||"[object AsyncGeneratorFunction]"===t}const ar=$t((function(e,t){return e&&t}));const lr=$t((function(e,t){var n=Yt(e,t);return Yt(e,(function(){return wn(er,Pn(n,arguments[0]),Array.prototype.slice.call(arguments,1))}))}));const cr=Bt((function(e){return lr(e.length,e)}));const ur=$t((function(e,t){return ir(e)?function(){return e.apply(this,arguments)&&t.apply(this,arguments)}:cr(ar)(e,t)}));const pr=Bt((function(e){return function(t,n){return e(t,n)?-1:e(n,t)?1:0}}));const hr=cr(Bt((function(e){return!e})));function fr(e,t){return function(){return t.call(this,e.apply(this,arguments))}}function dr(e,t){return function(){var n=arguments.length;if(0===n)return t();var r=arguments[n-1];return qt(r)||"function"!=typeof r[e]?t.apply(this,arguments):r[e].apply(r,Array.prototype.slice.call(arguments,0,n-1))}}const mr=Dn(dr("slice",(function(e,t,n){return Array.prototype.slice.call(n,e,t)})));const gr=Bt(dr("tail",mr(1,1/0)));function yr(){if(0===arguments.length)throw new Error("pipe requires at least one argument");return Gt(arguments[0].length,Kn(fr,arguments[0],gr(arguments)))}var vr=$t((function(e,t){return Yt(Kn(An,0,Mn("length",t)),(function(){var n=arguments,r=this;return e.apply(r,mn((function(e){return e.apply(r,n)}),t))}))}));const br=vr;function wr(e){return new RegExp(e.source,e.flags?e.flags:(e.global?"g":"")+(e.ignoreCase?"i":"")+(e.multiline?"m":"")+(e.sticky?"y":"")+(e.unicode?"u":"")+(e.dotAll?"s":""))}function Er(e,t,n){if(n||(n=new xr),function(e){var t=typeof e;return null==e||"object"!=t&&"function"!=t}(e))return e;var r=function(r){var o=n.get(e);if(o)return o;for(var s in n.set(e,r),e)Object.prototype.hasOwnProperty.call(e,s)&&(r[s]=t?Er(e[s],!0,n):e[s]);return r};switch(un(e)){case"Object":return r(Object.create(Object.getPrototypeOf(e)));case"Array":return r([]);case"Date":return new Date(e.valueOf());case"RegExp":return wr(e);case"Int8Array":case"Uint8Array":case"Uint8ClampedArray":case"Int16Array":case"Uint16Array":case"Int32Array":case"Uint32Array":case"Float32Array":case"Float64Array":case"BigInt64Array":case"BigUint64Array":return e.slice();default:return e}}var xr=function(){function e(){this.map={},this.length=0}return e.prototype.set=function(e,t){const n=this.hash(e);let r=this.map[n];r||(this.map[n]=r=[]),r.push([e,t]),this.length+=1},e.prototype.hash=function(e){let t=[];for(var n in e)t.push(Object.prototype.toString.call(e[n]));return t.join()},e.prototype.get=function(e){if(this.length<=180){for(const t in this.map){const n=this.map[t];for(let t=0;t=0&&this.i>=this.n?zt(n):n},e}();function Tr(e){return function(t){return new Ir(e,t)}}const Rr=$t(Ut(["take"],Tr,(function(e,t){return mr(0,e<0?1/0:e,t)})));function Mr(e,t){for(var n=t.length-1;n>=0&&e(t[n]);)n-=1;return mr(0,n+1,t)}var Dr=function(){function e(e,t){this.f=e,this.retained=[],this.xf=t}return e.prototype["@@transducer/init"]=Vt,e.prototype["@@transducer/result"]=function(e){return this.retained=null,this.xf["@@transducer/result"](e)},e.prototype["@@transducer/step"]=function(e,t){return this.f(t)?this.retain(e,t):this.flush(e,t)},e.prototype.flush=function(e,t){return e=Vn(this.xf,e,this.retained),this.retained=[],this.xf["@@transducer/step"](e,t)},e.prototype.retain=function(e,t){return this.retained.push(t),e},e}();function Fr(e){return function(t){return new Dr(e,t)}}const Lr=$t(Ut([],Fr,Mr));var Br=function(){function e(e,t){this.xf=t,this.f=e}return e.prototype["@@transducer/init"]=Vt,e.prototype["@@transducer/result"]=Wt,e.prototype["@@transducer/step"]=function(e,t){if(this.f){if(this.f(t))return e;this.f=null}return this.xf["@@transducer/step"](e,t)},e}();function $r(e){return function(t){return new Br(e,t)}}const qr=$t(Ut(["dropWhile"],$r,(function(e,t){for(var n=0,r=t.length;ne.classes.contains("api"))).first}get results(){return this.children.filter((e=>e.classes.contains("result")))}get result(){return this.results.first}get annotations(){return this.children.filter((e=>"annotation"===e.element))}get warnings(){return this.children.filter((e=>"annotation"===e.element&&e.classes.contains("warning")))}get errors(){return this.children.filter((e=>"annotation"===e.element&&e.classes.contains("error")))}get isEmpty(){return this.children.reject((e=>"annotation"===e.element)).isEmpty}replaceResult(e){const{result:t}=this;if(Uo(t))return!1;const n=this.content.findIndex((e=>e===t));return-1!==n&&(this.content[n]=e,!0)}}const Vo=zo;class Wo extends Nt.ON{constructor(e,t,n){super(e,t,n),this.element="sourceMap"}get positionStart(){return this.children.filter((e=>e.classes.contains("position"))).get(0)}get positionEnd(){return this.children.filter((e=>e.classes.contains("position"))).get(1)}set position(e){if(null===e)return;const t=new Nt.ON([e.start.row,e.start.column,e.start.char]),n=new Nt.ON([e.end.row,e.end.column,e.end.char]);t.classes.push("position"),n.classes.push("position"),this.push(t).push(n)}}const Jo=Wo;var Ko=n(80621),Ho=n(52201),Go=n(27398);function Zo(e){return Zo="function"==typeof Ho&&"symbol"==typeof Go?function(e){return typeof e}:function(e){return e&&"function"==typeof Ho&&e.constructor===Ho&&e!==Ho.prototype?"symbol":typeof e},Zo(e)}var Yo=n(26189);function Xo(e){var t=function(e,t){if("object"!==Zo(e)||null===e)return e;var n=e[Yo];if(void 0!==n){var r=n.call(e,t||"default");if("object"!==Zo(r))return r;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"===Zo(t)?t:String(t)}function Qo(e,t,n){return(t=Xo(t))in e?Ko(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}const es=Yt(1,yr(un,Qr("GeneratorFunction")));const ts=Yt(1,yr(un,Qr("AsyncFunction")));const ns=Zn([yr(un,Qr("Function")),es,ts]);const rs=hr(ns);const os=Yt(1,ns(Array.isArray)?Array.isArray:yr(un,Qr("Array")));const ss=ur(os,io);var is=Yt(3,(function(e,t,n){var r=po(e,n),o=po(oo(e),n);if(!rs(r)&&!ss(e)){var s=qn(r,o);return tr(s,t)}}));const as=is;const ls=Jr(ro),cs=(e,t)=>"function"==typeof(null==t?void 0:t[e]),us=e=>null!=e&&Object.prototype.hasOwnProperty.call(e,"_storedElement")&&Object.prototype.hasOwnProperty.call(e,"_content"),ps=(e,t)=>{var n;return(null==t||null===(n=t.primitive)||void 0===n?void 0:n.call(t))===e},hs=(e,t)=>{var n,r;return(null==t||null===(n=t.classes)||void 0===n||null===(r=n.includes)||void 0===r?void 0:r.call(n,e))||!1},fs=(e,t)=>(null==t?void 0:t.element)===e,ds=e=>e({hasMethod:cs,hasBasicElementProps:us,primitiveEq:ps,isElementType:fs,hasClass:hs}),ms=ds((({hasBasicElementProps:e,primitiveEq:t})=>n=>n instanceof Nt.W_||e(n)&&t(void 0,n))),gs=ds((({hasBasicElementProps:e,primitiveEq:t})=>n=>n instanceof Nt.RP||e(n)&&t("string",n))),ys=ds((({hasBasicElementProps:e,primitiveEq:t})=>n=>n instanceof Nt.VL||e(n)&&t("number",n))),vs=ds((({hasBasicElementProps:e,primitiveEq:t})=>n=>n instanceof Nt.zr||e(n)&&t("null",n))),bs=ds((({hasBasicElementProps:e,primitiveEq:t})=>n=>n instanceof Nt.hh||e(n)&&t("boolean",n))),ws=ds((({hasBasicElementProps:e,primitiveEq:t,hasMethod:n})=>r=>r instanceof Nt.Sb||e(r)&&t("object",r)&&n("keys",r)&&n("values",r)&&n("items",r))),Es=ds((({hasBasicElementProps:e,primitiveEq:t,hasMethod:n})=>r=>r instanceof Nt.ON&&!(r instanceof Nt.Sb)||e(r)&&t("array",r)&&n("push",r)&&n("unshift",r)&&n("map",r)&&n("reduce",r))),xs=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Nt.c6||e(r)&&t("member",r)&&n(void 0,r))),Ss=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Nt.EA||e(r)&&t("link",r)&&n(void 0,r))),_s=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Nt.tK||e(r)&&t("ref",r)&&n(void 0,r))),js=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Tt||e(r)&&t("annotation",r)&&n("array",r))),Os=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Mt||e(r)&&t("comment",r)&&n("string",r))),ks=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Vo||e(r)&&t("parseResult",r)&&n("array",r))),As=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Jo||e(r)&&t("sourceMap",r)&&n("array",r))),Cs=e=>fs("object",e)||fs("array",e)||fs("boolean",e)||fs("number",e)||fs("string",e)||fs("null",e)||fs("member",e),Ps=e=>{var t,n;return As(null==e||null===(t=e.meta)||void 0===t||null===(n=t.get)||void 0===n?void 0:n.call(t,"sourceMap"))},Ns=(e,t)=>{if(0===e.length)return!0;const n=t.attributes.get("symbols");return!!Es(n)&&Ht(ls(n.toValue()),e)},Is=(e,t)=>0===e.length||Ht(ls(t.classes.toValue()),e);const Ts=fn(null);const Rs=hr(Ts);function Ms(e){return Ms="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},Ms(e)}const Ds=function(e){return"object"===Ms(e)};const Fs=Yt(1,ur(Rs,Ds));var Ls=yr(un,Qr("Object")),Bs=yr(kn,fn(kn(Object))),$s=Eo(ur(ns,Bs),["constructor"]);const qs=Yt(1,(function(e){if(!Fs(e)||!Ls(e))return!1;var t=Object.getPrototypeOf(e);return!!Ts(t)||$s(t)}));class Us extends Nt.lS{constructor(){super(),this.register("annotation",Tt),this.register("comment",Mt),this.register("parseResult",Vo),this.register("sourceMap",Jo)}}const zs=new Us,Vs=e=>{const t=new Us;return qs(e)&&t.use(e),t},Ws=zs;function Js(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}const Ks=()=>({predicates:function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Hs){var s=Hs(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var Xs=n(43992);const Qs=Yt(1,yr(un,Qr("String"))),ei=(e,t,n)=>{const r=e[t];if(null!=r){if(!n&&"function"==typeof r)return r;const e=n?r.leave:r.enter;if("function"==typeof e)return e}else{const r=n?e.leave:e.enter;if(null!=r){if("function"==typeof r)return r;const e=r[t];if("function"==typeof e)return e}}return null},ti={},ni=e=>null==e?void 0:e.type,ri=e=>"string"==typeof ni(e),oi=(e,{visitFnGetter:t=ei,nodeTypeGetter:n=ni}={})=>{const r=new Array(e.length);return{enter(o,...s){for(let i=0;i{const p=n||{};let h,f,d=Array.isArray(e),m=[e],g=-1,y=[];const v=[],b=[];let w=e;do{g+=1;const e=g===m.length;let n,E;const x=e&&0!==y.length;if(e){if(n=0===b.length?void 0:v.pop(),E=f,f=b.pop(),x){E=d?E.slice():Object.create(Object.getPrototypeOf(E),Object.getOwnPropertyDescriptors(E));let e=0;for(let t=0;t{const p=n||{};let h,f,d=Array.isArray(e),m=[e],g=-1,y=[];const v=[],b=[];let w=e;do{g+=1;const e=g===m.length;let n,E;const x=e&&0!==y.length;if(e){if(n=0===b.length?void 0:v.pop(),E=f,f=b.pop(),x){E=d?E.slice():Object.create(Object.getPrototypeOf(E),Object.getOwnPropertyDescriptors(E));let e=0;for(let t=0;tws(e)?"ObjectElement":Es(e)?"ArrayElement":xs(e)?"MemberElement":gs(e)?"StringElement":bs(e)?"BooleanElement":ys(e)?"NumberElement":vs(e)?"NullElement":Ss(e)?"LinkElement":_s(e)?"RefElement":void 0,pi=yr(ui,Qs),hi={ObjectElement:["content"],ArrayElement:["content"],MemberElement:["key","value"],StringElement:[],BooleanElement:[],NumberElement:[],NullElement:[],RefElement:[],LinkElement:[],Annotation:[],Comment:[],ParseResultElement:["content"],SourceMap:["content"]},fi=Xs({props:{result:[],predicate:Dt,returnOnTrue:void 0,returnOnFalse:void 0},init({predicate:e=this.predicate,returnOnTrue:t=this.returnOnTrue,returnOnFalse:n=this.returnOnFalse}={}){this.result=[],this.predicate=e,this.returnOnTrue=t,this.returnOnFalse=n},methods:{enter(e){return this.predicate(e)?(this.result.push(e),this.returnOnTrue):this.returnOnFalse}}}),di=(e,t,n={})=>{let{keyMap:r=hi}=n,o=Ys(n,ii);return si(e,t,ci({keyMap:r,nodeTypeGetter:ui,nodePredicate:pi},o))};di[Symbol.for("nodejs.util.promisify.custom")]=async(e,t,n={})=>{let{keyMap:r=hi}=n,o=Ys(n,ai);return si[Symbol.for("nodejs.util.promisify.custom")](e,t,ci({keyMap:r,nodeTypeGetter:ui,nodePredicate:pi},o))};const mi=(e,t,n={})=>{if(0===t.length)return e;const r=_o(Ks,"toolboxCreator",n),o=_o({},"visitorOptions",n),s=_o(ui,"nodeTypeGetter",o),i=r(),a=t.map((e=>e(i))),l=oi(a.map(_o({},"visitor")),{nodeTypeGetter:s});a.forEach(as(["pre"],[]));const c=di(e,l,o);return a.forEach(as(["post"],[])),c};function gi(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function yi(e){for(var t=1;t{const r=new t(e);return mi(r,n,{toolboxCreator:Ks,visitorOptions:{nodeTypeGetter:ui}})},bi=e=>(t,n={})=>vi(t,yi(yi({},n),{},{Type:e}));Nt.Sb.refract=bi(Nt.Sb),Nt.ON.refract=bi(Nt.ON),Nt.RP.refract=bi(Nt.RP),Nt.hh.refract=bi(Nt.hh),Nt.zr.refract=bi(Nt.zr),Nt.VL.refract=bi(Nt.VL),Nt.EA.refract=bi(Nt.EA),Nt.tK.refract=bi(Nt.tK),Tt.refract=bi(Tt),Mt.refract=bi(Mt),Vo.refract=bi(Vo),Jo.refract=bi(Jo);const wi=(e,t=new WeakMap)=>(xs(e)?(t.set(e.key,e),wi(e.key,t),t.set(e.value,e),wi(e.value,t)):e.children.forEach((n=>{t.set(n,e),wi(n,t)})),t),Ei=Xs.init((function({element:e}){let t;this.transclude=function(n,r){var o;if(n===e)return r;if(n===r)return e;t=null!==(o=t)&&void 0!==o?o:wi(e);const s=t.get(n);return Uo(s)?void 0:(ws(s)?((e,t,n)=>{const r=n.get(e);ws(r)&&(r.content=r.map(((o,s,i)=>i===e?(n.delete(e),n.set(t,r),t):i)))})(n,r,t):Es(s)?((e,t,n)=>{const r=n.get(e);Es(r)&&(r.content=r.map((o=>o===e?(n.delete(e),n.set(t,r),t):o)))})(n,r,t):xs(s)&&((e,t,n)=>{const r=n.get(e);xs(r)&&(r.key===e&&(r.key=t,n.delete(e),n.set(t,r)),r.value===e&&(r.value=t,n.delete(e),n.set(t,r)))})(n,r,t),e)}})),xi=Ei,Si=["keyMap"],_i=["keyMap"];function ji(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Oi(e){for(var t=1;t"string"==typeof(null==e?void 0:e.type)?e.type:ui(e),Ai=Oi({EphemeralObject:["content"],EphemeralArray:["content"]},hi),Ci=(e,t,n={})=>{let{keyMap:r=Ai}=n,o=Ys(n,Si);return di(e,t,Oi({keyMap:r,nodeTypeGetter:ki,nodePredicate:Ft,detectCycles:!1,deleteNodeSymbol:Symbol.for("delete-node"),skipVisitingNodeSymbol:Symbol.for("skip-visiting-node")},o))};Ci[Symbol.for("nodejs.util.promisify.custom")]=async(e,t={})=>{let{keyMap:n=Ai}=t,r=Ys(t,_i);return di[Symbol.for("nodejs.util.promisify.custom")](e,visitor,Oi({keyMap:n,nodeTypeGetter:ki,nodePredicate:Ft,detectCycles:!1,deleteNodeSymbol:Symbol.for("delete-node"),skipVisitingNodeSymbol:Symbol.for("skip-visiting-node")},r))};const Pi=class{constructor(e){Qo(this,"type","EphemeralArray"),Qo(this,"content",[]),Qo(this,"reference",void 0),this.content=e,this.reference=[]}toReference(){return this.reference}toArray(){return this.reference.push(...this.content),this.reference}};const Ni=class{constructor(e){Qo(this,"type","EphemeralObject"),Qo(this,"content",[]),Qo(this,"reference",void 0),this.content=e,this.reference={}}toReference(){return this.reference}toObject(){return Object.assign(this.reference,Object.fromEntries(this.content))}},Ii=Xs.init((function(){const e=new WeakMap;this.BooleanElement=function(e){return e.toValue()},this.NumberElement=function(e){return e.toValue()},this.StringElement=function(e){return e.toValue()},this.NullElement=function(){return null},this.ObjectElement={enter(t){if(e.has(t))return e.get(t).toReference();const n=new Ni(t.content);return e.set(t,n),n}},this.EphemeralObject={leave:e=>e.toObject()},this.MemberElement={enter:e=>[e.key,e.value]},this.ArrayElement={enter(t){if(e.has(t))return e.get(t).toReference();const n=new Pi(t.content);return e.set(t,n),n}},this.EphemeralArray={leave:e=>e.toArray()}})),Ti=(e,t=Ws)=>{if(Qs(e))try{return t.fromRefract(JSON.parse(e))}catch{}return qs(e)&&Gr("element",e)?t.fromRefract(e):t.toElement(e)},Ri=e=>Ci(e,Ii());const Mi=fn("");var Di=ur(Yt(1,yr(un,Qr("Number"))),isFinite);var Fi=Yt(1,Di);var Li=ur(ns(Number.isFinite)?Yt(1,qn(Number.isFinite,Number)):Fi,br(fn,[Math.floor,to]));var Bi=Yt(1,Li);const $i=ns(Number.isInteger)?Yt(1,qn(Number.isInteger,Number)):Bi;var qi=kr((function(e,t){return yr(To(""),qr(ls(e)),ao(""))(t)}));const Ui=qi;class zi extends Error{constructor(e){super(`Invalid $ref pointer "${e}". Pointers must begin with "/"`),this.name=this.constructor.name,this.message=`Invalid $ref pointer "${e}". Pointers must begin with "/"`,"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error(`Invalid $ref pointer "${e}". Pointers must begin with "/"`).stack}}class Vi extends Error{constructor(e){super(e),this.name=this.constructor.name,this.message=e,"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack}}const Wi=yr(Po(/~/g,"~0"),Po(/\//g,"~1"),encodeURIComponent),Ji=yr(Po(/~1/g,"/"),Po(/~0/g,"~"),(e=>{try{return decodeURIComponent(e)}catch{return e}})),Ki=(e,t)=>{const n=(e=>{if(Mi(e))return[];if(!Ro("/",e))throw new zi(e);const t=yr(To("/"),Pn(Ji))(e);return gr(t)})(e);return n.reduce(((e,t)=>{if(ws(e)){if(!e.hasKey(t))throw new Vi(`Evaluation failed on token: "${t}"`);return e.get(t)}if(Es(e)){if(!(t in e.content)||!$i(Number(t)))throw new Vi(`Evaluation failed on token: "${t}"`);return e.get(Number(t))}throw new Vi(`Evaluation failed on token: "${t}"`)}),t)},Hi=e=>{const t=(e=>{const t=e.indexOf("#");return-1!==t?e.substring(t):"#"})(e);return Ui("#",t)};class Gi extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="callback"}}const Zi=Gi;class Yi extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="components"}get schemas(){return this.get("schemas")}set schemas(e){this.set("schemas",e)}get responses(){return this.get("responses")}set responses(e){this.set("responses",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get requestBodies(){return this.get("requestBodies")}set requestBodies(e){this.set("requestBodies",e)}get headers(){return this.get("headers")}set headers(e){this.set("headers",e)}get securitySchemes(){return this.get("securitySchemes")}set securitySchemes(e){this.set("securitySchemes",e)}get links(){return this.get("links")}set links(e){this.set("links",e)}get callbacks(){return this.get("callbacks")}set callbacks(e){this.set("callbacks",e)}}const Xi=Yi;class Qi extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="contact"}get name(){return this.get("name")}set name(e){this.set("name",e)}get url(){return this.get("url")}set url(e){this.set("url",e)}get email(){return this.get("email")}set email(e){this.set("email",e)}}const ea=Qi;class ta extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="discriminator"}get propertyName(){return this.get("propertyName")}set propertyName(e){this.set("propertyName",e)}get mapping(){return this.get("mapping")}set mapping(e){this.set("mapping",e)}}const na=ta;class ra extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="encoding"}get contentType(){return this.get("contentType")}set contentType(e){this.set("contentType",e)}get headers(){return this.get("headers")}set headers(e){this.set("headers",e)}get style(){return this.get("style")}set style(e){this.set("style",e)}get explode(){return this.get("explode")}set explode(e){this.set("explode",e)}get allowedReserved(){return this.get("allowedReserved")}set allowedReserved(e){this.set("allowedReserved",e)}}const oa=ra;class sa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="example"}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get value(){return this.get("value")}set value(e){this.set("value",e)}get externalValue(){return this.get("externalValue")}set externalValue(e){this.set("externalValue",e)}}const ia=sa;class aa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="externalDocumentation"}get description(){return this.get("description")}set description(e){this.set("description",e)}get url(){return this.get("url")}set url(e){this.set("url",e)}}const la=aa;class ca extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="header"}get required(){return this.hasKey("required")?this.get("required"):new Nt.hh(!1)}set required(e){this.set("required",e)}get deprecated(){return this.hasKey("deprecated")?this.get("deprecated"):new Nt.hh(!1)}set deprecated(e){this.set("deprecated",e)}get allowEmptyValue(){return this.get("allowEmptyValue")}set allowEmptyValue(e){this.set("allowEmptyValue",e)}get style(){return this.get("style")}set style(e){this.set("style",e)}get explode(){return this.get("explode")}set explode(e){this.set("explode",e)}get allowReserved(){return this.get("allowReserved")}set allowReserved(e){this.set("allowReserved",e)}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}}Object.defineProperty(ca.prototype,"description",{get(){return this.get("description")},set(e){this.set("description",e)},enumerable:!0});const ua=ca;class pa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="info",this.classes.push("info")}get title(){return this.get("title")}set title(e){this.set("title",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get termsOfService(){return this.get("termsOfService")}set termsOfService(e){this.set("termsOfService",e)}get contact(){return this.get("contact")}set contact(e){this.set("contact",e)}get license(){return this.get("license")}set license(e){this.set("license",e)}get version(){return this.get("version")}set version(e){this.set("version",e)}}const ha=pa;class fa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="license"}get name(){return this.get("name")}set name(e){this.set("name",e)}get url(){return this.get("url")}set url(e){this.set("url",e)}}const da=fa;class ma extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="link"}get operationRef(){return this.get("operationRef")}set operationRef(e){this.set("operationRef",e)}get operationId(){return this.get("operationId")}set operationId(e){this.set("operationId",e)}get operation(){var e,t;return gs(this.operationRef)?null===(e=this.operationRef)||void 0===e?void 0:e.meta.get("operation"):gs(this.operationId)?null===(t=this.operationId)||void 0===t?void 0:t.meta.get("operation"):void 0}set operation(e){this.set("operation",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}get requestBody(){return this.get("requestBody")}set requestBody(e){this.set("requestBody",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get server(){return this.get("server")}set server(e){this.set("server",e)}}const ga=ma;class ya extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="mediaType"}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get encoding(){return this.get("encoding")}set encoding(e){this.set("encoding",e)}}const va=ya;class ba extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="oAuthFlow"}get authorizationUrl(){return this.get("authorizationUrl")}set authorizationUrl(e){this.set("authorizationUrl",e)}get tokenUrl(){return this.get("tokenUrl")}set tokenUrl(e){this.set("tokenUrl",e)}get refreshUrl(){return this.get("refreshUrl")}set refreshUrl(e){this.set("refreshUrl",e)}get scopes(){return this.get("scopes")}set scopes(e){this.set("scopes",e)}}const wa=ba;class Ea extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="oAuthFlows"}get implicit(){return this.get("implicit")}set implicit(e){this.set("implicit",e)}get password(){return this.get("password")}set password(e){this.set("password",e)}get clientCredentials(){return this.get("clientCredentials")}set clientCredentials(e){this.set("clientCredentials",e)}get authorizationCode(){return this.get("authorizationCode")}set authorizationCode(e){this.set("authorizationCode",e)}}const xa=Ea;class Sa extends Nt.RP{constructor(e,t,n){super(e,t,n),this.element="openapi",this.classes.push("spec-version"),this.classes.push("version")}}const _a=Sa;class ja extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="openApi3_0",this.classes.push("api")}get openapi(){return this.get("openapi")}set openapi(e){this.set("openapi",e)}get info(){return this.get("info")}set info(e){this.set("info",e)}get servers(){return this.get("servers")}set servers(e){this.set("servers",e)}get paths(){return this.get("paths")}set paths(e){this.set("paths",e)}get components(){return this.get("components")}set components(e){this.set("components",e)}get security(){return this.get("security")}set security(e){this.set("security",e)}get tags(){return this.get("tags")}set tags(e){this.set("tags",e)}get externalDocs(){return this.get("externalDocs")}set externalDocs(e){this.set("externalDocs",e)}}const Oa=ja;class ka extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="operation"}get tags(){return this.get("tags")}set tags(e){this.set("tags",e)}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}set externalDocs(e){this.set("externalDocs",e)}get externalDocs(){return this.get("externalDocs")}get operationId(){return this.get("operationId")}set operationId(e){this.set("operationId",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}get requestBody(){return this.get("requestBody")}set requestBody(e){this.set("requestBody",e)}get responses(){return this.get("responses")}set responses(e){this.set("responses",e)}get callbacks(){return this.get("callbacks")}set callbacks(e){this.set("callbacks",e)}get deprecated(){return this.hasKey("deprecated")?this.get("deprecated"):new Nt.hh(!1)}set deprecated(e){this.set("deprecated",e)}get security(){return this.get("security")}set security(e){this.set("security",e)}get servers(){return this.get("severs")}set servers(e){this.set("servers",e)}}const Aa=ka;class Ca extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="parameter"}get name(){return this.get("name")}set name(e){this.set("name",e)}get in(){return this.get("in")}set in(e){this.set("in",e)}get required(){return this.hasKey("required")?this.get("required"):new Nt.hh(!1)}set required(e){this.set("required",e)}get deprecated(){return this.hasKey("deprecated")?this.get("deprecated"):new Nt.hh(!1)}set deprecated(e){this.set("deprecated",e)}get allowEmptyValue(){return this.get("allowEmptyValue")}set allowEmptyValue(e){this.set("allowEmptyValue",e)}get style(){return this.get("style")}set style(e){this.set("style",e)}get explode(){return this.get("explode")}set explode(e){this.set("explode",e)}get allowReserved(){return this.get("allowReserved")}set allowReserved(e){this.set("allowReserved",e)}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}get example(){return this.get("example")}set example(e){this.set("example",e)}get examples(){return this.get("examples")}set examples(e){this.set("examples",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}}Object.defineProperty(Ca.prototype,"description",{get(){return this.get("description")},set(e){this.set("description",e)},enumerable:!0});const Pa=Ca;class Na extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="pathItem"}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}get summary(){return this.get("summary")}set summary(e){this.set("summary",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get GET(){return this.get("get")}set GET(e){this.set("GET",e)}get PUT(){return this.get("put")}set PUT(e){this.set("PUT",e)}get POST(){return this.get("post")}set POST(e){this.set("POST",e)}get DELETE(){return this.get("delete")}set DELETE(e){this.set("DELETE",e)}get OPTIONS(){return this.get("options")}set OPTIONS(e){this.set("OPTIONS",e)}get HEAD(){return this.get("head")}set HEAD(e){this.set("HEAD",e)}get PATCH(){return this.get("patch")}set PATCH(e){this.set("PATCH",e)}get TRACE(){return this.get("trace")}set TRACE(e){this.set("TRACE",e)}get servers(){return this.get("servers")}set servers(e){this.set("servers",e)}get parameters(){return this.get("parameters")}set parameters(e){this.set("parameters",e)}}const Ia=Na;class Ta extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="paths"}}const Ra=Ta;class Ma extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="reference",this.classes.push("openapi-reference")}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}}const Da=Ma;class Fa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="requestBody"}get description(){return this.get("description")}set description(e){this.set("description",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}get required(){return this.hasKey("required")?this.get("required"):new Nt.hh(!1)}set required(e){this.set("required",e)}}const La=Fa;class Ba extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="response"}get description(){return this.get("description")}set description(e){this.set("description",e)}get headers(){return this.get("headers")}set headers(e){this.set("headers",e)}get contentProp(){return this.get("content")}set contentProp(e){this.set("content",e)}get links(){return this.get("links")}set links(e){this.set("links",e)}}const $a=Ba;class qa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="responses"}get default(){return this.get("default")}set default(e){this.set("default",e)}}const Ua=qa;class za extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="JSONSchemaDraft4"}get idProp(){return this.get("id")}set idProp(e){this.set("id",e)}get $schema(){return this.get("$schema")}set $schema(e){this.set("idProp",e)}get multipleOf(){return this.get("multipleOf")}set multipleOf(e){this.set("multipleOf",e)}get maximum(){return this.get("maximum")}set maximum(e){this.set("maximum",e)}get exclusiveMaximum(){return this.get("exclusiveMaximum")}set exclusiveMaximum(e){this.set("exclusiveMaximum",e)}get minimum(){return this.get("minimum")}set minimum(e){this.set("minimum",e)}get exclusiveMinimum(){return this.get("exclusiveMinimum")}set exclusiveMinimum(e){this.set("exclusiveMinimum",e)}get maxLength(){return this.get("maxLength")}set maxLength(e){this.set("maxLength",e)}get minLength(){return this.get("minLength")}set minLength(e){this.set("minLength",e)}get pattern(){return this.get("pattern")}set pattern(e){this.set("pattern",e)}get additionalItems(){return this.get("additionalItems")}set additionalItems(e){this.set("additionalItems",e)}get items(){return this.get("items")}set items(e){this.set("items",e)}get maxItems(){return this.get("maxItems")}set maxItems(e){this.set("maxItems",e)}get minItems(){return this.get("minItems")}set minItems(e){this.set("minItems",e)}get uniqueItems(){return this.get("uniqueItems")}set uniqueItems(e){this.set("uniqueItems",e)}get maxProperties(){return this.get("maxProperties")}set maxProperties(e){this.set("maxProperties",e)}get minProperties(){return this.get("minProperties")}set minProperties(e){this.set("minProperties",e)}get required(){return this.get("required")}set required(e){this.set("required",e)}get properties(){return this.get("properties")}set properties(e){this.set("properties",e)}get additionalProperties(){return this.get("additionalProperties")}set additionalProperties(e){this.set("additionalProperties",e)}get patternProperties(){return this.get("patternProperties")}set patternProperties(e){this.set("patternProperties",e)}get dependencies(){return this.get("dependencies")}set dependencies(e){this.set("dependencies",e)}get enum(){return this.get("enum")}set enum(e){this.set("enum",e)}get type(){return this.get("type")}set type(e){this.set("type",e)}get allOf(){return this.get("allOf")}set allOf(e){this.set("allOf",e)}get anyOf(){return this.get("anyOf")}set anyOf(e){this.set("anyOf",e)}get oneOf(){return this.get("oneOf")}set oneOf(e){this.set("oneOf",e)}get not(){return this.get("not")}set not(e){this.set("not",e)}get definitions(){return this.get("definitions")}set definitions(e){this.set("definitions",e)}get title(){return this.get("title")}set title(e){this.set("title",e)}get description(){return this.get("description")}set description(e){this.set("description",e)}get default(){return this.get("default")}set default(e){this.set("default",e)}get format(){return this.get("format")}set format(e){this.set("format",e)}get base(){return this.get("base")}set base(e){this.set("base",e)}get links(){return this.get("links")}set links(e){this.set("links",e)}get media(){return this.get("media")}set media(e){this.set("media",e)}get readOnly(){return this.get("readOnly")}set readOnly(e){this.set("readOnly",e)}}const Va=za;class Wa extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="JSONReference",this.classes.push("json-reference")}get $ref(){return this.get("$ref")}set $ref(e){this.set("$ref",e)}}const Ja=Wa;class Ka extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="media"}get binaryEncoding(){return this.get("binaryEncoding")}set binaryEncoding(e){this.set("binaryEncoding",e)}get type(){return this.get("type")}set type(e){this.set("type",e)}}const Ha=Ka;class Ga extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.element="linkDescription"}get href(){return this.get("href")}set href(e){this.set("href",e)}get rel(){return this.get("rel")}set rel(e){this.set("rel",e)}get title(){return this.get("title")}set title(e){this.set("title",e)}get targetSchema(){return this.get("targetSchema")}set targetSchema(e){this.set("targetSchema",e)}get mediaType(){return this.get("mediaType")}set mediaType(e){this.set("mediaType",e)}get method(){return this.get("method")}set method(e){this.set("method",e)}get encType(){return this.get("encType")}set encType(e){this.set("encType",e)}get schema(){return this.get("schema")}set schema(e){this.set("schema",e)}}const Za=Ga,Ya=(e,t)=>{const n=Ar(e,t);return ho((e=>{if(qs(e)&&Gr("$ref",e)&&jo(Qs,"$ref",e)){const t=po(["$ref"],e),r=Ui("#/",t);return po(r.split("/"),n)}return qs(e)?Ya(e,n):e}),e)},Xa=Xs({props:{element:null},methods:{copyMetaAndAttributes(e,t){Ps(e)&&t.meta.set("sourceMap",e.meta.get("sourceMap"))}}}),Qa=Xa,el=Xs(Qa,{methods:{enter(e){return this.element=e.clone(),ti}}});const tl=Gn(qo());function nl(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}const rl=e=>{if(ms(e))return`${e.element.charAt(0).toUpperCase()+e.element.slice(1)}Element`},ol=function(e){for(var t=1;t{if(gs(r)&&n.includes(r.toValue())&&!this.ignoredFields.includes(r.toValue())){const n=this.toRefractedElement([...t,"fixedFields",r.toValue()],e),s=new Nt.c6(r.clone(),n);this.copyMetaAndAttributes(o,s),s.classes.push("fixed-field"),this.element.content.push(s)}else this.ignoredFields.includes(r.toValue())||this.element.content.push(o.clone())})),this.copyMetaAndAttributes(e,this.element),ti}}}),cl=ll,ul=Xs(cl,el,{props:{specPath:Gn(["document","objects","JSONSchema"])},init(){this.element=new Va}}),pl=el,hl=el,fl=el,dl=el,ml=el,gl=el,yl=el,vl=el,bl=el,wl=el,El=Xs({props:{parent:null},init({parent:e=this.parent}){this.parent=e,this.passingOptionsNames=[...this.passingOptionsNames,"parent"]}}),xl=e=>ws(e)&&e.hasKey("$ref"),Sl=Xs(al,El,el,{methods:{ObjectElement(e){const t=xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"];return this.element=this.toRefractedElement(t,e),ti},ArrayElement(e){return this.element=new Nt.ON,this.element.classes.push("json-schema-items"),e.forEach((e=>{const t=xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}}),_l=el,jl=el,Ol=el,kl=el,Al=el,Cl=Xs(el,{methods:{ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-required"),ti}}});const Pl=hr(Yt(1,ur(Rs,zr(Ds,ns))));const Nl=hr(io);const Il=Hn([Qs,Pl,Nl]),Tl=Xs(al,{props:{fieldPatternPredicate:Dt,specPath:tl,ignoredFields:[]},init({specPath:e=this.specPath,ignoredFields:t=this.ignoredFields}={}){this.specPath=e,this.ignoredFields=t},methods:{ObjectElement(e){return e.forEach(((e,t,n)=>{if(!this.ignoredFields.includes(t.toValue())&&this.fieldPatternPredicate(t.toValue())){const r=this.specPath(e),o=this.toRefractedElement(r,e),s=new Nt.c6(t.clone(),o);this.copyMetaAndAttributes(n,s),s.classes.push("patterned-field"),this.element.content.push(s)}else this.ignoredFields.includes(t.toValue())||this.element.content.push(n.clone())})),this.copyMetaAndAttributes(e,this.element),ti}}}),Rl=Xs(Tl,{props:{fieldPatternPredicate:Il}}),Ml=Xs(Rl,El,el,{props:{specPath:e=>xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-properties")}}),Dl=Xs(Rl,El,el,{props:{specPath:e=>xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-patternProperties")}}),Fl=Xs(Rl,El,el,{props:{specPath:e=>xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-dependencies")}}),Ll=Xs(el,{methods:{ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-enum"),ti}}}),Bl=Xs(el,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-type"),ti},ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-type"),ti}}}),$l=Xs(al,El,el,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-allOf")},methods:{ArrayElement(e){return e.forEach((e=>{const t=xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}}),ql=Xs(al,El,el,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-anyOf")},methods:{ArrayElement(e){return e.forEach((e=>{const t=xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}}),Ul=Xs(al,El,el,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-oneOf")},methods:{ArrayElement(e){return e.forEach((e=>{const t=xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}}),zl=Xs(Rl,El,el,{props:{specPath:e=>xl(e)?["document","objects","JSONReference"]:["document","objects","JSONSchema"]},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-definitions")}}),Vl=el,Wl=el,Jl=el,Kl=el,Hl=el,Gl=Xs(al,El,el,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-links")},methods:{ArrayElement(e){return e.forEach((e=>{const t=this.toRefractedElement(["document","objects","LinkDescription"],e);this.element.push(t)})),this.copyMetaAndAttributes(e,this.element),ti}}}),Zl=el,Yl=Xs(cl,el,{props:{specPath:Gn(["document","objects","JSONReference"])},init(){this.element=new Ja},methods:{ObjectElement(e){const t=cl.compose.methods.ObjectElement.call(this,e);return gs(this.element.$ref)&&this.element.classes.push("reference-element"),t}}}),Xl=Xs(el,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}});const Ql=hr(or);const ec=ur(os,Nl);function tc(e){return function(e){if(Array.isArray(e))return nc(e)}(e)||function(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}(e)||function(e,t){if(!e)return;if("string"==typeof e)return nc(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);"Object"===n&&e.constructor&&(n=e.constructor.name);if("Map"===n||"Set"===n)return Array.from(e);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return nc(e,t)}(e)||function(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()}function nc(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);nt.length}))),Yr,Rn("length")),oc=kr((function(e,t,n){var r=n.apply(void 0,tc(e));return Ql(r)?Co(r):t}));const sc=no(ec,(function(e){var t=rc(e);return Yt(t,(function(){for(var t=arguments.length,n=new Array(t),r=0;rno(e,Gn(t),qo))),n=sc(t)(e);return this.element=this.toRefractedElement(n,e),ti}}}),ac=Xs(ic,{props:{alternator:[{predicate:xl,specPath:["document","objects","JSONReference"]},{predicate:Ft,specPath:["document","objects","JSONSchema"]}]}}),lc={visitors:{value:el,JSONSchemaOrJSONReferenceVisitor:ac,document:{objects:{JSONSchema:{$visitor:ul,fixedFields:{id:pl,$schema:hl,multipleOf:fl,maximum:dl,exclusiveMaximum:ml,minimum:gl,exclusiveMinimum:yl,maxLength:vl,minLength:bl,pattern:wl,additionalItems:ac,items:Sl,maxItems:_l,minItems:jl,uniqueItems:Ol,maxProperties:kl,minProperties:Al,required:Cl,properties:Ml,additionalProperties:ac,patternProperties:Dl,dependencies:Fl,enum:Ll,type:Bl,allOf:$l,anyOf:ql,oneOf:Ul,not:ac,definitions:zl,title:Vl,description:Wl,default:Jl,format:Kl,base:Hl,links:Gl,media:{$ref:"#/visitors/document/objects/Media"},readOnly:Zl}},JSONReference:{$visitor:Yl,fixedFields:{$ref:Xl}},Media:{$visitor:Xs(cl,el,{props:{specPath:Gn(["document","objects","Media"])},init(){this.element=new Ha}}),fixedFields:{binaryEncoding:el,type:el}},LinkDescription:{$visitor:Xs(cl,el,{props:{specPath:Gn(["document","objects","LinkDescription"])},init(){this.element=new Za}}),fixedFields:{href:el,rel:el,title:el,targetSchema:ac,mediaType:el,method:el,encType:el,schema:ac}}}}}},cc=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Va||e(r)&&t("JSONSchemaDraft4",r)&&n("object",r))),uc=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ja||e(r)&&t("JSONReference",r)&&n("object",r))),pc=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ha||e(r)&&t("media",r)&&n("object",r))),hc=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Za||e(r)&&t("linkDescription",r)&&n("object",r))),fc={namespace:e=>{const{base:t}=e;return t.register("jSONSchemaDraft4",Va),t.register("jSONReference",Ja),t.register("media",Ha),t.register("linkDescription",Za),t}};function dc(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function mc(e){for(var t=1;t{const e=Vs(fc);return{predicates:mc(mc({},i),{},{isStringElement:gs}),namespace:e}};function yc(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}const vc=(e,{specPath:t=["visitors","document","objects","JSONSchema","$visitor"],plugins:n=[],specificationObj:r=lc}={})=>{const o=(0,Nt.Qc)(e),s=Ya(r),i=as(t,[],s);return di(o,i,{state:{specObj:s}}),mi(i.element,n,{toolboxCreator:gc,visitorOptions:{keyMap:ol,nodeTypeGetter:rl}})},bc=e=>(t,n={})=>vc(t,function(e){for(var t=1;t{if(ms(e))return`${e.element.charAt(0).toUpperCase()+e.element.slice(1)}Element`},Fc=function(e){for(var t=1;tws(e)&&e.hasKey("openapi")&&e.hasKey("info"),Uc=e=>ws(e)&&e.hasKey("name")&&e.hasKey("in"),zc=e=>ws(e)&&e.hasKey("$ref"),Vc=e=>ws(e)&&e.hasKey("content"),Wc=e=>ws(e)&&e.hasKey("description"),Jc=ws,Kc=ws,Hc=e=>gs(e.key)&&Ro("x-",e.key.toValue()),Gc=Xs($c,{props:{specPath:tl,ignoredFields:[],canSupportSpecificationExtensions:!0,specificationExtensionPredicate:Hc},init({specPath:e=this.specPath,ignoredFields:t=this.ignoredFields,canSupportSpecificationExtensions:n=this.canSupportSpecificationExtensions,specificationExtensionPredicate:r=this.specificationExtensionPredicate}={}){this.specPath=e,this.ignoredFields=t,this.canSupportSpecificationExtensions=n,this.specificationExtensionPredicate=r},methods:{ObjectElement(e){const t=this.specPath(e),n=this.retrieveFixedFields(t);return e.forEach(((e,r,o)=>{if(gs(r)&&n.includes(r.toValue())&&!this.ignoredFields.includes(r.toValue())){const n=this.toRefractedElement([...t,"fixedFields",r.toValue()],e),s=new Nt.c6(r.clone(),n);this.copyMetaAndAttributes(o,s),s.classes.push("fixed-field"),this.element.content.push(s)}else if(this.canSupportSpecificationExtensions&&this.specificationExtensionPredicate(o)){const e=this.toRefractedElement(["document","extension"],o);this.element.content.push(e)}else this.ignoredFields.includes(r.toValue())||this.element.content.push(o.clone())})),this.copyMetaAndAttributes(e,this.element),ti}}}),Zc=Gc,Yc=Xs(Rc,{methods:{enter(e){return this.element=e.clone(),ti}}}),Xc=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","OpenApi"]),canSupportSpecificationExtensions:!0},init(){this.element=new Oa},methods:{ObjectElement(e){return this.unrefractedElement=e,Zc.compose.methods.ObjectElement.call(this,e)}}}),Qc=Xs($c,Yc,{methods:{StringElement(e){const t=new _a(e.toValue());return this.copyMetaAndAttributes(e,t),this.element=t,ti}}}),eu=Xs($c,{methods:{MemberElement(e){return this.element=e.clone(),this.element.classes.push("specification-extension"),ti}}}),tu=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Info"]),canSupportSpecificationExtensions:!0},init(){this.element=new ha}}),nu=Yc,ru=Yc,ou=Yc,su=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("api-version"),this.element.classes.push("version"),ti}}}),iu=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Contact"]),canSupportSpecificationExtensions:!0},init(){this.element=new ea}}),au=Yc,lu=Yc,cu=Yc,uu=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","License"]),canSupportSpecificationExtensions:!0},init(){this.element=new da}}),pu=Yc,hu=Yc,fu=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Link"]),canSupportSpecificationExtensions:!0},init(){this.element=new ga},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return(gs(this.element.operationId)||gs(this.element.operationRef))&&this.element.classes.push("reference-element"),t}}}),du=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}}),mu=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}}),gu=Xs($c,{props:{fieldPatternPredicate:Dt,specPath:tl,ignoredFields:[],canSupportSpecificationExtensions:!1,specificationExtensionPredicate:Hc},init({specPath:e=this.specPath,ignoredFields:t=this.ignoredFields,canSupportSpecificationExtensions:n=this.canSupportSpecificationExtensions,specificationExtensionPredicate:r=this.specificationExtensionPredicate}={}){this.specPath=e,this.ignoredFields=t,this.canSupportSpecificationExtensions=n,this.specificationExtensionPredicate=r},methods:{ObjectElement(e){return e.forEach(((e,t,n)=>{if(this.canSupportSpecificationExtensions&&this.specificationExtensionPredicate(n)){const e=this.toRefractedElement(["document","extension"],n);this.element.content.push(e)}else if(!this.ignoredFields.includes(t.toValue())&&this.fieldPatternPredicate(t.toValue())){const r=this.specPath(e),o=this.toRefractedElement(r,e),s=new Nt.c6(t.clone(),o);this.copyMetaAndAttributes(n,s),s.classes.push("patterned-field"),this.element.content.push(s)}else this.ignoredFields.includes(t.toValue())||this.element.content.push(n.clone())})),this.copyMetaAndAttributes(e,this.element),ti}}}),yu=gu,vu=Xs(yu,{props:{fieldPatternPredicate:Il}});class bu extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(bu.primaryClass)}}Qo(bu,"primaryClass","link-parameters");const wu=bu,Eu=Xs(vu,Yc,{props:{specPath:Gn(["value"])},init(){this.element=new wu}}),xu=Yc,Su=Yc,_u=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Server"]),canSupportSpecificationExtensions:!0},init(){this.element=new Oc}}),ju=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("server-url"),ti}}}),Ou=Yc;class ku extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(ku.primaryClass)}}Qo(ku,"primaryClass","servers");const Au=ku,Cu=Xs($c,Yc,{init(){this.element=new Au},methods:{ArrayElement(e){return e.forEach((e=>{const t=Jc(e)?["document","objects","Server"]:["value"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}}),Pu=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","ServerVariable"]),canSupportSpecificationExtensions:!0},init(){this.element=new Ac}}),Nu=Yc,Iu=Yc,Tu=Yc;class Ru extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Ru.primaryClass)}}Qo(Ru,"primaryClass","server-variables");const Mu=Ru,Du=Xs(vu,Yc,{props:{specPath:Gn(["document","objects","ServerVariable"])},init(){this.element=new Mu}}),Fu=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","MediaType"]),canSupportSpecificationExtensions:!0},init(){this.element=new va}}),Lu=Xs($c,{props:{alternator:[]},methods:{enter(e){const t=this.alternator.map((({predicate:e,specPath:t})=>no(e,Gn(t),qo))),n=sc(t)(e);return this.element=this.toRefractedElement(n,e),ti}}}),Bu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Zi||e(r)&&t("callback",r)&&n("object",r))),$u=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Xi||e(r)&&t("components",r)&&n("object",r))),qu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof ea||e(r)&&t("contact",r)&&n("object",r))),Uu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof ia||e(r)&&t("example",r)&&n("object",r))),zu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof la||e(r)&&t("externalDocumentation",r)&&n("object",r))),Vu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof ua||e(r)&&t("header",r)&&n("object",r))),Wu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof ha||e(r)&&t("info",r)&&n("object",r))),Ju=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof da||e(r)&&t("license",r)&&n("object",r))),Ku=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof ga||e(r)&&t("link",r)&&n("object",r))),Hu=e=>{if(!Ku(e))return!1;if(!gs(e.operationRef))return!1;const t=e.operationRef.toValue();return"string"==typeof t&&t.length>0&&!t.startsWith("#")},Gu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof _a||e(r)&&t("openapi",r)&&n("string",r))),Zu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n,hasClass:r})=>o=>o instanceof Oa||e(o)&&t("openApi3_0",o)&&n("object",o)&&r("api",o))),Yu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Aa||e(r)&&t("operation",r)&&n("object",r))),Xu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Pa||e(r)&&t("parameter",r)&&n("object",r))),Qu=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ia||e(r)&&t("pathItem",r)&&n("object",r))),ep=e=>{if(!Qu(e))return!1;if(!gs(e.$ref))return!1;const t=e.$ref.toValue();return"string"==typeof t&&t.length>0&&!t.startsWith("#")},tp=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ra||e(r)&&t("paths",r)&&n("object",r))),np=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Da||e(r)&&t("reference",r)&&n("object",r))),rp=e=>{if(!np(e))return!1;if(!gs(e.$ref))return!1;const t=e.$ref.toValue();return"string"==typeof t&&t.length>0&&!t.startsWith("#")},op=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof La||e(r)&&t("requestBody",r)&&n("object",r))),sp=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof $a||e(r)&&t("response",r)&&n("object",r))),ip=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ua||e(r)&&t("responses",r)&&n("object",r))),ap=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof wc||e(r)&&t("schema",r)&&n("object",r))),lp=e=>bs(e)&&e.classes.includes("boolean-json-schema"),cp=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof xc||e(r)&&t("securityRequirement",r)&&n("object",r))),up=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Oc||e(r)&&t("server",r)&&n("object",r))),pp=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ac||e(r)&&t("serverVariable",r)&&n("object",r))),hp=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof va||e(r)&&t("mediaType",r)&&n("object",r))),fp=Xs(Lu,Yc,{props:{alternator:[{predicate:zc,specPath:["document","objects","Reference"]},{predicate:Ft,specPath:["document","objects","Schema"]}]},methods:{ObjectElement(e){const t=Lu.compose.methods.enter.call(this,e);return np(this.element)&&this.element.setMetaProperty("referenced-element","schema"),t}}}),dp=Yc,mp=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Example"],canSupportSpecificationExtensions:!0},init(){this.element=new Nt.Sb,this.element.classes.push("examples")},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","example")})),t}}});class gp extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(gp.primaryClass),this.classes.push("examples")}}Qo(gp,"primaryClass","media-type-examples");const yp=gp,vp=Xs(mp,{init(){this.element=new yp}});class bp extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(bp.primaryClass)}}Qo(bp,"primaryClass","media-type-encoding");const wp=bp,Ep=Xs(vu,Yc,{props:{specPath:Gn(["document","objects","Encoding"])},init(){this.element=new wp}}),xp=Xs(vu,Yc,{props:{specPath:Gn(["value"])},init(){this.element=new xc}});class Sp extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(Sp.primaryClass)}}Qo(Sp,"primaryClass","security");const _p=Sp,jp=Xs($c,Yc,{init(){this.element=new _p},methods:{ArrayElement(e){return e.forEach((e=>{if(ws(e)){const t=this.toRefractedElement(["document","objects","SecurityRequirement"],e);this.element.push(t)}else this.element.push(e.clone())})),this.copyMetaAndAttributes(e,this.element),ti}}}),Op=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Components"]),canSupportSpecificationExtensions:!0},init(){this.element=new Xi}}),kp=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Tag"]),canSupportSpecificationExtensions:!0},init(){this.element=new Pc}}),Ap=Yc,Cp=Yc,Pp=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Reference"]),canSupportSpecificationExtensions:!1},init(){this.element=new Da},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return gs(this.element.$ref)&&this.element.classes.push("reference-element"),t}}}),Np=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}}),Ip=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Parameter"]),canSupportSpecificationExtensions:!0},init(){this.element=new Pa},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return ws(this.element.contentProp)&&this.element.contentProp.filter(hp).forEach(((e,t)=>{e.setMetaProperty("media-type",t.toValue())})),t}}}),Tp=Yc,Rp=Yc,Mp=Yc,Dp=Yc,Fp=Yc,Lp=Yc,Bp=Yc,$p=Yc,qp=Yc,Up=Xs(Lu,Yc,{props:{alternator:[{predicate:zc,specPath:["document","objects","Reference"]},{predicate:Ft,specPath:["document","objects","Schema"]}]},methods:{ObjectElement(e){const t=Lu.compose.methods.enter.call(this,e);return np(this.element)&&this.element.setMetaProperty("referenced-element","schema"),t}}}),zp=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Header"]),canSupportSpecificationExtensions:!0},init(){this.element=new ua}}),Vp=Yc,Wp=Yc,Jp=Yc,Kp=Yc,Hp=Yc,Gp=Yc,Zp=Yc,Yp=Xs(Lu,Yc,{props:{alternator:[{predicate:zc,specPath:["document","objects","Reference"]},{predicate:Ft,specPath:["document","objects","Schema"]}]},methods:{ObjectElement(e){const t=Lu.compose.methods.enter.call(this,e);return np(this.element)&&this.element.setMetaProperty("referenced-element","schema"),t}}}),Xp=Yc;class Qp extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Qp.primaryClass),this.classes.push("examples")}}Qo(Qp,"primaryClass","header-examples");const eh=Qp,th=Xs(mp,{init(){this.element=new eh}}),nh=Xs(vu,Yc,{props:{specPath:Gn(["document","objects","MediaType"])},init(){this.element=new Nt.Sb,this.element.classes.push("content")}});class rh extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(rh.primaryClass),this.classes.push("content")}}Qo(rh,"primaryClass","header-content");const oh=rh,sh=Xs(nh,{init(){this.element=new oh}}),ih=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Schema"]),canSupportSpecificationExtensions:!0},init(){this.element=new wc}}),{allOf:ah}=lc.visitors.document.objects.JSONSchema.fixedFields,lh=Xs(ah,{methods:{ArrayElement(e){const t=ah.compose.methods.ArrayElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{anyOf:ch}=lc.visitors.document.objects.JSONSchema.fixedFields,uh=Xs(ch,{methods:{ArrayElement(e){const t=ch.compose.methods.ArrayElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{oneOf:ph}=lc.visitors.document.objects.JSONSchema.fixedFields,hh=Xs(ph,{methods:{ArrayElement(e){const t=ph.compose.methods.ArrayElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{definitions:fh}=lc.visitors.document.objects.JSONSchema.fixedFields,dh=Xs(fh,{methods:{ObjectElement(e){const t=fh.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{dependencies:mh}=lc.visitors.document.objects.JSONSchema.fixedFields,gh=Xs(mh,{methods:{ObjectElement(e){const t=mh.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{items:yh}=lc.visitors.document.objects.JSONSchema.fixedFields,vh=Xs(yh,{methods:{ObjectElement(e){const t=yh.compose.methods.ObjectElement.call(this,e);return np(this.element)&&this.element.setMetaProperty("referenced-element","schema"),t},ArrayElement(e){return this.element=e.clone(),ti}}}),{properties:bh}=lc.visitors.document.objects.JSONSchema.fixedFields,wh=Xs(bh,{methods:{ObjectElement(e){const t=bh.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{patternProperties:Eh}=lc.visitors.document.objects.JSONSchema.fixedFields,xh=Xs(Eh,{methods:{ObjectElement(e){const t=Eh.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}}),{type:Sh}=lc.visitors.document.objects.JSONSchema.fixedFields,_h=Xs(Sh,{methods:{ArrayElement(e){return this.element=e.clone(),ti}}}),jh=Yc,Oh=Yc,kh=Yc,Ah=Yc,{JSONSchemaOrJSONReferenceVisitor:Ch}=lc.visitors,Ph=Xs(Ch,{methods:{ObjectElement(e){const t=Ch.compose.methods.enter.call(this,e);return np(this.element)&&this.element.setMetaProperty("referenced-element","schema"),t}}}),Nh=Object.fromEntries(Object.entries(lc.visitors.document.objects.JSONSchema.fixedFields).map((([e,t])=>t===lc.visitors.JSONSchemaOrJSONReferenceVisitor?[e,Ph]:[e,t]))),Ih=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Discriminator"]),canSupportSpecificationExtensions:!1},init(){this.element=new na}}),Th=Yc;class Rh extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Rh.primaryClass)}}Qo(Rh,"primaryClass","discriminator-mapping");const Mh=Rh,Dh=Xs(vu,Yc,{props:{specPath:Gn(["value"])},init(){this.element=new Mh}}),Fh=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","XML"]),canSupportSpecificationExtensions:!0},init(){this.element=new Ic}}),Lh=Yc,Bh=Yc,$h=Yc,qh=Yc,Uh=Yc,zh=Yc;class Vh extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Vh.primaryClass),this.classes.push("examples")}}Qo(Vh,"primaryClass","parameter-examples");const Wh=Vh,Jh=Xs(mp,{init(){this.element=new Wh}});class Kh extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Kh.primaryClass),this.classes.push("content")}}Qo(Kh,"primaryClass","parameter-content");const Hh=Kh,Gh=Xs(nh,{init(){this.element=new Hh}});class Zh extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Zh.primaryClass)}}Qo(Zh,"primaryClass","components-schemas");const Yh=Zh,Xh=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Schema"]},init(){this.element=new Yh},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","schema")})),t}}});class Qh extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Qh.primaryClass)}}Qo(Qh,"primaryClass","components-responses");const ef=Qh,tf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Response"]},init(){this.element=new ef},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","response")})),this.element.filter(sp).forEach(((e,t)=>{e.setMetaProperty("http-status-code",t.toValue())})),t}}}),nf=tf;class rf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(rf.primaryClass),this.classes.push("parameters")}}Qo(rf,"primaryClass","components-parameters");const of=rf,sf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Parameter"]},init(){this.element=new of},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","parameter")})),t}}});class af extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(af.primaryClass),this.classes.push("examples")}}Qo(af,"primaryClass","components-examples");const lf=af,cf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Example"]},init(){this.element=new lf},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","example")})),t}}});class uf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(uf.primaryClass)}}Qo(uf,"primaryClass","components-request-bodies");const pf=uf,hf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","RequestBody"]},init(){this.element=new pf},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","requestBody")})),t}}});class ff extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(ff.primaryClass)}}Qo(ff,"primaryClass","components-headers");const df=ff,mf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Header"]},init(){this.element=new df},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","header")})),this.element.filter(Vu).forEach(((e,t)=>{e.setMetaProperty("header-name",t.toValue())})),t}}}),gf=mf;class yf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(yf.primaryClass)}}Qo(yf,"primaryClass","components-security-schemes");const vf=yf,bf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","SecurityScheme"]},init(){this.element=new vf},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","securityScheme")})),t}}});class wf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(wf.primaryClass)}}Qo(wf,"primaryClass","components-links");const Ef=wf,xf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Link"]},init(){this.element=new Ef},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","link")})),t}}});class Sf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Sf.primaryClass)}}Qo(Sf,"primaryClass","components-callbacks");const _f=Sf,jf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Callback"]},init(){this.element=new _f},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","callback")})),t}}}),Of=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Example"]),canSupportSpecificationExtensions:!0},init(){this.element=new ia},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return gs(this.element.externalValue)&&this.element.classes.push("reference-element"),t}}}),kf=Yc,Af=Yc,Cf=Yc,Pf=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}}),Nf=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","ExternalDocumentation"]),canSupportSpecificationExtensions:!0},init(){this.element=new la}}),If=Yc,Tf=Yc,Rf=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Encoding"]),canSupportSpecificationExtensions:!0},init(){this.element=new oa},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return ws(this.element.headers)&&this.element.headers.filter(Vu).forEach(((e,t)=>{e.setMetaProperty("header-name",t.toValue())})),t}}}),Mf=Yc;class Df extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Df.primaryClass)}}Qo(Df,"primaryClass","encoding-headers");const Ff=Df,Lf=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Header"]},init(){this.element=new Ff},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","header")})),this.element.forEach(((e,t)=>{if(!Vu(e))return;const n=t.toValue();e.setMetaProperty("headerName",n)})),t}}}),Bf=Lf,$f=Yc,qf=Yc,Uf=Yc,zf=Xs(yu,Yc,{props:{fieldPatternPredicate:Mo(/^\/(?.*)$/),specPath:Gn(["document","objects","PathItem"]),canSupportSpecificationExtensions:!0},init(){this.element=new Ra},methods:{ObjectElement(e){const t=yu.compose.methods.ObjectElement.call(this,e);return this.element.filter(Qu).forEach(((e,t)=>{e.setMetaProperty("path",t.clone())})),t}}}),Vf=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","RequestBody"])},init(){this.element=new La},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return ws(this.element.contentProp)&&this.element.contentProp.filter(hp).forEach(((e,t)=>{e.setMetaProperty("media-type",t.toValue())})),t}}}),Wf=Yc;class Jf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Jf.primaryClass),this.classes.push("content")}}Qo(Jf,"primaryClass","request-body-content");const Kf=Jf,Hf=Xs(nh,{init(){this.element=new Kf}}),Gf=Yc,Zf=Xs(yu,Yc,{props:{fieldPatternPredicate:Mo(/{(?.*)}/),specPath:Gn(["document","objects","PathItem"]),canSupportSpecificationExtensions:!0},init(){this.element=new Zi},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(Qu).forEach(((e,t)=>{e.setMetaProperty("runtime-expression",t.toValue())})),t}}}),Yf=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Response"])},init(){this.element=new $a},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return ws(this.element.contentProp)&&this.element.contentProp.filter(hp).forEach(((e,t)=>{e.setMetaProperty("media-type",t.toValue())})),ws(this.element.headers)&&this.element.headers.filter(Vu).forEach(((e,t)=>{e.setMetaProperty("header-name",t.toValue())})),t}}}),Xf=Yc;class Qf extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(Qf.primaryClass)}}Qo(Qf,"primaryClass","response-headers");const ed=Qf,td=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Header"]},init(){this.element=new ed},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","header")})),this.element.forEach(((e,t)=>{if(!Vu(e))return;const n=t.toValue();e.setMetaProperty("header-name",n)})),t}}}),nd=td;class rd extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(rd.primaryClass),this.classes.push("content")}}Qo(rd,"primaryClass","response-content");const od=rd,sd=Xs(nh,{init(){this.element=new od}});class id extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(id.primaryClass)}}Qo(id,"primaryClass","response-links");const ad=id,ld=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Link"]},init(){this.element=new ad},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","link")})),t}}}),cd=Xs(Zc,yu,{props:{specPathFixedFields:tl,specPathPatternedFields:tl},methods:{ObjectElement(e){const{specPath:t,ignoredFields:n}=this;try{this.specPath=this.specPathFixedFields;const t=this.retrieveFixedFields(this.specPath(e));this.ignoredFields=[...n,...Nr(e.keys(),t)],Zc.compose.methods.ObjectElement.call(this,e),this.specPath=this.specPathPatternedFields,this.ignoredFields=t,yu.compose.methods.ObjectElement.call(this,e)}catch(e){throw this.specPath=t,e}return ti}}}),ud=Xs(cd,Yc,{props:{specPathFixedFields:Gn(["document","objects","Responses"]),specPathPatternedFields:e=>zc(e)?["document","objects","Reference"]:["document","objects","Response"],fieldPatternPredicate:Mo(new RegExp(`^(1XX|2XX|3XX|4XX|5XX|${Ao(100,600).join("|")})$`)),canSupportSpecificationExtensions:!0},init(){this.element=new Ua},methods:{ObjectElement(e){const t=cd.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","response")})),this.element.filter(sp).forEach(((e,t)=>{const n=t.clone();this.fieldPatternPredicate(n.toValue())&&e.setMetaProperty("http-status-code",n)})),t}}}),pd=ud,hd=Xs(Lu,Yc,{props:{alternator:[{predicate:zc,specPath:["document","objects","Reference"]},{predicate:Ft,specPath:["document","objects","Response"]}]},methods:{ObjectElement(e){const t=Lu.compose.methods.enter.call(this,e);return np(this.element)?this.element.setMetaProperty("referenced-element","response"):sp(this.element)&&this.element.setMetaProperty("http-status-code","default"),t}}}),fd=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","Operation"])},init(){this.element=new Aa}});class dd extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(dd.primaryClass)}}Qo(dd,"primaryClass","operation-tags");const md=dd,gd=Xs(Yc,{init(){this.element=new md},methods:{ArrayElement(e){return this.element=this.element.concat(e.clone()),ti}}}),yd=Yc,vd=Yc,bd=Yc;class wd extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(wd.primaryClass),this.classes.push("parameters")}}Qo(wd,"primaryClass","operation-parameters");const Ed=wd,xd=Xs($c,Yc,{init(){this.element=new Nt.ON,this.element.classes.push("parameters")},methods:{ArrayElement(e){return e.forEach((e=>{const t=zc(e)?["document","objects","Reference"]:["document","objects","Parameter"],n=this.toRefractedElement(t,e);np(n)&&n.setMetaProperty("referenced-element","parameter"),this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}}),Sd=Xs(xd,{init(){this.element=new Ed}}),_d=Xs(Lu,{props:{alternator:[{predicate:zc,specPath:["document","objects","Reference"]},{predicate:Ft,specPath:["document","objects","RequestBody"]}]},methods:{ObjectElement(e){const t=Lu.compose.methods.enter.call(this,e);return np(this.element)&&this.element.setMetaProperty("referenced-element","requestBody"),t}}});class jd extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(jd.primaryClass)}}Qo(jd,"primaryClass","operation-callbacks");const Od=jd,kd=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","Callback"]},init(){this.element=new Od},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(np).forEach((e=>{e.setMetaProperty("referenced-element","callback")})),t}}}),Ad=Yc;class Cd extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(Cd.primaryClass),this.classes.push("security")}}Qo(Cd,"primaryClass","operation-security");const Pd=Cd,Nd=Xs($c,Yc,{init(){this.element=new Pd},methods:{ArrayElement(e){return e.forEach((e=>{const t=ws(e)?["document","objects","SecurityRequirement"]:["value"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}});class Id extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(Id.primaryClass),this.classes.push("servers")}}Qo(Id,"primaryClass","operation-servers");const Td=Id,Rd=Xs(Cu,{init(){this.element=new Td}}),Md=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","PathItem"])},init(){this.element=new Ia},methods:{ObjectElement(e){const t=Zc.compose.methods.ObjectElement.call(this,e);return this.element.filter(Yu).forEach(((e,t)=>{const n=t.clone();n.content=n.toValue().toUpperCase(),e.setMetaProperty("http-method",n)})),gs(this.element.$ref)&&this.element.classes.push("reference-element"),t}}}),Dd=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}}),Fd=Yc,Ld=Yc;class Bd extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(Bd.primaryClass),this.classes.push("servers")}}Qo(Bd,"primaryClass","path-item-servers");const $d=Bd,qd=Xs(Cu,{init(){this.element=new $d}});class Ud extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(Ud.primaryClass),this.classes.push("parameters")}}Qo(Ud,"primaryClass","path-item-parameters");const zd=Ud,Vd=Xs(xd,{init(){this.element=new zd}}),Wd=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","SecurityScheme"]),canSupportSpecificationExtensions:!0},init(){this.element=new _c}}),Jd=Yc,Kd=Yc,Hd=Yc,Gd=Yc,Zd=Yc,Yd=Yc,Xd=Yc,Qd=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","OAuthFlows"]),canSupportSpecificationExtensions:!0},init(){this.element=new xa}}),em=Xs(Zc,Yc,{props:{specPath:Gn(["document","objects","OAuthFlow"]),canSupportSpecificationExtensions:!0},init(){this.element=new wa}}),tm=Yc,nm=Yc,rm=Yc;class om extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(om.primaryClass)}}Qo(om,"primaryClass","oauth-flow-scopes");const sm=om,im=Xs(vu,Yc,{props:{specPath:Gn(["value"])},init(){this.element=new sm}});class am extends Nt.ON{constructor(e,t,n){super(e,t,n),this.classes.push(am.primaryClass)}}Qo(am,"primaryClass","tags");const lm=am,cm=Xs($c,Yc,{init(){this.element=new lm},methods:{ArrayElement(e){return e.forEach((e=>{const t=Kc(e)?["document","objects","Tag"]:["value"],n=this.toRefractedElement(t,e);this.element.push(n)})),this.copyMetaAndAttributes(e,this.element),ti}}});function um(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function pm(e){for(var t=1;t{const{base:t}=e;return t.register("callback",Zi),t.register("components",Xi),t.register("contact",ea),t.register("discriminator",na),t.register("encoding",oa),t.register("example",ia),t.register("externalDocumentation",la),t.register("header",ua),t.register("info",ha),t.register("license",da),t.register("link",ga),t.register("mediaType",va),t.register("oAuthFlow",wa),t.register("oAuthFlows",xa),t.register("openapi",_a),t.register("openApi3_0",Oa),t.register("operation",Aa),t.register("parameter",Pa),t.register("pathItem",Ia),t.register("paths",Ra),t.register("reference",Da),t.register("requestBody",La),t.register("response",$a),t.register("responses",Ua),t.register("schema",wc),t.register("securityRequirement",xc),t.register("securityScheme",_c),t.register("server",Oc),t.register("serverVariable",Ac),t.register("tag",Pc),t.register("xml",Ic),t}};function gm(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function ym(e){for(var t=1;t{const e=Vs(mm);return{predicates:ym(ym(ym({},a),l),{},{isStringElement:gs}),namespace:e}};function bm(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}const wm=(e,{specPath:t=["visitors","document","objects","OpenApi","$visitor"],plugins:n=[]}={})=>{const r=(0,Nt.Qc)(e),o=Ya(dm),s=as(t,[],o);return di(r,s,{state:{specObj:o}}),mi(s.element,n,{toolboxCreator:vm,visitorOptions:{keyMap:Fc,nodeTypeGetter:Dc}})},Em=e=>(t,n={})=>wm(t,function(e){for(var t=1;tr=>r instanceof xm||e(r)&&t("callback",r)&&n("object",r))),Fg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Sm||e(r)&&t("components",r)&&n("object",r))),Lg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof _m||e(r)&&t("contact",r)&&n("object",r))),Bg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof km||e(r)&&t("example",r)&&n("object",r))),$g=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Am||e(r)&&t("externalDocumentation",r)&&n("object",r))),qg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Cm||e(r)&&t("header",r)&&n("object",r))),Ug=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Pm||e(r)&&t("info",r)&&n("object",r))),zg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Im||e(r)&&t("jsonSchemaDialect",r)&&n("string",r))),Vg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Tm||e(r)&&t("license",r)&&n("object",r))),Wg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Rm||e(r)&&t("link",r)&&n("object",r))),Jg=e=>{if(!Wg(e))return!1;if(!gs(e.operationRef))return!1;const t=e.operationRef.toValue();return"string"==typeof t&&t.length>0&&!t.startsWith("#")},Kg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Lm||e(r)&&t("openapi",r)&&n("string",r))),Hg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n,hasClass:r})=>o=>o instanceof $m||e(o)&&t("openApi3_1",o)&&n("object",o)&&r("api",o))),Gg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof qm||e(r)&&t("operation",r)&&n("object",r))),Zg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Um||e(r)&&t("parameter",r)&&n("object",r))),Yg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof zm||e(r)&&t("pathItem",r)&&n("object",r))),Xg=e=>{if(!Yg(e))return!1;if(!gs(e.$ref))return!1;const t=e.$ref.toValue();return"string"==typeof t&&t.length>0&&!t.startsWith("#")},Qg=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Vm||e(r)&&t("paths",r)&&n("object",r))),ey=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Jm||e(r)&&t("reference",r)&&n("object",r))),ty=e=>{if(!ey(e))return!1;if(!gs(e.$ref))return!1;const t=e.$ref.toValue();return"string"==typeof t&&t.length>0&&!t.startsWith("#")},ny=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Km||e(r)&&t("requestBody",r)&&n("object",r))),ry=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Hm||e(r)&&t("response",r)&&n("object",r))),oy=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Gm||e(r)&&t("responses",r)&&n("object",r))),sy=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Ym||e(r)&&t("schema",r)&&n("object",r))),iy=e=>bs(e)&&e.classes.includes("boolean-json-schema"),ay=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Xm||e(r)&&t("securityRequirement",r)&&n("object",r))),ly=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof eg||e(r)&&t("server",r)&&n("object",r))),cy=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof tg||e(r)&&t("serverVariable",r)&&n("object",r))),uy=ds((({hasBasicElementProps:e,isElementType:t,primitiveEq:n})=>r=>r instanceof Mm||e(r)&&t("mediaType",r)&&n("object",r))),py=Xs({props:{parent:null},init({parent:e=this.parent}){this.parent=e,this.passingOptionsNames=[...this.passingOptionsNames,"parent"]}}),hy=Xs(Zc,py,Yc,{props:{specPath:Gn(["document","objects","Schema"]),canSupportSpecificationExtensions:!0},init(){const e=()=>{let e;return e=null!==this.openApiSemanticElement&&zg(this.openApiSemanticElement.jsonSchemaDialect)?this.openApiSemanticElement.jsonSchemaDialect.toValue():null!==this.openApiGenericElement&&gs(this.openApiGenericElement.get("jsonSchemaDialect"))?this.openApiGenericElement.get("jsonSchemaDialect").toValue():Im.default.toValue(),e},t=t=>{if(Ts(this.parent)&&!gs(t.get("$schema")))this.element.setMetaProperty("inherited$schema",e());else if(sy(this.parent)&&!gs(t.get("$schema"))){var n,r;const e=Ar(null===(n=this.parent.meta.get("inherited$schema"))||void 0===n?void 0:n.toValue(),null===(r=this.parent.$schema)||void 0===r?void 0:r.toValue());this.element.setMetaProperty("inherited$schema",e)}},n=e=>{var t;const n=null!==this.parent?this.parent.getMetaProperty("inherited$id",[]).clone():new Nt.ON,r=null===(t=e.get("$id"))||void 0===t?void 0:t.toValue();Il(r)&&n.push(r),this.element.setMetaProperty("inherited$id",n)};this.ObjectElement=function(e){this.element=new Ym,t(e),n(e),this.parent=this.element;const r=Zc.compose.methods.ObjectElement.call(this,e);return gs(this.element.$ref)&&(this.element.classes.push("reference-element"),this.element.setMetaProperty("referenced-element","schema")),r},this.BooleanElement=function(e){return this.element=e.clone(),this.element.classes.push("boolean-json-schema"),ti}}}),fy=Yc,dy=Xs(Yc,{methods:{ObjectElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-$vocabulary"),ti}}}),my=Yc,gy=Yc,yy=Yc,vy=Yc,by=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("reference-value"),ti}}}),wy=Xs(vu,py,Yc,{props:{specPath:Gn(["document","objects","Schema"])},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-$defs")}}),Ey=Yc,xy=Xs($c,py,Yc,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-allOf")},methods:{ArrayElement(e){return e.forEach((e=>{if(ws(e)){const t=this.toRefractedElement(["document","objects","Schema"],e);this.element.push(t)}else{const t=e.clone();this.element.push(t)}})),this.copyMetaAndAttributes(e,this.element),ti}}}),Sy=Xs($c,py,Yc,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-anyOf")},methods:{ArrayElement(e){return e.forEach((e=>{if(ws(e)){const t=this.toRefractedElement(["document","objects","Schema"],e);this.element.push(t)}else{const t=e.clone();this.element.push(t)}})),this.copyMetaAndAttributes(e,this.element),ti}}}),_y=Xs($c,py,Yc,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-oneOf")},methods:{ArrayElement(e){return e.forEach((e=>{if(ws(e)){const t=this.toRefractedElement(["document","objects","Schema"],e);this.element.push(t)}else{const t=e.clone();this.element.push(t)}})),this.copyMetaAndAttributes(e,this.element),ti}}}),jy=Xs(vu,py,Yc,{props:{specPath:Gn(["document","objects","Schema"])},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-dependentSchemas")}}),Oy=Xs($c,py,Yc,{init(){this.element=new Nt.ON,this.element.classes.push("json-schema-prefixItems")},methods:{ArrayElement(e){return e.forEach((e=>{if(ws(e)){const t=this.toRefractedElement(["document","objects","Schema"],e);this.element.push(t)}else{const t=e.clone();this.element.push(t)}})),this.copyMetaAndAttributes(e,this.element),ti}}}),ky=Xs(vu,py,Yc,{props:{specPath:Gn(["document","objects","Schema"])},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-properties")}}),Ay=Xs(vu,py,Yc,{props:{specPath:Gn(["document","objects","Schema"])},init(){this.element=new Nt.Sb,this.element.classes.push("json-schema-patternProperties")}}),Cy=Xs(Yc,{methods:{StringElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-type"),ti},ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-type"),ti}}}),Py=Xs(Yc,{methods:{ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-enum"),ti}}}),Ny=Yc,Iy=Yc,Ty=Yc,Ry=Yc,My=Yc,Dy=Yc,Fy=Yc,Ly=Yc,By=Yc,$y=Yc,qy=Yc,Uy=Yc,zy=Yc,Vy=Yc,Wy=Yc,Jy=Yc,Ky=Xs(Yc,{methods:{ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-required"),ti}}}),Hy=Xs(Yc,{methods:{ObjectElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-dependentRequired"),ti}}}),Gy=Yc,Zy=Yc,Yy=Yc,Xy=Yc,Qy=Yc,ev=Yc,tv=Xs(Yc,{methods:{ArrayElement(e){return this.element=e.clone(),this.element.classes.push("json-schema-examples"),ti}}}),nv=Yc,rv=Yc,ov=Yc,sv=Yc,{visitors:{document:{objects:{Discriminator:{$visitor:iv}}}}}=dm,av=Xs(iv,{props:{canSupportSpecificationExtensions:!0},init(){this.element=new jm}}),{visitors:{document:{objects:{XML:{$visitor:lv}}}}}=dm,cv=Xs(lv,{init(){this.element=new rg}}),uv=Xs(vu,Yc,{props:{specPath:Gn(["document","objects","Schema"])},init(){this.element=new Yh}});class pv extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push(pv.primaryClass)}}Qo(pv,"primaryClass","components-path-items");const hv=pv,fv=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","PathItem"]},init(){this.element=new hv},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(ey).forEach((e=>{e.setMetaProperty("referenced-element","pathItem")})),t}}}),{visitors:{document:{objects:{Example:{$visitor:dv}}}}}=dm,mv=Xs(dv,{init(){this.element=new km}}),{visitors:{document:{objects:{ExternalDocumentation:{$visitor:gv}}}}}=dm,yv=Xs(gv,{init(){this.element=new Am}}),{visitors:{document:{objects:{Encoding:{$visitor:vv}}}}}=dm,bv=Xs(vv,{init(){this.element=new Om}}),{visitors:{document:{objects:{Paths:{$visitor:wv}}}}}=dm,Ev=Xs(wv,{init(){this.element=new Vm}}),{visitors:{document:{objects:{RequestBody:{$visitor:xv}}}}}=dm,Sv=Xs(xv,{init(){this.element=new Km}}),{visitors:{document:{objects:{Callback:{$visitor:_v}}}}}=dm,jv=Xs(_v,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","PathItem"]},init(){this.element=new xm},methods:{ObjectElement(e){const t=_v.compose.methods.ObjectElement.call(this,e);return this.element.filter(ey).forEach((e=>{e.setMetaProperty("referenced-element","pathItem")})),t}}}),{visitors:{document:{objects:{Response:{$visitor:Ov}}}}}=dm,kv=Xs(Ov,{init(){this.element=new Hm}}),{visitors:{document:{objects:{Responses:{$visitor:Av}}}}}=dm,Cv=Xs(Av,{init(){this.element=new Gm}}),{visitors:{document:{objects:{Operation:{$visitor:Pv}}}}}=dm,Nv=Xs(Pv,{init(){this.element=new qm}}),{visitors:{document:{objects:{PathItem:{$visitor:Iv}}}}}=dm,Tv=Xs(Iv,{init(){this.element=new zm}}),{visitors:{document:{objects:{SecurityScheme:{$visitor:Rv}}}}}=dm,Mv=Xs(Rv,{init(){this.element=new Qm}}),{visitors:{document:{objects:{OAuthFlows:{$visitor:Dv}}}}}=dm,Fv=Xs(Dv,{init(){this.element=new Fm}}),{visitors:{document:{objects:{OAuthFlow:{$visitor:Lv}}}}}=dm,Bv=Xs(Lv,{init(){this.element=new Dm}});class $v extends Nt.Sb{constructor(e,t,n){super(e,t,n),this.classes.push($v.primaryClass)}}Qo($v,"primaryClass","webhooks");const qv=$v,Uv=Xs(vu,Yc,{props:{specPath:e=>zc(e)?["document","objects","Reference"]:["document","objects","PathItem"]},init(){this.element=new qv},methods:{ObjectElement(e){const t=vu.compose.methods.ObjectElement.call(this,e);return this.element.filter(ey).forEach((e=>{e.setMetaProperty("referenced-element","pathItem")})),this.element.filter(Yg).forEach(((e,t)=>{e.setMetaProperty("webhook-name",t.toValue())})),t}}}),zv={visitors:{value:dm.visitors.value,document:{objects:{OpenApi:{$visitor:og,fixedFields:{openapi:dm.visitors.document.objects.OpenApi.fixedFields.openapi,info:{$ref:"#/visitors/document/objects/Info"},jsonSchemaDialect:mg,servers:dm.visitors.document.objects.OpenApi.fixedFields.servers,paths:{$ref:"#/visitors/document/objects/Paths"},webhooks:Uv,components:{$ref:"#/visitors/document/objects/Components"},security:dm.visitors.document.objects.OpenApi.fixedFields.security,tags:dm.visitors.document.objects.OpenApi.fixedFields.tags,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"}}},Info:{$visitor:ig,fixedFields:{title:dm.visitors.document.objects.Info.fixedFields.title,description:dm.visitors.document.objects.Info.fixedFields.description,summary:ag,termsOfService:dm.visitors.document.objects.Info.fixedFields.termsOfService,contact:{$ref:"#/visitors/document/objects/Contact"},license:{$ref:"#/visitors/document/objects/License"},version:dm.visitors.document.objects.Info.fixedFields.version}},Contact:{$visitor:cg,fixedFields:{name:dm.visitors.document.objects.Contact.fixedFields.name,url:dm.visitors.document.objects.Contact.fixedFields.url,email:dm.visitors.document.objects.Contact.fixedFields.email}},License:{$visitor:pg,fixedFields:{name:dm.visitors.document.objects.License.fixedFields.name,identifier:hg,url:dm.visitors.document.objects.License.fixedFields.url}},Server:{$visitor:yg,fixedFields:{url:dm.visitors.document.objects.Server.fixedFields.url,description:dm.visitors.document.objects.Server.fixedFields.description,variables:dm.visitors.document.objects.Server.fixedFields.variables}},ServerVariable:{$visitor:bg,fixedFields:{enum:dm.visitors.document.objects.ServerVariable.fixedFields.enum,default:dm.visitors.document.objects.ServerVariable.fixedFields.default,description:dm.visitors.document.objects.ServerVariable.fixedFields.description}},Components:{$visitor:jg,fixedFields:{schemas:uv,responses:dm.visitors.document.objects.Components.fixedFields.responses,parameters:dm.visitors.document.objects.Components.fixedFields.parameters,examples:dm.visitors.document.objects.Components.fixedFields.examples,requestBodies:dm.visitors.document.objects.Components.fixedFields.requestBodies,headers:dm.visitors.document.objects.Components.fixedFields.headers,securitySchemes:dm.visitors.document.objects.Components.fixedFields.securitySchemes,links:dm.visitors.document.objects.Components.fixedFields.links,callbacks:dm.visitors.document.objects.Components.fixedFields.callbacks,pathItems:fv}},Paths:{$visitor:Ev},PathItem:{$visitor:Tv,fixedFields:{$ref:dm.visitors.document.objects.PathItem.fixedFields.$ref,summary:dm.visitors.document.objects.PathItem.fixedFields.summary,description:dm.visitors.document.objects.PathItem.fixedFields.description,get:{$ref:"#/visitors/document/objects/Operation"},put:{$ref:"#/visitors/document/objects/Operation"},post:{$ref:"#/visitors/document/objects/Operation"},delete:{$ref:"#/visitors/document/objects/Operation"},options:{$ref:"#/visitors/document/objects/Operation"},head:{$ref:"#/visitors/document/objects/Operation"},patch:{$ref:"#/visitors/document/objects/Operation"},trace:{$ref:"#/visitors/document/objects/Operation"},servers:dm.visitors.document.objects.PathItem.fixedFields.servers,parameters:dm.visitors.document.objects.PathItem.fixedFields.parameters}},Operation:{$visitor:Nv,fixedFields:{tags:dm.visitors.document.objects.Operation.fixedFields.tags,summary:dm.visitors.document.objects.Operation.fixedFields.summary,description:dm.visitors.document.objects.Operation.fixedFields.description,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"},operationId:dm.visitors.document.objects.Operation.fixedFields.operationId,parameters:dm.visitors.document.objects.Operation.fixedFields.parameters,requestBody:dm.visitors.document.objects.Operation.fixedFields.requestBody,responses:{$ref:"#/visitors/document/objects/Responses"},callbacks:dm.visitors.document.objects.Operation.fixedFields.callbacks,deprecated:dm.visitors.document.objects.Operation.fixedFields.deprecated,security:dm.visitors.document.objects.Operation.fixedFields.security,servers:dm.visitors.document.objects.Operation.fixedFields.servers}},ExternalDocumentation:{$visitor:yv,fixedFields:{description:dm.visitors.document.objects.ExternalDocumentation.fixedFields.description,url:dm.visitors.document.objects.ExternalDocumentation.fixedFields.url}},Parameter:{$visitor:Tg,fixedFields:{name:dm.visitors.document.objects.Parameter.fixedFields.name,in:dm.visitors.document.objects.Parameter.fixedFields.in,description:dm.visitors.document.objects.Parameter.fixedFields.description,required:dm.visitors.document.objects.Parameter.fixedFields.required,deprecated:dm.visitors.document.objects.Parameter.fixedFields.deprecated,allowEmptyValue:dm.visitors.document.objects.Parameter.fixedFields.allowEmptyValue,style:dm.visitors.document.objects.Parameter.fixedFields.style,explode:dm.visitors.document.objects.Parameter.fixedFields.explode,allowReserved:dm.visitors.document.objects.Parameter.fixedFields.allowReserved,schema:{$ref:"#/visitors/document/objects/Schema"},example:dm.visitors.document.objects.Parameter.fixedFields.example,examples:dm.visitors.document.objects.Parameter.fixedFields.examples,content:dm.visitors.document.objects.Parameter.fixedFields.content}},RequestBody:{$visitor:Sv,fixedFields:{description:dm.visitors.document.objects.RequestBody.fixedFields.description,content:dm.visitors.document.objects.RequestBody.fixedFields.content,required:dm.visitors.document.objects.RequestBody.fixedFields.required}},MediaType:{$visitor:Eg,fixedFields:{schema:{$ref:"#/visitors/document/objects/Schema"},example:dm.visitors.document.objects.MediaType.fixedFields.example,examples:dm.visitors.document.objects.MediaType.fixedFields.examples,encoding:dm.visitors.document.objects.MediaType.fixedFields.encoding}},Encoding:{$visitor:bv,fixedFields:{contentType:dm.visitors.document.objects.Encoding.fixedFields.contentType,headers:dm.visitors.document.objects.Encoding.fixedFields.headers,style:dm.visitors.document.objects.Encoding.fixedFields.style,explode:dm.visitors.document.objects.Encoding.fixedFields.explode,allowReserved:dm.visitors.document.objects.Encoding.fixedFields.allowReserved}},Responses:{$visitor:Cv,fixedFields:{default:dm.visitors.document.objects.Responses.fixedFields.default}},Response:{$visitor:kv,fixedFields:{description:dm.visitors.document.objects.Response.fixedFields.description,headers:dm.visitors.document.objects.Response.fixedFields.headers,content:dm.visitors.document.objects.Response.fixedFields.content,links:dm.visitors.document.objects.Response.fixedFields.links}},Callback:{$visitor:jv},Example:{$visitor:mv,fixedFields:{summary:dm.visitors.document.objects.Example.fixedFields.summary,description:dm.visitors.document.objects.Example.fixedFields.description,value:dm.visitors.document.objects.Example.fixedFields.value,externalValue:dm.visitors.document.objects.Example.fixedFields.externalValue}},Link:{$visitor:dg,fixedFields:{operationRef:dm.visitors.document.objects.Link.fixedFields.operationRef,operationId:dm.visitors.document.objects.Link.fixedFields.operationId,parameters:dm.visitors.document.objects.Link.fixedFields.parameters,requestBody:dm.visitors.document.objects.Link.fixedFields.requestBody,description:dm.visitors.document.objects.Link.fixedFields.description,server:{$ref:"#/visitors/document/objects/Server"}}},Header:{$visitor:Mg,fixedFields:{description:dm.visitors.document.objects.Header.fixedFields.description,required:dm.visitors.document.objects.Header.fixedFields.required,deprecated:dm.visitors.document.objects.Header.fixedFields.deprecated,allowEmptyValue:dm.visitors.document.objects.Header.fixedFields.allowEmptyValue,style:dm.visitors.document.objects.Header.fixedFields.style,explode:dm.visitors.document.objects.Header.fixedFields.explode,allowReserved:dm.visitors.document.objects.Header.fixedFields.allowReserved,schema:{$ref:"#/visitors/document/objects/Schema"},example:dm.visitors.document.objects.Header.fixedFields.example,examples:dm.visitors.document.objects.Header.fixedFields.examples,content:dm.visitors.document.objects.Header.fixedFields.content}},Tag:{$visitor:kg,fixedFields:{name:dm.visitors.document.objects.Tag.fixedFields.name,description:dm.visitors.document.objects.Tag.fixedFields.description,externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"}}},Reference:{$visitor:Cg,fixedFields:{$ref:dm.visitors.document.objects.Reference.fixedFields.$ref,summary:Pg,description:Ng}},Schema:{$visitor:hy,fixedFields:{$schema:fy,$vocabulary:dy,$id:my,$anchor:gy,$dynamicAnchor:yy,$dynamicRef:vy,$ref:by,$defs:wy,$comment:Ey,allOf:xy,anyOf:Sy,oneOf:_y,not:{$ref:"#/visitors/document/objects/Schema"},if:{$ref:"#/visitors/document/objects/Schema"},then:{$ref:"#/visitors/document/objects/Schema"},else:{$ref:"#/visitors/document/objects/Schema"},dependentSchemas:jy,prefixItems:Oy,items:{$ref:"#/visitors/document/objects/Schema"},contains:{$ref:"#/visitors/document/objects/Schema"},properties:ky,patternProperties:Ay,additionalProperties:{$ref:"#/visitors/document/objects/Schema"},propertyNames:{$ref:"#/visitors/document/objects/Schema"},unevaluatedItems:{$ref:"#/visitors/document/objects/Schema"},unevaluatedProperties:{$ref:"#/visitors/document/objects/Schema"},type:Cy,enum:Py,const:Ny,multipleOf:Iy,maximum:Ty,exclusiveMaximum:Ry,minimum:My,exclusiveMinimum:Dy,maxLength:Fy,minLength:Ly,pattern:By,maxItems:$y,minItems:qy,uniqueItems:Uy,maxContains:zy,minContains:Vy,maxProperties:Wy,minProperties:Jy,required:Ky,dependentRequired:Hy,title:Gy,description:Zy,default:Yy,deprecated:Xy,readOnly:Qy,writeOnly:ev,examples:tv,format:nv,contentEncoding:rv,contentMediaType:ov,contentSchema:{$ref:"#/visitors/document/objects/Schema"},discriminator:{$ref:"#/visitors/document/objects/Discriminator"},xml:{$ref:"#/visitors/document/objects/XML"},externalDocs:{$ref:"#/visitors/document/objects/ExternalDocumentation"},example:sv}},Discriminator:{$visitor:av,fixedFields:{propertyName:dm.visitors.document.objects.Discriminator.fixedFields.propertyName,mapping:dm.visitors.document.objects.Discriminator.fixedFields.mapping}},XML:{$visitor:cv,fixedFields:{name:dm.visitors.document.objects.XML.fixedFields.name,namespace:dm.visitors.document.objects.XML.fixedFields.namespace,prefix:dm.visitors.document.objects.XML.fixedFields.prefix,attribute:dm.visitors.document.objects.XML.fixedFields.attribute,wrapped:dm.visitors.document.objects.XML.fixedFields.wrapped}},SecurityScheme:{$visitor:Mv,fixedFields:{type:dm.visitors.document.objects.SecurityScheme.fixedFields.type,description:dm.visitors.document.objects.SecurityScheme.fixedFields.description,name:dm.visitors.document.objects.SecurityScheme.fixedFields.name,in:dm.visitors.document.objects.SecurityScheme.fixedFields.in,scheme:dm.visitors.document.objects.SecurityScheme.fixedFields.scheme,bearerFormat:dm.visitors.document.objects.SecurityScheme.fixedFields.bearerFormat,flows:{$ref:"#/visitors/document/objects/OAuthFlows"},openIdConnectUrl:dm.visitors.document.objects.SecurityScheme.fixedFields.openIdConnectUrl}},OAuthFlows:{$visitor:Fv,fixedFields:{implicit:{$ref:"#/visitors/document/objects/OAuthFlow"},password:{$ref:"#/visitors/document/objects/OAuthFlow"},clientCredentials:{$ref:"#/visitors/document/objects/OAuthFlow"},authorizationCode:{$ref:"#/visitors/document/objects/OAuthFlow"}}},OAuthFlow:{$visitor:Bv,fixedFields:{authorizationUrl:dm.visitors.document.objects.OAuthFlow.fixedFields.authorizationUrl,tokenUrl:dm.visitors.document.objects.OAuthFlow.fixedFields.tokenUrl,refreshUrl:dm.visitors.document.objects.OAuthFlow.fixedFields.refreshUrl,scopes:dm.visitors.document.objects.OAuthFlow.fixedFields.scopes}},SecurityRequirement:{$visitor:Sg}},extension:{$visitor:dm.visitors.document.extension.$visitor}}}};function Vv(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}const Wv=e=>{if(ms(e))return`${e.element.charAt(0).toUpperCase()+e.element.slice(1)}Element`},Jv=function(e){for(var t=1;t{const{base:t}=e;return t.register("callback",xm),t.register("components",Sm),t.register("contact",_m),t.register("discriminator",jm),t.register("encoding",Om),t.register("example",km),t.register("externalDocumentation",Am),t.register("header",Cm),t.register("info",Pm),t.register("jsonSchemaDialect",Im),t.register("license",Tm),t.register("link",Rm),t.register("mediaType",Mm),t.register("oAuthFlow",Dm),t.register("oAuthFlows",Fm),t.register("openapi",Lm),t.register("openApi3_1",$m),t.register("operation",qm),t.register("parameter",Um),t.register("pathItem",zm),t.register("paths",Vm),t.register("reference",Jm),t.register("requestBody",Km),t.register("response",Hm),t.register("responses",Gm),t.register("schema",Ym),t.register("securityRequirement",Xm),t.register("securityScheme",Qm),t.register("server",eg),t.register("serverVariable",tg),t.register("tag",ng),t.register("xml",rg),t}};function Hv(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Gv(e){for(var t=1;t{const e=Vs(Kv);return{predicates:Gv(Gv({},c),{},{isStringElement:gs,isArrayElement:Es,isObjectElement:ws,includesClasses:Is}),namespace:e}};function Yv(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}const Xv=(e,{specPath:t=["visitors","document","objects","OpenApi","$visitor"],plugins:n=[]}={})=>{const r=(0,Nt.Qc)(e),o=Ya(zv),s=as(t,[],o);return di(r,s,{state:{specObj:o}}),mi(s.element,n,{toolboxCreator:Zv,visitorOptions:{keyMap:Jv,nodeTypeGetter:Wv}})},Qv=e=>(t,n={})=>Xv(t,function(e){for(var t=1;te.includes(t)))}findBy(e="3.1.0",t="generic"){const n="generic"===t?`vnd.oai.openapi;version=${e}`:`vnd.oai.openapi+${t};version=${e}`;return this.find((e=>e.includes(n)))||this.unknownMediaType}latest(e="generic"){return lo(this.filterByFormat(e))}}const nb=new tb("application/vnd.oai.openapi;version=3.1.0","application/vnd.oai.openapi+json;version=3.1.0","application/vnd.oai.openapi+yaml;version=3.1.0");var rb=n(34155),ob=kr((function(e,t){return yr(To(""),Lr(ls(e)),ao(""))(t)}));const sb=ob;const ib=hr(Uo);const ab=Yt(1,yr(un,Qr("RegExp")));const lb=$o(Qs,Po(/[.*+?^${}()|[\]\\-]/g,"\\$&"));var cb=function(e,t){if("string"!=typeof e&&!(e instanceof String))throw TypeError("`".concat(t,"` must be a string"))};var ub=Yt(3,(function(e,t,n){!function(e,t,n){if(null==n||null==e||null==t)throw TypeError("Input values must not be `null` or `undefined`")}(e,t,n),cb(n,"str"),cb(t,"replaceValue"),function(e){if(!("string"==typeof e||e instanceof String||e instanceof RegExp))throw TypeError("`searchValue` must be a string or an regexp")}(e);var r=new RegExp(ab(e)?e:lb(e),"g");return Po(r,t,n)})),pb=so(2,"replaceAll");const hb=ns(String.prototype.replaceAll)?pb:ub,fb=()=>Eo(Mo(/^win/),["platform"],rb),db=e=>{try{const t=new URL(e);return sb(":",t.protocol)}catch{return}},mb=(yr(db,ib),e=>{if(rb.browser)return!1;const t=db(e);return Uo(t)||"file"===t||/^[a-zA-Z]$/.test(t)}),gb=e=>{const t=db(e);return"http"===t||"https"===t},yb=(e,t)=>{const n=[/%23/g,"#",/%24/g,"$",/%26/g,"&",/%2C/g,",",/%40/g,"@"],r=_o(!1,"keepFileProtocol",t),o=_o(fb,"isWindows",t);let s=decodeURI(e);for(let e=0;e{const t=e.indexOf("#");return-1!==t?e.substr(t):"#"},bb=e=>{const t=e.indexOf("#");let n=e;return t>=0&&(n=e.substr(0,t)),n},wb=()=>{if(rb.browser)return bb(globalThis.location.href);const e=rb.cwd(),t=lo(e);return["/","\\"].includes(t)?e:e+(fb()?"\\":"/")},Eb=(e,t)=>{const n=new URL(t,new URL(e,"resolve://"));if("resolve:"===n.protocol){const{pathname:e,search:t,hash:r}=n;return e+t+r}return n.toString()},xb=e=>mb(e)?(e=>{const t=[/\?/g,"%3F",/#/g,"%23"];let n=e;fb()&&(n=n.replace(/\\/g,"/")),n=encodeURI(n);for(let e=0;emb(e)?yb(e):decodeURI(e),_b=Xs({props:{uri:"",value:null,depth:0,refSet:null,errors:[]},init({depth:e=this.depth,refSet:t=this.refSet,uri:n=this.uri,value:r=this.value}={}){this.uri=n,this.value=r,this.depth=e,this.refSet=t,this.errors=[]}}),jb=_b,Ob=Xs({props:{rootRef:null,refs:[],circular:!1},init({refs:e=[]}={}){this.refs=[],e.forEach((e=>this.add(e)))},methods:{get size(){return this.refs.length},add(e){return this.has(e)||(this.refs.push(e),this.rootRef=null===this.rootRef?e:this.rootRef,e.refSet=this),this},merge(e){for(const t of e.values())this.add(t);return this},has(e){const t=Qs(e)?e:e.uri;return ib(this.find(So(t,"uri")))},find(e){return this.refs.find(e)},*values(){yield*this.refs},clean(){this.refs.forEach((e=>{e.refSet=null})),this.refs=[]}}}),kb=Ob,Ab={parse:{mediaType:"text/plain",parsers:[],parserOpts:{}},resolve:{baseURI:"",resolvers:[],resolverOpts:{},strategies:[],external:!0,maxDepth:1/0},dereference:{strategies:[],refSet:null,maxDepth:1/0}},Cb=co(po(["resolve","baseURI"]),sr(["resolve","baseURI"])),Pb=e=>Mi(e)?wb():e,Nb=Xs({props:{uri:null,mediaType:"text/plain",data:null,parseResult:null},init({uri:e=this.uri,mediaType:t=this.mediaType,data:n=this.data,parseResult:r=this.parseResult}={}){this.uri=e,this.mediaType=t,this.data=n,this.parseResult=r},methods:{get extension(){return Qs(this.uri)?(e=>{const t=e.lastIndexOf(".");return t>=0?e.substr(t).toLowerCase():""})(this.uri):""},toString(){if("string"==typeof this.data)return this.data;if(this.data instanceof ArrayBuffer||["ArrayBuffer"].includes(un(this.data))||ArrayBuffer.isView(this.data)){return new TextDecoder("utf-8").decode(this.data)}return String(this.data)}}});class Ib extends Error{constructor(e,t){if(super(e),this.name=this.constructor.name,this.message=e,"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack,qs(t)&&Zr("cause",t)&&!Zr("cause",this)){const{cause:e}=t;this.cause=e,Zr("stack",e)&&(this.stack=`${this.stack}\nCAUSE: ${null==e?void 0:e.stack}`)}}}const Tb=Ib;const Rb=class extends Tb{constructor(e,t){super(e,{cause:t.cause}),Qo(this,"plugin",void 0),this.plugin=t.plugin}},Mb=async(e,t,n)=>{const r=await Promise.all(n.map(as([e],[t])));return n.filter(((e,t)=>r[t]))},Db=async(e,t,n)=>{let r;for(const o of n)try{const n=await o[e].call(o,...t);return{plugin:o,result:n}}catch(e){r=new Rb("Error while running plugin",{cause:e,plugin:o})}return Promise.reject(r)};const Fb=class extends Tb{};const Lb=class extends Fb{};const Bb=class extends Tb{},$b=async(e,t)=>{let n=e,r=!1;if(!ks(e)){const t=new e.constructor(e.content,e.meta.clone(),e.attributes);t.classes.push("result"),n=new Vo([t]),r=!0}const o=Nb({uri:t.resolve.baseURI,parseResult:n,mediaType:t.parse.mediaType}),s=await Mb("canDereference",o,t.dereference.strategies);if(io(s))throw new Lb(o.uri);try{const{result:e}=await Db("dereference",[o,t],s);return r?e.get(0):e}catch(e){throw new Bb(`Error while dereferencing file "${o.uri}"`,{cause:e})}},qb=async(e,t={})=>{const n=((e,t)=>{const n=go(e,t);return bo(Cb,Pb,n)})(Ab,t);return $b(e,n)};const Ub=class extends Tb{constructor(e="Not Implemented",t){super(e,t)}},zb=Xs({props:{name:"",allowEmpty:!0,sourceMap:!1,fileExtensions:[],mediaTypes:[]},init({allowEmpty:e=this.allowEmpty,sourceMap:t=this.sourceMap,fileExtensions:n=this.fileExtensions,mediaTypes:r=this.mediaTypes}={}){this.allowEmpty=e,this.sourceMap=t,this.fileExtensions=n,this.mediaTypes=r},methods:{async canParse(){throw new Ub},async parse(){throw new Ub}}}),Vb=zb,Wb=Xs(Vb,{props:{name:"binary"},methods:{async canParse(e){return 0===this.fileExtensions.length||this.fileExtensions.includes(e.extension)},async parse(e){try{const t=unescape(encodeURIComponent(e.toString())),n=btoa(t),r=new Vo;if(0!==n.length){const e=new Nt.RP(n);e.classes.push("result"),r.push(e)}return r}catch(t){throw new Fb(`Error parsing "${e.uri}"`,{cause:t})}}}}),Jb=Xs({props:{name:null},methods:{canResolve:()=>!1,async resolve(){throw new Ub}}});const Kb=Yt(1,qn(Promise.all,Promise));const Hb=class extends Tb{};const Gb=class extends Hb{};const Zb=class extends Bb{};const Yb=class extends Hb{};function Xb(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function Qb(e){for(var t=1;t{const n=Nb({uri:xb(bb(e)),mediaType:t.parse.mediaType}),r=await(async(e,t)=>{const n=t.resolve.resolvers.map((e=>{const n=Object.create(e);return Object.assign(n,t.resolve.resolverOpts)})),r=await Mb("canRead",e,n);if(io(r))throw new Yb(e.uri);try{const{result:t}=await Db("read",[e],r);return t}catch(t){throw new Hb(`Error while reading file "${e.uri}"`,{cause:t})}})(n,t);return(async(e,t)=>{const n=t.parse.parsers.map((e=>{const n=Object.create(e);return Object.assign(n,t.parse.parserOpts)})),r=await Mb("canParse",e,n);if(io(r))throw new Yb(e.uri);try{const{plugin:t,result:n}=await Db("parse",[e],r);return!t.allowEmpty&&n.isEmpty?Promise.reject(new Fb(`Error while parsing file "${e.uri}". File is empty.`)):n}catch(t){throw new Fb(`Error while parsing file "${e.uri}"`,{cause:t})}})(Nb(Qb(Qb({},n),{},{data:r})),t)},tw=(e,t)=>{const n=fi({predicate:e});return di(t,n),new Nt.O4(n.result)};class nw extends Error{constructor(e){super(e),this.name=this.constructor.name,this.message=e,"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack}}const rw=(e,t)=>{const n=fi({predicate:e,returnOnTrue:ti});return di(t,n),wo(void 0,[0],n.result)};const ow=class extends Tb{};class sw extends ow{constructor(e){super(`Invalid JSON Schema $anchor "${e}".`)}}class iw extends Error{constructor(e){super(e),this.name=this.constructor.name,this.message=e,"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=new Error(e).stack}}const aw=e=>/^[A-Za-z_][A-Za-z_0-9.-]*$/.test(e),lw=e=>{const t=vb(e);return Ui("#",t)},cw=(e,t)=>{const n=(e=>{if(!aw(e))throw new sw(e);return e})(e),r=rw((e=>{var t;return sy(e)&&(null===(t=e.$anchor)||void 0===t?void 0:t.toValue())===n}),t);if(Uo(r))throw new iw(`Evaluation failed on token: "${n}"`);return r},uw=(e,t)=>{if(void 0===t.$ref)return;const n=vb(t.$ref.toValue()),r=t.meta.get("inherited$id").toValue();return`${Kn(((e,t)=>Eb(e,xb(bb(t)))),e,[...r,t.$ref.toValue()])}${"#"===n?"":n}`},pw=e=>{if(pw.cache.has(e))return pw.cache.get(e);const t=Ym.refract(e);return pw.cache.set(e,t),t};pw.cache=new WeakMap;const hw=e=>Cs(e)?pw(e):e,fw=(e,t)=>{const{cache:n}=fw,r=bb(e),o=e=>sy(e)&&void 0!==e.$id;if(!n.has(t)){const e=tw(o,t);n.set(t,Array.from(e))}const s=n.get(t).find((e=>((e,t)=>{if(void 0===t.$id)return;const n=t.meta.get("inherited$id").toValue();return Kn(((e,t)=>Eb(e,xb(bb(t)))),e,[...n,t.$id.toValue()])})(r,e)===r));if(Uo(s))throw new nw(`Evaluation failed on URI: "${e}"`);let i,a;return aw(lw(e))?(i=cw,a=lw(e)):(i=Ki,a=Hi(e)),i(a,s)};function dw(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function mw(e){for(var t=1;t=this.options.resolve.maxDepth)throw new Gb(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);const t=this.toBaseURI(e),{refSet:n}=this.reference;if(n.has(t))return n.find(So(t,"uri"));const r=await ew(Sb(t),mw(mw({},this.options),{},{parse:mw(mw({},this.options.parse),{},{mediaType:"text/plain"})})),o=jb({uri:t,value:r,depth:this.reference.depth+1});return n.add(o),o},ReferenceElement(e){var t;if(!this.options.resolve.external&&ty(e))return!1;const n=null===(t=e.$ref)||void 0===t?void 0:t.toValue(),r=this.toBaseURI(n);Gr(r,this.crawlingMap)||(this.crawlingMap[r]=this.toReference(n)),this.crawledElements.push(e)},PathItemElement(e){var t;if(!gs(e.$ref))return;if(!this.options.resolve.external&&Xg(e))return;const n=null===(t=e.$ref)||void 0===t?void 0:t.toValue(),r=this.toBaseURI(n);Gr(r,this.crawlingMap)||(this.crawlingMap[r]=this.toReference(n)),this.crawledElements.push(e)},LinkElement(e){if((gs(e.operationRef)||gs(e.operationId))&&(this.options.resolve.external||!Jg(e))){if(gs(e.operationRef)&&gs(e.operationId))throw new Error("LinkElement operationRef and operationId are mutually exclusive.");if(Jg(e)){var t;const n=null===(t=e.operationRef)||void 0===t?void 0:t.toValue(),r=this.toBaseURI(n);Gr(r,this.crawlingMap)||(this.crawlingMap[r]=this.toReference(n))}}},ExampleElement(e){var t;if(!gs(e.externalValue))return;if(!this.options.resolve.external&&gs(e.externalValue))return;if(e.hasKey("value")&&gs(e.externalValue))throw new Error("ExampleElement value and externalValue fields are mutually exclusive.");const n=null===(t=e.externalValue)||void 0===t?void 0:t.toValue(),r=this.toBaseURI(n);Gr(r,this.crawlingMap)||(this.crawlingMap[r]=this.toReference(n))},async SchemaElement(e){if(this.visited.has(e))return!1;if(!gs(e.$ref))return void this.visited.add(e);const t=await this.toReference(Sb(this.reference.uri)),{uri:n}=t,r=uw(n,e),o=bb(r),s=Nb({uri:o}),i=yo((e=>e.canRead(s)),this.options.resolve.resolvers),a=!i,l=!i&&n!==o;if(this.options.resolve.external||!l){if(!Gr(o,this.crawlingMap))try{this.crawlingMap[o]=i||a?t:this.toReference(Sb(r))}catch(e){if(!(a&&e instanceof nw))throw e;this.crawlingMap[o]=this.toReference(Sb(r))}this.crawledElements.push(e)}else this.visited.add(e)},async crawlReferenceElement(e){var t;const n=await this.toReference(e.$ref.toValue());this.indirections.push(e);const r=Hi(null===(t=e.$ref)||void 0===t?void 0:t.toValue());let o=Ki(r,n.value.result);if(Cs(o)){const t=e.meta.get("referenced-element").toValue();if(zc(o))o=Jm.refract(o),o.setMetaProperty("referenced-element",t);else{o=this.namespace.getElementClass(t).refract(o)}}if(this.indirections.includes(o))throw new Error("Recursive Reference Object detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);const s=yw({reference:n,namespace:this.namespace,indirections:[...this.indirections],options:this.options});await gw(o,s,{keyMap:Jv,nodeTypeGetter:Wv}),await s.crawl(),this.indirections.pop()},async crawlPathItemElement(e){var t;const n=await this.toReference(e.$ref.toValue());this.indirections.push(e);const r=Hi(null===(t=e.$ref)||void 0===t?void 0:t.toValue());let o=Ki(r,n.value.result);if(Cs(o)&&(o=zm.refract(o)),this.indirections.includes(o))throw new Error("Recursive Path Item Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);const s=yw({reference:n,namespace:this.namespace,indirections:[...this.indirections],options:this.options});await gw(o,s,{keyMap:Jv,nodeTypeGetter:Wv}),await s.crawl(),this.indirections.pop()},async crawlSchemaElement(e){let t=await this.toReference(Sb(this.reference.uri));const{uri:n}=t,r=uw(n,e),o=bb(r),s=Nb({uri:o}),i=yo((e=>e.canRead(s)),this.options.resolve.resolvers),a=!i;let l;this.indirections.push(e);try{if(i||a){l=fw(r,hw(t.value.result))}else{t=await this.toReference(Sb(r));const e=Hi(r);l=hw(Ki(e,t.value.result))}}catch(e){if(!(a&&e instanceof nw))throw e;if(aw(lw(r))){t=await this.toReference(Sb(r));const e=lw(r);l=cw(e,hw(t.value.result))}else{t=await this.toReference(Sb(r));const e=Hi(r);l=hw(Ki(e,t.value.result))}}if(this.visited.add(e),this.indirections.includes(l))throw new Error("Recursive Schema Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);const c=yw({reference:t,namespace:this.namespace,indirections:[...this.indirections],options:this.options,visited:this.visited});await gw(l,c,{keyMap:Jv,nodeTypeGetter:Wv}),await c.crawl(),this.indirections.pop()},async crawl(){await yr(rr,Kb)(this.crawlingMap),this.crawlingMap=null;for(const e of this.crawledElements)ey(e)?await this.crawlReferenceElement(e):sy(e)?await this.crawlSchemaElement(e):Yg(e)&&await this.crawlPathItemElement(e)}}}),vw=yw,bw=di[Symbol.for("nodejs.util.promisify.custom")],ww=Xs(Jb,{init(){this.name="openapi-3-1"},methods:{canResolve(e){var t;return"text/plain"!==e.mediaType?nb.includes(e.mediaType):Hg(null===(t=e.parseResult)||void 0===t?void 0:t.result)},async resolve(e,t){const n=Vs(Kv),r=jb({uri:e.uri,value:e.parseResult}),o=vw({reference:r,namespace:n,options:t}),s=kb();return s.add(r),await bw(s.rootRef.value,o,{keyMap:Jv,nodeTypeGetter:Wv}),await o.crawl(),s}}}),Ew=ww,xw=e=>e.replace(/\s/g,""),Sw=e=>e.replace(/\W/gi,"_"),_w=(e,t,n)=>{const r=xw(e);return r.length>0?Sw(r):((e,t)=>`${Sw(xw(t.toLowerCase()))}${Sw(xw(e))}`)(t,n)},jw=({operationIdNormalizer:e=_w}={})=>({predicates:t,namespace:n})=>{const r=[],o=[],s=[];return{visitor:{OpenApi3_1Element:{leave(){const e=Kr((e=>Ri(e.operationId)),o);Object.entries(e).forEach((([e,t])=>{Array.isArray(t)&&(t.length<=1||t.forEach(((t,r)=>{const o=`${e}${r+1}`;t.operationId=new n.elements.String(o)})))})),s.forEach((e=>{var t;if(void 0===e.operationId)return;const n=String(Ri(e.operationId)),r=o.find((e=>Ri(e.meta.get("originalOperationId"))===n));void 0!==r&&(e.operationId=null===(t=r.operationId)||void 0===t?void 0:t.clone(),e.meta.set("originalOperationId",n),e.set("__originalOperationId",n))})),o.length=0,s.length=0}},PathItemElement:{enter(e){const t=Ar("path",Ri(e.meta.get("path")));r.push(t)},leave(){r.pop()}},OperationElement:{enter(t){if(void 0===t.operationId)return;const s=String(Ri(t.operationId)),i=lo(r),a=Ar("method",Ri(t.meta.get("http-method"))),l=e(s,i,a);s!==l&&(t.operationId=new n.elements.String(l),t.set("__originalOperationId",s),t.meta.set("originalOperationId",s),o.push(t))}},LinkElement:{leave(e){t.isLinkElement(e)&&void 0!==e.operationId&&s.push(e)}}}}},Ow=()=>({predicates:e})=>{const t=(t,n)=>!!e.isParameterElement(t)&&(!!e.isParameterElement(n)&&(!!e.isStringElement(t.name)&&(!!e.isStringElement(t.in)&&(!!e.isStringElement(n.name)&&(!!e.isStringElement(n.in)&&(Ri(t.name)===Ri(n.name)&&Ri(t.in)===Ri(n.in))))))),n=[];return{visitor:{PathItemElement:{enter(t,r,o,s,i){if(i.some(e.isComponentsElement))return;const{parameters:a}=t;e.isArrayElement(a)?n.push([...a.content]):n.push([])},leave(){n.pop()}},OperationElement:{leave(e){const r=lo(n);if(!Array.isArray(r)||0===r.length)return;const o=wo([],["parameters","content"],e),s=Bo(t,[...o,...r]);e.parameters=new Ed(s)}}}}},kw=()=>({predicates:e})=>{let t;return{visitor:{OpenApi3_1Element:{enter(n){e.isArrayElement(n.security)&&(t=n.security)},leave(){t=void 0}},OperationElement:{leave(n,r,o,s,i){if(i.some(e.isComponentsElement))return;var a;void 0===n.security&&void 0!==t&&(n.security=new Pd(null===(a=t)||void 0===a?void 0:a.content))}}}}},Aw=()=>({predicates:e})=>{let t;const n=[];return{visitor:{OpenApi3_1Element:{enter(n){var r;e.isArrayElement(n.servers)&&(t=null===(r=n.servers)||void 0===r?void 0:r.content)},leave(){t=void 0}},PathItemElement:{enter(r,o,s,i,a){if(a.some(e.isComponentsElement))return;void 0===r.servers&&void 0!==t&&(r.servers=new $d(t));const{servers:l}=r;void 0!==l&&e.isArrayElement(l)?n.push([...l.content]):n.push(void 0)},leave(){n.pop()}},OperationElement:{enter(t){const r=lo(n);void 0!==r&&(e.isArrayElement(t.servers)||(t.servers=new Td(r)))}}}}},Cw=()=>({predicates:e})=>({visitor:{ParameterElement:{leave(t,n,r,o,s){var i,a;if(!s.some(e.isComponentsElement)&&void 0!==t.schema&&e.isSchemaElement(t.schema)&&(void 0!==(null===(i=t.schema)||void 0===i?void 0:i.example)||void 0!==(null===(a=t.schema)||void 0===a?void 0:a.examples))){if(void 0!==t.examples&&e.isObjectElement(t.examples)){const e=t.examples.map((e=>{var t;return null===(t=e.value)||void 0===t?void 0:t.clone()}));return void 0!==t.schema.examples&&t.schema.set("examples",e),void(void 0!==t.schema.example&&t.schema.set("example",e))}void 0!==t.example&&(void 0!==t.schema.examples&&t.schema.set("examples",[t.example.clone()]),void 0!==t.schema.example&&t.schema.set("example",t.example.clone()))}}}}}),Pw=()=>({predicates:e})=>({visitor:{HeaderElement:{leave(t,n,r,o,s){var i,a;if(!s.some(e.isComponentsElement)&&void 0!==t.schema&&e.isSchemaElement(t.schema)&&(void 0!==(null===(i=t.schema)||void 0===i?void 0:i.example)||void 0!==(null===(a=t.schema)||void 0===a?void 0:a.examples))){if(void 0!==t.examples&&e.isObjectElement(t.examples)){const e=t.examples.map((e=>{var t;return null===(t=e.value)||void 0===t?void 0:t.clone()}));return void 0!==t.schema.examples&&t.schema.set("examples",e),void(void 0!==t.schema.example&&t.schema.set("example",e))}void 0!==t.example&&(void 0!==t.schema.examples&&t.schema.set("examples",[t.example.clone()]),void 0!==t.schema.example&&t.schema.set("example",t.example.clone()))}}}}}),Nw=e=>t=>{if(t?.$$normalized)return t;if(Nw.cache.has(t))return t;const n=$m.refract(t),r=e(n),o=Ri(r);return Nw.cache.set(t,o),o};Nw.cache=new WeakMap;const Iw=e=>{if(!ws(e))return e;if(e.hasKey("$$normalized"))return e;const t=[jw({operationIdNormalizer:(e,t,n)=>(0,He.Z)({operationId:e},t,n,{v2OperationIdCompatibilityMode:!1})}),Ow(),kw(),Aw(),Cw(),Pw()],n=mi(e,t,{toolboxCreator:Zv,visitorOptions:{keyMap:Jv,nodeTypeGetter:Wv}});return n.set("$$normalized",!0),n},Tw=Xs({props:{name:null},methods:{canRead:()=>!1,async read(){throw new Ub}}}),Rw=Xs(Tw,{props:{timeout:5e3,redirects:5,withCredentials:!1},init({timeout:e=this.timeout,redirects:t=this.redirects,withCredentials:n=this.withCredentials}={}){this.timeout=e,this.redirects=t,this.withCredentials=n},methods:{canRead:e=>gb(e.uri),async read(){throw new Ub},getHttpClient(){throw new Ub}}}).compose({props:{name:"http-swagger-client",swaggerHTTPClient:ct,swaggerHTTPClientConfig:{}},init(){let{swaggerHTTPClient:e=this.swaggerHTTPClient}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.swaggerHTTPClient=e},methods:{getHttpClient(){return this.swaggerHTTPClient},async read(e){const t=this.getHttpClient(),n=new AbortController,{signal:r}=n,o=setTimeout((()=>{n.abort()}),this.timeout),s=this.getHttpClient().withCredentials||this.withCredentials?"include":"same-origin",i=0===this.redirects?"error":"follow",a=this.redirects>0?this.redirects:void 0;try{return(await t(f()({url:e.uri,signal:r,userFetch:async(e,t)=>{let n=await fetch(e,t);try{n.headers.delete("Content-Type")}catch{n=new Response(n.body,f()(f()({},n),{},{headers:new Headers(n.headers)})),n.headers.delete("Content-Type")}return n},credentials:s,redirects:i,follow:a},this.swaggerHTTPClientConfig))).text.arrayBuffer()}catch(t){throw new Hb(`Error downloading "${e.uri}"`,{cause:t})}finally{clearTimeout(o)}}}}),Mw=Vb.compose({props:{name:"json-swagger-client",fileExtensions:[".json"],mediaTypes:["application/json"]},methods:{async canParse(e){const t=0===this.fileExtensions.length||this.fileExtensions.includes(e.extension),n=this.mediaTypes.includes(e.mediaType);if(!t)return!1;if(n)return!0;if(!n)try{return JSON.parse(e.toString()),!0}catch(e){return!1}return!1},async parse(e){if(this.sourceMap)throw new Fb("json-swagger-client parser plugin doesn't support sourceMaps option");const t=new Vo,n=e.toString();if(this.allowEmpty&&""===n.trim())return t;try{const e=Ti(JSON.parse(n));return e.classes.push("result"),t.push(e),t}catch(t){throw new Fb(`Error parsing "${e.uri}"`,{cause:t})}}}}),Dw=Vb.compose({props:{name:"yaml-1-2-swagger-client",fileExtensions:[".yaml",".yml"],mediaTypes:["text/yaml","application/yaml"]},methods:{async canParse(e){const t=0===this.fileExtensions.length||this.fileExtensions.includes(e.extension),n=this.mediaTypes.includes(e.mediaType);if(!t)return!1;if(n)return!0;if(!n)try{return le.ZP.load(e.toString(),{schema:le.A8}),!0}catch(e){return!1}return!1},async parse(e){if(this.sourceMap)throw new Fb("yaml-1-2-swagger-client parser plugin doesn't support sourceMaps option");const t=new Vo,n=e.toString();try{const e=le.ZP.load(n,{schema:le.A8});if(this.allowEmpty&&void 0===e)return t;const r=Ti(e);return r.classes.push("result"),t.push(r),t}catch(t){throw new Fb(`Error parsing "${e.uri}"`,{cause:t})}}}}),Fw=Vb.compose({props:{name:"openapi-json-3-1-swagger-client",fileExtensions:[".json"],mediaTypes:new tb(...nb.filterByFormat("generic"),...nb.filterByFormat("json")),detectionRegExp:/"openapi"\s*:\s*"(?3\.1\.(?:[1-9]\d*|0))"/},methods:{async canParse(e){const t=0===this.fileExtensions.length||this.fileExtensions.includes(e.extension),n=this.mediaTypes.includes(e.mediaType);if(!t)return!1;if(n)return!0;if(!n)try{const t=e.toString();return JSON.parse(t),this.detectionRegExp.test(t)}catch(e){return!1}return!1},async parse(e){if(this.sourceMap)throw new Fb("openapi-json-3-1-swagger-client parser plugin doesn't support sourceMaps option");const t=new Vo,n=e.toString();if(this.allowEmpty&&""===n.trim())return t;try{const e=JSON.parse(n),r=$m.refract(e,this.refractorOpts);return r.classes.push("result"),t.push(r),t}catch(t){throw new Fb(`Error parsing "${e.uri}"`,{cause:t})}}}}),Lw=Vb.compose({props:{name:"openapi-yaml-3-1-swagger-client",fileExtensions:[".yaml",".yml"],mediaTypes:new tb(...nb.filterByFormat("generic"),...nb.filterByFormat("yaml")),detectionRegExp:/(?^(["']?)openapi\2\s*:\s*(["']?)(?3\.1\.(?:[1-9]\d*|0))\3(?:\s+|$))|(?"openapi"\s*:\s*"(?3\.1\.(?:[1-9]\d*|0))")/m},methods:{async canParse(e){const t=0===this.fileExtensions.length||this.fileExtensions.includes(e.extension),n=this.mediaTypes.includes(e.mediaType);if(!t)return!1;if(n)return!0;if(!n)try{const t=e.toString();return le.ZP.load(t),this.detectionRegExp.test(t)}catch(e){return!1}return!1},async parse(e){if(this.sourceMap)throw new Fb("openapi-yaml-3-1-swagger-client parser plugin doesn't support sourceMaps option");const t=new Vo,n=e.toString();try{const e=le.ZP.load(n,{schema:le.A8});if(this.allowEmpty&&void 0===e)return t;const r=$m.refract(e,this.refractorOpts);return r.classes.push("result"),t.push(r),t}catch(t){throw new Fb(`Error parsing "${e.uri}"`,{cause:t})}}}}),Bw=Xs({props:{name:null},methods:{canDereference:()=>!1,async dereference(){throw new Ub}}});function $w(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function qw(e){for(var t=1;t=this.options.resolve.maxDepth)throw new Gb(`Maximum resolution depth of ${this.options.resolve.maxDepth} has been exceeded by file "${this.reference.uri}"`);const t=this.toBaseURI(e),{refSet:n}=this.reference;if(n.has(t))return n.find(So(t,"uri"));const r=await ew(Sb(t),qw(qw({},this.options),{},{parse:qw(qw({},this.options.parse),{},{mediaType:"text/plain"})})),o=jb({uri:t,value:r,depth:this.reference.depth+1});return n.add(o),o},async ReferenceElement(e,t,n,r,o){var s,i,a,l,c;const[u,p]=this.toAncestorLineage([...o,n]);if(u.some((t=>t.has(e))))return!1;if(!this.options.resolve.external&&ty(e))return!1;const h=await this.toReference(null===(s=e.$ref)||void 0===s?void 0:s.toValue()),{uri:f}=h,d=Eb(f,null===(i=e.$ref)||void 0===i?void 0:i.toValue());this.indirections.push(e);const m=Hi(d);let g=Ki(m,h.value.result);if(Cs(g)){const t=e.meta.get("referenced-element").toValue();if(zc(g))g=Jm.refract(g),g.setMetaProperty("referenced-element",t);else{g=this.namespace.getElementClass(t).refract(g)}}if(this.indirections.includes(g))throw new Error("Recursive Reference Object detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);p.add(e);const y=zw({reference:h,namespace:this.namespace,indirections:[...this.indirections],options:this.options,ancestors:u});g=await Uw(g,y,{keyMap:Jv,nodeTypeGetter:Wv}),p.delete(e),this.indirections.pop(),g=g.clone(),g.setMetaProperty("ref-fields",{$ref:null===(a=e.$ref)||void 0===a?void 0:a.toValue(),description:null===(l=e.description)||void 0===l?void 0:l.toValue(),summary:null===(c=e.summary)||void 0===c?void 0:c.toValue()}),g.setMetaProperty("ref-origin",h.uri);const v=Eo(ib,["description"],e),b=Eo(ib,["summary"],e);return v&&Zr("description",g)&&(g.description=e.description),b&&Zr("summary",g)&&(g.summary=e.summary),this.indirections.pop(),g},async PathItemElement(e,t,n,r,o){var s,i,a;const[l,c]=this.toAncestorLineage([...o,n]);if(!gs(e.$ref))return;if(l.some((t=>t.has(e))))return!1;if(!this.options.resolve.external&&Xg(e))return;const u=await this.toReference(null===(s=e.$ref)||void 0===s?void 0:s.toValue()),{uri:p}=u,h=Eb(p,null===(i=e.$ref)||void 0===i?void 0:i.toValue());this.indirections.push(e);const f=Hi(h);let d=Ki(f,u.value.result);if(Cs(d)&&(d=zm.refract(d)),this.indirections.includes(d))throw new Error("Recursive Path Item Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);c.add(e);const m=zw({reference:u,namespace:this.namespace,indirections:[...this.indirections],options:this.options,ancestors:l});d=await Uw(d,m,{keyMap:Jv,nodeTypeGetter:Wv}),c.delete(e),this.indirections.pop();const g=new zm([...d.content],d.meta.clone(),d.attributes.clone());return e.forEach(((e,t,n)=>{g.remove(t.toValue()),g.content.push(n)})),g.remove("$ref"),g.setMetaProperty("ref-fields",{$ref:null===(a=e.$ref)||void 0===a?void 0:a.toValue()}),g.setMetaProperty("ref-origin",u.uri),g},async LinkElement(e){if(!gs(e.operationRef)&&!gs(e.operationId))return;if(!this.options.resolve.external&&Jg(e))return;if(gs(e.operationRef)&&gs(e.operationId))throw new Error("LinkElement operationRef and operationId fields are mutually exclusive.");let t;if(gs(e.operationRef)){var n,r,o;const s=Hi(null===(n=e.operationRef)||void 0===n?void 0:n.toValue()),i=await this.toReference(null===(r=e.operationRef)||void 0===r?void 0:r.toValue());t=Ki(s,i.value.result),Cs(t)&&(t=qm.refract(t)),t=new qm([...t.content],t.meta.clone(),t.attributes.clone()),t.setMetaProperty("ref-origin",i.uri),null===(o=e.operationRef)||void 0===o||o.meta.set("operation",t)}else if(gs(e.operationId)){var s,i;const n=null===(s=e.operationId)||void 0===s?void 0:s.toValue(),r=await this.toReference(Sb(this.reference.uri));if(t=rw((e=>Gg(e)&&e.operationId.equals(n)),r.value.result),Uo(t))throw new Error(`OperationElement(operationId=${n}) not found.`);null===(i=e.operationId)||void 0===i||i.meta.set("operation",t)}},async ExampleElement(e){var t;if(!gs(e.externalValue))return;if(!this.options.resolve.external&&gs(e.externalValue))return;if(e.hasKey("value")&&gs(e.externalValue))throw new Error("ExampleElement value and externalValue fields are mutually exclusive.");const n=await this.toReference(null===(t=e.externalValue)||void 0===t?void 0:t.toValue()),r=new n.value.result.constructor(n.value.result.content,n.value.result.meta.clone(),n.value.result.attributes.clone());r.setMetaProperty("ref-origin",n.uri),e.value=r},async SchemaElement(e,t,n,r,o){var s;const[i,a]=this.toAncestorLineage([...o,n]);if(!gs(e.$ref))return;if(i.some((t=>t.has(e))))return!1;let l=await this.toReference(Sb(this.reference.uri)),{uri:c}=l;const u=uw(c,e),p=bb(u),h=Nb({uri:p}),f=yo((e=>e.canRead(h)),this.options.resolve.resolvers),d=!f,m=d&&c!==p;if(!this.options.resolve.external&&m)return;let g;this.indirections.push(e);try{if(f||d){g=fw(u,hw(l.value.result))}else{l=await this.toReference(Sb(u));const e=Hi(u);g=hw(Ki(e,l.value.result))}}catch(e){if(!(d&&e instanceof nw))throw e;if(aw(lw(u))){l=await this.toReference(Sb(u)),c=l.uri;const e=lw(u);g=cw(e,hw(l.value.result))}else{l=await this.toReference(Sb(u)),c=l.uri;const e=Hi(u);g=hw(Ki(e,l.value.result))}}if(this.indirections.includes(g))throw new Error("Recursive Schema Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);a.add(e);const y=zw({reference:l,namespace:this.namespace,indirections:[...this.indirections],options:this.options,ancestors:i});if(g=await Uw(g,y,{keyMap:Jv,nodeTypeGetter:Wv}),a.delete(e),this.indirections.pop(),iy(g)){var v;const t=g.clone();return t.setMetaProperty("ref-fields",{$ref:null===(v=e.$ref)||void 0===v?void 0:v.toValue()}),t.setMetaProperty("ref-origin",l.uri),t}const b=new Ym([...g.content],g.meta.clone(),g.attributes.clone());return e.forEach(((e,t,n)=>{b.remove(t.toValue()),b.content.push(n)})),b.remove("$ref"),b.setMetaProperty("ref-fields",{$ref:null===(s=e.$ref)||void 0===s?void 0:s.toValue()}),b.setMetaProperty("ref-origin",l.uri),b}}}),Vw=zw,Ww=di[Symbol.for("nodejs.util.promisify.custom")],Jw=Xs(Bw,{init(){this.name="openapi-3-1"},methods:{canDereference(e){var t;return"text/plain"!==e.mediaType?nb.includes(e.mediaType):Hg(null===(t=e.parseResult)||void 0===t?void 0:t.result)},async dereference(e,t){const n=Vs(Kv),r=Ar(kb(),t.dereference.refSet);let o;r.has(e.uri)?o=r.find(So(e.uri,"uri")):(o=jb({uri:e.uri,value:e.parseResult}),r.add(o));const s=Vw({reference:o,namespace:n,options:t}),i=await Ww(r.rootRef.value,s,{keyMap:Jv,nodeTypeGetter:Wv});return null===t.dereference.refSet&&r.clean(),i}}}),Kw=Jw,Hw=e=>{const t=(e=>e.slice(2))(e);return t.reduce(((e,n,r)=>{if(xs(n)){const t=String(n.key.toValue());e.push(t)}else if(Es(t[r-2])){const o=t[r-2].content.indexOf(n);e.push(o)}return e}),[])},Gw=e=>{if(null==e.cause)return e;let{cause:t}=e;for(;null!=t.cause;)t=t.cause;return t},Zw=ue("SchemaRefError",(function(e,t,n){this.originalError=n,Object.assign(this,t||{})})),{wrapError:Yw}=ke,Xw=di[Symbol.for("nodejs.util.promisify.custom")],Qw=Vw.compose({props:{useCircularStructures:!0,allowMetaPatches:!1,basePath:null},init(e){let{allowMetaPatches:t=this.allowMetaPatches,useCircularStructures:n=this.useCircularStructures,basePath:r=this.basePath}=e;this.allowMetaPatches=t,this.useCircularStructures=n,this.basePath=r},methods:{async ReferenceElement(e,t,n,r,o){try{const[t,r]=this.toAncestorLineage([...o,n]);if(Is(["cycle"],e.$ref))return!1;if(t.some((t=>t.has(e))))return!1;if(!this.options.resolve.external&&ty(e))return!1;const s=await this.toReference(e.$ref.toValue()),{uri:i}=s,a=Eb(i,e.$ref.toValue());this.indirections.push(e);const l=Hi(a);let c=Ki(l,s.value.result);if(Cs(c)){const t=e.meta.get("referenced-element").toValue();if(zc(c))c=Jm.refract(c),c.setMetaProperty("referenced-element",t);else{const e=this.namespace.getElementClass(t);c=e.refract(c)}}if(this.indirections.includes(c))throw new Error("Recursive JSON Pointer detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);if(!this.useCircularStructures){if(t.some((e=>e.has(c)))){if(gb(i)||mb(i)){const t=new Jm({$ref:a},e.meta.clone(),e.attributes.clone());return t.get("$ref").classes.push("cycle"),t}return!1}}r.add(e);const u=Qw({reference:s,namespace:this.namespace,indirections:[...this.indirections],options:this.options,ancestors:t,allowMetaPatches:this.allowMetaPatches,useCircularStructures:this.useCircularStructures,basePath:this.basePath??[...Hw([...o,n,e]),"$ref"]});c=await Xw(c,u,{keyMap:Jv,nodeTypeGetter:Wv}),r.delete(e),this.indirections.pop(),c=c.clone(),c.setMetaProperty("ref-fields",{$ref:e.$ref?.toValue(),description:e.description?.toValue(),summary:e.summary?.toValue()}),c.setMetaProperty("ref-origin",s.uri);const p=void 0!==e.description,h=void 0!==e.summary;if(p&&"description"in c&&(c.description=e.description),h&&"summary"in c&&(c.summary=e.summary),this.allowMetaPatches&&ws(c)){const e=c;if(void 0===e.get("$$ref")){const t=Eb(i,a);e.set("$$ref",t)}}return c}catch(t){const r=Gw(t),s=Yw(r,{baseDoc:this.reference.uri,$ref:e.$ref.toValue(),pointer:Hi(e.$ref.toValue()),fullPath:this.basePath??[...Hw([...o,n,e]),"$ref"]});return void this.options.dereference.dereferenceOpts?.errors?.push?.(s)}},async PathItemElement(e,t,n,r,o){try{const[t,r]=this.toAncestorLineage([...o,n]);if(!gs(e.$ref))return;if(Is(["cycle"],e.$ref))return!1;if(t.some((t=>t.has(e))))return!1;if(!this.options.resolve.external&&Xg(e))return;const s=await this.toReference(e.$ref.toValue()),{uri:i}=s,a=Eb(i,e.$ref.toValue());this.indirections.push(e);const l=Hi(a);let c=Ki(l,s.value.result);if(Cs(c)&&(c=zm.refract(c)),this.indirections.includes(c))throw new Error("Recursive JSON Pointer detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);if(!this.useCircularStructures){if(t.some((e=>e.has(c)))){if(gb(i)||mb(i)){const t=new zm({$ref:a},e.meta.clone(),e.attributes.clone());return t.get("$ref").classes.push("cycle"),t}return!1}}r.add(e);const u=Qw({reference:s,namespace:this.namespace,indirections:[...this.indirections],options:this.options,ancestors:t,allowMetaPatches:this.allowMetaPatches,useCircularStructures:this.useCircularStructures,basePath:this.basePath??[...Hw([...o,n,e]),"$ref"]});c=await Xw(c,u,{keyMap:Jv,nodeTypeGetter:Wv}),r.delete(e),this.indirections.pop();const p=new zm([...c.content],c.meta.clone(),c.attributes.clone());if(e.forEach(((e,t,n)=>{p.remove(t.toValue()),p.content.push(n)})),p.remove("$ref"),p.setMetaProperty("ref-fields",{$ref:e.$ref?.toValue()}),p.setMetaProperty("ref-origin",s.uri),this.allowMetaPatches&&void 0===p.get("$$ref")){const e=Eb(i,a);p.set("$$ref",e)}return p}catch(t){const r=Gw(t),s=Yw(r,{baseDoc:this.reference.uri,$ref:e.$ref.toValue(),pointer:Hi(e.$ref.toValue()),fullPath:this.basePath??[...Hw([...o,n,e]),"$ref"]});return void this.options.dereference.dereferenceOpts?.errors?.push?.(s)}},async SchemaElement(e,t,n,r,o){try{const[t,r]=this.toAncestorLineage([...o,n]);if(!gs(e.$ref))return;if(Is(["cycle"],e.$ref))return!1;if(t.some((t=>t.has(e))))return!1;let s=await this.toReference(Sb(this.reference.uri)),{uri:i}=s;const a=uw(i,e),l=bb(a),c=Nb({uri:l}),u=!this.options.resolve.resolvers.some((e=>e.canRead(c))),p=!u,h=p&&i!==l;if(!this.options.resolve.external&&h)return;let f;this.indirections.push(e);try{if(u||p){f=fw(a,hw(s.value.result))}else{s=await this.toReference(Sb(a)),i=s.uri;const e=Hi(a);f=hw(Ki(e,s.value.result))}}catch(e){if(!(p&&e instanceof nw))throw e;if(aw(lw(a))){s=await this.toReference(Sb(a)),i=s.uri;const e=lw(a);f=cw(e,hw(s.value.result))}else{s=await this.toReference(Sb(a)),i=s.uri;const e=Hi(a);f=hw(Ki(e,s.value.result))}}if(this.indirections.includes(f))throw new Error("Recursive Schema Object reference detected");if(this.indirections.length>this.options.dereference.maxDepth)throw new Zb(`Maximum dereference depth of "${this.options.dereference.maxDepth}" has been exceeded in file "${this.reference.uri}"`);if(!this.useCircularStructures){if(t.some((e=>e.has(f)))){if(gb(i)||mb(i)){const t=Eb(i,a),n=new Ym({$ref:t},e.meta.clone(),e.attributes.clone());return n.get("$ref").classes.push("cycle"),n}return!1}}r.add(e);const d=Qw({reference:s,namespace:this.namespace,indirections:[...this.indirections],options:this.options,useCircularStructures:this.useCircularStructures,allowMetaPatches:this.allowMetaPatches,ancestors:t,basePath:this.basePath??[...Hw([...o,n,e]),"$ref"]});if(f=await Xw(f,d,{keyMap:Jv,nodeTypeGetter:Wv}),r.delete(e),this.indirections.pop(),iy(f)){const t=f.clone();return t.setMetaProperty("ref-fields",{$ref:e.$ref?.toValue()}),t.setMetaProperty("ref-origin",i),t}const m=new Ym([...f.content],f.meta.clone(),f.attributes.clone());if(e.forEach(((e,t,n)=>{m.remove(t.toValue()),m.content.push(n)})),m.remove("$ref"),m.setMetaProperty("ref-fields",{$ref:e.$ref?.toValue()}),m.setMetaProperty("ref-origin",i),this.allowMetaPatches&&void 0===m.get("$$ref")){const e=Eb(i,a);m.set("$$ref",e)}return m}catch(t){const r=Gw(t),s=new Zw(`Could not resolve reference: ${r.message}`,{baseDoc:this.reference.uri,$ref:e.$ref.toValue(),fullPath:this.basePath??[...Hw([...o,n,e]),"$ref"]},r);return void this.options.dereference.dereferenceOpts?.errors?.push?.(s)}},async LinkElement(){},async ExampleElement(e,t,n,r,o){try{return await Vw.compose.methods.ExampleElement.call(this,e,t,n,r,o)}catch(t){const r=Gw(t),s=Yw(r,{baseDoc:this.reference.uri,externalValue:e.externalValue?.toValue(),fullPath:this.basePath??[...Hw([...o,n,e]),"externalValue"]});return void this.options.dereference.dereferenceOpts?.errors?.push?.(s)}}}}),eE=Qw,tE=Kw.compose.bind(),nE=tE({init(e){let{parameterMacro:t,options:n}=e;this.parameterMacro=t,this.options=n},props:{parameterMacro:null,options:null,macroOperation:null,OperationElement:{enter(e){this.macroOperation=e},leave(){this.macroOperation=null}},ParameterElement:{leave(e,t,n,r,o){const s=null===this.macroOperation?null:Ri(this.macroOperation),i=Ri(e);try{const t=this.parameterMacro(s,i);e.set("default",t)}catch(e){const t=new Error(e,{cause:e});t.fullPath=Hw([...o,n]),this.options.dereference.dereferenceOpts?.errors?.push?.(t)}}}}}),rE=tE({init(e){let{modelPropertyMacro:t,options:n}=e;this.modelPropertyMacro=t,this.options=n},props:{modelPropertyMacro:null,options:null,SchemaElement:{leave(e,t,n,r,o){void 0!==e.properties&&ws(e.properties)&&e.properties.forEach((t=>{if(ws(t))try{const e=this.modelPropertyMacro(Ri(t));t.set("default",e)}catch(t){const r=new Error(t,{cause:t});r.fullPath=[...Hw([...o,n,e]),"properties"],this.options.dereference.dereferenceOpts?.errors?.push?.(r)}}))}}}});function oE(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function sE(e){for(var t=1;t{const t=e.meta.clone(),n=e.attributes.clone();return new e.constructor(void 0,t,n)},aE=e=>new Nt.c6(e.key,e.value,e.meta.clone(),e.attributes.clone()),lE=(e,t)=>t.clone&&t.isMergeableElement(e)?pE(iE(e),e,t):e,cE=(e,t,n)=>e.concat(t)["fantasy-land/map"]((e=>lE(e,n))),uE=(e,t,n)=>{const r=ws(e)?iE(e):iE(t);return ws(e)&&e.forEach(((e,t,o)=>{const s=aE(o);s.value=lE(e,n),r.content.push(s)})),t.forEach(((t,o,s)=>{const i=o.toValue();let a;if(ws(e)&&e.hasKey(i)&&n.isMergeableElement(t)){const r=e.get(i);a=aE(s),a.value=((e,t)=>{if("function"!=typeof t.customMerge)return pE;const n=t.customMerge(e,t);return"function"==typeof n?n:pE})(o,n)(r,t)}else a=aE(s),a.value=lE(t,n);r.remove(i),r.content.push(a)})),r};function pE(e,t,n){var r,o,s;const i={clone:!0,isMergeableElement:e=>ws(e)||Es(e),arrayElementMerge:cE,objectElementMerge:uE,customMerge:void 0},a=sE(sE({},i),n);a.isMergeableElement=null!==(r=a.isMergeableElement)&&void 0!==r?r:i.isMergeableElement,a.arrayElementMerge=null!==(o=a.arrayElementMerge)&&void 0!==o?o:i.arrayElementMerge,a.objectElementMerge=null!==(s=a.objectElementMerge)&&void 0!==s?s:i.objectElementMerge;const l=Es(t);return l===Es(e)?l&&"function"==typeof a.arrayElementMerge?a.arrayElementMerge(e,t,a):a.objectElementMerge(e,t,a):lE(t,a)}pE.all=(e,t)=>{if(!Array.isArray(e))throw new Error("first argument should be an array");return 0===e.length?new Nt.Sb:e.reduce(((e,n)=>pE(e,n,t)),iE(e[0]))};const hE=tE({init(e){let{options:t}=e;this.options=t},props:{options:null,SchemaElement:{leave(e,t,n,r,o){if(void 0===e.allOf)return;if(!Es(e.allOf)){const t=new TypeError("allOf must be an array");return t.fullPath=[...Hw([...o,n,e]),"allOf"],void this.options.dereference.dereferenceOpts?.errors?.push?.(t)}if(e.allOf.isEmpty)return new Ym(e.content.filter((e=>"allOf"!==e.key.toValue())),e.meta.clone(),e.attributes.clone());if(!e.allOf.content.every(sy)){const t=new TypeError("Elements in allOf must be objects");return t.fullPath=[...Hw([...o,n,e]),"allOf"],void this.options.dereference.dereferenceOpts?.errors?.push?.(t)}const s=pE.all([...e.allOf.content,e]);if(e.hasKey("$$ref")||s.remove("$$ref"),e.hasKey("example")){s.getMember("example").value=e.get("example")}if(e.hasKey("examples")){s.getMember("examples").value=e.get("examples")}return s.remove("allOf"),s}}}}),fE=di[Symbol.for("nodejs.util.promisify.custom")],dE=Kw.compose({props:{useCircularStructures:!0,allowMetaPatches:!1,parameterMacro:null,modelPropertyMacro:null,mode:"non-strict",ancestors:null},init(){let{useCircularStructures:e=this.useCircularStructures,allowMetaPatches:t=this.allowMetaPatches,parameterMacro:n=this.parameterMacro,modelPropertyMacro:r=this.modelPropertyMacro,mode:o=this.mode,ancestors:s=[]}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.name="openapi-3-1-swagger-client",this.useCircularStructures=e,this.allowMetaPatches=t,this.parameterMacro=n,this.modelPropertyMacro=r,this.mode=o,this.ancestors=[...s]},methods:{async dereference(e,t){const n=[],r=Vs(Kv),o=t.dereference.refSet??kb();let s;o.has(e.uri)?s=o.find((t=>t.uri===e.uri)):(s=jb({uri:e.uri,value:e.parseResult}),o.add(s));const i=eE({reference:s,namespace:r,options:t,useCircularStructures:this.useCircularStructures,allowMetaPatches:this.allowMetaPatches,ancestors:this.ancestors});if(n.push(i),"function"==typeof this.parameterMacro){const e=nE({parameterMacro:this.parameterMacro,options:t});n.push(e)}if("function"==typeof this.modelPropertyMacro){const e=rE({modelPropertyMacro:this.modelPropertyMacro,options:t});n.push(e)}if("strict"!==this.mode){const e=hE({options:t});n.push(e)}const a=oi(n,{nodeTypeGetter:Wv}),l=await fE(o.rootRef.value,a,{keyMap:Jv,nodeTypeGetter:Wv});return null===t.dereference.refSet&&o.clean(),l}}}),mE=dE,gE=async e=>{const{spec:t,timeout:n,redirects:r,requestInterceptor:o,responseInterceptor:s,pathDiscriminator:i=[],allowMetaPatches:a=!1,useCircularStructures:l=!1,skipNormalization:c=!1,parameterMacro:u=null,modelPropertyMacro:p=null,mode:h="non-strict"}=e;try{const{cache:d}=gE,m=gb(wb())?wb():"https://smartbear.com/",g=xt(e),y=Eb(m,g);let v;d.has(t)?v=d.get(t):(v=$m.refract(t),v.classes.push("result"),d.set(t,v));const b=new Vo([v]),w=0===(f=i).length?"":`/${f.map(Wi).join("/")}`,E=""===w?"":`#${w}`,x=Ki(w,v),S=jb({uri:y,value:b}),_=kb({refs:[S]});""!==w&&(_.rootRef=null);const j=[new WeakSet([x])],O=[],k=((e,t,n)=>xi({element:n}).transclude(e,t))(x,await qb(x,{resolve:{baseURI:`${y}${E}`,resolvers:[Rw({timeout:n||1e4,redirects:r||10})],resolverOpts:{swaggerHTTPClientConfig:{requestInterceptor:o,responseInterceptor:s}},strategies:[Ew()]},parse:{mediaType:nb.latest(),parsers:[Fw({allowEmpty:!1,sourceMap:!1}),Lw({allowEmpty:!1,sourceMap:!1}),Mw({allowEmpty:!1,sourceMap:!1}),Dw({allowEmpty:!1,sourceMap:!1}),Wb({allowEmpty:!1,sourceMap:!1})]},dereference:{maxDepth:100,strategies:[mE({allowMetaPatches:a,useCircularStructures:l,parameterMacro:u,modelPropertyMacro:p,mode:h,ancestors:j})],refSet:_,dereferenceOpts:{errors:O}}}),v),A=c?k:Iw(k);return{spec:Ri(A),errors:O}}catch(e){if(e instanceof zi||e instanceof Vi)return{spec:null,errors:[]};throw e}var f};gE.cache=new WeakMap;const yE=gE,vE={name:"openapi-3-1-apidom",match(e){let{spec:t}=e;return kt(t)},normalize(e){let{spec:t}=e;return Nw(Iw)(t)},resolve:async e=>yE(e)},bE=e=>async t=>(async e=>{const{spec:t,requestInterceptor:n,responseInterceptor:r}=e,o=xt(e),s=St(e),i=t||await Ze(s,{requestInterceptor:n,responseInterceptor:r})(o),a=f()(f()({},e),{},{spec:i});return e.strategies.find((e=>e.match(a))).resolve(a)})(f()(f()({},e),t)),wE=bE({strategies:[Pt,Ct,jt]});var EE=n(88436),xE=n.n(EE),SE=n(27361),_E=n.n(SE),jE=n(76489);function OE(e){return"[object Object]"===Object.prototype.toString.call(e)}function kE(e){var t,n;return!1!==OE(e)&&(void 0===(t=e.constructor)||!1!==OE(n=t.prototype)&&!1!==n.hasOwnProperty("isPrototypeOf"))}const AE={body:function(e){let{req:t,value:n}=e;t.body=n},header:function(e){let{req:t,parameter:n,value:r}=e;t.headers=t.headers||{},void 0!==r&&(t.headers[n.name]=r)},query:function(e){let{req:t,value:n,parameter:r}=e;t.query=t.query||{},!1===n&&"boolean"===r.type&&(n="false");0===n&&["number","integer"].indexOf(r.type)>-1&&(n="0");if(n)t.query[r.name]={collectionFormat:r.collectionFormat,value:n};else if(r.allowEmptyValue&&void 0!==n){const e=r.name;t.query[e]=t.query[e]||{},t.query[e].allowEmptyValue=!0}},path:function(e){let{req:t,value:n,parameter:r}=e;t.url=t.url.split(`{${r.name}}`).join(encodeURIComponent(n))},formData:function(e){let{req:t,value:n,parameter:r}=e;(n||r.allowEmptyValue)&&(t.form=t.form||{},t.form[r.name]={value:n,allowEmptyValue:r.allowEmptyValue,collectionFormat:r.collectionFormat})}};function CE(e,t){return t.includes("application/json")?"string"==typeof e?e:JSON.stringify(e):e.toString()}function PE(e){let{req:t,value:n,parameter:r}=e;const{name:o,style:s,explode:i,content:a}=r;if(a){const e=Object.keys(a)[0];return void(t.url=t.url.split(`{${o}}`).join(st(CE(n,e),{escape:!0})))}const l=it({key:r.name,value:n,style:s||"simple",explode:i||!1,escape:!0});t.url=t.url.split(`{${o}}`).join(l)}function NE(e){let{req:t,value:n,parameter:r}=e;if(t.query=t.query||{},r.content){const e=CE(n,Object.keys(r.content)[0]);if(e)t.query[r.name]=e;else if(r.allowEmptyValue&&void 0!==n){const e=r.name;t.query[e]=t.query[e]||{},t.query[e].allowEmptyValue=!0}}else if(!1===n&&(n="false"),0===n&&(n="0"),n){const{style:e,explode:o,allowReserved:s}=r;t.query[r.name]={value:n,serializationOption:{style:e,explode:o,allowReserved:s}}}else if(r.allowEmptyValue&&void 0!==n){const e=r.name;t.query[e]=t.query[e]||{},t.query[e].allowEmptyValue=!0}}const IE=["accept","authorization","content-type"];function TE(e){let{req:t,parameter:n,value:r}=e;if(t.headers=t.headers||{},!(IE.indexOf(n.name.toLowerCase())>-1))if(n.content){const e=Object.keys(n.content)[0];t.headers[n.name]=CE(r,e)}else void 0!==r&&(t.headers[n.name]=it({key:n.name,value:r,style:n.style||"simple",explode:void 0!==n.explode&&n.explode,escape:!1}))}function RE(e){let{req:t,parameter:n,value:r}=e;t.headers=t.headers||{};const o=typeof r;if(n.content){const e=Object.keys(n.content)[0];t.headers.Cookie=`${n.name}=${CE(r,e)}`}else if("undefined"!==o){const e="object"===o&&!Array.isArray(r)&&n.explode?"":`${n.name}=`;t.headers.Cookie=e+it({key:n.name,value:r,escape:!1,style:n.style||"form",explode:void 0!==n.explode&&n.explode})}}const ME="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:window,{btoa:DE}=ME,FE=DE;function LE(e,t){const{operation:n,requestBody:r,securities:o,spec:s,attachContentTypeForEmptyPayload:i}=e;let{requestContentType:a}=e;t=function(e){let{request:t,securities:n={},operation:r={},spec:o}=e;const s=f()({},t),{authorized:i={}}=n,a=r.security||o.security||[],l=i&&!!Object.keys(i).length,c=_E()(o,["components","securitySchemes"])||{};if(s.headers=s.headers||{},s.query=s.query||{},!Object.keys(n).length||!l||!a||Array.isArray(r.security)&&!r.security.length)return t;return a.forEach((e=>{Object.keys(e).forEach((e=>{const t=i[e],n=c[e];if(!t)return;const r=t.value||t,{type:o}=n;if(t)if("apiKey"===o)"query"===n.in&&(s.query[n.name]=r),"header"===n.in&&(s.headers[n.name]=r),"cookie"===n.in&&(s.cookies[n.name]=r);else if("http"===o){if(/^basic$/i.test(n.scheme)){const e=r.username||"",t=r.password||"",n=FE(`${e}:${t}`);s.headers.Authorization=`Basic ${n}`}/^bearer$/i.test(n.scheme)&&(s.headers.Authorization=`Bearer ${r}`)}else if("oauth2"===o||"openIdConnect"===o){const e=t.token||{},r=e[n["x-tokenName"]||"access_token"];let o=e.token_type;o&&"bearer"!==o.toLowerCase()||(o="Bearer"),s.headers.Authorization=`${o} ${r}`}}))})),s}({request:t,securities:o,operation:n,spec:s});const l=n.requestBody||{},c=Object.keys(l.content||{}),u=a&&c.indexOf(a)>-1;if(r||i){if(a&&u)t.headers["Content-Type"]=a;else if(!a){const e=c[0];e&&(t.headers["Content-Type"]=e,a=e)}}else a&&u&&(t.headers["Content-Type"]=a);if(!e.responseContentType&&n.responses){const e=Object.entries(n.responses).filter((e=>{let[t,n]=e;const r=parseInt(t,10);return r>=200&&r<300&&kE(n.content)})).reduce(((e,t)=>{let[,n]=t;return e.concat(Object.keys(n.content))}),[]);e.length>0&&(t.headers.accept=e.join(", "))}if(r)if(a){if(c.indexOf(a)>-1)if("application/x-www-form-urlencoded"===a||"multipart/form-data"===a)if("object"==typeof r){const e=l.content[a]?.encoding??{};t.form={},Object.keys(r).forEach((n=>{t.form[n]={value:r[n],encoding:e[n]||{}}}))}else t.form=r;else t.body=r}else t.body=r;return t}function BE(e,t){const{spec:n,operation:r,securities:o,requestContentType:s,responseContentType:i,attachContentTypeForEmptyPayload:a}=e;if(t=function(e){let{request:t,securities:n={},operation:r={},spec:o}=e;const s=f()({},t),{authorized:i={},specSecurity:a=[]}=n,l=r.security||a,c=i&&!!Object.keys(i).length,u=o.securityDefinitions;if(s.headers=s.headers||{},s.query=s.query||{},!Object.keys(n).length||!c||!l||Array.isArray(r.security)&&!r.security.length)return t;return l.forEach((e=>{Object.keys(e).forEach((e=>{const t=i[e];if(!t)return;const{token:n}=t,r=t.value||t,o=u[e],{type:a}=o,l=o["x-tokenName"]||"access_token",c=n&&n[l];let p=n&&n.token_type;if(t)if("apiKey"===a){const e="query"===o.in?"query":"headers";s[e]=s[e]||{},s[e][o.name]=r}else if("basic"===a)if(r.header)s.headers.authorization=r.header;else{const e=r.username||"",t=r.password||"";r.base64=FE(`${e}:${t}`),s.headers.authorization=`Basic ${r.base64}`}else"oauth2"===a&&c&&(p=p&&"bearer"!==p.toLowerCase()?p:"Bearer",s.headers.authorization=`${p} ${c}`)}))})),s}({request:t,securities:o,operation:r,spec:n}),t.body||t.form||a)s?t.headers["Content-Type"]=s:Array.isArray(r.consumes)?[t.headers["Content-Type"]]=r.consumes:Array.isArray(n.consumes)?[t.headers["Content-Type"]]=n.consumes:r.parameters&&r.parameters.filter((e=>"file"===e.type)).length?t.headers["Content-Type"]="multipart/form-data":r.parameters&&r.parameters.filter((e=>"formData"===e.in)).length&&(t.headers["Content-Type"]="application/x-www-form-urlencoded");else if(s){const e=r.parameters&&r.parameters.filter((e=>"body"===e.in)).length>0,n=r.parameters&&r.parameters.filter((e=>"formData"===e.in)).length>0;(e||n)&&(t.headers["Content-Type"]=s)}return!i&&Array.isArray(r.produces)&&r.produces.length>0&&(t.headers.accept=r.produces.join(", ")),t}function $E(e,t){return`${t.toLowerCase()}-${e}`}const qE=["http","fetch","spec","operationId","pathName","method","parameters","securities"],UE=e=>Array.isArray(e)?e:[],zE=ue("OperationNotFoundError",(function(e,t,n){this.originalError=n,Object.assign(this,t||{})})),VE=(e,t)=>t.filter((t=>t.name===e)),WE=e=>{const t={};e.forEach((e=>{t[e.in]||(t[e.in]={}),t[e.in][e.name]=e}));const n=[];return Object.keys(t).forEach((e=>{Object.keys(t[e]).forEach((r=>{n.push(t[e][r])}))})),n},JE={buildRequest:HE};function KE(e){let{http:t,fetch:n,spec:r,operationId:o,pathName:s,method:i,parameters:a,securities:l}=e,c=xE()(e,qE);const u=t||n||ct;s&&i&&!o&&(o=$E(s,i));const p=JE.buildRequest(f()({spec:r,operationId:o,parameters:a,securities:l,http:u},c));return p.body&&(kE(p.body)||Array.isArray(p.body))&&(p.body=JSON.stringify(p.body)),u(p)}function HE(e){const{spec:t,operationId:n,responseContentType:r,scheme:o,requestInterceptor:s,responseInterceptor:i,contextUrl:a,userFetch:l,server:c,serverVariables:p,http:h,signal:d}=e;let{parameters:m,parameterBuilders:g}=e;const y=At(t);g||(g=y?u:AE);let v={url:"",credentials:h&&h.withCredentials?"include":"same-origin",headers:{},cookies:{}};d&&(v.signal=d),s&&(v.requestInterceptor=s),i&&(v.responseInterceptor=i),l&&(v.userFetch=l);const b=function(e,t){return e&&e.paths?function(e,t){return function(e,t,n){if(!e||"object"!=typeof e||!e.paths||"object"!=typeof e.paths)return null;const{paths:r}=e;for(const o in r)for(const s in r[o]){if("PARAMETERS"===s.toUpperCase())continue;const i=r[o][s];if(!i||"object"!=typeof i)continue;const a={spec:e,pathName:o,method:s.toUpperCase(),operation:i},l=t(a);if(n&&l)return a}}(e,t,!0)||null}(e,(e=>{let{pathName:n,method:r,operation:o}=e;if(!o||"object"!=typeof o)return!1;const s=o.operationId;return[(0,He.Z)(o,n,r),$E(n,r),s].some((e=>e&&e===t))})):null}(t,n);if(!b)throw new zE(`Operation ${n} not found`);const{operation:w={},method:E,pathName:x}=b;if(v.url+=function(e){const t=At(e.spec);return t?function(e){let{spec:t,pathName:n,method:r,server:o,contextUrl:s,serverVariables:i={}}=e;const a=_E()(t,["paths",n,(r||"").toLowerCase(),"servers"])||_E()(t,["paths",n,"servers"])||_E()(t,["servers"]);let l="",c=null;if(o&&a&&a.length){const e=a.map((e=>e.url));e.indexOf(o)>-1&&(l=o,c=a[e.indexOf(o)])}!l&&a&&a.length&&(l=a[0].url,[c]=a);if(l.indexOf("{")>-1){(function(e){const t=[],n=/{([^}]+)}/g;let r;for(;r=n.exec(e);)t.push(r[1]);return t})(l).forEach((e=>{if(c.variables&&c.variables[e]){const t=c.variables[e],n=i[e]||t.default,r=new RegExp(`{${e}}`,"g");l=l.replace(r,n)}}))}return function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"",t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";const n=e&&t?ce.parse(ce.resolve(t,e)):ce.parse(e),r=ce.parse(t),o=GE(n.protocol)||GE(r.protocol)||"",s=n.host||r.host,i=n.pathname||"";let a;a=o&&s?`${o}://${s+i}`:i;return"/"===a[a.length-1]?a.slice(0,-1):a}(l,s)}(e):function(e){let{spec:t,scheme:n,contextUrl:r=""}=e;const o=ce.parse(r),s=Array.isArray(t.schemes)?t.schemes[0]:null,i=n||s||GE(o.protocol)||"http",a=t.host||o.host||"",l=t.basePath||"";let c;c=i&&a?`${i}://${a+l}`:l;return"/"===c[c.length-1]?c.slice(0,-1):c}(e)}({spec:t,scheme:o,contextUrl:a,server:c,serverVariables:p,pathName:x,method:E}),!n)return delete v.cookies,v;v.url+=x,v.method=`${E}`.toUpperCase(),m=m||{};const S=t.paths[x]||{};r&&(v.headers.accept=r);const _=WE([].concat(UE(w.parameters)).concat(UE(S.parameters)));_.forEach((e=>{const n=g[e.in];let r;if("body"===e.in&&e.schema&&e.schema.properties&&(r=m),r=e&&e.name&&m[e.name],void 0===r?r=e&&e.name&&m[`${e.in}.${e.name}`]:VE(e.name,_).length>1&&console.warn(`Parameter '${e.name}' is ambiguous because the defined spec has more than one parameter with the name: '${e.name}' and the passed-in parameter values did not define an 'in' value.`),null!==r){if(void 0!==e.default&&void 0===r&&(r=e.default),void 0===r&&e.required&&!e.allowEmptyValue)throw new Error(`Required parameter ${e.name} is not provided`);if(y&&e.schema&&"object"===e.schema.type&&"string"==typeof r)try{r=JSON.parse(r)}catch(e){throw new Error("Could not parse object parameter value string as JSON")}n&&n({req:v,parameter:e,value:r,operation:w,spec:t})}}));const j=f()(f()({},e),{},{operation:w});if(v=y?LE(j,v):BE(j,v),v.cookies&&Object.keys(v.cookies).length){const e=Object.keys(v.cookies).reduce(((e,t)=>{const n=v.cookies[t];return e+(e?"&":"")+jE.serialize(t,n)}),"");v.headers.Cookie=e}return v.cookies&&delete v.cookies,Et(v),v}const GE=e=>e?e.replace(/\W/g,""):null;const ZE=e=>async function(t,n){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return async function(e,t){let n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const{returnEntireTree:r,baseDoc:o,requestInterceptor:s,responseInterceptor:i,parameterMacro:a,modelPropertyMacro:l,useCircularStructures:c,strategies:u}=n,p={spec:e,pathDiscriminator:t,baseDoc:o,requestInterceptor:s,responseInterceptor:i,parameterMacro:a,modelPropertyMacro:l,useCircularStructures:c,strategies:u},h=u.find((e=>e.match(p))).normalize(p),d=await wE(f()(f()({},p),{},{spec:h,allowMetaPatches:!0,skipNormalization:!0}));return!r&&Array.isArray(t)&&t.length&&(d.spec=_E()(d.spec,t)||null),d}(t,n,f()(f()({},e),r))};ZE({strategies:[Pt,Ct,jt]});var YE=n(34852);function XE(e){let{configs:t,getConfigs:n}=e;return{fn:{fetch:(r=ct,o=t.preFetch,s=t.postFetch,s=s||(e=>e),o=o||(e=>e),e=>("string"==typeof e&&(e={url:e}),lt.mergeInQueryOrForm(e),e=o(e),s(r(e)))),buildRequest:HE,execute:KE,resolve:bE({strategies:[vE,Pt,Ct,jt]}),resolveSubtree:async function(e,t){let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};const o=n(),s={modelPropertyMacro:o.modelPropertyMacro,parameterMacro:o.parameterMacro,requestInterceptor:o.requestInterceptor,responseInterceptor:o.responseInterceptor,strategies:[vE,Pt,Ct,jt]};return ZE(s)(e,t,r)},serializeRes:pt,opId:He.Z},statePlugins:{configs:{wrapActions:{loaded:YE.loaded}}}};var r,o,s}},98525:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>o});var r=n(90242);function o(){return{fn:{shallowEqualKeys:r.be}}}},48347:(e,t,n)=>{"use strict";n.r(t),n.d(t,{getDisplayName:()=>r});const r=e=>e.displayName||e.name||"Component"},73420:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>c});var r=n(35627),o=n.n(r),s=n(90242),i=n(11092),a=n(48347),l=n(60314);const c=e=>{let{getComponents:t,getStore:n,getSystem:r}=e;const c=(u=(0,i.getComponent)(r,n,t),(0,s.HP)(u,(function(){for(var e=arguments.length,t=new Array(e),n=0;n(0,l.Z)(e,(function(){for(var e=arguments.length,t=new Array(e),n=0;n{"use strict";n.r(t),n.d(t,{getComponent:()=>te,render:()=>ee,withMappedContainer:()=>Q});var r=n(23101),o=n.n(r),s=n(28222),i=n.n(s),a=n(67294),l=n(73935),c=n(97779),u=n(61688),p=n(52798);let h=function(e){e()};const f=()=>h,d=Symbol.for("react-redux-context"),m="undefined"!=typeof globalThis?globalThis:{};function g(){var e;if(!a.createContext)return{};const t=null!=(e=m[d])?e:m[d]=new Map;let n=t.get(a.createContext);return n||(n=a.createContext(null),t.set(a.createContext,n)),n}const y=g();let v=null;var b=n(87462),w=n(63366),E=n(8679),x=n.n(E),S=n(59864);const _=["initMapStateToProps","initMapDispatchToProps","initMergeProps"];function j(e,t,n,r,{areStatesEqual:o,areOwnPropsEqual:s,areStatePropsEqual:i}){let a,l,c,u,p,h=!1;function f(h,f){const d=!s(f,l),m=!o(h,a,f,l);return a=h,l=f,d&&m?(c=e(a,l),t.dependsOnOwnProps&&(u=t(r,l)),p=n(c,u,l),p):d?(e.dependsOnOwnProps&&(c=e(a,l)),t.dependsOnOwnProps&&(u=t(r,l)),p=n(c,u,l),p):m?function(){const t=e(a,l),r=!i(t,c);return c=t,r&&(p=n(c,u,l)),p}():p}return function(o,s){return h?f(o,s):(a=o,l=s,c=e(a,l),u=t(r,l),p=n(c,u,l),h=!0,p)}}function O(e){return function(t){const n=e(t);function r(){return n}return r.dependsOnOwnProps=!1,r}}function k(e){return e.dependsOnOwnProps?Boolean(e.dependsOnOwnProps):1!==e.length}function A(e,t){return function(t,{displayName:n}){const r=function(e,t){return r.dependsOnOwnProps?r.mapToProps(e,t):r.mapToProps(e,void 0)};return r.dependsOnOwnProps=!0,r.mapToProps=function(t,n){r.mapToProps=e,r.dependsOnOwnProps=k(e);let o=r(t,n);return"function"==typeof o&&(r.mapToProps=o,r.dependsOnOwnProps=k(o),o=r(t,n)),o},r}}function C(e,t){return(n,r)=>{throw new Error(`Invalid value of type ${typeof e} for ${t} argument when connecting component ${r.wrappedComponentName}.`)}}function P(e,t,n){return(0,b.Z)({},n,e,t)}const N={notify(){},get:()=>[]};function I(e,t){let n,r=N;function o(){i.onStateChange&&i.onStateChange()}function s(){n||(n=t?t.addNestedSub(o):e.subscribe(o),r=function(){const e=f();let t=null,n=null;return{clear(){t=null,n=null},notify(){e((()=>{let e=t;for(;e;)e.callback(),e=e.next}))},get(){let e=[],n=t;for(;n;)e.push(n),n=n.next;return e},subscribe(e){let r=!0,o=n={callback:e,next:null,prev:n};return o.prev?o.prev.next=o:t=o,function(){r&&null!==t&&(r=!1,o.next?o.next.prev=o.prev:n=o.prev,o.prev?o.prev.next=o.next:t=o.next)}}}}())}const i={addNestedSub:function(e){return s(),r.subscribe(e)},notifyNestedSubs:function(){r.notify()},handleChangeWrapper:o,isSubscribed:function(){return Boolean(n)},trySubscribe:s,tryUnsubscribe:function(){n&&(n(),n=void 0,r.clear(),r=N)},getListeners:()=>r};return i}const T=!("undefined"==typeof window||void 0===window.document||void 0===window.document.createElement)?a.useLayoutEffect:a.useEffect;function R(e,t){return e===t?0!==e||0!==t||1/e==1/t:e!=e&&t!=t}function M(e,t){if(R(e,t))return!0;if("object"!=typeof e||null===e||"object"!=typeof t||null===t)return!1;const n=Object.keys(e),r=Object.keys(t);if(n.length!==r.length)return!1;for(let r=0;r{throw new Error("uSES not initialized!")};const L=[null,null];function B(e,t,n,r,o,s){e.current=r,n.current=!1,o.current&&(o.current=null,s())}function $(e,t){return e===t}const q=function(e,t,n,{pure:r,areStatesEqual:o=$,areOwnPropsEqual:s=M,areStatePropsEqual:i=M,areMergedPropsEqual:l=M,forwardRef:c=!1,context:u=y}={}){const p=u,h=function(e){return e?"function"==typeof e?A(e):C(e,"mapStateToProps"):O((()=>({})))}(e),f=function(e){return e&&"object"==typeof e?O((t=>function(e,t){const n={};for(const r in e){const o=e[r];"function"==typeof o&&(n[r]=(...e)=>t(o(...e)))}return n}(e,t))):e?"function"==typeof e?A(e):C(e,"mapDispatchToProps"):O((e=>({dispatch:e})))}(t),d=function(e){return e?"function"==typeof e?function(e){return function(t,{displayName:n,areMergedPropsEqual:r}){let o,s=!1;return function(t,n,i){const a=e(t,n,i);return s?r(a,o)||(o=a):(s=!0,o=a),o}}}(e):C(e,"mergeProps"):()=>P}(n),m=Boolean(e);return e=>{const t=e.displayName||e.name||"Component",n=`Connect(${t})`,r={shouldHandleStateChanges:m,displayName:n,wrappedComponentName:t,WrappedComponent:e,initMapStateToProps:h,initMapDispatchToProps:f,initMergeProps:d,areStatesEqual:o,areStatePropsEqual:i,areOwnPropsEqual:s,areMergedPropsEqual:l};function u(t){const[n,o,s]=a.useMemo((()=>{const{reactReduxForwardedRef:e}=t,n=(0,w.Z)(t,D);return[t.context,e,n]}),[t]),i=a.useMemo((()=>n&&n.Consumer&&(0,S.isContextConsumer)(a.createElement(n.Consumer,null))?n:p),[n,p]),l=a.useContext(i),c=Boolean(t.store)&&Boolean(t.store.getState)&&Boolean(t.store.dispatch),u=Boolean(l)&&Boolean(l.store);const h=c?t.store:l.store,f=u?l.getServerState:h.getState,d=a.useMemo((()=>function(e,t){let{initMapStateToProps:n,initMapDispatchToProps:r,initMergeProps:o}=t,s=(0,w.Z)(t,_);return j(n(e,s),r(e,s),o(e,s),e,s)}(h.dispatch,r)),[h]),[g,y]=a.useMemo((()=>{if(!m)return L;const e=I(h,c?void 0:l.subscription),t=e.notifyNestedSubs.bind(e);return[e,t]}),[h,c,l]),v=a.useMemo((()=>c?l:(0,b.Z)({},l,{subscription:g})),[c,l,g]),E=a.useRef(),x=a.useRef(s),O=a.useRef(),k=a.useRef(!1),A=(a.useRef(!1),a.useRef(!1)),C=a.useRef();T((()=>(A.current=!0,()=>{A.current=!1})),[]);const P=a.useMemo((()=>()=>O.current&&s===x.current?O.current:d(h.getState(),s)),[h,s]),N=a.useMemo((()=>e=>g?function(e,t,n,r,o,s,i,a,l,c,u){if(!e)return()=>{};let p=!1,h=null;const f=()=>{if(p||!a.current)return;const e=t.getState();let n,f;try{n=r(e,o.current)}catch(e){f=e,h=e}f||(h=null),n===s.current?i.current||c():(s.current=n,l.current=n,i.current=!0,u())};return n.onStateChange=f,n.trySubscribe(),f(),()=>{if(p=!0,n.tryUnsubscribe(),n.onStateChange=null,h)throw h}}(m,h,g,d,x,E,k,A,O,y,e):()=>{}),[g]);var R,M,$;let q;R=B,M=[x,E,k,s,O,y],T((()=>R(...M)),$);try{q=F(N,P,f?()=>d(f(),s):P)}catch(e){throw C.current&&(e.message+=`\nThe error may be correlated with this previous error:\n${C.current.stack}\n\n`),e}T((()=>{C.current=void 0,O.current=void 0,E.current=q}));const U=a.useMemo((()=>a.createElement(e,(0,b.Z)({},q,{ref:o}))),[o,e,q]);return a.useMemo((()=>m?a.createElement(i.Provider,{value:v},U):U),[i,U,v])}const g=a.memo(u);if(g.WrappedComponent=e,g.displayName=u.displayName=n,c){const t=a.forwardRef((function(e,t){return a.createElement(g,(0,b.Z)({},e,{reactReduxForwardedRef:t}))}));return t.displayName=n,t.WrappedComponent=e,x()(t,e)}return x()(g,e)}};const U=function({store:e,context:t,children:n,serverState:r,stabilityCheck:o="once",noopCheck:s="once"}){const i=a.useMemo((()=>{const t=I(e);return{store:e,subscription:t,getServerState:r?()=>r:void 0,stabilityCheck:o,noopCheck:s}}),[e,r,o,s]),l=a.useMemo((()=>e.getState()),[e]);T((()=>{const{subscription:t}=i;return t.onStateChange=t.notifyNestedSubs,t.trySubscribe(),l!==e.getState()&&t.notifyNestedSubs(),()=>{t.tryUnsubscribe(),t.onStateChange=void 0}}),[i,l]);const c=t||y;return a.createElement(c.Provider,{value:i},n)};var z,V;z=p.useSyncExternalStoreWithSelector,v=z,(e=>{F=e})(u.useSyncExternalStore),V=l.unstable_batchedUpdates,h=V;var W=n(57557),J=n.n(W),K=n(6557),H=n.n(K);const G=e=>t=>{const{fn:n}=e();class r extends a.Component{render(){return a.createElement(t,o()({},e(),this.props,this.context))}}return r.displayName=`WithSystem(${n.getDisplayName(t)})`,r},Z=(e,t)=>n=>{const{fn:r}=e();class s extends a.Component{render(){return a.createElement(U,{store:t},a.createElement(n,o()({},this.props,this.context)))}}return s.displayName=`WithRoot(${r.getDisplayName(n)})`,s},Y=(e,t,n)=>(0,c.qC)(n?Z(e,n):H(),q(((n,r)=>{var o;const s={...r,...e()},i=(null===(o=t.prototype)||void 0===o?void 0:o.mapStateToProps)||(e=>({state:e}));return i(n,s)})),G(e))(t),X=(e,t,n,r)=>{for(const o in t){const s=t[o];"function"==typeof s&&s(n[o],r[o],e())}},Q=(e,t,n)=>(t,r)=>{const{fn:o}=e(),s=n(t,"root");class l extends a.Component{constructor(t,n){super(t,n),X(e,r,t,{})}UNSAFE_componentWillReceiveProps(t){X(e,r,t,this.props)}render(){const e=J()(this.props,r?i()(r):[]);return a.createElement(s,e)}}return l.displayName=`WithMappedContainer(${o.getDisplayName(s)})`,l},ee=(e,t,n,r)=>o=>{const s=n(e,t,r)("App","root");l.render(a.createElement(s,null),o)},te=(e,t,n)=>function(r,o){let s=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if("string"!=typeof r)throw new TypeError("Need a string, to fetch a component. Was given a "+typeof r);const i=n(r);return i?o?"root"===o?Y(e,i,t()):Y(e,i):i:(s.failSilently||e().log.warn("Could not find component:",r),null)}},96513:(e,t,n)=>{"use strict";n.d(t,{d3:()=>D,C2:()=>ee});var r=n(28222),o=n.n(r),s=n(58118),i=n.n(s),a=n(63366);function l(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n1&&void 0!==arguments[1]?arguments[1]:{},n=arguments.length>2?arguments[2]:void 0;return function(e){if(0===e.length||1===e.length)return e;var t,n,r=e.join(".");return m[r]||(m[r]=0===(n=(t=e).length)||1===n?t:2===n?[t[0],t[1],"".concat(t[0],".").concat(t[1]),"".concat(t[1],".").concat(t[0])]:3===n?[t[0],t[1],t[2],"".concat(t[0],".").concat(t[1]),"".concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[0]),"".concat(t[1],".").concat(t[2]),"".concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[1]),"".concat(t[0],".").concat(t[1],".").concat(t[2]),"".concat(t[0],".").concat(t[2],".").concat(t[1]),"".concat(t[1],".").concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[0],".").concat(t[1]),"".concat(t[2],".").concat(t[1],".").concat(t[0])]:n>=4?[t[0],t[1],t[2],t[3],"".concat(t[0],".").concat(t[1]),"".concat(t[0],".").concat(t[2]),"".concat(t[0],".").concat(t[3]),"".concat(t[1],".").concat(t[0]),"".concat(t[1],".").concat(t[2]),"".concat(t[1],".").concat(t[3]),"".concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[1]),"".concat(t[2],".").concat(t[3]),"".concat(t[3],".").concat(t[0]),"".concat(t[3],".").concat(t[1]),"".concat(t[3],".").concat(t[2]),"".concat(t[0],".").concat(t[1],".").concat(t[2]),"".concat(t[0],".").concat(t[1],".").concat(t[3]),"".concat(t[0],".").concat(t[2],".").concat(t[1]),"".concat(t[0],".").concat(t[2],".").concat(t[3]),"".concat(t[0],".").concat(t[3],".").concat(t[1]),"".concat(t[0],".").concat(t[3],".").concat(t[2]),"".concat(t[1],".").concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[0],".").concat(t[3]),"".concat(t[1],".").concat(t[2],".").concat(t[0]),"".concat(t[1],".").concat(t[2],".").concat(t[3]),"".concat(t[1],".").concat(t[3],".").concat(t[0]),"".concat(t[1],".").concat(t[3],".").concat(t[2]),"".concat(t[2],".").concat(t[0],".").concat(t[1]),"".concat(t[2],".").concat(t[0],".").concat(t[3]),"".concat(t[2],".").concat(t[1],".").concat(t[0]),"".concat(t[2],".").concat(t[1],".").concat(t[3]),"".concat(t[2],".").concat(t[3],".").concat(t[0]),"".concat(t[2],".").concat(t[3],".").concat(t[1]),"".concat(t[3],".").concat(t[0],".").concat(t[1]),"".concat(t[3],".").concat(t[0],".").concat(t[2]),"".concat(t[3],".").concat(t[1],".").concat(t[0]),"".concat(t[3],".").concat(t[1],".").concat(t[2]),"".concat(t[3],".").concat(t[2],".").concat(t[0]),"".concat(t[3],".").concat(t[2],".").concat(t[1]),"".concat(t[0],".").concat(t[1],".").concat(t[2],".").concat(t[3]),"".concat(t[0],".").concat(t[1],".").concat(t[3],".").concat(t[2]),"".concat(t[0],".").concat(t[2],".").concat(t[1],".").concat(t[3]),"".concat(t[0],".").concat(t[2],".").concat(t[3],".").concat(t[1]),"".concat(t[0],".").concat(t[3],".").concat(t[1],".").concat(t[2]),"".concat(t[0],".").concat(t[3],".").concat(t[2],".").concat(t[1]),"".concat(t[1],".").concat(t[0],".").concat(t[2],".").concat(t[3]),"".concat(t[1],".").concat(t[0],".").concat(t[3],".").concat(t[2]),"".concat(t[1],".").concat(t[2],".").concat(t[0],".").concat(t[3]),"".concat(t[1],".").concat(t[2],".").concat(t[3],".").concat(t[0]),"".concat(t[1],".").concat(t[3],".").concat(t[0],".").concat(t[2]),"".concat(t[1],".").concat(t[3],".").concat(t[2],".").concat(t[0]),"".concat(t[2],".").concat(t[0],".").concat(t[1],".").concat(t[3]),"".concat(t[2],".").concat(t[0],".").concat(t[3],".").concat(t[1]),"".concat(t[2],".").concat(t[1],".").concat(t[0],".").concat(t[3]),"".concat(t[2],".").concat(t[1],".").concat(t[3],".").concat(t[0]),"".concat(t[2],".").concat(t[3],".").concat(t[0],".").concat(t[1]),"".concat(t[2],".").concat(t[3],".").concat(t[1],".").concat(t[0]),"".concat(t[3],".").concat(t[0],".").concat(t[1],".").concat(t[2]),"".concat(t[3],".").concat(t[0],".").concat(t[2],".").concat(t[1]),"".concat(t[3],".").concat(t[1],".").concat(t[0],".").concat(t[2]),"".concat(t[3],".").concat(t[1],".").concat(t[2],".").concat(t[0]),"".concat(t[3],".").concat(t[2],".").concat(t[0],".").concat(t[1]),"".concat(t[3],".").concat(t[2],".").concat(t[1],".").concat(t[0])]:void 0),m[r]}(e.filter((function(e){return"token"!==e}))).reduce((function(e,t){return d(d({},e),n[t])}),t)}function y(e){return e.join(" ")}function v(e){var t=e.node,n=e.stylesheet,r=e.style,o=void 0===r?{}:r,s=e.useInlineStyles,i=e.key,a=t.properties,l=t.type,c=t.tagName,u=t.value;if("text"===l)return u;if(c){var f,m=function(e,t){var n=0;return function(r){return n+=1,r.map((function(r,o){return v({node:r,stylesheet:e,useInlineStyles:t,key:"code-segment-".concat(n,"-").concat(o)})}))}}(n,s);if(s){var b=Object.keys(n).reduce((function(e,t){return t.split(".").forEach((function(t){e.includes(t)||e.push(t)})),e}),[]),w=a.className&&a.className.includes("token")?["token"]:[],E=a.className&&w.concat(a.className.filter((function(e){return!b.includes(e)})));f=d(d({},a),{},{className:y(E)||void 0,style:g(a.className,Object.assign({},a.style,o),n)})}else f=d(d({},a),{},{className:y(a.className)});var x=m(t.children);return p.createElement(c,(0,h.Z)({key:i},f),x)}}const b=function(e,t){return-1!==e.listLanguages().indexOf(t)};var w=["language","children","style","customStyle","codeTagProps","useInlineStyles","showLineNumbers","showInlineLineNumbers","startingLineNumber","lineNumberContainerStyle","lineNumberStyle","wrapLines","wrapLongLines","lineProps","renderer","PreTag","CodeTag","code","astGenerator"];function E(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function x(e){for(var t=1;t1&&void 0!==arguments[1]?arguments[1]:[],n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],r=0;r2&&void 0!==arguments[2]?arguments[2]:[];return t||c.length>0?function(e,t){return k({children:e,lineNumber:t,lineNumberStyle:a,largestLineNumber:i,showInlineLineNumbers:o,lineProps:n,className:arguments.length>2&&void 0!==arguments[2]?arguments[2]:[],showLineNumbers:r,wrapLongLines:l})}(e,s,c):function(e,t){if(r&&t&&o){var n=O(a,t,i);e.unshift(j(t,n))}return e}(e,s)}for(var m=function(){var e=u[f],t=e.children[0].value;if(t.match(S)){var n=t.split("\n");n.forEach((function(t,o){var i=r&&p.length+s,a={type:"text",value:"".concat(t,"\n")};if(0===o){var l=d(u.slice(h+1,f).concat(k({children:[a],className:e.properties.className})),i);p.push(l)}else if(o===n.length-1){var c=u[f+1]&&u[f+1].children&&u[f+1].children[0],m={type:"text",value:"".concat(t)};if(c){var g=k({children:[m],className:e.properties.className});u.splice(f+1,0,g)}else{var y=d([m],i,e.properties.className);p.push(y)}}else{var v=d([a],i,e.properties.className);p.push(v)}})),h=f}f++};f=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}(e,w);z=z||I;var W=d?p.createElement(_,{containerStyle:E,codeStyle:c.style||{},numberStyle:j,startingLineNumber:v,codeString:U}):null,J=o.hljs||o['pre[class*="language-"]']||{backgroundColor:"#fff"},K=N(z)?"hljs":"prismjs",H=h?Object.assign({},V,{style:Object.assign({},J,i)}):Object.assign({},V,{className:V.className?"".concat(K," ").concat(V.className):K,style:Object.assign({},i)});if(c.style=x(x({},c.style),{},A?{whiteSpace:"pre-wrap"}:{whiteSpace:"pre"}),!z)return p.createElement(L,H,W,p.createElement($,c,U));(void 0===O&&D||A)&&(O=!0),D=D||P;var G=[{type:"text",value:U}],Z=function(e){var t=e.astGenerator,n=e.language,r=e.code,o=e.defaultCodeValue;if(N(t)){var s=b(t,n);return"text"===n?{value:o,language:"text"}:s?t.highlight(n,r):t.highlightAuto(r)}try{return n&&"text"!==n?{value:t.highlight(r,n)}:{value:o}}catch(e){return{value:o}}}({astGenerator:z,language:t,code:U,defaultCodeValue:G});null===Z.language&&(Z.value=G);var Y=C(Z,O,M,d,g,v,Z.value.length+v,j,A);return p.createElement(L,H,p.createElement($,c,!g&&W,D({rows:Y,stylesheet:o,useInlineStyles:h})))});M.registerLanguage=R.registerLanguage;const D=M;var F=n(96344);const L=n.n(F)();var B=n(82026);const $=n.n(B)();var q=n(42157);const U=n.n(q)();var z=n(61519);const V=n.n(z)();var W=n(54587);const J=n.n(W)();var K=n(30786);const H=n.n(K)();var G=n(66336);const Z=n.n(G)(),Y={hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#333",color:"white"},"hljs-name":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-code":{fontStyle:"italic",color:"#888"},"hljs-emphasis":{fontStyle:"italic"},"hljs-tag":{color:"#62c8f3"},"hljs-variable":{color:"#ade5fc"},"hljs-template-variable":{color:"#ade5fc"},"hljs-selector-id":{color:"#ade5fc"},"hljs-selector-class":{color:"#ade5fc"},"hljs-string":{color:"#a2fca2"},"hljs-bullet":{color:"#d36363"},"hljs-type":{color:"#ffa"},"hljs-title":{color:"#ffa"},"hljs-section":{color:"#ffa"},"hljs-attribute":{color:"#ffa"},"hljs-quote":{color:"#ffa"},"hljs-built_in":{color:"#ffa"},"hljs-builtin-name":{color:"#ffa"},"hljs-number":{color:"#d36363"},"hljs-symbol":{color:"#d36363"},"hljs-keyword":{color:"#fcc28c"},"hljs-selector-tag":{color:"#fcc28c"},"hljs-literal":{color:"#fcc28c"},"hljs-comment":{color:"#888"},"hljs-deletion":{color:"#333",backgroundColor:"#fc9b9b"},"hljs-regexp":{color:"#c6b4f0"},"hljs-link":{color:"#c6b4f0"},"hljs-meta":{color:"#fc9b9b"},"hljs-addition":{backgroundColor:"#a2fca2",color:"#333"}};D.registerLanguage("json",$),D.registerLanguage("js",L),D.registerLanguage("xml",U),D.registerLanguage("yaml",J),D.registerLanguage("http",H),D.registerLanguage("bash",V),D.registerLanguage("powershell",Z),D.registerLanguage("javascript",L);const X={agate:Y,arta:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#222",color:"#aaa"},"hljs-subst":{color:"#aaa"},"hljs-section":{color:"#fff",fontWeight:"bold"},"hljs-comment":{color:"#444"},"hljs-quote":{color:"#444"},"hljs-meta":{color:"#444"},"hljs-string":{color:"#ffcc33"},"hljs-symbol":{color:"#ffcc33"},"hljs-bullet":{color:"#ffcc33"},"hljs-regexp":{color:"#ffcc33"},"hljs-number":{color:"#00cc66"},"hljs-addition":{color:"#00cc66"},"hljs-built_in":{color:"#32aaee"},"hljs-builtin-name":{color:"#32aaee"},"hljs-literal":{color:"#32aaee"},"hljs-type":{color:"#32aaee"},"hljs-template-variable":{color:"#32aaee"},"hljs-attribute":{color:"#32aaee"},"hljs-link":{color:"#32aaee"},"hljs-keyword":{color:"#6644aa"},"hljs-selector-tag":{color:"#6644aa"},"hljs-name":{color:"#6644aa"},"hljs-selector-id":{color:"#6644aa"},"hljs-selector-class":{color:"#6644aa"},"hljs-title":{color:"#bb1166"},"hljs-variable":{color:"#bb1166"},"hljs-deletion":{color:"#bb1166"},"hljs-template-tag":{color:"#bb1166"},"hljs-doctag":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"},"hljs-emphasis":{fontStyle:"italic"}},monokai:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#272822",color:"#ddd"},"hljs-tag":{color:"#f92672"},"hljs-keyword":{color:"#f92672",fontWeight:"bold"},"hljs-selector-tag":{color:"#f92672",fontWeight:"bold"},"hljs-literal":{color:"#f92672",fontWeight:"bold"},"hljs-strong":{color:"#f92672"},"hljs-name":{color:"#f92672"},"hljs-code":{color:"#66d9ef"},"hljs-class .hljs-title":{color:"white"},"hljs-attribute":{color:"#bf79db"},"hljs-symbol":{color:"#bf79db"},"hljs-regexp":{color:"#bf79db"},"hljs-link":{color:"#bf79db"},"hljs-string":{color:"#a6e22e"},"hljs-bullet":{color:"#a6e22e"},"hljs-subst":{color:"#a6e22e"},"hljs-title":{color:"#a6e22e",fontWeight:"bold"},"hljs-section":{color:"#a6e22e",fontWeight:"bold"},"hljs-emphasis":{color:"#a6e22e"},"hljs-type":{color:"#a6e22e",fontWeight:"bold"},"hljs-built_in":{color:"#a6e22e"},"hljs-builtin-name":{color:"#a6e22e"},"hljs-selector-attr":{color:"#a6e22e"},"hljs-selector-pseudo":{color:"#a6e22e"},"hljs-addition":{color:"#a6e22e"},"hljs-variable":{color:"#a6e22e"},"hljs-template-tag":{color:"#a6e22e"},"hljs-template-variable":{color:"#a6e22e"},"hljs-comment":{color:"#75715e"},"hljs-quote":{color:"#75715e"},"hljs-deletion":{color:"#75715e"},"hljs-meta":{color:"#75715e"},"hljs-doctag":{fontWeight:"bold"},"hljs-selector-id":{fontWeight:"bold"}},nord:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#2E3440",color:"#D8DEE9"},"hljs-subst":{color:"#D8DEE9"},"hljs-selector-tag":{color:"#81A1C1"},"hljs-selector-id":{color:"#8FBCBB",fontWeight:"bold"},"hljs-selector-class":{color:"#8FBCBB"},"hljs-selector-attr":{color:"#8FBCBB"},"hljs-selector-pseudo":{color:"#88C0D0"},"hljs-addition":{backgroundColor:"rgba(163, 190, 140, 0.5)"},"hljs-deletion":{backgroundColor:"rgba(191, 97, 106, 0.5)"},"hljs-built_in":{color:"#8FBCBB"},"hljs-type":{color:"#8FBCBB"},"hljs-class":{color:"#8FBCBB"},"hljs-function":{color:"#88C0D0"},"hljs-function > .hljs-title":{color:"#88C0D0"},"hljs-keyword":{color:"#81A1C1"},"hljs-literal":{color:"#81A1C1"},"hljs-symbol":{color:"#81A1C1"},"hljs-number":{color:"#B48EAD"},"hljs-regexp":{color:"#EBCB8B"},"hljs-string":{color:"#A3BE8C"},"hljs-title":{color:"#8FBCBB"},"hljs-params":{color:"#D8DEE9"},"hljs-bullet":{color:"#81A1C1"},"hljs-code":{color:"#8FBCBB"},"hljs-emphasis":{fontStyle:"italic"},"hljs-formula":{color:"#8FBCBB"},"hljs-strong":{fontWeight:"bold"},"hljs-link:hover":{textDecoration:"underline"},"hljs-quote":{color:"#4C566A"},"hljs-comment":{color:"#4C566A"},"hljs-doctag":{color:"#8FBCBB"},"hljs-meta":{color:"#5E81AC"},"hljs-meta-keyword":{color:"#5E81AC"},"hljs-meta-string":{color:"#A3BE8C"},"hljs-attr":{color:"#8FBCBB"},"hljs-attribute":{color:"#D8DEE9"},"hljs-builtin-name":{color:"#81A1C1"},"hljs-name":{color:"#81A1C1"},"hljs-section":{color:"#88C0D0"},"hljs-tag":{color:"#81A1C1"},"hljs-variable":{color:"#D8DEE9"},"hljs-template-variable":{color:"#D8DEE9"},"hljs-template-tag":{color:"#5E81AC"},"abnf .hljs-attribute":{color:"#88C0D0"},"abnf .hljs-symbol":{color:"#EBCB8B"},"apache .hljs-attribute":{color:"#88C0D0"},"apache .hljs-section":{color:"#81A1C1"},"arduino .hljs-built_in":{color:"#88C0D0"},"aspectj .hljs-meta":{color:"#D08770"},"aspectj > .hljs-title":{color:"#88C0D0"},"bnf .hljs-attribute":{color:"#8FBCBB"},"clojure .hljs-name":{color:"#88C0D0"},"clojure .hljs-symbol":{color:"#EBCB8B"},"coq .hljs-built_in":{color:"#88C0D0"},"cpp .hljs-meta-string":{color:"#8FBCBB"},"css .hljs-built_in":{color:"#88C0D0"},"css .hljs-keyword":{color:"#D08770"},"diff .hljs-meta":{color:"#8FBCBB"},"ebnf .hljs-attribute":{color:"#8FBCBB"},"glsl .hljs-built_in":{color:"#88C0D0"},"groovy .hljs-meta:not(:first-child)":{color:"#D08770"},"haxe .hljs-meta":{color:"#D08770"},"java .hljs-meta":{color:"#D08770"},"ldif .hljs-attribute":{color:"#8FBCBB"},"lisp .hljs-name":{color:"#88C0D0"},"lua .hljs-built_in":{color:"#88C0D0"},"moonscript .hljs-built_in":{color:"#88C0D0"},"nginx .hljs-attribute":{color:"#88C0D0"},"nginx .hljs-section":{color:"#5E81AC"},"pf .hljs-built_in":{color:"#88C0D0"},"processing .hljs-built_in":{color:"#88C0D0"},"scss .hljs-keyword":{color:"#81A1C1"},"stylus .hljs-keyword":{color:"#81A1C1"},"swift .hljs-meta":{color:"#D08770"},"vim .hljs-built_in":{color:"#88C0D0",fontStyle:"italic"},"yaml .hljs-meta":{color:"#D08770"}},obsidian:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",background:"#282b2e",color:"#e0e2e4"},"hljs-keyword":{color:"#93c763",fontWeight:"bold"},"hljs-selector-tag":{color:"#93c763",fontWeight:"bold"},"hljs-literal":{color:"#93c763",fontWeight:"bold"},"hljs-selector-id":{color:"#93c763"},"hljs-number":{color:"#ffcd22"},"hljs-attribute":{color:"#668bb0"},"hljs-code":{color:"white"},"hljs-class .hljs-title":{color:"white"},"hljs-section":{color:"white",fontWeight:"bold"},"hljs-regexp":{color:"#d39745"},"hljs-link":{color:"#d39745"},"hljs-meta":{color:"#557182"},"hljs-tag":{color:"#8cbbad"},"hljs-name":{color:"#8cbbad",fontWeight:"bold"},"hljs-bullet":{color:"#8cbbad"},"hljs-subst":{color:"#8cbbad"},"hljs-emphasis":{color:"#8cbbad"},"hljs-type":{color:"#8cbbad",fontWeight:"bold"},"hljs-built_in":{color:"#8cbbad"},"hljs-selector-attr":{color:"#8cbbad"},"hljs-selector-pseudo":{color:"#8cbbad"},"hljs-addition":{color:"#8cbbad"},"hljs-variable":{color:"#8cbbad"},"hljs-template-tag":{color:"#8cbbad"},"hljs-template-variable":{color:"#8cbbad"},"hljs-string":{color:"#ec7600"},"hljs-symbol":{color:"#ec7600"},"hljs-comment":{color:"#818e96"},"hljs-quote":{color:"#818e96"},"hljs-deletion":{color:"#818e96"},"hljs-selector-class":{color:"#A082BD"},"hljs-doctag":{fontWeight:"bold"},"hljs-title":{fontWeight:"bold"},"hljs-strong":{fontWeight:"bold"}},"tomorrow-night":{"hljs-comment":{color:"#969896"},"hljs-quote":{color:"#969896"},"hljs-variable":{color:"#cc6666"},"hljs-template-variable":{color:"#cc6666"},"hljs-tag":{color:"#cc6666"},"hljs-name":{color:"#cc6666"},"hljs-selector-id":{color:"#cc6666"},"hljs-selector-class":{color:"#cc6666"},"hljs-regexp":{color:"#cc6666"},"hljs-deletion":{color:"#cc6666"},"hljs-number":{color:"#de935f"},"hljs-built_in":{color:"#de935f"},"hljs-builtin-name":{color:"#de935f"},"hljs-literal":{color:"#de935f"},"hljs-type":{color:"#de935f"},"hljs-params":{color:"#de935f"},"hljs-meta":{color:"#de935f"},"hljs-link":{color:"#de935f"},"hljs-attribute":{color:"#f0c674"},"hljs-string":{color:"#b5bd68"},"hljs-symbol":{color:"#b5bd68"},"hljs-bullet":{color:"#b5bd68"},"hljs-addition":{color:"#b5bd68"},"hljs-title":{color:"#81a2be"},"hljs-section":{color:"#81a2be"},"hljs-keyword":{color:"#b294bb"},"hljs-selector-tag":{color:"#b294bb"},hljs:{display:"block",overflowX:"auto",background:"#1d1f21",color:"#c5c8c6",padding:"0.5em"},"hljs-emphasis":{fontStyle:"italic"},"hljs-strong":{fontWeight:"bold"}},idea:{hljs:{display:"block",overflowX:"auto",padding:"0.5em",color:"#000",background:"#fff"},"hljs-subst":{fontWeight:"normal",color:"#000"},"hljs-title":{fontWeight:"normal",color:"#000"},"hljs-comment":{color:"#808080",fontStyle:"italic"},"hljs-quote":{color:"#808080",fontStyle:"italic"},"hljs-meta":{color:"#808000"},"hljs-tag":{background:"#efefef"},"hljs-section":{fontWeight:"bold",color:"#000080"},"hljs-name":{fontWeight:"bold",color:"#000080"},"hljs-literal":{fontWeight:"bold",color:"#000080"},"hljs-keyword":{fontWeight:"bold",color:"#000080"},"hljs-selector-tag":{fontWeight:"bold",color:"#000080"},"hljs-type":{fontWeight:"bold",color:"#000080"},"hljs-selector-id":{fontWeight:"bold",color:"#000080"},"hljs-selector-class":{fontWeight:"bold",color:"#000080"},"hljs-attribute":{fontWeight:"bold",color:"#0000ff"},"hljs-number":{fontWeight:"normal",color:"#0000ff"},"hljs-regexp":{fontWeight:"normal",color:"#0000ff"},"hljs-link":{fontWeight:"normal",color:"#0000ff"},"hljs-string":{color:"#008000",fontWeight:"bold"},"hljs-symbol":{color:"#000",background:"#d0eded",fontStyle:"italic"},"hljs-bullet":{color:"#000",background:"#d0eded",fontStyle:"italic"},"hljs-formula":{color:"#000",background:"#d0eded",fontStyle:"italic"},"hljs-doctag":{textDecoration:"underline"},"hljs-variable":{color:"#660e7a"},"hljs-template-variable":{color:"#660e7a"},"hljs-addition":{background:"#baeeba"},"hljs-deletion":{background:"#ffc8bd"},"hljs-emphasis":{fontStyle:"italic"},"hljs-strong":{fontWeight:"bold"}}},Q=o()(X),ee=e=>i()(Q).call(Q,e)?X[e]:(console.warn(`Request style '${e}' is not available, returning default instead`),Y)},90242:(e,t,n)=>{"use strict";n.d(t,{AF:()=>ae,Ay:()=>fe,D$:()=>De,DR:()=>ve,GZ:()=>je,HP:()=>he,Ik:()=>Ee,J6:()=>Ne,Kn:()=>ce,LQ:()=>le,Nm:()=>ke,O2:()=>Ue,Pz:()=>Me,Q2:()=>de,QG:()=>Ce,UG:()=>xe,Uj:()=>Be,V9:()=>Fe,Wl:()=>ue,XV:()=>Re,Xb:()=>$e,Zl:()=>be,_5:()=>me,be:()=>Oe,cz:()=>Le,gp:()=>ye,hW:()=>Ae,iQ:()=>ge,kJ:()=>pe,mz:()=>se,nX:()=>Ie,oG:()=>ie,oJ:()=>Pe,po:()=>Te,r3:()=>Se,wh:()=>_e});var r=n(58309),o=n.n(r),s=n(97606),i=n.n(s),a=n(74386),l=n.n(a),c=n(86),u=n.n(c),p=n(14418),h=n.n(p),f=n(28222),d=n.n(f),m=(n(11189),n(24282)),g=n.n(m),y=n(76986),v=n.n(y),b=n(2578),w=n.n(b),E=(n(24278),n(39022),n(92039)),x=n.n(E),S=(n(58118),n(11882)),_=n.n(S),j=n(51679),O=n.n(j),k=n(27043),A=n.n(k),C=n(81607),P=n.n(C),N=n(35627),I=n.n(N),T=n(43393),R=n.n(T),M=n(17967),D=n(68929),F=n.n(D),L=n(11700),B=n.n(L),$=n(88306),q=n.n($),U=n(13311),z=n.n(U),V=(n(59704),n(77813)),W=n.n(V),J=n(23560),K=n.n(J),H=n(27504),G=n(8269),Z=n.n(G),Y=n(19069),X=n(92282),Q=n.n(X),ee=n(89072),te=n.n(ee),ne=n(48764).Buffer;const re="default",oe=e=>R().Iterable.isIterable(e);function se(e){return ce(e)?oe(e)?e.toJS():e:{}}function ie(e){var t,n;if(oe(e))return e;if(e instanceof H.Z.File)return e;if(!ce(e))return e;if(o()(e))return i()(n=R().Seq(e)).call(n,ie).toList();if(K()(l()(e))){var r;const t=function(e){if(!K()(l()(e)))return e;const t={},n="_**[]",r={};for(let o of l()(e).call(e))if(t[o[0]]||r[o[0]]&&r[o[0]].containsMultiple){if(!r[o[0]]){r[o[0]]={containsMultiple:!0,length:1},t[`${o[0]}${n}${r[o[0]].length}`]=t[o[0]],delete t[o[0]]}r[o[0]].length+=1,t[`${o[0]}${n}${r[o[0]].length}`]=o[1]}else t[o[0]]=o[1];return t}(e);return i()(r=R().OrderedMap(t)).call(r,ie)}return i()(t=R().OrderedMap(e)).call(t,ie)}function ae(e){return o()(e)?e:[e]}function le(e){return"function"==typeof e}function ce(e){return!!e&&"object"==typeof e}function ue(e){return"function"==typeof e}function pe(e){return o()(e)}const he=q();function fe(e,t){var n;return g()(n=d()(e)).call(n,((n,r)=>(n[r]=t(e[r],r),n)),{})}function de(e,t){var n;return g()(n=d()(e)).call(n,((n,r)=>{let o=t(e[r],r);return o&&"object"==typeof o&&v()(n,o),n}),{})}function me(e){return t=>{let{dispatch:n,getState:r}=t;return t=>n=>"function"==typeof n?n(e()):t(n)}}function ge(e){var t;let n=e.keySeq();return n.contains(re)?re:w()(t=h()(n).call(n,(e=>"2"===(e+"")[0]))).call(t).first()}function ye(e,t){if(!R().Iterable.isIterable(e))return R().List();let n=e.getIn(o()(t)?t:[t]);return R().List.isList(n)?n:R().List()}function ve(e){let t,n=[/filename\*=[^']+'\w*'"([^"]+)";?/i,/filename\*=[^']+'\w*'([^;]+);?/i,/filename="([^;]*);?"/i,/filename=([^;]*);?/i];if(x()(n).call(n,(n=>(t=n.exec(e),null!==t))),null!==t&&t.length>1)try{return decodeURIComponent(t[1])}catch(e){console.error(e)}return null}function be(e){return t=e.replace(/\.[^./]*$/,""),B()(F()(t));var t}function we(e,t,n,r,s){if(!t)return[];let a=[],l=t.get("nullable"),c=t.get("required"),p=t.get("maximum"),f=t.get("minimum"),d=t.get("type"),m=t.get("format"),g=t.get("maxLength"),y=t.get("minLength"),v=t.get("uniqueItems"),b=t.get("maxItems"),w=t.get("minItems"),E=t.get("pattern");const S=n||!0===c,_=null!=e;if(l&&null===e||!d||!(S||_&&"array"===d||!(!S&&!_)))return[];let j="string"===d&&e,O="array"===d&&o()(e)&&e.length,k="array"===d&&R().List.isList(e)&&e.count();const A=[j,O,k,"array"===d&&"string"==typeof e&&e,"file"===d&&e instanceof H.Z.File,"boolean"===d&&(e||!1===e),"number"===d&&(e||0===e),"integer"===d&&(e||0===e),"object"===d&&"object"==typeof e&&null!==e,"object"===d&&"string"==typeof e&&e],C=x()(A).call(A,(e=>!!e));if(S&&!C&&!r)return a.push("Required field is not provided"),a;if("object"===d&&(null===s||"application/json"===s)){let n=e;if("string"==typeof e)try{n=JSON.parse(e)}catch(e){return a.push("Parameter string value must be valid JSON"),a}var P;if(t&&t.has("required")&&ue(c.isList)&&c.isList()&&u()(c).call(c,(e=>{void 0===n[e]&&a.push({propKey:e,error:"Required property not found"})})),t&&t.has("properties"))u()(P=t.get("properties")).call(P,((e,t)=>{const o=we(n[t],e,!1,r,s);a.push(...i()(o).call(o,(e=>({propKey:t,error:e}))))}))}if(E){let t=((e,t)=>{if(!new RegExp(t).test(e))return"Value must follow pattern "+t})(e,E);t&&a.push(t)}if(w&&"array"===d){let t=((e,t)=>{if(!e&&t>=1||e&&e.length{if(e&&e.length>t)return`Array must not contain more then ${t} item${1===t?"":"s"}`})(e,b);t&&a.push({needRemove:!0,error:t})}if(v&&"array"===d){let t=((e,t)=>{if(e&&("true"===t||!0===t)){const t=(0,T.fromJS)(e),n=t.toSet();if(e.length>n.size){let e=(0,T.Set)();if(u()(t).call(t,((n,r)=>{h()(t).call(t,(e=>ue(e.equals)?e.equals(n):e===n)).size>1&&(e=e.add(r))})),0!==e.size)return i()(e).call(e,(e=>({index:e,error:"No duplicates allowed."}))).toArray()}}})(e,v);t&&a.push(...t)}if(g||0===g){let t=((e,t)=>{if(e.length>t)return`Value must be no longer than ${t} character${1!==t?"s":""}`})(e,g);t&&a.push(t)}if(y){let t=((e,t)=>{if(e.length{if(e>t)return`Value must be less than ${t}`})(e,p);t&&a.push(t)}if(f||0===f){let t=((e,t)=>{if(e{if(isNaN(Date.parse(e)))return"Value must be a DateTime"})(e):"uuid"===m?(e=>{if(e=e.toString().toLowerCase(),!/^[{(]?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}[)}]?$/.test(e))return"Value must be a Guid"})(e):(e=>{if(e&&"string"!=typeof e)return"Value must be a string"})(e),!t)return a;a.push(t)}else if("boolean"===d){let t=(e=>{if("true"!==e&&"false"!==e&&!0!==e&&!1!==e)return"Value must be a boolean"})(e);if(!t)return a;a.push(t)}else if("number"===d){let t=(e=>{if(!/^-?\d+(\.?\d+)?$/.test(e))return"Value must be a number"})(e);if(!t)return a;a.push(t)}else if("integer"===d){let t=(e=>{if(!/^-?\d+$/.test(e))return"Value must be an integer"})(e);if(!t)return a;a.push(t)}else if("array"===d){if(!O&&!k)return a;e&&u()(e).call(e,((e,n)=>{const o=we(e,t.get("items"),!1,r,s);a.push(...i()(o).call(o,(e=>({index:n,error:e}))))}))}else if("file"===d){let t=(e=>{if(e&&!(e instanceof H.Z.File))return"Value must be a file"})(e);if(!t)return a;a.push(t)}return a}const Ee=function(e,t){let{isOAS3:n=!1,bypassRequiredCheck:r=!1}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},o=e.get("required"),{schema:s,parameterContentMediaType:i}=(0,Y.Z)(e,{isOAS3:n});return we(t,s,o,r,i)},xe=()=>{let e={},t=H.Z.location.search;if(!t)return{};if(""!=t){let n=t.substr(1).split("&");for(let t in n)Object.prototype.hasOwnProperty.call(n,t)&&(t=n[t].split("="),e[decodeURIComponent(t[0])]=t[1]&&decodeURIComponent(t[1])||"")}return e},Se=e=>{let t;return t=e instanceof ne?e:ne.from(e.toString(),"utf-8"),t.toString("base64")},_e={operationsSorter:{alpha:(e,t)=>e.get("path").localeCompare(t.get("path")),method:(e,t)=>e.get("method").localeCompare(t.get("method"))},tagsSorter:{alpha:(e,t)=>e.localeCompare(t)}},je=e=>{let t=[];for(let n in e){let r=e[n];void 0!==r&&""!==r&&t.push([n,"=",encodeURIComponent(r).replace(/%20/g,"+")].join(""))}return t.join("&")},Oe=(e,t,n)=>!!z()(n,(n=>W()(e[n],t[n])));function ke(e){return"string"!=typeof e||""===e?"":(0,M.Nm)(e)}function Ae(e){return!(!e||_()(e).call(e,"localhost")>=0||_()(e).call(e,"127.0.0.1")>=0||"none"===e)}function Ce(e){if(!R().OrderedMap.isOrderedMap(e))return null;if(!e.size)return null;const t=O()(e).call(e,((e,t)=>A()(t).call(t,"2")&&d()(e.get("content")||{}).length>0)),n=e.get("default")||R().OrderedMap(),r=(n.get("content")||R().OrderedMap()).keySeq().toJS().length?n:null;return t||r}const Pe=e=>"string"==typeof e||e instanceof String?P()(e).call(e).replace(/\s/g,"%20"):"",Ne=e=>Z()(Pe(e).replace(/%20/g,"_")),Ie=e=>h()(e).call(e,((e,t)=>/^x-/.test(t))),Te=e=>h()(e).call(e,((e,t)=>/^pattern|maxLength|minLength|maximum|minimum/.test(t)));function Re(e,t){var n;let r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:()=>!0;if("object"!=typeof e||o()(e)||null===e||!t)return e;const s=v()({},e);return u()(n=d()(s)).call(n,(e=>{e===t&&r(s[e],e)?delete s[e]:s[e]=Re(s[e],t,r)})),s}function Me(e){if("string"==typeof e)return e;if(e&&e.toJS&&(e=e.toJS()),"object"==typeof e&&null!==e)try{return I()(e,null,2)}catch(t){return String(e)}return null==e?"":e.toString()}function De(e){return"number"==typeof e?e.toString():e}function Fe(e){let{returnAll:t=!1,allowHashes:n=!0}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!R().Map.isMap(e))throw new Error("paramToIdentifier: received a non-Im.Map parameter as input");const r=e.get("name"),o=e.get("in");let s=[];return e&&e.hashCode&&o&&r&&n&&s.push(`${o}.${r}.hash-${e.hashCode()}`),o&&r&&s.push(`${o}.${r}`),s.push(r),t?s:s[0]||""}function Le(e,t){var n;const r=Fe(e,{returnAll:!0});return h()(n=i()(r).call(r,(e=>t[e]))).call(n,(e=>void 0!==e))[0]}function Be(){return qe(Q()(32).toString("base64"))}function $e(e){return qe(te()("sha256").update(e).digest("base64"))}function qe(e){return e.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}const Ue=e=>!e||!(!oe(e)||!e.isEmpty())},2518:(e,t,n)=>{"use strict";function r(e){return function(e){try{return!!JSON.parse(e)}catch(e){return null}}(e)?"json":null}n.d(t,{O:()=>r})},63543:(e,t,n)=>{"use strict";n.d(t,{mn:()=>a});var r=n(63460),o=n.n(r);function s(e){return e.match(/^(?:[a-z]+:)?\/\//i)}function i(e,t){return e?s(e)?function(e){return e.match(/^\/\//i)?`${window.location.protocol}${e}`:e}(e):new(o())(e,t).href:t}function a(e,t){let{selectedServer:n=""}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};try{return function(e,t){let{selectedServer:n=""}=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};if(!e)return;if(s(e))return e;const r=i(n,t);return s(r)?new(o())(e,r).href:new(o())(e,window.location.href).href}(e,t,{selectedServer:n})}catch{return}}},27504:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});const r=function(){var e={location:{},history:{},open:()=>{},close:()=>{},File:function(){},FormData:function(){}};if("undefined"==typeof window)return e;try{e=window;for(var t of["File","Blob","FormData"])t in window&&(e[t]=window[t])}catch(e){console.error(e)}return e}()},19069:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var r=n(14418),o=n.n(r),s=n(58118),i=n.n(s),a=n(43393),l=n.n(a);const c=l().Set.of("type","format","items","default","maximum","exclusiveMaximum","minimum","exclusiveMinimum","maxLength","minLength","pattern","maxItems","minItems","uniqueItems","enum","multipleOf");function u(e){let{isOAS3:t}=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!l().Map.isMap(e))return{schema:l().Map(),parameterContentMediaType:null};if(!t)return"body"===e.get("in")?{schema:e.get("schema",l().Map()),parameterContentMediaType:null}:{schema:o()(e).call(e,((e,t)=>i()(c).call(c,t))),parameterContentMediaType:null};if(e.get("content")){const t=e.get("content",l().Map({})).keySeq().first();return{schema:e.getIn(["content",t,"schema"],l().Map()),parameterContentMediaType:t}}return{schema:e.get("schema")?e.get("schema",l().Map()):l().Map(),parameterContentMediaType:null}}},60314:(e,t,n)=>{"use strict";n.d(t,{Z:()=>x});var r=n(58309),o=n.n(r),s=n(2250),i=n.n(s),a=n(25110),l=n.n(a),c=n(8712),u=n.n(c),p=n(51679),h=n.n(p),f=n(12373),d=n.n(f),m=n(18492),g=n.n(m),y=n(88306),v=n.n(y);const b=e=>t=>o()(e)&&o()(t)&&e.length===t.length&&i()(e).call(e,((e,n)=>e===t[n])),w=function(){for(var e=arguments.length,t=new Array(e),n=0;n1&&void 0!==arguments[1]?arguments[1]:w;const{Cache:n}=v();v().Cache=E;const r=v()(e,t);return v().Cache=n,r}},79742:(e,t)=>{"use strict";t.byteLength=function(e){var t=a(e),n=t[0],r=t[1];return 3*(n+r)/4-r},t.toByteArray=function(e){var t,n,s=a(e),i=s[0],l=s[1],c=new o(function(e,t,n){return 3*(t+n)/4-n}(0,i,l)),u=0,p=l>0?i-4:i;for(n=0;n>16&255,c[u++]=t>>8&255,c[u++]=255&t;2===l&&(t=r[e.charCodeAt(n)]<<2|r[e.charCodeAt(n+1)]>>4,c[u++]=255&t);1===l&&(t=r[e.charCodeAt(n)]<<10|r[e.charCodeAt(n+1)]<<4|r[e.charCodeAt(n+2)]>>2,c[u++]=t>>8&255,c[u++]=255&t);return c},t.fromByteArray=function(e){for(var t,r=e.length,o=r%3,s=[],i=16383,a=0,c=r-o;ac?c:a+i));1===o?(t=e[r-1],s.push(n[t>>2]+n[t<<4&63]+"==")):2===o&&(t=(e[r-2]<<8)+e[r-1],s.push(n[t>>10]+n[t>>4&63]+n[t<<2&63]+"="));return s.join("")};for(var n=[],r=[],o="undefined"!=typeof Uint8Array?Uint8Array:Array,s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",i=0;i<64;++i)n[i]=s[i],r[s.charCodeAt(i)]=i;function a(e){var t=e.length;if(t%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var n=e.indexOf("=");return-1===n&&(n=t),[n,n===t?0:4-n%4]}function l(e,t,r){for(var o,s,i=[],a=t;a>18&63]+n[s>>12&63]+n[s>>6&63]+n[63&s]);return i.join("")}r["-".charCodeAt(0)]=62,r["_".charCodeAt(0)]=63},48764:(e,t,n)=>{"use strict";const r=n(79742),o=n(80645),s="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=l,t.SlowBuffer=function(e){+e!=e&&(e=0);return l.alloc(+e)},t.INSPECT_MAX_BYTES=50;const i=2147483647;function a(e){if(e>i)throw new RangeError('The value "'+e+'" is invalid for option "size"');const t=new Uint8Array(e);return Object.setPrototypeOf(t,l.prototype),t}function l(e,t,n){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return p(e)}return c(e,t,n)}function c(e,t,n){if("string"==typeof e)return function(e,t){"string"==typeof t&&""!==t||(t="utf8");if(!l.isEncoding(t))throw new TypeError("Unknown encoding: "+t);const n=0|m(e,t);let r=a(n);const o=r.write(e,t);o!==n&&(r=r.slice(0,o));return r}(e,t);if(ArrayBuffer.isView(e))return function(e){if(G(e,Uint8Array)){const t=new Uint8Array(e);return f(t.buffer,t.byteOffset,t.byteLength)}return h(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e);if(G(e,ArrayBuffer)||e&&G(e.buffer,ArrayBuffer))return f(e,t,n);if("undefined"!=typeof SharedArrayBuffer&&(G(e,SharedArrayBuffer)||e&&G(e.buffer,SharedArrayBuffer)))return f(e,t,n);if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');const r=e.valueOf&&e.valueOf();if(null!=r&&r!==e)return l.from(r,t,n);const o=function(e){if(l.isBuffer(e)){const t=0|d(e.length),n=a(t);return 0===n.length||e.copy(n,0,0,t),n}if(void 0!==e.length)return"number"!=typeof e.length||Z(e.length)?a(0):h(e);if("Buffer"===e.type&&Array.isArray(e.data))return h(e.data)}(e);if(o)return o;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return l.from(e[Symbol.toPrimitive]("string"),t,n);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+typeof e)}function u(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function p(e){return u(e),a(e<0?0:0|d(e))}function h(e){const t=e.length<0?0:0|d(e.length),n=a(t);for(let r=0;r=i)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+i.toString(16)+" bytes");return 0|e}function m(e,t){if(l.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||G(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+typeof e);const n=e.length,r=arguments.length>2&&!0===arguments[2];if(!r&&0===n)return 0;let o=!1;for(;;)switch(t){case"ascii":case"latin1":case"binary":return n;case"utf8":case"utf-8":return J(e).length;case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return 2*n;case"hex":return n>>>1;case"base64":return K(e).length;default:if(o)return r?-1:J(e).length;t=(""+t).toLowerCase(),o=!0}}function g(e,t,n){let r=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===n||n>this.length)&&(n=this.length),n<=0)return"";if((n>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return P(this,t,n);case"utf8":case"utf-8":return O(this,t,n);case"ascii":return A(this,t,n);case"latin1":case"binary":return C(this,t,n);case"base64":return j(this,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return N(this,t,n);default:if(r)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),r=!0}}function y(e,t,n){const r=e[t];e[t]=e[n],e[n]=r}function v(e,t,n,r,o){if(0===e.length)return-1;if("string"==typeof n?(r=n,n=0):n>2147483647?n=2147483647:n<-2147483648&&(n=-2147483648),Z(n=+n)&&(n=o?0:e.length-1),n<0&&(n=e.length+n),n>=e.length){if(o)return-1;n=e.length-1}else if(n<0){if(!o)return-1;n=0}if("string"==typeof t&&(t=l.from(t,r)),l.isBuffer(t))return 0===t.length?-1:b(e,t,n,r,o);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?o?Uint8Array.prototype.indexOf.call(e,t,n):Uint8Array.prototype.lastIndexOf.call(e,t,n):b(e,[t],n,r,o);throw new TypeError("val must be string, number or Buffer")}function b(e,t,n,r,o){let s,i=1,a=e.length,l=t.length;if(void 0!==r&&("ucs2"===(r=String(r).toLowerCase())||"ucs-2"===r||"utf16le"===r||"utf-16le"===r)){if(e.length<2||t.length<2)return-1;i=2,a/=2,l/=2,n/=2}function c(e,t){return 1===i?e[t]:e.readUInt16BE(t*i)}if(o){let r=-1;for(s=n;sa&&(n=a-l),s=n;s>=0;s--){let n=!0;for(let r=0;ro&&(r=o):r=o;const s=t.length;let i;for(r>s/2&&(r=s/2),i=0;i>8,o=n%256,s.push(o),s.push(r);return s}(t,e.length-n),e,n,r)}function j(e,t,n){return 0===t&&n===e.length?r.fromByteArray(e):r.fromByteArray(e.slice(t,n))}function O(e,t,n){n=Math.min(e.length,n);const r=[];let o=t;for(;o239?4:t>223?3:t>191?2:1;if(o+i<=n){let n,r,a,l;switch(i){case 1:t<128&&(s=t);break;case 2:n=e[o+1],128==(192&n)&&(l=(31&t)<<6|63&n,l>127&&(s=l));break;case 3:n=e[o+1],r=e[o+2],128==(192&n)&&128==(192&r)&&(l=(15&t)<<12|(63&n)<<6|63&r,l>2047&&(l<55296||l>57343)&&(s=l));break;case 4:n=e[o+1],r=e[o+2],a=e[o+3],128==(192&n)&&128==(192&r)&&128==(192&a)&&(l=(15&t)<<18|(63&n)<<12|(63&r)<<6|63&a,l>65535&&l<1114112&&(s=l))}}null===s?(s=65533,i=1):s>65535&&(s-=65536,r.push(s>>>10&1023|55296),s=56320|1023&s),r.push(s),o+=i}return function(e){const t=e.length;if(t<=k)return String.fromCharCode.apply(String,e);let n="",r=0;for(;rr.length?(l.isBuffer(t)||(t=l.from(t)),t.copy(r,o)):Uint8Array.prototype.set.call(r,t,o);else{if(!l.isBuffer(t))throw new TypeError('"list" argument must be an Array of Buffers');t.copy(r,o)}o+=t.length}return r},l.byteLength=m,l.prototype._isBuffer=!0,l.prototype.swap16=function(){const e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(let t=0;tn&&(e+=" ... "),""},s&&(l.prototype[s]=l.prototype.inspect),l.prototype.compare=function(e,t,n,r,o){if(G(e,Uint8Array)&&(e=l.from(e,e.offset,e.byteLength)),!l.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+typeof e);if(void 0===t&&(t=0),void 0===n&&(n=e?e.length:0),void 0===r&&(r=0),void 0===o&&(o=this.length),t<0||n>e.length||r<0||o>this.length)throw new RangeError("out of range index");if(r>=o&&t>=n)return 0;if(r>=o)return-1;if(t>=n)return 1;if(this===e)return 0;let s=(o>>>=0)-(r>>>=0),i=(n>>>=0)-(t>>>=0);const a=Math.min(s,i),c=this.slice(r,o),u=e.slice(t,n);for(let e=0;e>>=0,isFinite(n)?(n>>>=0,void 0===r&&(r="utf8")):(r=n,n=void 0)}const o=this.length-t;if((void 0===n||n>o)&&(n=o),e.length>0&&(n<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");r||(r="utf8");let s=!1;for(;;)switch(r){case"hex":return w(this,e,t,n);case"utf8":case"utf-8":return E(this,e,t,n);case"ascii":case"latin1":case"binary":return x(this,e,t,n);case"base64":return S(this,e,t,n);case"ucs2":case"ucs-2":case"utf16le":case"utf-16le":return _(this,e,t,n);default:if(s)throw new TypeError("Unknown encoding: "+r);r=(""+r).toLowerCase(),s=!0}},l.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};const k=4096;function A(e,t,n){let r="";n=Math.min(e.length,n);for(let o=t;or)&&(n=r);let o="";for(let r=t;rn)throw new RangeError("Trying to access beyond buffer length")}function T(e,t,n,r,o,s){if(!l.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>o||te.length)throw new RangeError("Index out of range")}function R(e,t,n,r,o){U(t,r,o,e,n,7);let s=Number(t&BigInt(4294967295));e[n++]=s,s>>=8,e[n++]=s,s>>=8,e[n++]=s,s>>=8,e[n++]=s;let i=Number(t>>BigInt(32)&BigInt(4294967295));return e[n++]=i,i>>=8,e[n++]=i,i>>=8,e[n++]=i,i>>=8,e[n++]=i,n}function M(e,t,n,r,o){U(t,r,o,e,n,7);let s=Number(t&BigInt(4294967295));e[n+7]=s,s>>=8,e[n+6]=s,s>>=8,e[n+5]=s,s>>=8,e[n+4]=s;let i=Number(t>>BigInt(32)&BigInt(4294967295));return e[n+3]=i,i>>=8,e[n+2]=i,i>>=8,e[n+1]=i,i>>=8,e[n]=i,n+8}function D(e,t,n,r,o,s){if(n+r>e.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("Index out of range")}function F(e,t,n,r,s){return t=+t,n>>>=0,s||D(e,0,n,4),o.write(e,t,n,r,23,4),n+4}function L(e,t,n,r,s){return t=+t,n>>>=0,s||D(e,0,n,8),o.write(e,t,n,r,52,8),n+8}l.prototype.slice=function(e,t){const n=this.length;(e=~~e)<0?(e+=n)<0&&(e=0):e>n&&(e=n),(t=void 0===t?n:~~t)<0?(t+=n)<0&&(t=0):t>n&&(t=n),t>>=0,t>>>=0,n||I(e,t,this.length);let r=this[e],o=1,s=0;for(;++s>>=0,t>>>=0,n||I(e,t,this.length);let r=this[e+--t],o=1;for(;t>0&&(o*=256);)r+=this[e+--t]*o;return r},l.prototype.readUint8=l.prototype.readUInt8=function(e,t){return e>>>=0,t||I(e,1,this.length),this[e]},l.prototype.readUint16LE=l.prototype.readUInt16LE=function(e,t){return e>>>=0,t||I(e,2,this.length),this[e]|this[e+1]<<8},l.prototype.readUint16BE=l.prototype.readUInt16BE=function(e,t){return e>>>=0,t||I(e,2,this.length),this[e]<<8|this[e+1]},l.prototype.readUint32LE=l.prototype.readUInt32LE=function(e,t){return e>>>=0,t||I(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},l.prototype.readUint32BE=l.prototype.readUInt32BE=function(e,t){return e>>>=0,t||I(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},l.prototype.readBigUInt64LE=X((function(e){z(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=t+256*this[++e]+65536*this[++e]+this[++e]*2**24,o=this[++e]+256*this[++e]+65536*this[++e]+n*2**24;return BigInt(r)+(BigInt(o)<>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=t*2**24+65536*this[++e]+256*this[++e]+this[++e],o=this[++e]*2**24+65536*this[++e]+256*this[++e]+n;return(BigInt(r)<>>=0,t>>>=0,n||I(e,t,this.length);let r=this[e],o=1,s=0;for(;++s=o&&(r-=Math.pow(2,8*t)),r},l.prototype.readIntBE=function(e,t,n){e>>>=0,t>>>=0,n||I(e,t,this.length);let r=t,o=1,s=this[e+--r];for(;r>0&&(o*=256);)s+=this[e+--r]*o;return o*=128,s>=o&&(s-=Math.pow(2,8*t)),s},l.prototype.readInt8=function(e,t){return e>>>=0,t||I(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},l.prototype.readInt16LE=function(e,t){e>>>=0,t||I(e,2,this.length);const n=this[e]|this[e+1]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt16BE=function(e,t){e>>>=0,t||I(e,2,this.length);const n=this[e+1]|this[e]<<8;return 32768&n?4294901760|n:n},l.prototype.readInt32LE=function(e,t){return e>>>=0,t||I(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},l.prototype.readInt32BE=function(e,t){return e>>>=0,t||I(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},l.prototype.readBigInt64LE=X((function(e){z(e>>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=this[e+4]+256*this[e+5]+65536*this[e+6]+(n<<24);return(BigInt(r)<>>=0,"offset");const t=this[e],n=this[e+7];void 0!==t&&void 0!==n||V(e,this.length-8);const r=(t<<24)+65536*this[++e]+256*this[++e]+this[++e];return(BigInt(r)<>>=0,t||I(e,4,this.length),o.read(this,e,!0,23,4)},l.prototype.readFloatBE=function(e,t){return e>>>=0,t||I(e,4,this.length),o.read(this,e,!1,23,4)},l.prototype.readDoubleLE=function(e,t){return e>>>=0,t||I(e,8,this.length),o.read(this,e,!0,52,8)},l.prototype.readDoubleBE=function(e,t){return e>>>=0,t||I(e,8,this.length),o.read(this,e,!1,52,8)},l.prototype.writeUintLE=l.prototype.writeUIntLE=function(e,t,n,r){if(e=+e,t>>>=0,n>>>=0,!r){T(this,e,t,n,Math.pow(2,8*n)-1,0)}let o=1,s=0;for(this[t]=255&e;++s>>=0,n>>>=0,!r){T(this,e,t,n,Math.pow(2,8*n)-1,0)}let o=n-1,s=1;for(this[t+o]=255&e;--o>=0&&(s*=256);)this[t+o]=e/s&255;return t+n},l.prototype.writeUint8=l.prototype.writeUInt8=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,1,255,0),this[t]=255&e,t+1},l.prototype.writeUint16LE=l.prototype.writeUInt16LE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeUint16BE=l.prototype.writeUInt16BE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeUint32LE=l.prototype.writeUInt32LE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},l.prototype.writeUint32BE=l.prototype.writeUInt32BE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigUInt64LE=X((function(e,t=0){return R(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeBigUInt64BE=X((function(e,t=0){return M(this,e,t,BigInt(0),BigInt("0xffffffffffffffff"))})),l.prototype.writeIntLE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);T(this,e,t,n,r-1,-r)}let o=0,s=1,i=0;for(this[t]=255&e;++o>0)-i&255;return t+n},l.prototype.writeIntBE=function(e,t,n,r){if(e=+e,t>>>=0,!r){const r=Math.pow(2,8*n-1);T(this,e,t,n,r-1,-r)}let o=n-1,s=1,i=0;for(this[t+o]=255&e;--o>=0&&(s*=256);)e<0&&0===i&&0!==this[t+o+1]&&(i=1),this[t+o]=(e/s>>0)-i&255;return t+n},l.prototype.writeInt8=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},l.prototype.writeInt16LE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},l.prototype.writeInt16BE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},l.prototype.writeInt32LE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},l.prototype.writeInt32BE=function(e,t,n){return e=+e,t>>>=0,n||T(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},l.prototype.writeBigInt64LE=X((function(e,t=0){return R(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeBigInt64BE=X((function(e,t=0){return M(this,e,t,-BigInt("0x8000000000000000"),BigInt("0x7fffffffffffffff"))})),l.prototype.writeFloatLE=function(e,t,n){return F(this,e,t,!0,n)},l.prototype.writeFloatBE=function(e,t,n){return F(this,e,t,!1,n)},l.prototype.writeDoubleLE=function(e,t,n){return L(this,e,t,!0,n)},l.prototype.writeDoubleBE=function(e,t,n){return L(this,e,t,!1,n)},l.prototype.copy=function(e,t,n,r){if(!l.isBuffer(e))throw new TypeError("argument should be a Buffer");if(n||(n=0),r||0===r||(r=this.length),t>=e.length&&(t=e.length),t||(t=0),r>0&&r=this.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("sourceEnd out of bounds");r>this.length&&(r=this.length),e.length-t>>=0,n=void 0===n?this.length:n>>>0,e||(e=0),"number"==typeof e)for(o=t;o=r+4;n-=3)t=`_${e.slice(n-3,n)}${t}`;return`${e.slice(0,n)}${t}`}function U(e,t,n,r,o,s){if(e>n||e3?0===t||t===BigInt(0)?`>= 0${r} and < 2${r} ** ${8*(s+1)}${r}`:`>= -(2${r} ** ${8*(s+1)-1}${r}) and < 2 ** ${8*(s+1)-1}${r}`:`>= ${t}${r} and <= ${n}${r}`,new B.ERR_OUT_OF_RANGE("value",o,e)}!function(e,t,n){z(t,"offset"),void 0!==e[t]&&void 0!==e[t+n]||V(t,e.length-(n+1))}(r,o,s)}function z(e,t){if("number"!=typeof e)throw new B.ERR_INVALID_ARG_TYPE(t,"number",e)}function V(e,t,n){if(Math.floor(e)!==e)throw z(e,n),new B.ERR_OUT_OF_RANGE(n||"offset","an integer",e);if(t<0)throw new B.ERR_BUFFER_OUT_OF_BOUNDS;throw new B.ERR_OUT_OF_RANGE(n||"offset",`>= ${n?1:0} and <= ${t}`,e)}$("ERR_BUFFER_OUT_OF_BOUNDS",(function(e){return e?`${e} is outside of buffer bounds`:"Attempt to access memory outside buffer bounds"}),RangeError),$("ERR_INVALID_ARG_TYPE",(function(e,t){return`The "${e}" argument must be of type number. Received type ${typeof t}`}),TypeError),$("ERR_OUT_OF_RANGE",(function(e,t,n){let r=`The value of "${e}" is out of range.`,o=n;return Number.isInteger(n)&&Math.abs(n)>2**32?o=q(String(n)):"bigint"==typeof n&&(o=String(n),(n>BigInt(2)**BigInt(32)||n<-(BigInt(2)**BigInt(32)))&&(o=q(o)),o+="n"),r+=` It must be ${t}. Received ${o}`,r}),RangeError);const W=/[^+/0-9A-Za-z-_]/g;function J(e,t){let n;t=t||1/0;const r=e.length;let o=null;const s=[];for(let i=0;i55295&&n<57344){if(!o){if(n>56319){(t-=3)>-1&&s.push(239,191,189);continue}if(i+1===r){(t-=3)>-1&&s.push(239,191,189);continue}o=n;continue}if(n<56320){(t-=3)>-1&&s.push(239,191,189),o=n;continue}n=65536+(o-55296<<10|n-56320)}else o&&(t-=3)>-1&&s.push(239,191,189);if(o=null,n<128){if((t-=1)<0)break;s.push(n)}else if(n<2048){if((t-=2)<0)break;s.push(n>>6|192,63&n|128)}else if(n<65536){if((t-=3)<0)break;s.push(n>>12|224,n>>6&63|128,63&n|128)}else{if(!(n<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;s.push(n>>18|240,n>>12&63|128,n>>6&63|128,63&n|128)}}return s}function K(e){return r.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(W,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function H(e,t,n,r){let o;for(o=0;o=t.length||o>=e.length);++o)t[o+n]=e[o];return o}function G(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function Z(e){return e!=e}const Y=function(){const e="0123456789abcdef",t=new Array(256);for(let n=0;n<16;++n){const r=16*n;for(let o=0;o<16;++o)t[r+o]=e[n]+e[o]}return t}();function X(e){return"undefined"==typeof BigInt?Q:e}function Q(){throw new Error("BigInt not supported")}},21924:(e,t,n)=>{"use strict";var r=n(40210),o=n(55559),s=o(r("String.prototype.indexOf"));e.exports=function(e,t){var n=r(e,!!t);return"function"==typeof n&&s(e,".prototype.")>-1?o(n):n}},55559:(e,t,n)=>{"use strict";var r=n(58612),o=n(40210),s=o("%Function.prototype.apply%"),i=o("%Function.prototype.call%"),a=o("%Reflect.apply%",!0)||r.call(i,s),l=o("%Object.getOwnPropertyDescriptor%",!0),c=o("%Object.defineProperty%",!0),u=o("%Math.max%");if(c)try{c({},"a",{value:1})}catch(e){c=null}e.exports=function(e){var t=a(r,i,arguments);l&&c&&(l(t,"length").configurable&&c(t,"length",{value:1+u(0,e.length-(arguments.length-1))}));return t};var p=function(){return a(r,s,arguments)};c?c(e.exports,"apply",{value:p}):e.exports.apply=p},94184:(e,t)=>{var n;!function(){"use strict";var r={}.hasOwnProperty;function o(){for(var e=[],t=0;t{"use strict";t.parse=function(e,t){if("string"!=typeof e)throw new TypeError("argument str must be a string");var n={},r=(t||{}).decode||o,s=0;for(;s{"use strict";var r=n(11742),o={"text/plain":"Text","text/html":"Url",default:"Text"};e.exports=function(e,t){var n,s,i,a,l,c,u=!1;t||(t={}),n=t.debug||!1;try{if(i=r(),a=document.createRange(),l=document.getSelection(),(c=document.createElement("span")).textContent=e,c.ariaHidden="true",c.style.all="unset",c.style.position="fixed",c.style.top=0,c.style.clip="rect(0, 0, 0, 0)",c.style.whiteSpace="pre",c.style.webkitUserSelect="text",c.style.MozUserSelect="text",c.style.msUserSelect="text",c.style.userSelect="text",c.addEventListener("copy",(function(r){if(r.stopPropagation(),t.format)if(r.preventDefault(),void 0===r.clipboardData){n&&console.warn("unable to use e.clipboardData"),n&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var s=o[t.format]||o.default;window.clipboardData.setData(s,e)}else r.clipboardData.clearData(),r.clipboardData.setData(t.format,e);t.onCopy&&(r.preventDefault(),t.onCopy(r.clipboardData))})),document.body.appendChild(c),a.selectNodeContents(c),l.addRange(a),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");u=!0}catch(r){n&&console.error("unable to copy using execCommand: ",r),n&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(t.format||"text",e),t.onCopy&&t.onCopy(window.clipboardData),u=!0}catch(r){n&&console.error("unable to copy using clipboardData: ",r),n&&console.error("falling back to prompt"),s=function(e){var t=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C";return e.replace(/#{\s*key\s*}/g,t)}("message"in t?t.message:"Copy to clipboard: #{key}, Enter"),window.prompt(s,e)}}finally{l&&("function"==typeof l.removeRange?l.removeRange(a):l.removeAllRanges()),c&&document.body.removeChild(c),i()}return u}},90093:(e,t,n)=>{var r=n(28196);e.exports=r},3688:(e,t,n)=>{var r=n(11955);e.exports=r},83838:(e,t,n)=>{var r=n(46279);e.exports=r},15684:(e,t,n)=>{var r=n(19373);e.exports=r},81331:(e,t,n)=>{var r=n(52759);e.exports=r},65362:(e,t,n)=>{var r=n(63383);e.exports=r},91254:(e,t,n)=>{var r=n(57396);e.exports=r},43536:(e,t,n)=>{var r=n(41910);e.exports=r},37331:(e,t,n)=>{var r=n(79427);e.exports=r},68522:(e,t,n)=>{var r=n(62857);e.exports=r},73151:(e,t,n)=>{var r=n(9534);e.exports=r},45012:(e,t,n)=>{var r=n(23059);e.exports=r},80281:(e,t,n)=>{var r=n(92547);n(97522),n(43975),n(45414),e.exports=r},40031:(e,t,n)=>{var r=n(46509);e.exports=r},17487:(e,t,n)=>{var r=n(35774);e.exports=r},54493:(e,t,n)=>{n(77971),n(53242);var r=n(54058);e.exports=r.Array.from},24034:(e,t,n)=>{n(92737);var r=n(54058);e.exports=r.Array.isArray},15367:(e,t,n)=>{n(85906);var r=n(35703);e.exports=r("Array").concat},12710:(e,t,n)=>{n(66274),n(55967);var r=n(35703);e.exports=r("Array").entries},51459:(e,t,n)=>{n(48851);var r=n(35703);e.exports=r("Array").every},6172:(e,t,n)=>{n(80290);var r=n(35703);e.exports=r("Array").fill},62383:(e,t,n)=>{n(21501);var r=n(35703);e.exports=r("Array").filter},60009:(e,t,n)=>{n(44929);var r=n(35703);e.exports=r("Array").findIndex},17671:(e,t,n)=>{n(80833);var r=n(35703);e.exports=r("Array").find},99324:(e,t,n)=>{n(2437);var r=n(35703);e.exports=r("Array").forEach},80991:(e,t,n)=>{n(97690);var r=n(35703);e.exports=r("Array").includes},8700:(e,t,n)=>{n(99076);var r=n(35703);e.exports=r("Array").indexOf},95909:(e,t,n)=>{n(66274),n(55967);var r=n(35703);e.exports=r("Array").keys},6442:(e,t,n)=>{n(75915);var r=n(35703);e.exports=r("Array").lastIndexOf},23866:(e,t,n)=>{n(68787);var r=n(35703);e.exports=r("Array").map},9896:(e,t,n)=>{n(48528);var r=n(35703);e.exports=r("Array").push},52999:(e,t,n)=>{n(81876);var r=n(35703);e.exports=r("Array").reduce},24900:(e,t,n)=>{n(60186);var r=n(35703);e.exports=r("Array").slice},3824:(e,t,n)=>{n(36026);var r=n(35703);e.exports=r("Array").some},2948:(e,t,n)=>{n(4115);var r=n(35703);e.exports=r("Array").sort},78209:(e,t,n)=>{n(98611);var r=n(35703);e.exports=r("Array").splice},14423:(e,t,n)=>{n(66274),n(55967);var r=n(35703);e.exports=r("Array").values},81103:(e,t,n)=>{n(95160);var r=n(54058);e.exports=r.Date.now},27700:(e,t,n)=>{n(73381);var r=n(35703);e.exports=r("Function").bind},16246:(e,t,n)=>{var r=n(7046),o=n(27700),s=Function.prototype;e.exports=function(e){var t=e.bind;return e===s||r(s,e)&&t===s.bind?o:t}},56043:(e,t,n)=>{var r=n(7046),o=n(15367),s=Array.prototype;e.exports=function(e){var t=e.concat;return e===s||r(s,e)&&t===s.concat?o:t}},13160:(e,t,n)=>{var r=n(7046),o=n(51459),s=Array.prototype;e.exports=function(e){var t=e.every;return e===s||r(s,e)&&t===s.every?o:t}},80446:(e,t,n)=>{var r=n(7046),o=n(6172),s=Array.prototype;e.exports=function(e){var t=e.fill;return e===s||r(s,e)&&t===s.fill?o:t}},2480:(e,t,n)=>{var r=n(7046),o=n(62383),s=Array.prototype;e.exports=function(e){var t=e.filter;return e===s||r(s,e)&&t===s.filter?o:t}},7147:(e,t,n)=>{var r=n(7046),o=n(60009),s=Array.prototype;e.exports=function(e){var t=e.findIndex;return e===s||r(s,e)&&t===s.findIndex?o:t}},32236:(e,t,n)=>{var r=n(7046),o=n(17671),s=Array.prototype;e.exports=function(e){var t=e.find;return e===s||r(s,e)&&t===s.find?o:t}},58557:(e,t,n)=>{var r=n(7046),o=n(80991),s=n(21631),i=Array.prototype,a=String.prototype;e.exports=function(e){var t=e.includes;return e===i||r(i,e)&&t===i.includes?o:"string"==typeof e||e===a||r(a,e)&&t===a.includes?s:t}},34570:(e,t,n)=>{var r=n(7046),o=n(8700),s=Array.prototype;e.exports=function(e){var t=e.indexOf;return e===s||r(s,e)&&t===s.indexOf?o:t}},57564:(e,t,n)=>{var r=n(7046),o=n(6442),s=Array.prototype;e.exports=function(e){var t=e.lastIndexOf;return e===s||r(s,e)&&t===s.lastIndexOf?o:t}},88287:(e,t,n)=>{var r=n(7046),o=n(23866),s=Array.prototype;e.exports=function(e){var t=e.map;return e===s||r(s,e)&&t===s.map?o:t}},93993:(e,t,n)=>{var r=n(7046),o=n(9896),s=Array.prototype;e.exports=function(e){var t=e.push;return e===s||r(s,e)&&t===s.push?o:t}},68025:(e,t,n)=>{var r=n(7046),o=n(52999),s=Array.prototype;e.exports=function(e){var t=e.reduce;return e===s||r(s,e)&&t===s.reduce?o:t}},59257:(e,t,n)=>{var r=n(7046),o=n(80454),s=String.prototype;e.exports=function(e){var t=e.repeat;return"string"==typeof e||e===s||r(s,e)&&t===s.repeat?o:t}},69601:(e,t,n)=>{var r=n(7046),o=n(24900),s=Array.prototype;e.exports=function(e){var t=e.slice;return e===s||r(s,e)&&t===s.slice?o:t}},28299:(e,t,n)=>{var r=n(7046),o=n(3824),s=Array.prototype;e.exports=function(e){var t=e.some;return e===s||r(s,e)&&t===s.some?o:t}},69355:(e,t,n)=>{var r=n(7046),o=n(2948),s=Array.prototype;e.exports=function(e){var t=e.sort;return e===s||r(s,e)&&t===s.sort?o:t}},18339:(e,t,n)=>{var r=n(7046),o=n(78209),s=Array.prototype;e.exports=function(e){var t=e.splice;return e===s||r(s,e)&&t===s.splice?o:t}},71611:(e,t,n)=>{var r=n(7046),o=n(3269),s=String.prototype;e.exports=function(e){var t=e.startsWith;return"string"==typeof e||e===s||r(s,e)&&t===s.startsWith?o:t}},62774:(e,t,n)=>{var r=n(7046),o=n(13348),s=String.prototype;e.exports=function(e){var t=e.trim;return"string"==typeof e||e===s||r(s,e)&&t===s.trim?o:t}},84426:(e,t,n)=>{n(32619);var r=n(54058),o=n(79730);r.JSON||(r.JSON={stringify:JSON.stringify}),e.exports=function(e,t,n){return o(r.JSON.stringify,null,arguments)}},91018:(e,t,n)=>{n(66274),n(37501),n(55967),n(77971);var r=n(54058);e.exports=r.Map},97849:(e,t,n)=>{n(54973),e.exports=Math.pow(2,-52)},3820:(e,t,n)=>{n(30800);var r=n(54058);e.exports=r.Number.isInteger},45999:(e,t,n)=>{n(49221);var r=n(54058);e.exports=r.Object.assign},7702:(e,t,n)=>{n(74979);var r=n(54058).Object,o=e.exports=function(e,t){return r.defineProperties(e,t)};r.defineProperties.sham&&(o.sham=!0)},48171:(e,t,n)=>{n(86450);var r=n(54058).Object,o=e.exports=function(e,t,n){return r.defineProperty(e,t,n)};r.defineProperty.sham&&(o.sham=!0)},73081:(e,t,n)=>{n(94366);var r=n(54058);e.exports=r.Object.entries},7699:(e,t,n)=>{n(66274),n(28387);var r=n(54058);e.exports=r.Object.fromEntries},286:(e,t,n)=>{n(46924);var r=n(54058).Object,o=e.exports=function(e,t){return r.getOwnPropertyDescriptor(e,t)};r.getOwnPropertyDescriptor.sham&&(o.sham=!0)},92766:(e,t,n)=>{n(88482);var r=n(54058);e.exports=r.Object.getOwnPropertyDescriptors},30498:(e,t,n)=>{n(35824);var r=n(54058);e.exports=r.Object.getOwnPropertySymbols},48494:(e,t,n)=>{n(21724);var r=n(54058);e.exports=r.Object.keys},98430:(e,t,n)=>{n(26614);var r=n(54058);e.exports=r.Object.values},52956:(e,t,n)=>{n(47627),n(66274),n(55967),n(98881),n(4560),n(91302),n(44349),n(77971);var r=n(54058);e.exports=r.Promise},76998:(e,t,n)=>{n(66274),n(55967),n(69008),n(77971);var r=n(54058);e.exports=r.Set},97089:(e,t,n)=>{n(74679);var r=n(54058);e.exports=r.String.raw},21631:(e,t,n)=>{n(11035);var r=n(35703);e.exports=r("String").includes},80454:(e,t,n)=>{n(60986);var r=n(35703);e.exports=r("String").repeat},3269:(e,t,n)=>{n(94761);var r=n(35703);e.exports=r("String").startsWith},13348:(e,t,n)=>{n(57398);var r=n(35703);e.exports=r("String").trim},57473:(e,t,n)=>{n(85906),n(55967),n(35824),n(8555),n(52615),n(21732),n(35903),n(1825),n(28394),n(45915),n(61766),n(62737),n(89911),n(74315),n(63131),n(64714),n(70659),n(69120),n(79413),n(1502);var r=n(54058);e.exports=r.Symbol},24227:(e,t,n)=>{n(66274),n(55967),n(77971),n(1825);var r=n(11477);e.exports=r.f("iterator")},62978:(e,t,n)=>{n(18084),n(63131);var r=n(11477);e.exports=r.f("toPrimitive")},32304:(e,t,n)=>{n(66274),n(55967),n(54334);var r=n(54058);e.exports=r.WeakMap},29567:(e,t,n)=>{n(66274),n(55967),n(1773);var r=n(54058);e.exports=r.WeakSet},14122:(e,t,n)=>{e.exports=n(89097)},44442:(e,t,n)=>{e.exports=n(51675)},57152:(e,t,n)=>{e.exports=n(82507)},69447:(e,t,n)=>{e.exports=n(628)},1449:(e,t,n)=>{e.exports=n(34501)},60269:(e,t,n)=>{e.exports=n(76936)},70573:(e,t,n)=>{e.exports=n(18180)},73685:(e,t,n)=>{e.exports=n(80621)},27533:(e,t,n)=>{e.exports=n(22948)},39057:(e,t,n)=>{e.exports=n(82108)},84710:(e,t,n)=>{e.exports=n(14058)},93799:(e,t,n)=>{e.exports=n(92093)},86600:(e,t,n)=>{e.exports=n(52201)},9759:(e,t,n)=>{e.exports=n(27398)},71384:(e,t,n)=>{e.exports=n(26189)},89097:(e,t,n)=>{var r=n(90093);e.exports=r},51675:(e,t,n)=>{var r=n(3688);e.exports=r},82507:(e,t,n)=>{var r=n(83838);e.exports=r},628:(e,t,n)=>{var r=n(15684);e.exports=r},34501:(e,t,n)=>{var r=n(81331);e.exports=r},76936:(e,t,n)=>{var r=n(65362);e.exports=r},18180:(e,t,n)=>{var r=n(91254);e.exports=r},80621:(e,t,n)=>{var r=n(43536);e.exports=r},22948:(e,t,n)=>{var r=n(37331);e.exports=r},82108:(e,t,n)=>{var r=n(68522);e.exports=r},14058:(e,t,n)=>{var r=n(73151);e.exports=r},92093:(e,t,n)=>{var r=n(45012);e.exports=r},52201:(e,t,n)=>{var r=n(80281);n(28783),n(97618),n(6989),n(65799),n(46774),n(22731),n(85605),n(31943),n(80620),n(36172),e.exports=r},27398:(e,t,n)=>{var r=n(40031);e.exports=r},26189:(e,t,n)=>{var r=n(17487);e.exports=r},24883:(e,t,n)=>{var r=n(57475),o=n(69826),s=TypeError;e.exports=function(e){if(r(e))return e;throw s(o(e)+" is not a function")}},174:(e,t,n)=>{var r=n(24284),o=n(69826),s=TypeError;e.exports=function(e){if(r(e))return e;throw s(o(e)+" is not a constructor")}},11851:(e,t,n)=>{var r=n(57475),o=String,s=TypeError;e.exports=function(e){if("object"==typeof e||r(e))return e;throw s("Can't set "+o(e)+" as a prototype")}},18479:e=>{e.exports=function(){}},5743:(e,t,n)=>{var r=n(7046),o=TypeError;e.exports=function(e,t){if(r(t,e))return e;throw o("Incorrect invocation")}},96059:(e,t,n)=>{var r=n(10941),o=String,s=TypeError;e.exports=function(e){if(r(e))return e;throw s(o(e)+" is not an object")}},97135:(e,t,n)=>{var r=n(95981);e.exports=r((function(){if("function"==typeof ArrayBuffer){var e=new ArrayBuffer(8);Object.isExtensible(e)&&Object.defineProperty(e,"a",{value:8})}}))},91860:(e,t,n)=>{"use strict";var r=n(89678),o=n(59413),s=n(10623);e.exports=function(e){for(var t=r(this),n=s(t),i=arguments.length,a=o(i>1?arguments[1]:void 0,n),l=i>2?arguments[2]:void 0,c=void 0===l?n:o(l,n);c>a;)t[a++]=e;return t}},56837:(e,t,n)=>{"use strict";var r=n(3610).forEach,o=n(34194)("forEach");e.exports=o?[].forEach:function(e){return r(this,e,arguments.length>1?arguments[1]:void 0)}},11354:(e,t,n)=>{"use strict";var r=n(86843),o=n(78834),s=n(89678),i=n(75196),a=n(6782),l=n(24284),c=n(10623),u=n(55449),p=n(53476),h=n(22902),f=Array;e.exports=function(e){var t=s(e),n=l(this),d=arguments.length,m=d>1?arguments[1]:void 0,g=void 0!==m;g&&(m=r(m,d>2?arguments[2]:void 0));var y,v,b,w,E,x,S=h(t),_=0;if(!S||this===f&&a(S))for(y=c(t),v=n?new this(y):f(y);y>_;_++)x=g?m(t[_],_):t[_],u(v,_,x);else for(E=(w=p(t,S)).next,v=n?new this:[];!(b=o(E,w)).done;_++)x=g?i(w,m,[b.value,_],!0):b.value,u(v,_,x);return v.length=_,v}},31692:(e,t,n)=>{var r=n(74529),o=n(59413),s=n(10623),i=function(e){return function(t,n,i){var a,l=r(t),c=s(l),u=o(i,c);if(e&&n!=n){for(;c>u;)if((a=l[u++])!=a)return!0}else for(;c>u;u++)if((e||u in l)&&l[u]===n)return e||u||0;return!e&&-1}};e.exports={includes:i(!0),indexOf:i(!1)}},3610:(e,t,n)=>{var r=n(86843),o=n(95329),s=n(37026),i=n(89678),a=n(10623),l=n(64692),c=o([].push),u=function(e){var t=1==e,n=2==e,o=3==e,u=4==e,p=6==e,h=7==e,f=5==e||p;return function(d,m,g,y){for(var v,b,w=i(d),E=s(w),x=r(m,g),S=a(E),_=0,j=y||l,O=t?j(d,S):n||h?j(d,0):void 0;S>_;_++)if((f||_ in E)&&(b=x(v=E[_],_,w),e))if(t)O[_]=b;else if(b)switch(e){case 3:return!0;case 5:return v;case 6:return _;case 2:c(O,v)}else switch(e){case 4:return!1;case 7:c(O,v)}return p?-1:o||u?u:O}};e.exports={forEach:u(0),map:u(1),filter:u(2),some:u(3),every:u(4),find:u(5),findIndex:u(6),filterReject:u(7)}},67145:(e,t,n)=>{"use strict";var r=n(79730),o=n(74529),s=n(62435),i=n(10623),a=n(34194),l=Math.min,c=[].lastIndexOf,u=!!c&&1/[1].lastIndexOf(1,-0)<0,p=a("lastIndexOf"),h=u||!p;e.exports=h?function(e){if(u)return r(c,this,arguments)||0;var t=o(this),n=i(t),a=n-1;for(arguments.length>1&&(a=l(a,s(arguments[1]))),a<0&&(a=n+a);a>=0;a--)if(a in t&&t[a]===e)return a||0;return-1}:c},50568:(e,t,n)=>{var r=n(95981),o=n(99813),s=n(53385),i=o("species");e.exports=function(e){return s>=51||!r((function(){var t=[];return(t.constructor={})[i]=function(){return{foo:1}},1!==t[e](Boolean).foo}))}},34194:(e,t,n)=>{"use strict";var r=n(95981);e.exports=function(e,t){var n=[][e];return!!n&&r((function(){n.call(null,t||function(){return 1},1)}))}},46499:(e,t,n)=>{var r=n(24883),o=n(89678),s=n(37026),i=n(10623),a=TypeError,l=function(e){return function(t,n,l,c){r(n);var u=o(t),p=s(u),h=i(u),f=e?h-1:0,d=e?-1:1;if(l<2)for(;;){if(f in p){c=p[f],f+=d;break}if(f+=d,e?f<0:h<=f)throw a("Reduce of empty array with no initial value")}for(;e?f>=0:h>f;f+=d)f in p&&(c=n(c,p[f],f,u));return c}};e.exports={left:l(!1),right:l(!0)}},89779:(e,t,n)=>{"use strict";var r=n(55746),o=n(1052),s=TypeError,i=Object.getOwnPropertyDescriptor,a=r&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(e){return e instanceof TypeError}}();e.exports=a?function(e,t){if(o(e)&&!i(e,"length").writable)throw s("Cannot set read only .length");return e.length=t}:function(e,t){return e.length=t}},15790:(e,t,n)=>{var r=n(59413),o=n(10623),s=n(55449),i=Array,a=Math.max;e.exports=function(e,t,n){for(var l=o(e),c=r(t,l),u=r(void 0===n?l:n,l),p=i(a(u-c,0)),h=0;c{var r=n(95329);e.exports=r([].slice)},61388:(e,t,n)=>{var r=n(15790),o=Math.floor,s=function(e,t){var n=e.length,l=o(n/2);return n<8?i(e,t):a(e,s(r(e,0,l),t),s(r(e,l),t),t)},i=function(e,t){for(var n,r,o=e.length,s=1;s0;)e[r]=e[--r];r!==s++&&(e[r]=n)}return e},a=function(e,t,n,r){for(var o=t.length,s=n.length,i=0,a=0;i{var r=n(1052),o=n(24284),s=n(10941),i=n(99813)("species"),a=Array;e.exports=function(e){var t;return r(e)&&(t=e.constructor,(o(t)&&(t===a||r(t.prototype))||s(t)&&null===(t=t[i]))&&(t=void 0)),void 0===t?a:t}},64692:(e,t,n)=>{var r=n(5693);e.exports=function(e,t){return new(r(e))(0===t?0:t)}},75196:(e,t,n)=>{var r=n(96059),o=n(7609);e.exports=function(e,t,n,s){try{return s?t(r(n)[0],n[1]):t(n)}catch(t){o(e,"throw",t)}}},21385:(e,t,n)=>{var r=n(99813)("iterator"),o=!1;try{var s=0,i={next:function(){return{done:!!s++}},return:function(){o=!0}};i[r]=function(){return this},Array.from(i,(function(){throw 2}))}catch(e){}e.exports=function(e,t){if(!t&&!o)return!1;var n=!1;try{var s={};s[r]=function(){return{next:function(){return{done:n=!0}}}},e(s)}catch(e){}return n}},82532:(e,t,n)=>{var r=n(95329),o=r({}.toString),s=r("".slice);e.exports=function(e){return s(o(e),8,-1)}},9697:(e,t,n)=>{var r=n(22885),o=n(57475),s=n(82532),i=n(99813)("toStringTag"),a=Object,l="Arguments"==s(function(){return arguments}());e.exports=r?s:function(e){var t,n,r;return void 0===e?"Undefined":null===e?"Null":"string"==typeof(n=function(e,t){try{return e[t]}catch(e){}}(t=a(e),i))?n:l?s(t):"Object"==(r=s(t))&&o(t.callee)?"Arguments":r}},85616:(e,t,n)=>{"use strict";var r=n(29290),o=n(29202),s=n(94380),i=n(86843),a=n(5743),l=n(82119),c=n(93091),u=n(75105),p=n(23538),h=n(94431),f=n(55746),d=n(21647).fastKey,m=n(45402),g=m.set,y=m.getterFor;e.exports={getConstructor:function(e,t,n,u){var p=e((function(e,o){a(e,h),g(e,{type:t,index:r(null),first:void 0,last:void 0,size:0}),f||(e.size=0),l(o)||c(o,e[u],{that:e,AS_ENTRIES:n})})),h=p.prototype,m=y(t),v=function(e,t,n){var r,o,s=m(e),i=b(e,t);return i?i.value=n:(s.last=i={index:o=d(t,!0),key:t,value:n,previous:r=s.last,next:void 0,removed:!1},s.first||(s.first=i),r&&(r.next=i),f?s.size++:e.size++,"F"!==o&&(s.index[o]=i)),e},b=function(e,t){var n,r=m(e),o=d(t);if("F"!==o)return r.index[o];for(n=r.first;n;n=n.next)if(n.key==t)return n};return s(h,{clear:function(){for(var e=m(this),t=e.index,n=e.first;n;)n.removed=!0,n.previous&&(n.previous=n.previous.next=void 0),delete t[n.index],n=n.next;e.first=e.last=void 0,f?e.size=0:this.size=0},delete:function(e){var t=this,n=m(t),r=b(t,e);if(r){var o=r.next,s=r.previous;delete n.index[r.index],r.removed=!0,s&&(s.next=o),o&&(o.previous=s),n.first==r&&(n.first=o),n.last==r&&(n.last=s),f?n.size--:t.size--}return!!r},forEach:function(e){for(var t,n=m(this),r=i(e,arguments.length>1?arguments[1]:void 0);t=t?t.next:n.first;)for(r(t.value,t.key,this);t&&t.removed;)t=t.previous},has:function(e){return!!b(this,e)}}),s(h,n?{get:function(e){var t=b(this,e);return t&&t.value},set:function(e,t){return v(this,0===e?0:e,t)}}:{add:function(e){return v(this,e=0===e?0:e,e)}}),f&&o(h,"size",{configurable:!0,get:function(){return m(this).size}}),p},setStrong:function(e,t,n){var r=t+" Iterator",o=y(t),s=y(r);u(e,t,(function(e,t){g(this,{type:r,target:e,state:o(e),kind:t,last:void 0})}),(function(){for(var e=s(this),t=e.kind,n=e.last;n&&n.removed;)n=n.previous;return e.target&&(e.last=n=n?n.next:e.state.first)?p("keys"==t?n.key:"values"==t?n.value:[n.key,n.value],!1):(e.target=void 0,p(void 0,!0))}),n?"entries":"values",!n,!0),h(t)}}},8850:(e,t,n)=>{"use strict";var r=n(95329),o=n(94380),s=n(21647).getWeakData,i=n(5743),a=n(96059),l=n(82119),c=n(10941),u=n(93091),p=n(3610),h=n(90953),f=n(45402),d=f.set,m=f.getterFor,g=p.find,y=p.findIndex,v=r([].splice),b=0,w=function(e){return e.frozen||(e.frozen=new E)},E=function(){this.entries=[]},x=function(e,t){return g(e.entries,(function(e){return e[0]===t}))};E.prototype={get:function(e){var t=x(this,e);if(t)return t[1]},has:function(e){return!!x(this,e)},set:function(e,t){var n=x(this,e);n?n[1]=t:this.entries.push([e,t])},delete:function(e){var t=y(this.entries,(function(t){return t[0]===e}));return~t&&v(this.entries,t,1),!!~t}},e.exports={getConstructor:function(e,t,n,r){var p=e((function(e,o){i(e,f),d(e,{type:t,id:b++,frozen:void 0}),l(o)||u(o,e[r],{that:e,AS_ENTRIES:n})})),f=p.prototype,g=m(t),y=function(e,t,n){var r=g(e),o=s(a(t),!0);return!0===o?w(r).set(t,n):o[r.id]=n,e};return o(f,{delete:function(e){var t=g(this);if(!c(e))return!1;var n=s(e);return!0===n?w(t).delete(e):n&&h(n,t.id)&&delete n[t.id]},has:function(e){var t=g(this);if(!c(e))return!1;var n=s(e);return!0===n?w(t).has(e):n&&h(n,t.id)}}),o(f,n?{get:function(e){var t=g(this);if(c(e)){var n=s(e);return!0===n?w(t).get(e):n?n[t.id]:void 0}},set:function(e,t){return y(this,e,t)}}:{add:function(e){return y(this,e,!0)}}),p}}},24683:(e,t,n)=>{"use strict";var r=n(76887),o=n(21899),s=n(21647),i=n(95981),a=n(32029),l=n(93091),c=n(5743),u=n(57475),p=n(10941),h=n(90904),f=n(65988).f,d=n(3610).forEach,m=n(55746),g=n(45402),y=g.set,v=g.getterFor;e.exports=function(e,t,n){var g,b=-1!==e.indexOf("Map"),w=-1!==e.indexOf("Weak"),E=b?"set":"add",x=o[e],S=x&&x.prototype,_={};if(m&&u(x)&&(w||S.forEach&&!i((function(){(new x).entries().next()})))){var j=(g=t((function(t,n){y(c(t,j),{type:e,collection:new x}),null!=n&&l(n,t[E],{that:t,AS_ENTRIES:b})}))).prototype,O=v(e);d(["add","clear","delete","forEach","get","has","set","keys","values","entries"],(function(e){var t="add"==e||"set"==e;!(e in S)||w&&"clear"==e||a(j,e,(function(n,r){var o=O(this).collection;if(!t&&w&&!p(n))return"get"==e&&void 0;var s=o[e](0===n?0:n,r);return t?this:s}))})),w||f(j,"size",{configurable:!0,get:function(){return O(this).collection.size}})}else g=n.getConstructor(t,e,b,E),s.enable();return h(g,e,!1,!0),_[e]=g,r({global:!0,forced:!0},_),w||n.setStrong(g,e,b),g}},23489:(e,t,n)=>{var r=n(90953),o=n(31136),s=n(49677),i=n(65988);e.exports=function(e,t,n){for(var a=o(t),l=i.f,c=s.f,u=0;u{var r=n(99813)("match");e.exports=function(e){var t=/./;try{"/./"[e](t)}catch(n){try{return t[r]=!1,"/./"[e](t)}catch(e){}}return!1}},64160:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){function e(){}return e.prototype.constructor=null,Object.getPrototypeOf(new e)!==e.prototype}))},23538:e=>{e.exports=function(e,t){return{value:e,done:t}}},32029:(e,t,n)=>{var r=n(55746),o=n(65988),s=n(31887);e.exports=r?function(e,t,n){return o.f(e,t,s(1,n))}:function(e,t,n){return e[t]=n,e}},31887:e=>{e.exports=function(e,t){return{enumerable:!(1&e),configurable:!(2&e),writable:!(4&e),value:t}}},55449:(e,t,n)=>{"use strict";var r=n(83894),o=n(65988),s=n(31887);e.exports=function(e,t,n){var i=r(t);i in e?o.f(e,i,s(0,n)):e[i]=n}},29202:(e,t,n)=>{var r=n(65988);e.exports=function(e,t,n){return r.f(e,t,n)}},95929:(e,t,n)=>{var r=n(32029);e.exports=function(e,t,n,o){return o&&o.enumerable?e[t]=n:r(e,t,n),e}},94380:(e,t,n)=>{var r=n(95929);e.exports=function(e,t,n){for(var o in t)n&&n.unsafe&&e[o]?e[o]=t[o]:r(e,o,t[o],n);return e}},75609:(e,t,n)=>{var r=n(21899),o=Object.defineProperty;e.exports=function(e,t){try{o(r,e,{value:t,configurable:!0,writable:!0})}catch(n){r[e]=t}return t}},15863:(e,t,n)=>{"use strict";var r=n(69826),o=TypeError;e.exports=function(e,t){if(!delete e[t])throw o("Cannot delete property "+r(t)+" of "+r(e))}},55746:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}))},76616:e=>{var t="object"==typeof document&&document.all,n=void 0===t&&void 0!==t;e.exports={all:t,IS_HTMLDDA:n}},61333:(e,t,n)=>{var r=n(21899),o=n(10941),s=r.document,i=o(s)&&o(s.createElement);e.exports=function(e){return i?s.createElement(e):{}}},66796:e=>{var t=TypeError;e.exports=function(e){if(e>9007199254740991)throw t("Maximum allowed index exceeded");return e}},63281:e=>{e.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},34342:(e,t,n)=>{var r=n(2861).match(/firefox\/(\d+)/i);e.exports=!!r&&+r[1]},23321:(e,t,n)=>{var r=n(48501),o=n(6049);e.exports=!r&&!o&&"object"==typeof window&&"object"==typeof document},56491:e=>{e.exports="function"==typeof Bun&&Bun&&"string"==typeof Bun.version},48501:e=>{e.exports="object"==typeof Deno&&Deno&&"object"==typeof Deno.version},81046:(e,t,n)=>{var r=n(2861);e.exports=/MSIE|Trident/.test(r)},4470:(e,t,n)=>{var r=n(2861);e.exports=/ipad|iphone|ipod/i.test(r)&&"undefined"!=typeof Pebble},22749:(e,t,n)=>{var r=n(2861);e.exports=/(?:ipad|iphone|ipod).*applewebkit/i.test(r)},6049:(e,t,n)=>{var r=n(34155),o=n(82532);e.exports=void 0!==r&&"process"==o(r)},58045:(e,t,n)=>{var r=n(2861);e.exports=/web0s(?!.*chrome)/i.test(r)},2861:e=>{e.exports="undefined"!=typeof navigator&&String(navigator.userAgent)||""},53385:(e,t,n)=>{var r,o,s=n(21899),i=n(2861),a=s.process,l=s.Deno,c=a&&a.versions||l&&l.version,u=c&&c.v8;u&&(o=(r=u.split("."))[0]>0&&r[0]<4?1:+(r[0]+r[1])),!o&&i&&(!(r=i.match(/Edge\/(\d+)/))||r[1]>=74)&&(r=i.match(/Chrome\/(\d+)/))&&(o=+r[1]),e.exports=o},18938:(e,t,n)=>{var r=n(2861).match(/AppleWebKit\/(\d+)\./);e.exports=!!r&&+r[1]},35703:(e,t,n)=>{var r=n(54058);e.exports=function(e){return r[e+"Prototype"]}},56759:e=>{e.exports=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"]},53995:(e,t,n)=>{var r=n(95329),o=Error,s=r("".replace),i=String(o("zxcasd").stack),a=/\n\s*at [^:]*:[^\n]*/,l=a.test(i);e.exports=function(e,t){if(l&&"string"==typeof e&&!o.prepareStackTrace)for(;t--;)e=s(e,a,"");return e}},79585:(e,t,n)=>{var r=n(32029),o=n(53995),s=n(18780),i=Error.captureStackTrace;e.exports=function(e,t,n,a){s&&(i?i(e,t):r(e,"stack",o(n,a)))}},18780:(e,t,n)=>{var r=n(95981),o=n(31887);e.exports=!r((function(){var e=Error("a");return!("stack"in e)||(Object.defineProperty(e,"stack",o(1,7)),7!==e.stack)}))},76887:(e,t,n)=>{"use strict";var r=n(21899),o=n(79730),s=n(97484),i=n(57475),a=n(49677).f,l=n(37252),c=n(54058),u=n(86843),p=n(32029),h=n(90953),f=function(e){var t=function(n,r,s){if(this instanceof t){switch(arguments.length){case 0:return new e;case 1:return new e(n);case 2:return new e(n,r)}return new e(n,r,s)}return o(e,this,arguments)};return t.prototype=e.prototype,t};e.exports=function(e,t){var n,o,d,m,g,y,v,b,w,E=e.target,x=e.global,S=e.stat,_=e.proto,j=x?r:S?r[E]:(r[E]||{}).prototype,O=x?c:c[E]||p(c,E,{})[E],k=O.prototype;for(m in t)o=!(n=l(x?m:E+(S?".":"#")+m,e.forced))&&j&&h(j,m),y=O[m],o&&(v=e.dontCallGetSet?(w=a(j,m))&&w.value:j[m]),g=o&&v?v:t[m],o&&typeof y==typeof g||(b=e.bind&&o?u(g,r):e.wrap&&o?f(g):_&&i(g)?s(g):g,(e.sham||g&&g.sham||y&&y.sham)&&p(b,"sham",!0),p(O,m,b),_&&(h(c,d=E+"Prototype")||p(c,d,{}),p(c[d],m,g),e.real&&k&&(n||!k[m])&&p(k,m,g)))}},95981:e=>{e.exports=function(e){try{return!!e()}catch(e){return!0}}},45602:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){return Object.isExtensible(Object.preventExtensions({}))}))},79730:(e,t,n)=>{var r=n(18285),o=Function.prototype,s=o.apply,i=o.call;e.exports="object"==typeof Reflect&&Reflect.apply||(r?i.bind(s):function(){return i.apply(s,arguments)})},86843:(e,t,n)=>{var r=n(97484),o=n(24883),s=n(18285),i=r(r.bind);e.exports=function(e,t){return o(e),void 0===t?e:s?i(e,t):function(){return e.apply(t,arguments)}}},18285:(e,t,n)=>{var r=n(95981);e.exports=!r((function(){var e=function(){}.bind();return"function"!=typeof e||e.hasOwnProperty("prototype")}))},98308:(e,t,n)=>{"use strict";var r=n(95329),o=n(24883),s=n(10941),i=n(90953),a=n(93765),l=n(18285),c=Function,u=r([].concat),p=r([].join),h={};e.exports=l?c.bind:function(e){var t=o(this),n=t.prototype,r=a(arguments,1),l=function(){var n=u(r,a(arguments));return this instanceof l?function(e,t,n){if(!i(h,t)){for(var r=[],o=0;o{var r=n(18285),o=Function.prototype.call;e.exports=r?o.bind(o):function(){return o.apply(o,arguments)}},79417:(e,t,n)=>{var r=n(55746),o=n(90953),s=Function.prototype,i=r&&Object.getOwnPropertyDescriptor,a=o(s,"name"),l=a&&"something"===function(){}.name,c=a&&(!r||r&&i(s,"name").configurable);e.exports={EXISTS:a,PROPER:l,CONFIGURABLE:c}},45526:(e,t,n)=>{var r=n(95329),o=n(24883);e.exports=function(e,t,n){try{return r(o(Object.getOwnPropertyDescriptor(e,t)[n]))}catch(e){}}},97484:(e,t,n)=>{var r=n(82532),o=n(95329);e.exports=function(e){if("Function"===r(e))return o(e)}},95329:(e,t,n)=>{var r=n(18285),o=Function.prototype,s=o.call,i=r&&o.bind.bind(s,s);e.exports=r?i:function(e){return function(){return s.apply(e,arguments)}}},626:(e,t,n)=>{var r=n(54058),o=n(21899),s=n(57475),i=function(e){return s(e)?e:void 0};e.exports=function(e,t){return arguments.length<2?i(r[e])||i(o[e]):r[e]&&r[e][t]||o[e]&&o[e][t]}},22902:(e,t,n)=>{var r=n(9697),o=n(14229),s=n(82119),i=n(12077),a=n(99813)("iterator");e.exports=function(e){if(!s(e))return o(e,a)||o(e,"@@iterator")||i[r(e)]}},53476:(e,t,n)=>{var r=n(78834),o=n(24883),s=n(96059),i=n(69826),a=n(22902),l=TypeError;e.exports=function(e,t){var n=arguments.length<2?a(e):t;if(o(n))return s(r(n,e));throw l(i(e)+" is not iterable")}},33323:(e,t,n)=>{var r=n(95329),o=n(1052),s=n(57475),i=n(82532),a=n(85803),l=r([].push);e.exports=function(e){if(s(e))return e;if(o(e)){for(var t=e.length,n=[],r=0;r{var r=n(24883),o=n(82119);e.exports=function(e,t){var n=e[t];return o(n)?void 0:r(n)}},21899:function(e,t,n){var r=function(e){return e&&e.Math==Math&&e};e.exports=r("object"==typeof globalThis&&globalThis)||r("object"==typeof window&&window)||r("object"==typeof self&&self)||r("object"==typeof n.g&&n.g)||function(){return this}()||this||Function("return this")()},90953:(e,t,n)=>{var r=n(95329),o=n(89678),s=r({}.hasOwnProperty);e.exports=Object.hasOwn||function(e,t){return s(o(e),t)}},27748:e=>{e.exports={}},34845:e=>{e.exports=function(e,t){try{1==arguments.length?console.error(e):console.error(e,t)}catch(e){}}},15463:(e,t,n)=>{var r=n(626);e.exports=r("document","documentElement")},2840:(e,t,n)=>{var r=n(55746),o=n(95981),s=n(61333);e.exports=!r&&!o((function(){return 7!=Object.defineProperty(s("div"),"a",{get:function(){return 7}}).a}))},37026:(e,t,n)=>{var r=n(95329),o=n(95981),s=n(82532),i=Object,a=r("".split);e.exports=o((function(){return!i("z").propertyIsEnumerable(0)}))?function(e){return"String"==s(e)?a(e,""):i(e)}:i},81302:(e,t,n)=>{var r=n(95329),o=n(57475),s=n(63030),i=r(Function.toString);o(s.inspectSource)||(s.inspectSource=function(e){return i(e)}),e.exports=s.inspectSource},53794:(e,t,n)=>{var r=n(10941),o=n(32029);e.exports=function(e,t){r(t)&&"cause"in t&&o(e,"cause",t.cause)}},21647:(e,t,n)=>{var r=n(76887),o=n(95329),s=n(27748),i=n(10941),a=n(90953),l=n(65988).f,c=n(10946),u=n(684),p=n(91584),h=n(99418),f=n(45602),d=!1,m=h("meta"),g=0,y=function(e){l(e,m,{value:{objectID:"O"+g++,weakData:{}}})},v=e.exports={enable:function(){v.enable=function(){},d=!0;var e=c.f,t=o([].splice),n={};n[m]=1,e(n).length&&(c.f=function(n){for(var r=e(n),o=0,s=r.length;o{var r,o,s,i=n(47093),a=n(21899),l=n(10941),c=n(32029),u=n(90953),p=n(63030),h=n(44262),f=n(27748),d="Object already initialized",m=a.TypeError,g=a.WeakMap;if(i||p.state){var y=p.state||(p.state=new g);y.get=y.get,y.has=y.has,y.set=y.set,r=function(e,t){if(y.has(e))throw m(d);return t.facade=e,y.set(e,t),t},o=function(e){return y.get(e)||{}},s=function(e){return y.has(e)}}else{var v=h("state");f[v]=!0,r=function(e,t){if(u(e,v))throw m(d);return t.facade=e,c(e,v,t),t},o=function(e){return u(e,v)?e[v]:{}},s=function(e){return u(e,v)}}e.exports={set:r,get:o,has:s,enforce:function(e){return s(e)?o(e):r(e,{})},getterFor:function(e){return function(t){var n;if(!l(t)||(n=o(t)).type!==e)throw m("Incompatible receiver, "+e+" required");return n}}}},6782:(e,t,n)=>{var r=n(99813),o=n(12077),s=r("iterator"),i=Array.prototype;e.exports=function(e){return void 0!==e&&(o.Array===e||i[s]===e)}},1052:(e,t,n)=>{var r=n(82532);e.exports=Array.isArray||function(e){return"Array"==r(e)}},57475:(e,t,n)=>{var r=n(76616),o=r.all;e.exports=r.IS_HTMLDDA?function(e){return"function"==typeof e||e===o}:function(e){return"function"==typeof e}},24284:(e,t,n)=>{var r=n(95329),o=n(95981),s=n(57475),i=n(9697),a=n(626),l=n(81302),c=function(){},u=[],p=a("Reflect","construct"),h=/^\s*(?:class|function)\b/,f=r(h.exec),d=!h.exec(c),m=function(e){if(!s(e))return!1;try{return p(c,u,e),!0}catch(e){return!1}},g=function(e){if(!s(e))return!1;switch(i(e)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return d||!!f(h,l(e))}catch(e){return!0}};g.sham=!0,e.exports=!p||o((function(){var e;return m(m.call)||!m(Object)||!m((function(){e=!0}))||e}))?g:m},37252:(e,t,n)=>{var r=n(95981),o=n(57475),s=/#|\.prototype\./,i=function(e,t){var n=l[a(e)];return n==u||n!=c&&(o(t)?r(t):!!t)},a=i.normalize=function(e){return String(e).replace(s,".").toLowerCase()},l=i.data={},c=i.NATIVE="N",u=i.POLYFILL="P";e.exports=i},54639:(e,t,n)=>{var r=n(10941),o=Math.floor;e.exports=Number.isInteger||function(e){return!r(e)&&isFinite(e)&&o(e)===e}},82119:e=>{e.exports=function(e){return null==e}},10941:(e,t,n)=>{var r=n(57475),o=n(76616),s=o.all;e.exports=o.IS_HTMLDDA?function(e){return"object"==typeof e?null!==e:r(e)||e===s}:function(e){return"object"==typeof e?null!==e:r(e)}},82529:e=>{e.exports=!0},60685:(e,t,n)=>{var r=n(10941),o=n(82532),s=n(99813)("match");e.exports=function(e){var t;return r(e)&&(void 0!==(t=e[s])?!!t:"RegExp"==o(e))}},56664:(e,t,n)=>{var r=n(626),o=n(57475),s=n(7046),i=n(32302),a=Object;e.exports=i?function(e){return"symbol"==typeof e}:function(e){var t=r("Symbol");return o(t)&&s(t.prototype,a(e))}},93091:(e,t,n)=>{var r=n(86843),o=n(78834),s=n(96059),i=n(69826),a=n(6782),l=n(10623),c=n(7046),u=n(53476),p=n(22902),h=n(7609),f=TypeError,d=function(e,t){this.stopped=e,this.result=t},m=d.prototype;e.exports=function(e,t,n){var g,y,v,b,w,E,x,S=n&&n.that,_=!(!n||!n.AS_ENTRIES),j=!(!n||!n.IS_RECORD),O=!(!n||!n.IS_ITERATOR),k=!(!n||!n.INTERRUPTED),A=r(t,S),C=function(e){return g&&h(g,"normal",e),new d(!0,e)},P=function(e){return _?(s(e),k?A(e[0],e[1],C):A(e[0],e[1])):k?A(e,C):A(e)};if(j)g=e.iterator;else if(O)g=e;else{if(!(y=p(e)))throw f(i(e)+" is not iterable");if(a(y)){for(v=0,b=l(e);b>v;v++)if((w=P(e[v]))&&c(m,w))return w;return new d(!1)}g=u(e,y)}for(E=j?e.next:g.next;!(x=o(E,g)).done;){try{w=P(x.value)}catch(e){h(g,"throw",e)}if("object"==typeof w&&w&&c(m,w))return w}return new d(!1)}},7609:(e,t,n)=>{var r=n(78834),o=n(96059),s=n(14229);e.exports=function(e,t,n){var i,a;o(e);try{if(!(i=s(e,"return"))){if("throw"===t)throw n;return n}i=r(i,e)}catch(e){a=!0,i=e}if("throw"===t)throw n;if(a)throw i;return o(i),n}},53847:(e,t,n)=>{"use strict";var r=n(35143).IteratorPrototype,o=n(29290),s=n(31887),i=n(90904),a=n(12077),l=function(){return this};e.exports=function(e,t,n,c){var u=t+" Iterator";return e.prototype=o(r,{next:s(+!c,n)}),i(e,u,!1,!0),a[u]=l,e}},75105:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),s=n(82529),i=n(79417),a=n(57475),l=n(53847),c=n(249),u=n(88929),p=n(90904),h=n(32029),f=n(95929),d=n(99813),m=n(12077),g=n(35143),y=i.PROPER,v=i.CONFIGURABLE,b=g.IteratorPrototype,w=g.BUGGY_SAFARI_ITERATORS,E=d("iterator"),x="keys",S="values",_="entries",j=function(){return this};e.exports=function(e,t,n,i,d,g,O){l(n,t,i);var k,A,C,P=function(e){if(e===d&&M)return M;if(!w&&e in T)return T[e];switch(e){case x:case S:case _:return function(){return new n(this,e)}}return function(){return new n(this)}},N=t+" Iterator",I=!1,T=e.prototype,R=T[E]||T["@@iterator"]||d&&T[d],M=!w&&R||P(d),D="Array"==t&&T.entries||R;if(D&&(k=c(D.call(new e)))!==Object.prototype&&k.next&&(s||c(k)===b||(u?u(k,b):a(k[E])||f(k,E,j)),p(k,N,!0,!0),s&&(m[N]=j)),y&&d==S&&R&&R.name!==S&&(!s&&v?h(T,"name",S):(I=!0,M=function(){return o(R,this)})),d)if(A={values:P(S),keys:g?M:P(x),entries:P(_)},O)for(C in A)(w||I||!(C in T))&&f(T,C,A[C]);else r({target:t,proto:!0,forced:w||I},A);return s&&!O||T[E]===M||f(T,E,M,{name:d}),m[t]=M,A}},35143:(e,t,n)=>{"use strict";var r,o,s,i=n(95981),a=n(57475),l=n(10941),c=n(29290),u=n(249),p=n(95929),h=n(99813),f=n(82529),d=h("iterator"),m=!1;[].keys&&("next"in(s=[].keys())?(o=u(u(s)))!==Object.prototype&&(r=o):m=!0),!l(r)||i((function(){var e={};return r[d].call(e)!==e}))?r={}:f&&(r=c(r)),a(r[d])||p(r,d,(function(){return this})),e.exports={IteratorPrototype:r,BUGGY_SAFARI_ITERATORS:m}},12077:e=>{e.exports={}},10623:(e,t,n)=>{var r=n(43057);e.exports=function(e){return r(e.length)}},35331:e=>{var t=Math.ceil,n=Math.floor;e.exports=Math.trunc||function(e){var r=+e;return(r>0?n:t)(r)}},66132:(e,t,n)=>{var r,o,s,i,a,l=n(21899),c=n(86843),u=n(49677).f,p=n(42941).set,h=n(18397),f=n(22749),d=n(4470),m=n(58045),g=n(6049),y=l.MutationObserver||l.WebKitMutationObserver,v=l.document,b=l.process,w=l.Promise,E=u(l,"queueMicrotask"),x=E&&E.value;if(!x){var S=new h,_=function(){var e,t;for(g&&(e=b.domain)&&e.exit();t=S.get();)try{t()}catch(e){throw S.head&&r(),e}e&&e.enter()};f||g||m||!y||!v?!d&&w&&w.resolve?((i=w.resolve(void 0)).constructor=w,a=c(i.then,i),r=function(){a(_)}):g?r=function(){b.nextTick(_)}:(p=c(p,l),r=function(){p(_)}):(o=!0,s=v.createTextNode(""),new y(_).observe(s,{characterData:!0}),r=function(){s.data=o=!o}),x=function(e){S.head||r(),S.add(e)}}e.exports=x},69520:(e,t,n)=>{"use strict";var r=n(24883),o=TypeError,s=function(e){var t,n;this.promise=new e((function(e,r){if(void 0!==t||void 0!==n)throw o("Bad Promise constructor");t=e,n=r})),this.resolve=r(t),this.reject=r(n)};e.exports.f=function(e){return new s(e)}},14649:(e,t,n)=>{var r=n(85803);e.exports=function(e,t){return void 0===e?arguments.length<2?"":t:r(e)}},70344:(e,t,n)=>{var r=n(60685),o=TypeError;e.exports=function(e){if(r(e))throw o("The method doesn't accept regular expressions");return e}},24420:(e,t,n)=>{"use strict";var r=n(55746),o=n(95329),s=n(78834),i=n(95981),a=n(14771),l=n(87857),c=n(36760),u=n(89678),p=n(37026),h=Object.assign,f=Object.defineProperty,d=o([].concat);e.exports=!h||i((function(){if(r&&1!==h({b:1},h(f({},"a",{enumerable:!0,get:function(){f(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var e={},t={},n=Symbol(),o="abcdefghijklmnopqrst";return e[n]=7,o.split("").forEach((function(e){t[e]=e})),7!=h({},e)[n]||a(h({},t)).join("")!=o}))?function(e,t){for(var n=u(e),o=arguments.length,i=1,h=l.f,f=c.f;o>i;)for(var m,g=p(arguments[i++]),y=h?d(a(g),h(g)):a(g),v=y.length,b=0;v>b;)m=y[b++],r&&!s(f,g,m)||(n[m]=g[m]);return n}:h},29290:(e,t,n)=>{var r,o=n(96059),s=n(59938),i=n(56759),a=n(27748),l=n(15463),c=n(61333),u=n(44262),p="prototype",h="script",f=u("IE_PROTO"),d=function(){},m=function(e){return"<"+h+">"+e+""},g=function(e){e.write(m("")),e.close();var t=e.parentWindow.Object;return e=null,t},y=function(){try{r=new ActiveXObject("htmlfile")}catch(e){}var e,t,n;y="undefined"!=typeof document?document.domain&&r?g(r):(t=c("iframe"),n="java"+h+":",t.style.display="none",l.appendChild(t),t.src=String(n),(e=t.contentWindow.document).open(),e.write(m("document.F=Object")),e.close(),e.F):g(r);for(var o=i.length;o--;)delete y[p][i[o]];return y()};a[f]=!0,e.exports=Object.create||function(e,t){var n;return null!==e?(d[p]=o(e),n=new d,d[p]=null,n[f]=e):n=y(),void 0===t?n:s.f(n,t)}},59938:(e,t,n)=>{var r=n(55746),o=n(83937),s=n(65988),i=n(96059),a=n(74529),l=n(14771);t.f=r&&!o?Object.defineProperties:function(e,t){i(e);for(var n,r=a(t),o=l(t),c=o.length,u=0;c>u;)s.f(e,n=o[u++],r[n]);return e}},65988:(e,t,n)=>{var r=n(55746),o=n(2840),s=n(83937),i=n(96059),a=n(83894),l=TypeError,c=Object.defineProperty,u=Object.getOwnPropertyDescriptor,p="enumerable",h="configurable",f="writable";t.f=r?s?function(e,t,n){if(i(e),t=a(t),i(n),"function"==typeof e&&"prototype"===t&&"value"in n&&f in n&&!n[f]){var r=u(e,t);r&&r[f]&&(e[t]=n.value,n={configurable:h in n?n[h]:r[h],enumerable:p in n?n[p]:r[p],writable:!1})}return c(e,t,n)}:c:function(e,t,n){if(i(e),t=a(t),i(n),o)try{return c(e,t,n)}catch(e){}if("get"in n||"set"in n)throw l("Accessors not supported");return"value"in n&&(e[t]=n.value),e}},49677:(e,t,n)=>{var r=n(55746),o=n(78834),s=n(36760),i=n(31887),a=n(74529),l=n(83894),c=n(90953),u=n(2840),p=Object.getOwnPropertyDescriptor;t.f=r?p:function(e,t){if(e=a(e),t=l(t),u)try{return p(e,t)}catch(e){}if(c(e,t))return i(!o(s.f,e,t),e[t])}},684:(e,t,n)=>{var r=n(82532),o=n(74529),s=n(10946).f,i=n(15790),a="object"==typeof window&&window&&Object.getOwnPropertyNames?Object.getOwnPropertyNames(window):[];e.exports.f=function(e){return a&&"Window"==r(e)?function(e){try{return s(e)}catch(e){return i(a)}}(e):s(o(e))}},10946:(e,t,n)=>{var r=n(55629),o=n(56759).concat("length","prototype");t.f=Object.getOwnPropertyNames||function(e){return r(e,o)}},87857:(e,t)=>{t.f=Object.getOwnPropertySymbols},249:(e,t,n)=>{var r=n(90953),o=n(57475),s=n(89678),i=n(44262),a=n(64160),l=i("IE_PROTO"),c=Object,u=c.prototype;e.exports=a?c.getPrototypeOf:function(e){var t=s(e);if(r(t,l))return t[l];var n=t.constructor;return o(n)&&t instanceof n?n.prototype:t instanceof c?u:null}},91584:(e,t,n)=>{var r=n(95981),o=n(10941),s=n(82532),i=n(97135),a=Object.isExtensible,l=r((function(){a(1)}));e.exports=l||i?function(e){return!!o(e)&&((!i||"ArrayBuffer"!=s(e))&&(!a||a(e)))}:a},7046:(e,t,n)=>{var r=n(95329);e.exports=r({}.isPrototypeOf)},55629:(e,t,n)=>{var r=n(95329),o=n(90953),s=n(74529),i=n(31692).indexOf,a=n(27748),l=r([].push);e.exports=function(e,t){var n,r=s(e),c=0,u=[];for(n in r)!o(a,n)&&o(r,n)&&l(u,n);for(;t.length>c;)o(r,n=t[c++])&&(~i(u,n)||l(u,n));return u}},14771:(e,t,n)=>{var r=n(55629),o=n(56759);e.exports=Object.keys||function(e){return r(e,o)}},36760:(e,t)=>{"use strict";var n={}.propertyIsEnumerable,r=Object.getOwnPropertyDescriptor,o=r&&!n.call({1:2},1);t.f=o?function(e){var t=r(this,e);return!!t&&t.enumerable}:n},88929:(e,t,n)=>{var r=n(45526),o=n(96059),s=n(11851);e.exports=Object.setPrototypeOf||("__proto__"in{}?function(){var e,t=!1,n={};try{(e=r(Object.prototype,"__proto__","set"))(n,[]),t=n instanceof Array}catch(e){}return function(n,r){return o(n),s(r),t?e(n,r):n.__proto__=r,n}}():void 0)},88810:(e,t,n)=>{var r=n(55746),o=n(95981),s=n(95329),i=n(249),a=n(14771),l=n(74529),c=s(n(36760).f),u=s([].push),p=r&&o((function(){var e=Object.create(null);return e[2]=2,!c(e,2)})),h=function(e){return function(t){for(var n,o=l(t),s=a(o),h=p&&null===i(o),f=s.length,d=0,m=[];f>d;)n=s[d++],r&&!(h?n in o:c(o,n))||u(m,e?[n,o[n]]:o[n]);return m}};e.exports={entries:h(!0),values:h(!1)}},95623:(e,t,n)=>{"use strict";var r=n(22885),o=n(9697);e.exports=r?{}.toString:function(){return"[object "+o(this)+"]"}},39811:(e,t,n)=>{var r=n(78834),o=n(57475),s=n(10941),i=TypeError;e.exports=function(e,t){var n,a;if("string"===t&&o(n=e.toString)&&!s(a=r(n,e)))return a;if(o(n=e.valueOf)&&!s(a=r(n,e)))return a;if("string"!==t&&o(n=e.toString)&&!s(a=r(n,e)))return a;throw i("Can't convert object to primitive value")}},31136:(e,t,n)=>{var r=n(626),o=n(95329),s=n(10946),i=n(87857),a=n(96059),l=o([].concat);e.exports=r("Reflect","ownKeys")||function(e){var t=s.f(a(e)),n=i.f;return n?l(t,n(e)):t}},54058:e=>{e.exports={}},40002:e=>{e.exports=function(e){try{return{error:!1,value:e()}}catch(e){return{error:!0,value:e}}}},67742:(e,t,n)=>{var r=n(21899),o=n(6991),s=n(57475),i=n(37252),a=n(81302),l=n(99813),c=n(23321),u=n(48501),p=n(82529),h=n(53385),f=o&&o.prototype,d=l("species"),m=!1,g=s(r.PromiseRejectionEvent),y=i("Promise",(function(){var e=a(o),t=e!==String(o);if(!t&&66===h)return!0;if(p&&(!f.catch||!f.finally))return!0;if(!h||h<51||!/native code/.test(e)){var n=new o((function(e){e(1)})),r=function(e){e((function(){}),(function(){}))};if((n.constructor={})[d]=r,!(m=n.then((function(){}))instanceof r))return!0}return!t&&(c||u)&&!g}));e.exports={CONSTRUCTOR:y,REJECTION_EVENT:g,SUBCLASSING:m}},6991:(e,t,n)=>{var r=n(21899);e.exports=r.Promise},56584:(e,t,n)=>{var r=n(96059),o=n(10941),s=n(69520);e.exports=function(e,t){if(r(e),o(t)&&t.constructor===e)return t;var n=s.f(e);return(0,n.resolve)(t),n.promise}},31542:(e,t,n)=>{var r=n(6991),o=n(21385),s=n(67742).CONSTRUCTOR;e.exports=s||!o((function(e){r.all(e).then(void 0,(function(){}))}))},18397:e=>{var t=function(){this.head=null,this.tail=null};t.prototype={add:function(e){var t={item:e,next:null},n=this.tail;n?n.next=t:this.head=t,this.tail=t},get:function(){var e=this.head;if(e)return null===(this.head=e.next)&&(this.tail=null),e.item}},e.exports=t},48219:(e,t,n)=>{var r=n(82119),o=TypeError;e.exports=function(e){if(r(e))throw o("Can't call method on "+e);return e}},37620:(e,t,n)=>{"use strict";var r,o=n(21899),s=n(79730),i=n(57475),a=n(56491),l=n(2861),c=n(93765),u=n(18348),p=o.Function,h=/MSIE .\./.test(l)||a&&((r=o.Bun.version.split(".")).length<3||0==r[0]&&(r[1]<3||3==r[1]&&0==r[2]));e.exports=function(e,t){var n=t?2:1;return h?function(r,o){var a=u(arguments.length,1)>n,l=i(r)?r:p(r),h=a?c(arguments,n):[],f=a?function(){s(l,this,h)}:l;return t?e(f,o):e(f)}:e}},94431:(e,t,n)=>{"use strict";var r=n(626),o=n(29202),s=n(99813),i=n(55746),a=s("species");e.exports=function(e){var t=r(e);i&&t&&!t[a]&&o(t,a,{configurable:!0,get:function(){return this}})}},90904:(e,t,n)=>{var r=n(22885),o=n(65988).f,s=n(32029),i=n(90953),a=n(95623),l=n(99813)("toStringTag");e.exports=function(e,t,n,c){if(e){var u=n?e:e.prototype;i(u,l)||o(u,l,{configurable:!0,value:t}),c&&!r&&s(u,"toString",a)}}},44262:(e,t,n)=>{var r=n(68726),o=n(99418),s=r("keys");e.exports=function(e){return s[e]||(s[e]=o(e))}},63030:(e,t,n)=>{var r=n(21899),o=n(75609),s="__core-js_shared__",i=r[s]||o(s,{});e.exports=i},68726:(e,t,n)=>{var r=n(82529),o=n(63030);(e.exports=function(e,t){return o[e]||(o[e]=void 0!==t?t:{})})("versions",[]).push({version:"3.31.1",mode:r?"pure":"global",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.31.1/LICENSE",source:"https://github.com/zloirock/core-js"})},70487:(e,t,n)=>{var r=n(96059),o=n(174),s=n(82119),i=n(99813)("species");e.exports=function(e,t){var n,a=r(e).constructor;return void 0===a||s(n=r(a)[i])?t:o(n)}},64620:(e,t,n)=>{var r=n(95329),o=n(62435),s=n(85803),i=n(48219),a=r("".charAt),l=r("".charCodeAt),c=r("".slice),u=function(e){return function(t,n){var r,u,p=s(i(t)),h=o(n),f=p.length;return h<0||h>=f?e?"":void 0:(r=l(p,h))<55296||r>56319||h+1===f||(u=l(p,h+1))<56320||u>57343?e?a(p,h):r:e?c(p,h,h+2):u-56320+(r-55296<<10)+65536}};e.exports={codeAt:u(!1),charAt:u(!0)}},73291:(e,t,n)=>{var r=n(95329),o=2147483647,s=/[^\0-\u007E]/,i=/[.\u3002\uFF0E\uFF61]/g,a="Overflow: input needs wider integers to process",l=RangeError,c=r(i.exec),u=Math.floor,p=String.fromCharCode,h=r("".charCodeAt),f=r([].join),d=r([].push),m=r("".replace),g=r("".split),y=r("".toLowerCase),v=function(e){return e+22+75*(e<26)},b=function(e,t,n){var r=0;for(e=n?u(e/700):e>>1,e+=u(e/t);e>455;)e=u(e/35),r+=36;return u(r+36*e/(e+38))},w=function(e){var t=[];e=function(e){for(var t=[],n=0,r=e.length;n=55296&&o<=56319&&n=i&&ru((o-c)/E))throw l(a);for(c+=(w-i)*E,i=w,n=0;no)throw l(a);if(r==i){for(var x=c,S=36;;){var _=S<=m?1:S>=m+26?26:S-m;if(x<_)break;var j=x-_,O=36-_;d(t,p(v(_+j%O))),x=u(j/O),S+=36}d(t,p(v(x))),m=b(c,E,y==g),c=0,y++}}c++,i++}return f(t,"")};e.exports=function(e){var t,n,r=[],o=g(m(y(e),i,"."),".");for(t=0;t{"use strict";var r=n(62435),o=n(85803),s=n(48219),i=RangeError;e.exports=function(e){var t=o(s(this)),n="",a=r(e);if(a<0||a==1/0)throw i("Wrong number of repetitions");for(;a>0;(a>>>=1)&&(t+=t))1&a&&(n+=t);return n}},93093:(e,t,n)=>{var r=n(79417).PROPER,o=n(95981),s=n(73483);e.exports=function(e){return o((function(){return!!s[e]()||"​…᠎"!=="​…᠎"[e]()||r&&s[e].name!==e}))}},74853:(e,t,n)=>{var r=n(95329),o=n(48219),s=n(85803),i=n(73483),a=r("".replace),l=RegExp("^["+i+"]+"),c=RegExp("(^|[^"+i+"])["+i+"]+$"),u=function(e){return function(t){var n=s(o(t));return 1&e&&(n=a(n,l,"")),2&e&&(n=a(n,c,"$1")),n}};e.exports={start:u(1),end:u(2),trim:u(3)}},63405:(e,t,n)=>{var r=n(53385),o=n(95981),s=n(21899).String;e.exports=!!Object.getOwnPropertySymbols&&!o((function(){var e=Symbol();return!s(e)||!(Object(e)instanceof Symbol)||!Symbol.sham&&r&&r<41}))},29630:(e,t,n)=>{var r=n(78834),o=n(626),s=n(99813),i=n(95929);e.exports=function(){var e=o("Symbol"),t=e&&e.prototype,n=t&&t.valueOf,a=s("toPrimitive");t&&!t[a]&&i(t,a,(function(e){return r(n,this)}),{arity:1})}},32087:(e,t,n)=>{var r=n(626),o=n(95329),s=r("Symbol"),i=s.keyFor,a=o(s.prototype.valueOf);e.exports=s.isRegisteredSymbol||function(e){try{return void 0!==i(a(e))}catch(e){return!1}}},96559:(e,t,n)=>{for(var r=n(68726),o=n(626),s=n(95329),i=n(56664),a=n(99813),l=o("Symbol"),c=l.isWellKnownSymbol,u=o("Object","getOwnPropertyNames"),p=s(l.prototype.valueOf),h=r("wks"),f=0,d=u(l),m=d.length;f{var r=n(63405);e.exports=r&&!!Symbol.for&&!!Symbol.keyFor},42941:(e,t,n)=>{var r,o,s,i,a=n(21899),l=n(79730),c=n(86843),u=n(57475),p=n(90953),h=n(95981),f=n(15463),d=n(93765),m=n(61333),g=n(18348),y=n(22749),v=n(6049),b=a.setImmediate,w=a.clearImmediate,E=a.process,x=a.Dispatch,S=a.Function,_=a.MessageChannel,j=a.String,O=0,k={},A="onreadystatechange";h((function(){r=a.location}));var C=function(e){if(p(k,e)){var t=k[e];delete k[e],t()}},P=function(e){return function(){C(e)}},N=function(e){C(e.data)},I=function(e){a.postMessage(j(e),r.protocol+"//"+r.host)};b&&w||(b=function(e){g(arguments.length,1);var t=u(e)?e:S(e),n=d(arguments,1);return k[++O]=function(){l(t,void 0,n)},o(O),O},w=function(e){delete k[e]},v?o=function(e){E.nextTick(P(e))}:x&&x.now?o=function(e){x.now(P(e))}:_&&!y?(i=(s=new _).port2,s.port1.onmessage=N,o=c(i.postMessage,i)):a.addEventListener&&u(a.postMessage)&&!a.importScripts&&r&&"file:"!==r.protocol&&!h(I)?(o=I,a.addEventListener("message",N,!1)):o=A in m("script")?function(e){f.appendChild(m("script"))[A]=function(){f.removeChild(this),C(e)}}:function(e){setTimeout(P(e),0)}),e.exports={set:b,clear:w}},59413:(e,t,n)=>{var r=n(62435),o=Math.max,s=Math.min;e.exports=function(e,t){var n=r(e);return n<0?o(n+t,0):s(n,t)}},74529:(e,t,n)=>{var r=n(37026),o=n(48219);e.exports=function(e){return r(o(e))}},62435:(e,t,n)=>{var r=n(35331);e.exports=function(e){var t=+e;return t!=t||0===t?0:r(t)}},43057:(e,t,n)=>{var r=n(62435),o=Math.min;e.exports=function(e){return e>0?o(r(e),9007199254740991):0}},89678:(e,t,n)=>{var r=n(48219),o=Object;e.exports=function(e){return o(r(e))}},46935:(e,t,n)=>{var r=n(78834),o=n(10941),s=n(56664),i=n(14229),a=n(39811),l=n(99813),c=TypeError,u=l("toPrimitive");e.exports=function(e,t){if(!o(e)||s(e))return e;var n,l=i(e,u);if(l){if(void 0===t&&(t="default"),n=r(l,e,t),!o(n)||s(n))return n;throw c("Can't convert object to primitive value")}return void 0===t&&(t="number"),a(e,t)}},83894:(e,t,n)=>{var r=n(46935),o=n(56664);e.exports=function(e){var t=r(e,"string");return o(t)?t:t+""}},22885:(e,t,n)=>{var r={};r[n(99813)("toStringTag")]="z",e.exports="[object z]"===String(r)},85803:(e,t,n)=>{var r=n(9697),o=String;e.exports=function(e){if("Symbol"===r(e))throw TypeError("Cannot convert a Symbol value to a string");return o(e)}},69826:e=>{var t=String;e.exports=function(e){try{return t(e)}catch(e){return"Object"}}},99418:(e,t,n)=>{var r=n(95329),o=0,s=Math.random(),i=r(1..toString);e.exports=function(e){return"Symbol("+(void 0===e?"":e)+")_"+i(++o+s,36)}},14766:(e,t,n)=>{var r=n(95981),o=n(99813),s=n(55746),i=n(82529),a=o("iterator");e.exports=!r((function(){var e=new URL("b?a=1&b=2&c=3","http://a"),t=e.searchParams,n=new URLSearchParams("a=1&a=2"),r="";return e.pathname="c%20d",t.forEach((function(e,n){t.delete("b"),r+=n+e})),n.delete("a",2),i&&(!e.toJSON||!n.has("a",1)||n.has("a",2))||!t.size&&(i||!s)||!t.sort||"http://a/c%20d?a=1&c=3"!==e.href||"3"!==t.get("c")||"a=1"!==String(new URLSearchParams("?a=1"))||!t[a]||"a"!==new URL("https://a@b").username||"b"!==new URLSearchParams(new URLSearchParams("a=b")).get("a")||"xn--e1aybc"!==new URL("http://тест").host||"#%D0%B1"!==new URL("http://a#б").hash||"a1c3"!==r||"x"!==new URL("http://x",void 0).host}))},32302:(e,t,n)=>{var r=n(63405);e.exports=r&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},83937:(e,t,n)=>{var r=n(55746),o=n(95981);e.exports=r&&o((function(){return 42!=Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype}))},18348:e=>{var t=TypeError;e.exports=function(e,n){if(e{var r=n(21899),o=n(57475),s=r.WeakMap;e.exports=o(s)&&/native code/.test(String(s))},73464:(e,t,n)=>{var r=n(54058),o=n(90953),s=n(11477),i=n(65988).f;e.exports=function(e){var t=r.Symbol||(r.Symbol={});o(t,e)||i(t,e,{value:s.f(e)})}},11477:(e,t,n)=>{var r=n(99813);t.f=r},99813:(e,t,n)=>{var r=n(21899),o=n(68726),s=n(90953),i=n(99418),a=n(63405),l=n(32302),c=r.Symbol,u=o("wks"),p=l?c.for||c:c&&c.withoutSetter||i;e.exports=function(e){return s(u,e)||(u[e]=a&&s(c,e)?c[e]:p("Symbol."+e)),u[e]}},73483:e=>{e.exports="\t\n\v\f\r                 \u2028\u2029\ufeff"},49812:(e,t,n)=>{"use strict";var r=n(76887),o=n(7046),s=n(249),i=n(88929),a=n(23489),l=n(29290),c=n(32029),u=n(31887),p=n(53794),h=n(79585),f=n(93091),d=n(14649),m=n(99813)("toStringTag"),g=Error,y=[].push,v=function(e,t){var n,r=o(b,this);i?n=i(g(),r?s(this):b):(n=r?this:l(b),c(n,m,"Error")),void 0!==t&&c(n,"message",d(t)),h(n,v,n.stack,1),arguments.length>2&&p(n,arguments[2]);var a=[];return f(e,y,{that:a}),c(n,"errors",a),n};i?i(v,g):a(v,g,{name:!0});var b=v.prototype=l(g.prototype,{constructor:u(1,v),message:u(1,""),name:u(1,"AggregateError")});r({global:!0,constructor:!0,arity:2},{AggregateError:v})},47627:(e,t,n)=>{n(49812)},85906:(e,t,n)=>{"use strict";var r=n(76887),o=n(95981),s=n(1052),i=n(10941),a=n(89678),l=n(10623),c=n(66796),u=n(55449),p=n(64692),h=n(50568),f=n(99813),d=n(53385),m=f("isConcatSpreadable"),g=d>=51||!o((function(){var e=[];return e[m]=!1,e.concat()[0]!==e})),y=function(e){if(!i(e))return!1;var t=e[m];return void 0!==t?!!t:s(e)};r({target:"Array",proto:!0,arity:1,forced:!g||!h("concat")},{concat:function(e){var t,n,r,o,s,i=a(this),h=p(i,0),f=0;for(t=-1,r=arguments.length;t{"use strict";var r=n(76887),o=n(3610).every;r({target:"Array",proto:!0,forced:!n(34194)("every")},{every:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},80290:(e,t,n)=>{var r=n(76887),o=n(91860),s=n(18479);r({target:"Array",proto:!0},{fill:o}),s("fill")},21501:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).filter;r({target:"Array",proto:!0,forced:!n(50568)("filter")},{filter:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},44929:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).findIndex,s=n(18479),i="findIndex",a=!0;i in[]&&Array(1)[i]((function(){a=!1})),r({target:"Array",proto:!0,forced:a},{findIndex:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}}),s(i)},80833:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).find,s=n(18479),i="find",a=!0;i in[]&&Array(1)[i]((function(){a=!1})),r({target:"Array",proto:!0,forced:a},{find:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}}),s(i)},2437:(e,t,n)=>{"use strict";var r=n(76887),o=n(56837);r({target:"Array",proto:!0,forced:[].forEach!=o},{forEach:o})},53242:(e,t,n)=>{var r=n(76887),o=n(11354);r({target:"Array",stat:!0,forced:!n(21385)((function(e){Array.from(e)}))},{from:o})},97690:(e,t,n)=>{"use strict";var r=n(76887),o=n(31692).includes,s=n(95981),i=n(18479);r({target:"Array",proto:!0,forced:s((function(){return!Array(1).includes()}))},{includes:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}}),i("includes")},99076:(e,t,n)=>{"use strict";var r=n(76887),o=n(97484),s=n(31692).indexOf,i=n(34194),a=o([].indexOf),l=!!a&&1/a([1],1,-0)<0;r({target:"Array",proto:!0,forced:l||!i("indexOf")},{indexOf:function(e){var t=arguments.length>1?arguments[1]:void 0;return l?a(this,e,t)||0:s(this,e,t)}})},92737:(e,t,n)=>{n(76887)({target:"Array",stat:!0},{isArray:n(1052)})},66274:(e,t,n)=>{"use strict";var r=n(74529),o=n(18479),s=n(12077),i=n(45402),a=n(65988).f,l=n(75105),c=n(23538),u=n(82529),p=n(55746),h="Array Iterator",f=i.set,d=i.getterFor(h);e.exports=l(Array,"Array",(function(e,t){f(this,{type:h,target:r(e),index:0,kind:t})}),(function(){var e=d(this),t=e.target,n=e.kind,r=e.index++;return!t||r>=t.length?(e.target=void 0,c(void 0,!0)):c("keys"==n?r:"values"==n?t[r]:[r,t[r]],!1)}),"values");var m=s.Arguments=s.Array;if(o("keys"),o("values"),o("entries"),!u&&p&&"values"!==m.name)try{a(m,"name",{value:"values"})}catch(e){}},75915:(e,t,n)=>{var r=n(76887),o=n(67145);r({target:"Array",proto:!0,forced:o!==[].lastIndexOf},{lastIndexOf:o})},68787:(e,t,n)=>{"use strict";var r=n(76887),o=n(3610).map;r({target:"Array",proto:!0,forced:!n(50568)("map")},{map:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},48528:(e,t,n)=>{"use strict";var r=n(76887),o=n(89678),s=n(10623),i=n(89779),a=n(66796);r({target:"Array",proto:!0,arity:1,forced:n(95981)((function(){return 4294967297!==[].push.call({length:4294967296},1)}))||!function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(e){return e instanceof TypeError}}()},{push:function(e){var t=o(this),n=s(t),r=arguments.length;a(n+r);for(var l=0;l{"use strict";var r=n(76887),o=n(46499).left,s=n(34194),i=n(53385);r({target:"Array",proto:!0,forced:!n(6049)&&i>79&&i<83||!s("reduce")},{reduce:function(e){var t=arguments.length;return o(this,e,t,t>1?arguments[1]:void 0)}})},60186:(e,t,n)=>{"use strict";var r=n(76887),o=n(1052),s=n(24284),i=n(10941),a=n(59413),l=n(10623),c=n(74529),u=n(55449),p=n(99813),h=n(50568),f=n(93765),d=h("slice"),m=p("species"),g=Array,y=Math.max;r({target:"Array",proto:!0,forced:!d},{slice:function(e,t){var n,r,p,h=c(this),d=l(h),v=a(e,d),b=a(void 0===t?d:t,d);if(o(h)&&(n=h.constructor,(s(n)&&(n===g||o(n.prototype))||i(n)&&null===(n=n[m]))&&(n=void 0),n===g||void 0===n))return f(h,v,b);for(r=new(void 0===n?g:n)(y(b-v,0)),p=0;v{"use strict";var r=n(76887),o=n(3610).some;r({target:"Array",proto:!0,forced:!n(34194)("some")},{some:function(e){return o(this,e,arguments.length>1?arguments[1]:void 0)}})},4115:(e,t,n)=>{"use strict";var r=n(76887),o=n(95329),s=n(24883),i=n(89678),a=n(10623),l=n(15863),c=n(85803),u=n(95981),p=n(61388),h=n(34194),f=n(34342),d=n(81046),m=n(53385),g=n(18938),y=[],v=o(y.sort),b=o(y.push),w=u((function(){y.sort(void 0)})),E=u((function(){y.sort(null)})),x=h("sort"),S=!u((function(){if(m)return m<70;if(!(f&&f>3)){if(d)return!0;if(g)return g<603;var e,t,n,r,o="";for(e=65;e<76;e++){switch(t=String.fromCharCode(e),e){case 66:case 69:case 70:case 72:n=3;break;case 68:case 71:n=4;break;default:n=2}for(r=0;r<47;r++)y.push({k:t+r,v:n})}for(y.sort((function(e,t){return t.v-e.v})),r=0;rc(n)?1:-1}}(e)),n=a(o),r=0;r{"use strict";var r=n(76887),o=n(89678),s=n(59413),i=n(62435),a=n(10623),l=n(89779),c=n(66796),u=n(64692),p=n(55449),h=n(15863),f=n(50568)("splice"),d=Math.max,m=Math.min;r({target:"Array",proto:!0,forced:!f},{splice:function(e,t){var n,r,f,g,y,v,b=o(this),w=a(b),E=s(e,w),x=arguments.length;for(0===x?n=r=0:1===x?(n=0,r=w-E):(n=x-2,r=m(d(i(t),0),w-E)),c(w+n-r),f=u(b,r),g=0;gw-r+n;g--)h(b,g-1)}else if(n>r)for(g=w-r;g>E;g--)v=g+n-1,(y=g+r-1)in b?b[v]=b[y]:h(b,v);for(g=0;g{var r=n(76887),o=n(95329),s=Date,i=o(s.prototype.getTime);r({target:"Date",stat:!0},{now:function(){return i(new s)}})},18084:()=>{},73381:(e,t,n)=>{var r=n(76887),o=n(98308);r({target:"Function",proto:!0,forced:Function.bind!==o},{bind:o})},32619:(e,t,n)=>{var r=n(76887),o=n(626),s=n(79730),i=n(78834),a=n(95329),l=n(95981),c=n(57475),u=n(56664),p=n(93765),h=n(33323),f=n(63405),d=String,m=o("JSON","stringify"),g=a(/./.exec),y=a("".charAt),v=a("".charCodeAt),b=a("".replace),w=a(1..toString),E=/[\uD800-\uDFFF]/g,x=/^[\uD800-\uDBFF]$/,S=/^[\uDC00-\uDFFF]$/,_=!f||l((function(){var e=o("Symbol")();return"[null]"!=m([e])||"{}"!=m({a:e})||"{}"!=m(Object(e))})),j=l((function(){return'"\\udf06\\ud834"'!==m("\udf06\ud834")||'"\\udead"'!==m("\udead")})),O=function(e,t){var n=p(arguments),r=h(t);if(c(r)||void 0!==e&&!u(e))return n[1]=function(e,t){if(c(r)&&(t=i(r,this,d(e),t)),!u(t))return t},s(m,null,n)},k=function(e,t,n){var r=y(n,t-1),o=y(n,t+1);return g(x,e)&&!g(S,o)||g(S,e)&&!g(x,r)?"\\u"+w(v(e,0),16):e};m&&r({target:"JSON",stat:!0,arity:3,forced:_||j},{stringify:function(e,t,n){var r=p(arguments),o=s(_?O:m,null,r);return j&&"string"==typeof o?b(o,E,k):o}})},69120:(e,t,n)=>{var r=n(21899);n(90904)(r.JSON,"JSON",!0)},23112:(e,t,n)=>{"use strict";n(24683)("Map",(function(e){return function(){return e(this,arguments.length?arguments[0]:void 0)}}),n(85616))},37501:(e,t,n)=>{n(23112)},79413:()=>{},54973:(e,t,n)=>{n(76887)({target:"Number",stat:!0,nonConfigurable:!0,nonWritable:!0},{EPSILON:Math.pow(2,-52)})},30800:(e,t,n)=>{n(76887)({target:"Number",stat:!0},{isInteger:n(54639)})},49221:(e,t,n)=>{var r=n(76887),o=n(24420);r({target:"Object",stat:!0,arity:2,forced:Object.assign!==o},{assign:o})},74979:(e,t,n)=>{var r=n(76887),o=n(55746),s=n(59938).f;r({target:"Object",stat:!0,forced:Object.defineProperties!==s,sham:!o},{defineProperties:s})},86450:(e,t,n)=>{var r=n(76887),o=n(55746),s=n(65988).f;r({target:"Object",stat:!0,forced:Object.defineProperty!==s,sham:!o},{defineProperty:s})},94366:(e,t,n)=>{var r=n(76887),o=n(88810).entries;r({target:"Object",stat:!0},{entries:function(e){return o(e)}})},28387:(e,t,n)=>{var r=n(76887),o=n(93091),s=n(55449);r({target:"Object",stat:!0},{fromEntries:function(e){var t={};return o(e,(function(e,n){s(t,e,n)}),{AS_ENTRIES:!0}),t}})},46924:(e,t,n)=>{var r=n(76887),o=n(95981),s=n(74529),i=n(49677).f,a=n(55746);r({target:"Object",stat:!0,forced:!a||o((function(){i(1)})),sham:!a},{getOwnPropertyDescriptor:function(e,t){return i(s(e),t)}})},88482:(e,t,n)=>{var r=n(76887),o=n(55746),s=n(31136),i=n(74529),a=n(49677),l=n(55449);r({target:"Object",stat:!0,sham:!o},{getOwnPropertyDescriptors:function(e){for(var t,n,r=i(e),o=a.f,c=s(r),u={},p=0;c.length>p;)void 0!==(n=o(r,t=c[p++]))&&l(u,t,n);return u}})},37144:(e,t,n)=>{var r=n(76887),o=n(63405),s=n(95981),i=n(87857),a=n(89678);r({target:"Object",stat:!0,forced:!o||s((function(){i.f(1)}))},{getOwnPropertySymbols:function(e){var t=i.f;return t?t(a(e)):[]}})},21724:(e,t,n)=>{var r=n(76887),o=n(89678),s=n(14771);r({target:"Object",stat:!0,forced:n(95981)((function(){s(1)}))},{keys:function(e){return s(o(e))}})},55967:()=>{},26614:(e,t,n)=>{var r=n(76887),o=n(88810).values;r({target:"Object",stat:!0},{values:function(e){return o(e)}})},4560:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),s=n(24883),i=n(69520),a=n(40002),l=n(93091);r({target:"Promise",stat:!0,forced:n(31542)},{allSettled:function(e){var t=this,n=i.f(t),r=n.resolve,c=n.reject,u=a((function(){var n=s(t.resolve),i=[],a=0,c=1;l(e,(function(e){var s=a++,l=!1;c++,o(n,t,e).then((function(e){l||(l=!0,i[s]={status:"fulfilled",value:e},--c||r(i))}),(function(e){l||(l=!0,i[s]={status:"rejected",reason:e},--c||r(i))}))})),--c||r(i)}));return u.error&&c(u.value),n.promise}})},16890:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),s=n(24883),i=n(69520),a=n(40002),l=n(93091);r({target:"Promise",stat:!0,forced:n(31542)},{all:function(e){var t=this,n=i.f(t),r=n.resolve,c=n.reject,u=a((function(){var n=s(t.resolve),i=[],a=0,u=1;l(e,(function(e){var s=a++,l=!1;u++,o(n,t,e).then((function(e){l||(l=!0,i[s]=e,--u||r(i))}),c)})),--u||r(i)}));return u.error&&c(u.value),n.promise}})},91302:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),s=n(24883),i=n(626),a=n(69520),l=n(40002),c=n(93091),u=n(31542),p="No one promise resolved";r({target:"Promise",stat:!0,forced:u},{any:function(e){var t=this,n=i("AggregateError"),r=a.f(t),u=r.resolve,h=r.reject,f=l((function(){var r=s(t.resolve),i=[],a=0,l=1,f=!1;c(e,(function(e){var s=a++,c=!1;l++,o(r,t,e).then((function(e){c||f||(f=!0,u(e))}),(function(e){c||f||(c=!0,i[s]=e,--l||h(new n(i,p)))}))})),--l||h(new n(i,p))}));return f.error&&h(f.value),r.promise}})},83376:(e,t,n)=>{"use strict";var r=n(76887),o=n(82529),s=n(67742).CONSTRUCTOR,i=n(6991),a=n(626),l=n(57475),c=n(95929),u=i&&i.prototype;if(r({target:"Promise",proto:!0,forced:s,real:!0},{catch:function(e){return this.then(void 0,e)}}),!o&&l(i)){var p=a("Promise").prototype.catch;u.catch!==p&&c(u,"catch",p,{unsafe:!0})}},26934:(e,t,n)=>{"use strict";var r,o,s,i=n(76887),a=n(82529),l=n(6049),c=n(21899),u=n(78834),p=n(95929),h=n(88929),f=n(90904),d=n(94431),m=n(24883),g=n(57475),y=n(10941),v=n(5743),b=n(70487),w=n(42941).set,E=n(66132),x=n(34845),S=n(40002),_=n(18397),j=n(45402),O=n(6991),k=n(67742),A=n(69520),C="Promise",P=k.CONSTRUCTOR,N=k.REJECTION_EVENT,I=k.SUBCLASSING,T=j.getterFor(C),R=j.set,M=O&&O.prototype,D=O,F=M,L=c.TypeError,B=c.document,$=c.process,q=A.f,U=q,z=!!(B&&B.createEvent&&c.dispatchEvent),V="unhandledrejection",W=function(e){var t;return!(!y(e)||!g(t=e.then))&&t},J=function(e,t){var n,r,o,s=t.value,i=1==t.state,a=i?e.ok:e.fail,l=e.resolve,c=e.reject,p=e.domain;try{a?(i||(2===t.rejection&&Y(t),t.rejection=1),!0===a?n=s:(p&&p.enter(),n=a(s),p&&(p.exit(),o=!0)),n===e.promise?c(L("Promise-chain cycle")):(r=W(n))?u(r,n,l,c):l(n)):c(s)}catch(e){p&&!o&&p.exit(),c(e)}},K=function(e,t){e.notified||(e.notified=!0,E((function(){for(var n,r=e.reactions;n=r.get();)J(n,e);e.notified=!1,t&&!e.rejection&&G(e)})))},H=function(e,t,n){var r,o;z?((r=B.createEvent("Event")).promise=t,r.reason=n,r.initEvent(e,!1,!0),c.dispatchEvent(r)):r={promise:t,reason:n},!N&&(o=c["on"+e])?o(r):e===V&&x("Unhandled promise rejection",n)},G=function(e){u(w,c,(function(){var t,n=e.facade,r=e.value;if(Z(e)&&(t=S((function(){l?$.emit("unhandledRejection",r,n):H(V,n,r)})),e.rejection=l||Z(e)?2:1,t.error))throw t.value}))},Z=function(e){return 1!==e.rejection&&!e.parent},Y=function(e){u(w,c,(function(){var t=e.facade;l?$.emit("rejectionHandled",t):H("rejectionhandled",t,e.value)}))},X=function(e,t,n){return function(r){e(t,r,n)}},Q=function(e,t,n){e.done||(e.done=!0,n&&(e=n),e.value=t,e.state=2,K(e,!0))},ee=function(e,t,n){if(!e.done){e.done=!0,n&&(e=n);try{if(e.facade===t)throw L("Promise can't be resolved itself");var r=W(t);r?E((function(){var n={done:!1};try{u(r,t,X(ee,n,e),X(Q,n,e))}catch(t){Q(n,t,e)}})):(e.value=t,e.state=1,K(e,!1))}catch(t){Q({done:!1},t,e)}}};if(P&&(F=(D=function(e){v(this,F),m(e),u(r,this);var t=T(this);try{e(X(ee,t),X(Q,t))}catch(e){Q(t,e)}}).prototype,(r=function(e){R(this,{type:C,done:!1,notified:!1,parent:!1,reactions:new _,rejection:!1,state:0,value:void 0})}).prototype=p(F,"then",(function(e,t){var n=T(this),r=q(b(this,D));return n.parent=!0,r.ok=!g(e)||e,r.fail=g(t)&&t,r.domain=l?$.domain:void 0,0==n.state?n.reactions.add(r):E((function(){J(r,n)})),r.promise})),o=function(){var e=new r,t=T(e);this.promise=e,this.resolve=X(ee,t),this.reject=X(Q,t)},A.f=q=function(e){return e===D||undefined===e?new o(e):U(e)},!a&&g(O)&&M!==Object.prototype)){s=M.then,I||p(M,"then",(function(e,t){var n=this;return new D((function(e,t){u(s,n,e,t)})).then(e,t)}),{unsafe:!0});try{delete M.constructor}catch(e){}h&&h(M,F)}i({global:!0,constructor:!0,wrap:!0,forced:P},{Promise:D}),f(D,C,!1,!0),d(C)},44349:(e,t,n)=>{"use strict";var r=n(76887),o=n(82529),s=n(6991),i=n(95981),a=n(626),l=n(57475),c=n(70487),u=n(56584),p=n(95929),h=s&&s.prototype;if(r({target:"Promise",proto:!0,real:!0,forced:!!s&&i((function(){h.finally.call({then:function(){}},(function(){}))}))},{finally:function(e){var t=c(this,a("Promise")),n=l(e);return this.then(n?function(n){return u(t,e()).then((function(){return n}))}:e,n?function(n){return u(t,e()).then((function(){throw n}))}:e)}}),!o&&l(s)){var f=a("Promise").prototype.finally;h.finally!==f&&p(h,"finally",f,{unsafe:!0})}},98881:(e,t,n)=>{n(26934),n(16890),n(83376),n(55921),n(64069),n(14482)},55921:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),s=n(24883),i=n(69520),a=n(40002),l=n(93091);r({target:"Promise",stat:!0,forced:n(31542)},{race:function(e){var t=this,n=i.f(t),r=n.reject,c=a((function(){var i=s(t.resolve);l(e,(function(e){o(i,t,e).then(n.resolve,r)}))}));return c.error&&r(c.value),n.promise}})},64069:(e,t,n)=>{"use strict";var r=n(76887),o=n(78834),s=n(69520);r({target:"Promise",stat:!0,forced:n(67742).CONSTRUCTOR},{reject:function(e){var t=s.f(this);return o(t.reject,void 0,e),t.promise}})},14482:(e,t,n)=>{"use strict";var r=n(76887),o=n(626),s=n(82529),i=n(6991),a=n(67742).CONSTRUCTOR,l=n(56584),c=o("Promise"),u=s&&!a;r({target:"Promise",stat:!0,forced:s||a},{resolve:function(e){return l(u&&this===c?i:this,e)}})},1502:()=>{},82266:(e,t,n)=>{"use strict";n(24683)("Set",(function(e){return function(){return e(this,arguments.length?arguments[0]:void 0)}}),n(85616))},69008:(e,t,n)=>{n(82266)},11035:(e,t,n)=>{"use strict";var r=n(76887),o=n(95329),s=n(70344),i=n(48219),a=n(85803),l=n(67772),c=o("".indexOf);r({target:"String",proto:!0,forced:!l("includes")},{includes:function(e){return!!~c(a(i(this)),a(s(e)),arguments.length>1?arguments[1]:void 0)}})},77971:(e,t,n)=>{"use strict";var r=n(64620).charAt,o=n(85803),s=n(45402),i=n(75105),a=n(23538),l="String Iterator",c=s.set,u=s.getterFor(l);i(String,"String",(function(e){c(this,{type:l,string:o(e),index:0})}),(function(){var e,t=u(this),n=t.string,o=t.index;return o>=n.length?a(void 0,!0):(e=r(n,o),t.index+=e.length,a(e,!1))}))},74679:(e,t,n)=>{var r=n(76887),o=n(95329),s=n(74529),i=n(89678),a=n(85803),l=n(10623),c=o([].push),u=o([].join);r({target:"String",stat:!0},{raw:function(e){var t=s(i(e).raw),n=l(t);if(!n)return"";for(var r=arguments.length,o=[],p=0;;){if(c(o,a(t[p++])),p===n)return u(o,"");p{n(76887)({target:"String",proto:!0},{repeat:n(16178)})},94761:(e,t,n)=>{"use strict";var r,o=n(76887),s=n(97484),i=n(49677).f,a=n(43057),l=n(85803),c=n(70344),u=n(48219),p=n(67772),h=n(82529),f=s("".startsWith),d=s("".slice),m=Math.min,g=p("startsWith");o({target:"String",proto:!0,forced:!!(h||g||(r=i(String.prototype,"startsWith"),!r||r.writable))&&!g},{startsWith:function(e){var t=l(u(this));c(e);var n=a(m(arguments.length>1?arguments[1]:void 0,t.length)),r=l(e);return f?f(t,r,n):d(t,n,n+r.length)===r}})},57398:(e,t,n)=>{"use strict";var r=n(76887),o=n(74853).trim;r({target:"String",proto:!0,forced:n(93093)("trim")},{trim:function(){return o(this)}})},8555:(e,t,n)=>{n(73464)("asyncIterator")},48616:(e,t,n)=>{"use strict";var r=n(76887),o=n(21899),s=n(78834),i=n(95329),a=n(82529),l=n(55746),c=n(63405),u=n(95981),p=n(90953),h=n(7046),f=n(96059),d=n(74529),m=n(83894),g=n(85803),y=n(31887),v=n(29290),b=n(14771),w=n(10946),E=n(684),x=n(87857),S=n(49677),_=n(65988),j=n(59938),O=n(36760),k=n(95929),A=n(29202),C=n(68726),P=n(44262),N=n(27748),I=n(99418),T=n(99813),R=n(11477),M=n(73464),D=n(29630),F=n(90904),L=n(45402),B=n(3610).forEach,$=P("hidden"),q="Symbol",U="prototype",z=L.set,V=L.getterFor(q),W=Object[U],J=o.Symbol,K=J&&J[U],H=o.TypeError,G=o.QObject,Z=S.f,Y=_.f,X=E.f,Q=O.f,ee=i([].push),te=C("symbols"),ne=C("op-symbols"),re=C("wks"),oe=!G||!G[U]||!G[U].findChild,se=l&&u((function(){return 7!=v(Y({},"a",{get:function(){return Y(this,"a",{value:7}).a}})).a}))?function(e,t,n){var r=Z(W,t);r&&delete W[t],Y(e,t,n),r&&e!==W&&Y(W,t,r)}:Y,ie=function(e,t){var n=te[e]=v(K);return z(n,{type:q,tag:e,description:t}),l||(n.description=t),n},ae=function(e,t,n){e===W&&ae(ne,t,n),f(e);var r=m(t);return f(n),p(te,r)?(n.enumerable?(p(e,$)&&e[$][r]&&(e[$][r]=!1),n=v(n,{enumerable:y(0,!1)})):(p(e,$)||Y(e,$,y(1,{})),e[$][r]=!0),se(e,r,n)):Y(e,r,n)},le=function(e,t){f(e);var n=d(t),r=b(n).concat(he(n));return B(r,(function(t){l&&!s(ce,n,t)||ae(e,t,n[t])})),e},ce=function(e){var t=m(e),n=s(Q,this,t);return!(this===W&&p(te,t)&&!p(ne,t))&&(!(n||!p(this,t)||!p(te,t)||p(this,$)&&this[$][t])||n)},ue=function(e,t){var n=d(e),r=m(t);if(n!==W||!p(te,r)||p(ne,r)){var o=Z(n,r);return!o||!p(te,r)||p(n,$)&&n[$][r]||(o.enumerable=!0),o}},pe=function(e){var t=X(d(e)),n=[];return B(t,(function(e){p(te,e)||p(N,e)||ee(n,e)})),n},he=function(e){var t=e===W,n=X(t?ne:d(e)),r=[];return B(n,(function(e){!p(te,e)||t&&!p(W,e)||ee(r,te[e])})),r};c||(k(K=(J=function(){if(h(K,this))throw H("Symbol is not a constructor");var e=arguments.length&&void 0!==arguments[0]?g(arguments[0]):void 0,t=I(e),n=function(e){this===W&&s(n,ne,e),p(this,$)&&p(this[$],t)&&(this[$][t]=!1),se(this,t,y(1,e))};return l&&oe&&se(W,t,{configurable:!0,set:n}),ie(t,e)})[U],"toString",(function(){return V(this).tag})),k(J,"withoutSetter",(function(e){return ie(I(e),e)})),O.f=ce,_.f=ae,j.f=le,S.f=ue,w.f=E.f=pe,x.f=he,R.f=function(e){return ie(T(e),e)},l&&(A(K,"description",{configurable:!0,get:function(){return V(this).description}}),a||k(W,"propertyIsEnumerable",ce,{unsafe:!0}))),r({global:!0,constructor:!0,wrap:!0,forced:!c,sham:!c},{Symbol:J}),B(b(re),(function(e){M(e)})),r({target:q,stat:!0,forced:!c},{useSetter:function(){oe=!0},useSimple:function(){oe=!1}}),r({target:"Object",stat:!0,forced:!c,sham:!l},{create:function(e,t){return void 0===t?v(e):le(v(e),t)},defineProperty:ae,defineProperties:le,getOwnPropertyDescriptor:ue}),r({target:"Object",stat:!0,forced:!c},{getOwnPropertyNames:pe}),D(),F(J,q),N[$]=!0},52615:()=>{},64523:(e,t,n)=>{var r=n(76887),o=n(626),s=n(90953),i=n(85803),a=n(68726),l=n(34680),c=a("string-to-symbol-registry"),u=a("symbol-to-string-registry");r({target:"Symbol",stat:!0,forced:!l},{for:function(e){var t=i(e);if(s(c,t))return c[t];var n=o("Symbol")(t);return c[t]=n,u[n]=t,n}})},21732:(e,t,n)=>{n(73464)("hasInstance")},35903:(e,t,n)=>{n(73464)("isConcatSpreadable")},1825:(e,t,n)=>{n(73464)("iterator")},35824:(e,t,n)=>{n(48616),n(64523),n(38608),n(32619),n(37144)},38608:(e,t,n)=>{var r=n(76887),o=n(90953),s=n(56664),i=n(69826),a=n(68726),l=n(34680),c=a("symbol-to-string-registry");r({target:"Symbol",stat:!0,forced:!l},{keyFor:function(e){if(!s(e))throw TypeError(i(e)+" is not a symbol");if(o(c,e))return c[e]}})},45915:(e,t,n)=>{n(73464)("matchAll")},28394:(e,t,n)=>{n(73464)("match")},61766:(e,t,n)=>{n(73464)("replace")},62737:(e,t,n)=>{n(73464)("search")},89911:(e,t,n)=>{n(73464)("species")},74315:(e,t,n)=>{n(73464)("split")},63131:(e,t,n)=>{var r=n(73464),o=n(29630);r("toPrimitive"),o()},64714:(e,t,n)=>{var r=n(626),o=n(73464),s=n(90904);o("toStringTag"),s(r("Symbol"),"Symbol")},70659:(e,t,n)=>{n(73464)("unscopables")},94776:(e,t,n)=>{"use strict";var r,o=n(45602),s=n(21899),i=n(95329),a=n(94380),l=n(21647),c=n(24683),u=n(8850),p=n(10941),h=n(45402).enforce,f=n(95981),d=n(47093),m=Object,g=Array.isArray,y=m.isExtensible,v=m.isFrozen,b=m.isSealed,w=m.freeze,E=m.seal,x={},S={},_=!s.ActiveXObject&&"ActiveXObject"in s,j=function(e){return function(){return e(this,arguments.length?arguments[0]:void 0)}},O=c("WeakMap",j,u),k=O.prototype,A=i(k.set);if(d)if(_){r=u.getConstructor(j,"WeakMap",!0),l.enable();var C=i(k.delete),P=i(k.has),N=i(k.get);a(k,{delete:function(e){if(p(e)&&!y(e)){var t=h(this);return t.frozen||(t.frozen=new r),C(this,e)||t.frozen.delete(e)}return C(this,e)},has:function(e){if(p(e)&&!y(e)){var t=h(this);return t.frozen||(t.frozen=new r),P(this,e)||t.frozen.has(e)}return P(this,e)},get:function(e){if(p(e)&&!y(e)){var t=h(this);return t.frozen||(t.frozen=new r),P(this,e)?N(this,e):t.frozen.get(e)}return N(this,e)},set:function(e,t){if(p(e)&&!y(e)){var n=h(this);n.frozen||(n.frozen=new r),P(this,e)?A(this,e,t):n.frozen.set(e,t)}else A(this,e,t);return this}})}else o&&f((function(){var e=w([]);return A(new O,e,1),!v(e)}))&&a(k,{set:function(e,t){var n;return g(e)&&(v(e)?n=x:b(e)&&(n=S)),A(this,e,t),n==x&&w(e),n==S&&E(e),this}})},54334:(e,t,n)=>{n(94776)},31115:(e,t,n)=>{"use strict";n(24683)("WeakSet",(function(e){return function(){return e(this,arguments.length?arguments[0]:void 0)}}),n(8850))},1773:(e,t,n)=>{n(31115)},97522:(e,t,n)=>{var r=n(99813),o=n(65988).f,s=r("metadata"),i=Function.prototype;void 0===i[s]&&o(i,s,{value:null})},28783:(e,t,n)=>{n(73464)("asyncDispose")},43975:(e,t,n)=>{n(73464)("dispose")},97618:(e,t,n)=>{n(76887)({target:"Symbol",stat:!0},{isRegisteredSymbol:n(32087)})},22731:(e,t,n)=>{n(76887)({target:"Symbol",stat:!0,name:"isRegisteredSymbol"},{isRegistered:n(32087)})},6989:(e,t,n)=>{n(76887)({target:"Symbol",stat:!0,forced:!0},{isWellKnownSymbol:n(96559)})},85605:(e,t,n)=>{n(76887)({target:"Symbol",stat:!0,name:"isWellKnownSymbol",forced:!0},{isWellKnown:n(96559)})},65799:(e,t,n)=>{n(73464)("matcher")},31943:(e,t,n)=>{n(73464)("metadataKey")},45414:(e,t,n)=>{n(73464)("metadata")},46774:(e,t,n)=>{n(73464)("observable")},80620:(e,t,n)=>{n(73464)("patternMatch")},36172:(e,t,n)=>{n(73464)("replaceAll")},7634:(e,t,n)=>{n(66274);var r=n(63281),o=n(21899),s=n(9697),i=n(32029),a=n(12077),l=n(99813)("toStringTag");for(var c in r){var u=o[c],p=u&&u.prototype;p&&s(p)!==l&&i(p,l,c),a[c]=a.Array}},79229:(e,t,n)=>{var r=n(76887),o=n(21899),s=n(37620)(o.setInterval,!0);r({global:!0,bind:!0,forced:o.setInterval!==s},{setInterval:s})},17749:(e,t,n)=>{var r=n(76887),o=n(21899),s=n(37620)(o.setTimeout,!0);r({global:!0,bind:!0,forced:o.setTimeout!==s},{setTimeout:s})},71249:(e,t,n)=>{n(79229),n(17749)},62524:(e,t,n)=>{"use strict";n(66274);var r=n(76887),o=n(21899),s=n(78834),i=n(95329),a=n(55746),l=n(14766),c=n(95929),u=n(29202),p=n(94380),h=n(90904),f=n(53847),d=n(45402),m=n(5743),g=n(57475),y=n(90953),v=n(86843),b=n(9697),w=n(96059),E=n(10941),x=n(85803),S=n(29290),_=n(31887),j=n(53476),O=n(22902),k=n(18348),A=n(99813),C=n(61388),P=A("iterator"),N="URLSearchParams",I=N+"Iterator",T=d.set,R=d.getterFor(N),M=d.getterFor(I),D=Object.getOwnPropertyDescriptor,F=function(e){if(!a)return o[e];var t=D(o,e);return t&&t.value},L=F("fetch"),B=F("Request"),$=F("Headers"),q=B&&B.prototype,U=$&&$.prototype,z=o.RegExp,V=o.TypeError,W=o.decodeURIComponent,J=o.encodeURIComponent,K=i("".charAt),H=i([].join),G=i([].push),Z=i("".replace),Y=i([].shift),X=i([].splice),Q=i("".split),ee=i("".slice),te=/\+/g,ne=Array(4),re=function(e){return ne[e-1]||(ne[e-1]=z("((?:%[\\da-f]{2}){"+e+"})","gi"))},oe=function(e){try{return W(e)}catch(t){return e}},se=function(e){var t=Z(e,te," "),n=4;try{return W(t)}catch(e){for(;n;)t=Z(t,re(n--),oe);return t}},ie=/[!'()~]|%20/g,ae={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+"},le=function(e){return ae[e]},ce=function(e){return Z(J(e),ie,le)},ue=f((function(e,t){T(this,{type:I,iterator:j(R(e).entries),kind:t})}),"Iterator",(function(){var e=M(this),t=e.kind,n=e.iterator.next(),r=n.value;return n.done||(n.value="keys"===t?r.key:"values"===t?r.value:[r.key,r.value]),n}),!0),pe=function(e){this.entries=[],this.url=null,void 0!==e&&(E(e)?this.parseObject(e):this.parseQuery("string"==typeof e?"?"===K(e,0)?ee(e,1):e:x(e)))};pe.prototype={type:N,bindURL:function(e){this.url=e,this.update()},parseObject:function(e){var t,n,r,o,i,a,l,c=O(e);if(c)for(n=(t=j(e,c)).next;!(r=s(n,t)).done;){if(i=(o=j(w(r.value))).next,(a=s(i,o)).done||(l=s(i,o)).done||!s(i,o).done)throw V("Expected sequence with length 2");G(this.entries,{key:x(a.value),value:x(l.value)})}else for(var u in e)y(e,u)&&G(this.entries,{key:u,value:x(e[u])})},parseQuery:function(e){if(e)for(var t,n,r=Q(e,"&"),o=0;o0?arguments[0]:void 0));a||(this.size=e.entries.length)},fe=he.prototype;if(p(fe,{append:function(e,t){var n=R(this);k(arguments.length,2),G(n.entries,{key:x(e),value:x(t)}),a||this.length++,n.updateURL()},delete:function(e){for(var t=R(this),n=k(arguments.length,1),r=t.entries,o=x(e),s=n<2?void 0:arguments[1],i=void 0===s?s:x(s),l=0;lt.key?1:-1})),e.updateURL()},forEach:function(e){for(var t,n=R(this).entries,r=v(e,arguments.length>1?arguments[1]:void 0),o=0;o1?ge(arguments[1]):{})}}),g(B)){var ye=function(e){return m(this,q),new B(e,arguments.length>1?ge(arguments[1]):{})};q.constructor=ye,ye.prototype=q,r({global:!0,constructor:!0,dontCallGetSet:!0,forced:!0},{Request:ye})}}e.exports={URLSearchParams:he,getState:R}},16454:()=>{},73305:()=>{},95304:(e,t,n)=>{n(62524)},62337:()=>{},84630:(e,t,n)=>{var r=n(76887),o=n(626),s=n(95981),i=n(18348),a=n(85803),l=n(14766),c=o("URL");r({target:"URL",stat:!0,forced:!(l&&s((function(){c.canParse()})))},{canParse:function(e){var t=i(arguments.length,1),n=a(e),r=t<2||void 0===arguments[1]?void 0:a(arguments[1]);try{return!!new c(n,r)}catch(e){return!1}}})},47250:(e,t,n)=>{"use strict";n(77971);var r,o=n(76887),s=n(55746),i=n(14766),a=n(21899),l=n(86843),c=n(95329),u=n(95929),p=n(29202),h=n(5743),f=n(90953),d=n(24420),m=n(11354),g=n(15790),y=n(64620).codeAt,v=n(73291),b=n(85803),w=n(90904),E=n(18348),x=n(62524),S=n(45402),_=S.set,j=S.getterFor("URL"),O=x.URLSearchParams,k=x.getState,A=a.URL,C=a.TypeError,P=a.parseInt,N=Math.floor,I=Math.pow,T=c("".charAt),R=c(/./.exec),M=c([].join),D=c(1..toString),F=c([].pop),L=c([].push),B=c("".replace),$=c([].shift),q=c("".split),U=c("".slice),z=c("".toLowerCase),V=c([].unshift),W="Invalid scheme",J="Invalid host",K="Invalid port",H=/[a-z]/i,G=/[\d+-.a-z]/i,Z=/\d/,Y=/^0x/i,X=/^[0-7]+$/,Q=/^\d+$/,ee=/^[\da-f]+$/i,te=/[\0\t\n\r #%/:<>?@[\\\]^|]/,ne=/[\0\t\n\r #/:<>?@[\\\]^|]/,re=/^[\u0000-\u0020]+/,oe=/(^|[^\u0000-\u0020])[\u0000-\u0020]+$/,se=/[\t\n\r]/g,ie=function(e){var t,n,r,o;if("number"==typeof e){for(t=[],n=0;n<4;n++)V(t,e%256),e=N(e/256);return M(t,".")}if("object"==typeof e){for(t="",r=function(e){for(var t=null,n=1,r=null,o=0,s=0;s<8;s++)0!==e[s]?(o>n&&(t=r,n=o),r=null,o=0):(null===r&&(r=s),++o);return o>n&&(t=r,n=o),t}(e),n=0;n<8;n++)o&&0===e[n]||(o&&(o=!1),r===n?(t+=n?":":"::",o=!0):(t+=D(e[n],16),n<7&&(t+=":")));return"["+t+"]"}return e},ae={},le=d({},ae,{" ":1,'"':1,"<":1,">":1,"`":1}),ce=d({},le,{"#":1,"?":1,"{":1,"}":1}),ue=d({},ce,{"/":1,":":1,";":1,"=":1,"@":1,"[":1,"\\":1,"]":1,"^":1,"|":1}),pe=function(e,t){var n=y(e,0);return n>32&&n<127&&!f(t,e)?e:encodeURIComponent(e)},he={ftp:21,file:null,http:80,https:443,ws:80,wss:443},fe=function(e,t){var n;return 2==e.length&&R(H,T(e,0))&&(":"==(n=T(e,1))||!t&&"|"==n)},de=function(e){var t;return e.length>1&&fe(U(e,0,2))&&(2==e.length||"/"===(t=T(e,2))||"\\"===t||"?"===t||"#"===t)},me=function(e){return"."===e||"%2e"===z(e)},ge={},ye={},ve={},be={},we={},Ee={},xe={},Se={},_e={},je={},Oe={},ke={},Ae={},Ce={},Pe={},Ne={},Ie={},Te={},Re={},Me={},De={},Fe=function(e,t,n){var r,o,s,i=b(e);if(t){if(o=this.parse(i))throw C(o);this.searchParams=null}else{if(void 0!==n&&(r=new Fe(n,!0)),o=this.parse(i,null,r))throw C(o);(s=k(new O)).bindURL(this),this.searchParams=s}};Fe.prototype={type:"URL",parse:function(e,t,n){var o,s,i,a,l,c=this,u=t||ge,p=0,h="",d=!1,y=!1,v=!1;for(e=b(e),t||(c.scheme="",c.username="",c.password="",c.host=null,c.port=null,c.path=[],c.query=null,c.fragment=null,c.cannotBeABaseURL=!1,e=B(e,re,""),e=B(e,oe,"$1")),e=B(e,se,""),o=m(e);p<=o.length;){switch(s=o[p],u){case ge:if(!s||!R(H,s)){if(t)return W;u=ve;continue}h+=z(s),u=ye;break;case ye:if(s&&(R(G,s)||"+"==s||"-"==s||"."==s))h+=z(s);else{if(":"!=s){if(t)return W;h="",u=ve,p=0;continue}if(t&&(c.isSpecial()!=f(he,h)||"file"==h&&(c.includesCredentials()||null!==c.port)||"file"==c.scheme&&!c.host))return;if(c.scheme=h,t)return void(c.isSpecial()&&he[c.scheme]==c.port&&(c.port=null));h="","file"==c.scheme?u=Ce:c.isSpecial()&&n&&n.scheme==c.scheme?u=be:c.isSpecial()?u=Se:"/"==o[p+1]?(u=we,p++):(c.cannotBeABaseURL=!0,L(c.path,""),u=Re)}break;case ve:if(!n||n.cannotBeABaseURL&&"#"!=s)return W;if(n.cannotBeABaseURL&&"#"==s){c.scheme=n.scheme,c.path=g(n.path),c.query=n.query,c.fragment="",c.cannotBeABaseURL=!0,u=De;break}u="file"==n.scheme?Ce:Ee;continue;case be:if("/"!=s||"/"!=o[p+1]){u=Ee;continue}u=_e,p++;break;case we:if("/"==s){u=je;break}u=Te;continue;case Ee:if(c.scheme=n.scheme,s==r)c.username=n.username,c.password=n.password,c.host=n.host,c.port=n.port,c.path=g(n.path),c.query=n.query;else if("/"==s||"\\"==s&&c.isSpecial())u=xe;else if("?"==s)c.username=n.username,c.password=n.password,c.host=n.host,c.port=n.port,c.path=g(n.path),c.query="",u=Me;else{if("#"!=s){c.username=n.username,c.password=n.password,c.host=n.host,c.port=n.port,c.path=g(n.path),c.path.length--,u=Te;continue}c.username=n.username,c.password=n.password,c.host=n.host,c.port=n.port,c.path=g(n.path),c.query=n.query,c.fragment="",u=De}break;case xe:if(!c.isSpecial()||"/"!=s&&"\\"!=s){if("/"!=s){c.username=n.username,c.password=n.password,c.host=n.host,c.port=n.port,u=Te;continue}u=je}else u=_e;break;case Se:if(u=_e,"/"!=s||"/"!=T(h,p+1))continue;p++;break;case _e:if("/"!=s&&"\\"!=s){u=je;continue}break;case je:if("@"==s){d&&(h="%40"+h),d=!0,i=m(h);for(var w=0;w65535)return K;c.port=c.isSpecial()&&S===he[c.scheme]?null:S,h=""}if(t)return;u=Ie;continue}return K}h+=s;break;case Ce:if(c.scheme="file","/"==s||"\\"==s)u=Pe;else{if(!n||"file"!=n.scheme){u=Te;continue}if(s==r)c.host=n.host,c.path=g(n.path),c.query=n.query;else if("?"==s)c.host=n.host,c.path=g(n.path),c.query="",u=Me;else{if("#"!=s){de(M(g(o,p),""))||(c.host=n.host,c.path=g(n.path),c.shortenPath()),u=Te;continue}c.host=n.host,c.path=g(n.path),c.query=n.query,c.fragment="",u=De}}break;case Pe:if("/"==s||"\\"==s){u=Ne;break}n&&"file"==n.scheme&&!de(M(g(o,p),""))&&(fe(n.path[0],!0)?L(c.path,n.path[0]):c.host=n.host),u=Te;continue;case Ne:if(s==r||"/"==s||"\\"==s||"?"==s||"#"==s){if(!t&&fe(h))u=Te;else if(""==h){if(c.host="",t)return;u=Ie}else{if(a=c.parseHost(h))return a;if("localhost"==c.host&&(c.host=""),t)return;h="",u=Ie}continue}h+=s;break;case Ie:if(c.isSpecial()){if(u=Te,"/"!=s&&"\\"!=s)continue}else if(t||"?"!=s)if(t||"#"!=s){if(s!=r&&(u=Te,"/"!=s))continue}else c.fragment="",u=De;else c.query="",u=Me;break;case Te:if(s==r||"/"==s||"\\"==s&&c.isSpecial()||!t&&("?"==s||"#"==s)){if(".."===(l=z(l=h))||"%2e."===l||".%2e"===l||"%2e%2e"===l?(c.shortenPath(),"/"==s||"\\"==s&&c.isSpecial()||L(c.path,"")):me(h)?"/"==s||"\\"==s&&c.isSpecial()||L(c.path,""):("file"==c.scheme&&!c.path.length&&fe(h)&&(c.host&&(c.host=""),h=T(h,0)+":"),L(c.path,h)),h="","file"==c.scheme&&(s==r||"?"==s||"#"==s))for(;c.path.length>1&&""===c.path[0];)$(c.path);"?"==s?(c.query="",u=Me):"#"==s&&(c.fragment="",u=De)}else h+=pe(s,ce);break;case Re:"?"==s?(c.query="",u=Me):"#"==s?(c.fragment="",u=De):s!=r&&(c.path[0]+=pe(s,ae));break;case Me:t||"#"!=s?s!=r&&("'"==s&&c.isSpecial()?c.query+="%27":c.query+="#"==s?"%23":pe(s,ae)):(c.fragment="",u=De);break;case De:s!=r&&(c.fragment+=pe(s,le))}p++}},parseHost:function(e){var t,n,r;if("["==T(e,0)){if("]"!=T(e,e.length-1))return J;if(t=function(e){var t,n,r,o,s,i,a,l=[0,0,0,0,0,0,0,0],c=0,u=null,p=0,h=function(){return T(e,p)};if(":"==h()){if(":"!=T(e,1))return;p+=2,u=++c}for(;h();){if(8==c)return;if(":"!=h()){for(t=n=0;n<4&&R(ee,h());)t=16*t+P(h(),16),p++,n++;if("."==h()){if(0==n)return;if(p-=n,c>6)return;for(r=0;h();){if(o=null,r>0){if(!("."==h()&&r<4))return;p++}if(!R(Z,h()))return;for(;R(Z,h());){if(s=P(h(),10),null===o)o=s;else{if(0==o)return;o=10*o+s}if(o>255)return;p++}l[c]=256*l[c]+o,2!=++r&&4!=r||c++}if(4!=r)return;break}if(":"==h()){if(p++,!h())return}else if(h())return;l[c++]=t}else{if(null!==u)return;p++,u=++c}}if(null!==u)for(i=c-u,c=7;0!=c&&i>0;)a=l[c],l[c--]=l[u+i-1],l[u+--i]=a;else if(8!=c)return;return l}(U(e,1,-1)),!t)return J;this.host=t}else if(this.isSpecial()){if(e=v(e),R(te,e))return J;if(t=function(e){var t,n,r,o,s,i,a,l=q(e,".");if(l.length&&""==l[l.length-1]&&l.length--,(t=l.length)>4)return e;for(n=[],r=0;r1&&"0"==T(o,0)&&(s=R(Y,o)?16:8,o=U(o,8==s?1:2)),""===o)i=0;else{if(!R(10==s?Q:8==s?X:ee,o))return e;i=P(o,s)}L(n,i)}for(r=0;r=I(256,5-t))return null}else if(i>255)return null;for(a=F(n),r=0;r1?arguments[1]:void 0,r=_(t,new Fe(e,!1,n));s||(t.href=r.serialize(),t.origin=r.getOrigin(),t.protocol=r.getProtocol(),t.username=r.getUsername(),t.password=r.getPassword(),t.host=r.getHost(),t.hostname=r.getHostname(),t.port=r.getPort(),t.pathname=r.getPathname(),t.search=r.getSearch(),t.searchParams=r.getSearchParams(),t.hash=r.getHash())},Be=Le.prototype,$e=function(e,t){return{get:function(){return j(this)[e]()},set:t&&function(e){return j(this)[t](e)},configurable:!0,enumerable:!0}};if(s&&(p(Be,"href",$e("serialize","setHref")),p(Be,"origin",$e("getOrigin")),p(Be,"protocol",$e("getProtocol","setProtocol")),p(Be,"username",$e("getUsername","setUsername")),p(Be,"password",$e("getPassword","setPassword")),p(Be,"host",$e("getHost","setHost")),p(Be,"hostname",$e("getHostname","setHostname")),p(Be,"port",$e("getPort","setPort")),p(Be,"pathname",$e("getPathname","setPathname")),p(Be,"search",$e("getSearch","setSearch")),p(Be,"searchParams",$e("getSearchParams")),p(Be,"hash",$e("getHash","setHash"))),u(Be,"toJSON",(function(){return j(this).serialize()}),{enumerable:!0}),u(Be,"toString",(function(){return j(this).serialize()}),{enumerable:!0}),A){var qe=A.createObjectURL,Ue=A.revokeObjectURL;qe&&u(Le,"createObjectURL",l(qe,A)),Ue&&u(Le,"revokeObjectURL",l(Ue,A))}w(Le,"URL"),o({global:!0,constructor:!0,forced:!i,sham:!s},{URL:Le})},33601:(e,t,n)=>{n(47250)},98947:()=>{},24848:(e,t,n)=>{var r=n(54493);e.exports=r},83363:(e,t,n)=>{var r=n(24034);e.exports=r},62908:(e,t,n)=>{var r=n(12710);e.exports=r},49216:(e,t,n)=>{var r=n(99324);e.exports=r},56668:(e,t,n)=>{var r=n(95909);e.exports=r},74719:(e,t,n)=>{var r=n(14423);e.exports=r},57784:(e,t,n)=>{var r=n(81103);e.exports=r},28196:(e,t,n)=>{var r=n(16246);e.exports=r},8065:(e,t,n)=>{var r=n(56043);e.exports=r},57448:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),s=n(7046),i=n(62908),a=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.entries;return e===a||s(a,e)&&t===a.entries||o(l,r(e))?i:t}},29455:(e,t,n)=>{var r=n(13160);e.exports=r},69743:(e,t,n)=>{var r=n(80446);e.exports=r},11955:(e,t,n)=>{var r=n(2480);e.exports=r},96064:(e,t,n)=>{var r=n(7147);e.exports=r},61577:(e,t,n)=>{var r=n(32236);e.exports=r},46279:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),s=n(7046),i=n(49216),a=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.forEach;return e===a||s(a,e)&&t===a.forEach||o(l,r(e))?i:t}},33778:(e,t,n)=>{var r=n(58557);e.exports=r},19373:(e,t,n)=>{var r=n(34570);e.exports=r},73819:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),s=n(7046),i=n(56668),a=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.keys;return e===a||s(a,e)&&t===a.keys||o(l,r(e))?i:t}},11022:(e,t,n)=>{var r=n(57564);e.exports=r},61798:(e,t,n)=>{var r=n(88287);e.exports=r},52759:(e,t,n)=>{var r=n(93993);e.exports=r},52527:(e,t,n)=>{var r=n(68025);e.exports=r},36857:(e,t,n)=>{var r=n(59257);e.exports=r},82073:(e,t,n)=>{var r=n(69601);e.exports=r},45286:(e,t,n)=>{var r=n(28299);e.exports=r},62856:(e,t,n)=>{var r=n(69355);e.exports=r},2348:(e,t,n)=>{var r=n(18339);e.exports=r},35178:(e,t,n)=>{var r=n(71611);e.exports=r},76361:(e,t,n)=>{var r=n(62774);e.exports=r},71815:(e,t,n)=>{n(7634);var r=n(9697),o=n(90953),s=n(7046),i=n(74719),a=Array.prototype,l={DOMTokenList:!0,NodeList:!0};e.exports=function(e){var t=e.values;return e===a||s(a,e)&&t===a.values||o(l,r(e))?i:t}},8933:(e,t,n)=>{var r=n(84426);e.exports=r},15868:(e,t,n)=>{var r=n(91018);n(7634),e.exports=r},14873:(e,t,n)=>{var r=n(97849);e.exports=r},38849:(e,t,n)=>{var r=n(3820);e.exports=r},63383:(e,t,n)=>{var r=n(45999);e.exports=r},57396:(e,t,n)=>{var r=n(7702);e.exports=r},41910:(e,t,n)=>{var r=n(48171);e.exports=r},86209:(e,t,n)=>{var r=n(73081);e.exports=r},53402:(e,t,n)=>{var r=n(7699);n(7634),e.exports=r},79427:(e,t,n)=>{var r=n(286);e.exports=r},62857:(e,t,n)=>{var r=n(92766);e.exports=r},9534:(e,t,n)=>{var r=n(30498);e.exports=r},23059:(e,t,n)=>{var r=n(48494);e.exports=r},47795:(e,t,n)=>{var r=n(98430);e.exports=r},27460:(e,t,n)=>{var r=n(52956);n(7634),e.exports=r},27989:(e,t,n)=>{n(71249);var r=n(54058);e.exports=r.setTimeout},5519:(e,t,n)=>{var r=n(76998);n(7634),e.exports=r},23452:(e,t,n)=>{var r=n(97089);e.exports=r},92547:(e,t,n)=>{var r=n(57473);n(7634),e.exports=r},46509:(e,t,n)=>{var r=n(24227);n(7634),e.exports=r},35774:(e,t,n)=>{var r=n(62978);e.exports=r},57641:(e,t,n)=>{var r=n(71459);e.exports=r},72010:(e,t,n)=>{var r=n(32304);n(7634),e.exports=r},93726:(e,t,n)=>{var r=n(29567);n(7634),e.exports=r},47610:(e,t,n)=>{n(95304),n(16454),n(73305),n(62337);var r=n(54058);e.exports=r.URLSearchParams},71459:(e,t,n)=>{n(47610),n(33601),n(84630),n(98947);var r=n(54058);e.exports=r.URL},31905:function(){!function(e){!function(t){var n="URLSearchParams"in e,r="Symbol"in e&&"iterator"in Symbol,o="FileReader"in e&&"Blob"in e&&function(){try{return new Blob,!0}catch(e){return!1}}(),s="FormData"in e,i="ArrayBuffer"in e;if(i)var a=["[object Int8Array]","[object Uint8Array]","[object Uint8ClampedArray]","[object Int16Array]","[object Uint16Array]","[object Int32Array]","[object Uint32Array]","[object Float32Array]","[object Float64Array]"],l=ArrayBuffer.isView||function(e){return e&&a.indexOf(Object.prototype.toString.call(e))>-1};function c(e){if("string"!=typeof e&&(e=String(e)),/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(e))throw new TypeError("Invalid character in header field name");return e.toLowerCase()}function u(e){return"string"!=typeof e&&(e=String(e)),e}function p(e){var t={next:function(){var t=e.shift();return{done:void 0===t,value:t}}};return r&&(t[Symbol.iterator]=function(){return t}),t}function h(e){this.map={},e instanceof h?e.forEach((function(e,t){this.append(t,e)}),this):Array.isArray(e)?e.forEach((function(e){this.append(e[0],e[1])}),this):e&&Object.getOwnPropertyNames(e).forEach((function(t){this.append(t,e[t])}),this)}function f(e){if(e.bodyUsed)return Promise.reject(new TypeError("Already read"));e.bodyUsed=!0}function d(e){return new Promise((function(t,n){e.onload=function(){t(e.result)},e.onerror=function(){n(e.error)}}))}function m(e){var t=new FileReader,n=d(t);return t.readAsArrayBuffer(e),n}function g(e){if(e.slice)return e.slice(0);var t=new Uint8Array(e.byteLength);return t.set(new Uint8Array(e)),t.buffer}function y(){return this.bodyUsed=!1,this._initBody=function(e){var t;this._bodyInit=e,e?"string"==typeof e?this._bodyText=e:o&&Blob.prototype.isPrototypeOf(e)?this._bodyBlob=e:s&&FormData.prototype.isPrototypeOf(e)?this._bodyFormData=e:n&&URLSearchParams.prototype.isPrototypeOf(e)?this._bodyText=e.toString():i&&o&&((t=e)&&DataView.prototype.isPrototypeOf(t))?(this._bodyArrayBuffer=g(e.buffer),this._bodyInit=new Blob([this._bodyArrayBuffer])):i&&(ArrayBuffer.prototype.isPrototypeOf(e)||l(e))?this._bodyArrayBuffer=g(e):this._bodyText=e=Object.prototype.toString.call(e):this._bodyText="",this.headers.get("content-type")||("string"==typeof e?this.headers.set("content-type","text/plain;charset=UTF-8"):this._bodyBlob&&this._bodyBlob.type?this.headers.set("content-type",this._bodyBlob.type):n&&URLSearchParams.prototype.isPrototypeOf(e)&&this.headers.set("content-type","application/x-www-form-urlencoded;charset=UTF-8"))},o&&(this.blob=function(){var e=f(this);if(e)return e;if(this._bodyBlob)return Promise.resolve(this._bodyBlob);if(this._bodyArrayBuffer)return Promise.resolve(new Blob([this._bodyArrayBuffer]));if(this._bodyFormData)throw new Error("could not read FormData body as blob");return Promise.resolve(new Blob([this._bodyText]))},this.arrayBuffer=function(){return this._bodyArrayBuffer?f(this)||Promise.resolve(this._bodyArrayBuffer):this.blob().then(m)}),this.text=function(){var e,t,n,r=f(this);if(r)return r;if(this._bodyBlob)return e=this._bodyBlob,t=new FileReader,n=d(t),t.readAsText(e),n;if(this._bodyArrayBuffer)return Promise.resolve(function(e){for(var t=new Uint8Array(e),n=new Array(t.length),r=0;r-1?r:n),this.mode=t.mode||this.mode||null,this.signal=t.signal||this.signal,this.referrer=null,("GET"===this.method||"HEAD"===this.method)&&o)throw new TypeError("Body not allowed for GET or HEAD requests");this._initBody(o)}function w(e){var t=new FormData;return e.trim().split("&").forEach((function(e){if(e){var n=e.split("="),r=n.shift().replace(/\+/g," "),o=n.join("=").replace(/\+/g," ");t.append(decodeURIComponent(r),decodeURIComponent(o))}})),t}function E(e,t){t||(t={}),this.type="default",this.status=void 0===t.status?200:t.status,this.ok=this.status>=200&&this.status<300,this.statusText="statusText"in t?t.statusText:"OK",this.headers=new h(t.headers),this.url=t.url||"",this._initBody(e)}b.prototype.clone=function(){return new b(this,{body:this._bodyInit})},y.call(b.prototype),y.call(E.prototype),E.prototype.clone=function(){return new E(this._bodyInit,{status:this.status,statusText:this.statusText,headers:new h(this.headers),url:this.url})},E.error=function(){var e=new E(null,{status:0,statusText:""});return e.type="error",e};var x=[301,302,303,307,308];E.redirect=function(e,t){if(-1===x.indexOf(t))throw new RangeError("Invalid status code");return new E(null,{status:t,headers:{location:e}})},t.DOMException=e.DOMException;try{new t.DOMException}catch(e){t.DOMException=function(e,t){this.message=e,this.name=t;var n=Error(e);this.stack=n.stack},t.DOMException.prototype=Object.create(Error.prototype),t.DOMException.prototype.constructor=t.DOMException}function S(e,n){return new Promise((function(r,s){var i=new b(e,n);if(i.signal&&i.signal.aborted)return s(new t.DOMException("Aborted","AbortError"));var a=new XMLHttpRequest;function l(){a.abort()}a.onload=function(){var e,t,n={status:a.status,statusText:a.statusText,headers:(e=a.getAllResponseHeaders()||"",t=new h,e.replace(/\r?\n[\t ]+/g," ").split(/\r?\n/).forEach((function(e){var n=e.split(":"),r=n.shift().trim();if(r){var o=n.join(":").trim();t.append(r,o)}})),t)};n.url="responseURL"in a?a.responseURL:n.headers.get("X-Request-URL");var o="response"in a?a.response:a.responseText;r(new E(o,n))},a.onerror=function(){s(new TypeError("Network request failed"))},a.ontimeout=function(){s(new TypeError("Network request failed"))},a.onabort=function(){s(new t.DOMException("Aborted","AbortError"))},a.open(i.method,i.url,!0),"include"===i.credentials?a.withCredentials=!0:"omit"===i.credentials&&(a.withCredentials=!1),"responseType"in a&&o&&(a.responseType="blob"),i.headers.forEach((function(e,t){a.setRequestHeader(t,e)})),i.signal&&(i.signal.addEventListener("abort",l),a.onreadystatechange=function(){4===a.readyState&&i.signal.removeEventListener("abort",l)}),a.send(void 0===i._bodyInit?null:i._bodyInit)}))}S.polyfill=!0,e.fetch||(e.fetch=S,e.Headers=h,e.Request=b,e.Response=E),t.Headers=h,t.Request=b,t.Response=E,t.fetch=S,Object.defineProperty(t,"__esModule",{value:!0})}({})}("undefined"!=typeof self?self:this)},8269:function(e,t,n){var r;r=void 0!==n.g?n.g:this,e.exports=function(e){if(e.CSS&&e.CSS.escape)return e.CSS.escape;var t=function(e){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var t,n=String(e),r=n.length,o=-1,s="",i=n.charCodeAt(0);++o=1&&t<=31||127==t||0==o&&t>=48&&t<=57||1==o&&t>=48&&t<=57&&45==i?"\\"+t.toString(16)+" ":0==o&&1==r&&45==t||!(t>=128||45==t||95==t||t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122)?"\\"+n.charAt(o):n.charAt(o):s+="�";return s};return e.CSS||(e.CSS={}),e.CSS.escape=t,t}(r)},27698:(e,t,n)=>{"use strict";var r=n(48764).Buffer;function o(e){return e instanceof r||e instanceof Date||e instanceof RegExp}function s(e){if(e instanceof r){var t=r.alloc?r.alloc(e.length):new r(e.length);return e.copy(t),t}if(e instanceof Date)return new Date(e.getTime());if(e instanceof RegExp)return new RegExp(e);throw new Error("Unexpected situation")}function i(e){var t=[];return e.forEach((function(e,n){"object"==typeof e&&null!==e?Array.isArray(e)?t[n]=i(e):o(e)?t[n]=s(e):t[n]=l({},e):t[n]=e})),t}function a(e,t){return"__proto__"===t?void 0:e[t]}var l=e.exports=function(){if(arguments.length<1||"object"!=typeof arguments[0])return!1;if(arguments.length<2)return arguments[0];var e,t,n=arguments[0];return Array.prototype.slice.call(arguments,1).forEach((function(r){"object"!=typeof r||null===r||Array.isArray(r)||Object.keys(r).forEach((function(c){return t=a(n,c),(e=a(r,c))===n?void 0:"object"!=typeof e||null===e?void(n[c]=e):Array.isArray(e)?void(n[c]=i(e)):o(e)?void(n[c]=s(e)):"object"!=typeof t||null===t||Array.isArray(t)?void(n[c]=l({},e)):void(n[c]=l(t,e))}))})),n}},9996:e=>{"use strict";var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===n}(e)}(e)};var n="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function r(e,t){return!1!==t.clone&&t.isMergeableObject(e)?l((n=e,Array.isArray(n)?[]:{}),e,t):e;var n}function o(e,t,n){return e.concat(t).map((function(e){return r(e,n)}))}function s(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return Object.propertyIsEnumerable.call(e,t)})):[]}(e))}function i(e,t){try{return t in e}catch(e){return!1}}function a(e,t,n){var o={};return n.isMergeableObject(e)&&s(e).forEach((function(t){o[t]=r(e[t],n)})),s(t).forEach((function(s){(function(e,t){return i(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,s)||(i(e,s)&&n.isMergeableObject(t[s])?o[s]=function(e,t){if(!t.customMerge)return l;var n=t.customMerge(e);return"function"==typeof n?n:l}(s,n)(e[s],t[s],n):o[s]=r(t[s],n))})),o}function l(e,n,s){(s=s||{}).arrayMerge=s.arrayMerge||o,s.isMergeableObject=s.isMergeableObject||t,s.cloneUnlessOtherwiseSpecified=r;var i=Array.isArray(n);return i===Array.isArray(e)?i?s.arrayMerge(e,n,s):a(e,n,s):r(n,s)}l.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,n){return l(e,n,t)}),{})};var c=l;e.exports=c},27856:function(e){e.exports=function(){"use strict";const{entries:e,setPrototypeOf:t,isFrozen:n,getPrototypeOf:r,getOwnPropertyDescriptor:o}=Object;let{freeze:s,seal:i,create:a}=Object,{apply:l,construct:c}="undefined"!=typeof Reflect&&Reflect;l||(l=function(e,t,n){return e.apply(t,n)}),s||(s=function(e){return e}),i||(i=function(e){return e}),c||(c=function(e,t){return new e(...t)});const u=E(Array.prototype.forEach),p=E(Array.prototype.pop),h=E(Array.prototype.push),f=E(String.prototype.toLowerCase),d=E(String.prototype.toString),m=E(String.prototype.match),g=E(String.prototype.replace),y=E(String.prototype.indexOf),v=E(String.prototype.trim),b=E(RegExp.prototype.test),w=x(TypeError);function E(e){return function(t){for(var n=arguments.length,r=new Array(n>1?n-1:0),o=1;o/gm),B=i(/\${[\w\W]*}/gm),$=i(/^data-[\-\w.\u00B7-\uFFFF]/),q=i(/^aria-[\-\w]+$/),U=i(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i),z=i(/^(?:\w+script|data):/i),V=i(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g),W=i(/^html$/i);var J=Object.freeze({__proto__:null,MUSTACHE_EXPR:F,ERB_EXPR:L,TMPLIT_EXPR:B,DATA_ATTR:$,ARIA_ATTR:q,IS_ALLOWED_URI:U,IS_SCRIPT_OR_DATA:z,ATTR_WHITESPACE:V,DOCTYPE_NAME:W});const K=()=>"undefined"==typeof window?null:window,H=function(e,t){if("object"!=typeof e||"function"!=typeof e.createPolicy)return null;let n=null;const r="data-tt-policy-suffix";t&&t.hasAttribute(r)&&(n=t.getAttribute(r));const o="dompurify"+(n?"#"+n:"");try{return e.createPolicy(o,{createHTML:e=>e,createScriptURL:e=>e})}catch(e){return console.warn("TrustedTypes policy "+o+" could not be created."),null}};function G(){let t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:K();const n=e=>G(e);if(n.version="3.0.5",n.removed=[],!t||!t.document||9!==t.document.nodeType)return n.isSupported=!1,n;const r=t.document,o=r.currentScript;let{document:i}=t;const{DocumentFragment:a,HTMLTemplateElement:l,Node:c,Element:E,NodeFilter:x,NamedNodeMap:F=t.NamedNodeMap||t.MozNamedAttrMap,HTMLFormElement:L,DOMParser:B,trustedTypes:$}=t,q=E.prototype,z=j(q,"cloneNode"),V=j(q,"nextSibling"),Z=j(q,"childNodes"),Y=j(q,"parentNode");if("function"==typeof l){const e=i.createElement("template");e.content&&e.content.ownerDocument&&(i=e.content.ownerDocument)}let X,Q="";const{implementation:ee,createNodeIterator:te,createDocumentFragment:ne,getElementsByTagName:re}=i,{importNode:oe}=r;let se={};n.isSupported="function"==typeof e&&"function"==typeof Y&&ee&&void 0!==ee.createHTMLDocument;const{MUSTACHE_EXPR:ie,ERB_EXPR:ae,TMPLIT_EXPR:le,DATA_ATTR:ce,ARIA_ATTR:ue,IS_SCRIPT_OR_DATA:pe,ATTR_WHITESPACE:he}=J;let{IS_ALLOWED_URI:fe}=J,de=null;const me=S({},[...O,...k,...A,...P,...I]);let ge=null;const ye=S({},[...T,...R,...M,...D]);let ve=Object.seal(Object.create(null,{tagNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},attributeNameCheck:{writable:!0,configurable:!1,enumerable:!0,value:null},allowCustomizedBuiltInElements:{writable:!0,configurable:!1,enumerable:!0,value:!1}})),be=null,we=null,Ee=!0,xe=!0,Se=!1,_e=!0,je=!1,Oe=!1,ke=!1,Ae=!1,Ce=!1,Pe=!1,Ne=!1,Ie=!0,Te=!1;const Re="user-content-";let Me=!0,De=!1,Fe={},Le=null;const Be=S({},["annotation-xml","audio","colgroup","desc","foreignobject","head","iframe","math","mi","mn","mo","ms","mtext","noembed","noframes","noscript","plaintext","script","style","svg","template","thead","title","video","xmp"]);let $e=null;const qe=S({},["audio","video","img","source","image","track"]);let Ue=null;const ze=S({},["alt","class","for","id","label","name","pattern","placeholder","role","summary","title","value","style","xmlns"]),Ve="http://www.w3.org/1998/Math/MathML",We="http://www.w3.org/2000/svg",Je="http://www.w3.org/1999/xhtml";let Ke=Je,He=!1,Ge=null;const Ze=S({},[Ve,We,Je],d);let Ye;const Xe=["application/xhtml+xml","text/html"],Qe="text/html";let et,tt=null;const nt=i.createElement("form"),rt=function(e){return e instanceof RegExp||e instanceof Function},ot=function(e){if(!tt||tt!==e){if(e&&"object"==typeof e||(e={}),e=_(e),Ye=Ye=-1===Xe.indexOf(e.PARSER_MEDIA_TYPE)?Qe:e.PARSER_MEDIA_TYPE,et="application/xhtml+xml"===Ye?d:f,de="ALLOWED_TAGS"in e?S({},e.ALLOWED_TAGS,et):me,ge="ALLOWED_ATTR"in e?S({},e.ALLOWED_ATTR,et):ye,Ge="ALLOWED_NAMESPACES"in e?S({},e.ALLOWED_NAMESPACES,d):Ze,Ue="ADD_URI_SAFE_ATTR"in e?S(_(ze),e.ADD_URI_SAFE_ATTR,et):ze,$e="ADD_DATA_URI_TAGS"in e?S(_(qe),e.ADD_DATA_URI_TAGS,et):qe,Le="FORBID_CONTENTS"in e?S({},e.FORBID_CONTENTS,et):Be,be="FORBID_TAGS"in e?S({},e.FORBID_TAGS,et):{},we="FORBID_ATTR"in e?S({},e.FORBID_ATTR,et):{},Fe="USE_PROFILES"in e&&e.USE_PROFILES,Ee=!1!==e.ALLOW_ARIA_ATTR,xe=!1!==e.ALLOW_DATA_ATTR,Se=e.ALLOW_UNKNOWN_PROTOCOLS||!1,_e=!1!==e.ALLOW_SELF_CLOSE_IN_ATTR,je=e.SAFE_FOR_TEMPLATES||!1,Oe=e.WHOLE_DOCUMENT||!1,Ce=e.RETURN_DOM||!1,Pe=e.RETURN_DOM_FRAGMENT||!1,Ne=e.RETURN_TRUSTED_TYPE||!1,Ae=e.FORCE_BODY||!1,Ie=!1!==e.SANITIZE_DOM,Te=e.SANITIZE_NAMED_PROPS||!1,Me=!1!==e.KEEP_CONTENT,De=e.IN_PLACE||!1,fe=e.ALLOWED_URI_REGEXP||U,Ke=e.NAMESPACE||Je,ve=e.CUSTOM_ELEMENT_HANDLING||{},e.CUSTOM_ELEMENT_HANDLING&&rt(e.CUSTOM_ELEMENT_HANDLING.tagNameCheck)&&(ve.tagNameCheck=e.CUSTOM_ELEMENT_HANDLING.tagNameCheck),e.CUSTOM_ELEMENT_HANDLING&&rt(e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck)&&(ve.attributeNameCheck=e.CUSTOM_ELEMENT_HANDLING.attributeNameCheck),e.CUSTOM_ELEMENT_HANDLING&&"boolean"==typeof e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements&&(ve.allowCustomizedBuiltInElements=e.CUSTOM_ELEMENT_HANDLING.allowCustomizedBuiltInElements),je&&(xe=!1),Pe&&(Ce=!0),Fe&&(de=S({},[...I]),ge=[],!0===Fe.html&&(S(de,O),S(ge,T)),!0===Fe.svg&&(S(de,k),S(ge,R),S(ge,D)),!0===Fe.svgFilters&&(S(de,A),S(ge,R),S(ge,D)),!0===Fe.mathMl&&(S(de,P),S(ge,M),S(ge,D))),e.ADD_TAGS&&(de===me&&(de=_(de)),S(de,e.ADD_TAGS,et)),e.ADD_ATTR&&(ge===ye&&(ge=_(ge)),S(ge,e.ADD_ATTR,et)),e.ADD_URI_SAFE_ATTR&&S(Ue,e.ADD_URI_SAFE_ATTR,et),e.FORBID_CONTENTS&&(Le===Be&&(Le=_(Le)),S(Le,e.FORBID_CONTENTS,et)),Me&&(de["#text"]=!0),Oe&&S(de,["html","head","body"]),de.table&&(S(de,["tbody"]),delete be.tbody),e.TRUSTED_TYPES_POLICY){if("function"!=typeof e.TRUSTED_TYPES_POLICY.createHTML)throw w('TRUSTED_TYPES_POLICY configuration option must provide a "createHTML" hook.');if("function"!=typeof e.TRUSTED_TYPES_POLICY.createScriptURL)throw w('TRUSTED_TYPES_POLICY configuration option must provide a "createScriptURL" hook.');X=e.TRUSTED_TYPES_POLICY,Q=X.createHTML("")}else void 0===X&&(X=H($,o)),null!==X&&"string"==typeof Q&&(Q=X.createHTML(""));s&&s(e),tt=e}},st=S({},["mi","mo","mn","ms","mtext"]),it=S({},["foreignobject","desc","title","annotation-xml"]),at=S({},["title","style","font","a","script"]),lt=S({},k);S(lt,A),S(lt,C);const ct=S({},P);S(ct,N);const ut=function(e){let t=Y(e);t&&t.tagName||(t={namespaceURI:Ke,tagName:"template"});const n=f(e.tagName),r=f(t.tagName);return!!Ge[e.namespaceURI]&&(e.namespaceURI===We?t.namespaceURI===Je?"svg"===n:t.namespaceURI===Ve?"svg"===n&&("annotation-xml"===r||st[r]):Boolean(lt[n]):e.namespaceURI===Ve?t.namespaceURI===Je?"math"===n:t.namespaceURI===We?"math"===n&&it[r]:Boolean(ct[n]):e.namespaceURI===Je?!(t.namespaceURI===We&&!it[r])&&!(t.namespaceURI===Ve&&!st[r])&&!ct[n]&&(at[n]||!lt[n]):!("application/xhtml+xml"!==Ye||!Ge[e.namespaceURI]))},pt=function(e){h(n.removed,{element:e});try{e.parentNode.removeChild(e)}catch(t){e.remove()}},ht=function(e,t){try{h(n.removed,{attribute:t.getAttributeNode(e),from:t})}catch(e){h(n.removed,{attribute:null,from:t})}if(t.removeAttribute(e),"is"===e&&!ge[e])if(Ce||Pe)try{pt(t)}catch(e){}else try{t.setAttribute(e,"")}catch(e){}},ft=function(e){let t,n;if(Ae)e=""+e;else{const t=m(e,/^[\r\n\t ]+/);n=t&&t[0]}"application/xhtml+xml"===Ye&&Ke===Je&&(e=''+e+"");const r=X?X.createHTML(e):e;if(Ke===Je)try{t=(new B).parseFromString(r,Ye)}catch(e){}if(!t||!t.documentElement){t=ee.createDocument(Ke,"template",null);try{t.documentElement.innerHTML=He?Q:r}catch(e){}}const o=t.body||t.documentElement;return e&&n&&o.insertBefore(i.createTextNode(n),o.childNodes[0]||null),Ke===Je?re.call(t,Oe?"html":"body")[0]:Oe?t.documentElement:o},dt=function(e){return te.call(e.ownerDocument||e,e,x.SHOW_ELEMENT|x.SHOW_COMMENT|x.SHOW_TEXT,null,!1)},mt=function(e){return e instanceof L&&("string"!=typeof e.nodeName||"string"!=typeof e.textContent||"function"!=typeof e.removeChild||!(e.attributes instanceof F)||"function"!=typeof e.removeAttribute||"function"!=typeof e.setAttribute||"string"!=typeof e.namespaceURI||"function"!=typeof e.insertBefore||"function"!=typeof e.hasChildNodes)},gt=function(e){return"object"==typeof c?e instanceof c:e&&"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName},yt=function(e,t,r){se[e]&&u(se[e],(e=>{e.call(n,t,r,tt)}))},vt=function(e){let t;if(yt("beforeSanitizeElements",e,null),mt(e))return pt(e),!0;const r=et(e.nodeName);if(yt("uponSanitizeElement",e,{tagName:r,allowedTags:de}),e.hasChildNodes()&&!gt(e.firstElementChild)&&(!gt(e.content)||!gt(e.content.firstElementChild))&&b(/<[/\w]/g,e.innerHTML)&&b(/<[/\w]/g,e.textContent))return pt(e),!0;if(!de[r]||be[r]){if(!be[r]&&wt(r)){if(ve.tagNameCheck instanceof RegExp&&b(ve.tagNameCheck,r))return!1;if(ve.tagNameCheck instanceof Function&&ve.tagNameCheck(r))return!1}if(Me&&!Le[r]){const t=Y(e)||e.parentNode,n=Z(e)||e.childNodes;if(n&&t)for(let r=n.length-1;r>=0;--r)t.insertBefore(z(n[r],!0),V(e))}return pt(e),!0}return e instanceof E&&!ut(e)?(pt(e),!0):"noscript"!==r&&"noembed"!==r&&"noframes"!==r||!b(/<\/no(script|embed|frames)/i,e.innerHTML)?(je&&3===e.nodeType&&(t=e.textContent,t=g(t,ie," "),t=g(t,ae," "),t=g(t,le," "),e.textContent!==t&&(h(n.removed,{element:e.cloneNode()}),e.textContent=t)),yt("afterSanitizeElements",e,null),!1):(pt(e),!0)},bt=function(e,t,n){if(Ie&&("id"===t||"name"===t)&&(n in i||n in nt))return!1;if(xe&&!we[t]&&b(ce,t));else if(Ee&&b(ue,t));else if(!ge[t]||we[t]){if(!(wt(e)&&(ve.tagNameCheck instanceof RegExp&&b(ve.tagNameCheck,e)||ve.tagNameCheck instanceof Function&&ve.tagNameCheck(e))&&(ve.attributeNameCheck instanceof RegExp&&b(ve.attributeNameCheck,t)||ve.attributeNameCheck instanceof Function&&ve.attributeNameCheck(t))||"is"===t&&ve.allowCustomizedBuiltInElements&&(ve.tagNameCheck instanceof RegExp&&b(ve.tagNameCheck,n)||ve.tagNameCheck instanceof Function&&ve.tagNameCheck(n))))return!1}else if(Ue[t]);else if(b(fe,g(n,he,"")));else if("src"!==t&&"xlink:href"!==t&&"href"!==t||"script"===e||0!==y(n,"data:")||!$e[e])if(Se&&!b(pe,g(n,he,"")));else if(n)return!1;return!0},wt=function(e){return e.indexOf("-")>0},Et=function(e){let t,r,o,s;yt("beforeSanitizeAttributes",e,null);const{attributes:i}=e;if(!i)return;const a={attrName:"",attrValue:"",keepAttr:!0,allowedAttributes:ge};for(s=i.length;s--;){t=i[s];const{name:l,namespaceURI:c}=t;if(r="value"===l?t.value:v(t.value),o=et(l),a.attrName=o,a.attrValue=r,a.keepAttr=!0,a.forceKeepAttr=void 0,yt("uponSanitizeAttribute",e,a),r=a.attrValue,a.forceKeepAttr)continue;if(ht(l,e),!a.keepAttr)continue;if(!_e&&b(/\/>/i,r)){ht(l,e);continue}je&&(r=g(r,ie," "),r=g(r,ae," "),r=g(r,le," "));const u=et(e.nodeName);if(bt(u,o,r)){if(!Te||"id"!==o&&"name"!==o||(ht(l,e),r=Re+r),X&&"object"==typeof $&&"function"==typeof $.getAttributeType)if(c);else switch($.getAttributeType(u,o)){case"TrustedHTML":r=X.createHTML(r);break;case"TrustedScriptURL":r=X.createScriptURL(r)}try{c?e.setAttributeNS(c,l,r):e.setAttribute(l,r),p(n.removed)}catch(e){}}}yt("afterSanitizeAttributes",e,null)},xt=function e(t){let n;const r=dt(t);for(yt("beforeSanitizeShadowDOM",t,null);n=r.nextNode();)yt("uponSanitizeShadowNode",n,null),vt(n)||(n.content instanceof a&&e(n.content),Et(n));yt("afterSanitizeShadowDOM",t,null)};return n.sanitize=function(e){let t,o,s,i,l=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(He=!e,He&&(e="\x3c!--\x3e"),"string"!=typeof e&&!gt(e)){if("function"!=typeof e.toString)throw w("toString is not a function");if("string"!=typeof(e=e.toString()))throw w("dirty is not a string, aborting")}if(!n.isSupported)return e;if(ke||ot(l),n.removed=[],"string"==typeof e&&(De=!1),De){if(e.nodeName){const t=et(e.nodeName);if(!de[t]||be[t])throw w("root node is forbidden and cannot be sanitized in-place")}}else if(e instanceof c)t=ft("\x3c!----\x3e"),o=t.ownerDocument.importNode(e,!0),1===o.nodeType&&"BODY"===o.nodeName||"HTML"===o.nodeName?t=o:t.appendChild(o);else{if(!Ce&&!je&&!Oe&&-1===e.indexOf("<"))return X&&Ne?X.createHTML(e):e;if(t=ft(e),!t)return Ce?null:Ne?Q:""}t&&Ae&&pt(t.firstChild);const u=dt(De?e:t);for(;s=u.nextNode();)vt(s)||(s.content instanceof a&&xt(s.content),Et(s));if(De)return e;if(Ce){if(Pe)for(i=ne.call(t.ownerDocument);t.firstChild;)i.appendChild(t.firstChild);else i=t;return(ge.shadowroot||ge.shadowrootmode)&&(i=oe.call(r,i,!0)),i}let p=Oe?t.outerHTML:t.innerHTML;return Oe&&de["!doctype"]&&t.ownerDocument&&t.ownerDocument.doctype&&t.ownerDocument.doctype.name&&b(W,t.ownerDocument.doctype.name)&&(p="\n"+p),je&&(p=g(p,ie," "),p=g(p,ae," "),p=g(p,le," ")),X&&Ne?X.createHTML(p):p},n.setConfig=function(e){ot(e),ke=!0},n.clearConfig=function(){tt=null,ke=!1},n.isValidAttribute=function(e,t,n){tt||ot({});const r=et(e),o=et(t);return bt(r,o,n)},n.addHook=function(e,t){"function"==typeof t&&(se[e]=se[e]||[],h(se[e],t))},n.removeHook=function(e){if(se[e])return p(se[e])},n.removeHooks=function(e){se[e]&&(se[e]=[])},n.removeAllHooks=function(){se={}},n}return G()}()},69450:e=>{"use strict";class t{constructor(e,t){this.low=e,this.high=t,this.length=1+t-e}overlaps(e){return!(this.highe.high)}touches(e){return!(this.high+1e.high)}add(e){return new t(Math.min(this.low,e.low),Math.max(this.high,e.high))}subtract(e){return e.low<=this.low&&e.high>=this.high?[]:e.low>this.low&&e.highe+t.length),0)}add(e,r){var o=e=>{for(var t=0;t{for(var t=0;t{for(var n=0;n{for(var n=t.low;n<=t.high;)e.push(n),n++;return e}),[])}subranges(){return this.ranges.map((e=>({low:e.low,high:e.high,length:1+e.high-e.low})))}}e.exports=n},17187:e=>{"use strict";var t,n="object"==typeof Reflect?Reflect:null,r=n&&"function"==typeof n.apply?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};t=n&&"function"==typeof n.ownKeys?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var o=Number.isNaN||function(e){return e!=e};function s(){s.init.call(this)}e.exports=s,e.exports.once=function(e,t){return new Promise((function(n,r){function o(n){e.removeListener(t,s),r(n)}function s(){"function"==typeof e.removeListener&&e.removeListener("error",o),n([].slice.call(arguments))}m(e,t,s,{once:!0}),"error"!==t&&function(e,t,n){"function"==typeof e.on&&m(e,"error",t,n)}(e,o,{once:!0})}))},s.EventEmitter=s,s.prototype._events=void 0,s.prototype._eventsCount=0,s.prototype._maxListeners=void 0;var i=10;function a(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function l(e){return void 0===e._maxListeners?s.defaultMaxListeners:e._maxListeners}function c(e,t,n,r){var o,s,i,c;if(a(n),void 0===(s=e._events)?(s=e._events=Object.create(null),e._eventsCount=0):(void 0!==s.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),s=e._events),i=s[t]),void 0===i)i=s[t]=n,++e._eventsCount;else if("function"==typeof i?i=s[t]=r?[n,i]:[i,n]:r?i.unshift(n):i.push(n),(o=l(e))>0&&i.length>o&&!i.warned){i.warned=!0;var u=new Error("Possible EventEmitter memory leak detected. "+i.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");u.name="MaxListenersExceededWarning",u.emitter=e,u.type=t,u.count=i.length,c=u,console&&console.warn&&console.warn(c)}return e}function u(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var r={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=u.bind(r);return o.listener=n,r.wrapFn=o,o}function h(e,t,n){var r=e._events;if(void 0===r)return[];var o=r[t];return void 0===o?[]:"function"==typeof o?n?[o.listener||o]:[o]:n?function(e){for(var t=new Array(e.length),n=0;n0&&(i=t[0]),i instanceof Error)throw i;var a=new Error("Unhandled error."+(i?" ("+i.message+")":""));throw a.context=i,a}var l=s[e];if(void 0===l)return!1;if("function"==typeof l)r(l,this,t);else{var c=l.length,u=d(l,c);for(n=0;n=0;s--)if(n[s]===t||n[s].listener===t){i=n[s].listener,o=s;break}if(o<0)return this;0===o?n.shift():function(e,t){for(;t+1=0;r--)this.removeListener(e,t[r]);return this},s.prototype.listeners=function(e){return h(this,e,!0)},s.prototype.rawListeners=function(e){return h(this,e,!1)},s.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):f.call(e,t)},s.prototype.listenerCount=f,s.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}},21102:(e,t,n)=>{"use strict";var r=n(46291),o=s(Error);function s(e){return t.displayName=e.displayName||e.name,t;function t(t){return t&&(t=r.apply(null,arguments)),new e(t)}}e.exports=o,o.eval=s(EvalError),o.range=s(RangeError),o.reference=s(ReferenceError),o.syntax=s(SyntaxError),o.type=s(TypeError),o.uri=s(URIError),o.create=s},46291:e=>{!function(){var t;function n(e){for(var t,n,r,o,s=1,i=[].slice.call(arguments),a=0,l=e.length,c="",u=!1,p=!1,h=function(){return i[s++]},f=function(){for(var n="";/\d/.test(e[a]);)n+=e[a++],t=e[a];return n.length>0?parseInt(n):null};a{"use strict";var t=Array.prototype.slice,n=Object.prototype.toString;e.exports=function(e){var r=this;if("function"!=typeof r||"[object Function]"!==n.call(r))throw new TypeError("Function.prototype.bind called on incompatible "+r);for(var o,s=t.call(arguments,1),i=Math.max(0,r.length-s.length),a=[],l=0;l{"use strict";var r=n(17648);e.exports=Function.prototype.bind||r},40210:(e,t,n)=>{"use strict";var r,o=SyntaxError,s=Function,i=TypeError,a=function(e){try{return s('"use strict"; return ('+e+").constructor;")()}catch(e){}},l=Object.getOwnPropertyDescriptor;if(l)try{l({},"")}catch(e){l=null}var c=function(){throw new i},u=l?function(){try{return c}catch(e){try{return l(arguments,"callee").get}catch(e){return c}}}():c,p=n(41405)(),h=n(28185)(),f=Object.getPrototypeOf||(h?function(e){return e.__proto__}:null),d={},m="undefined"!=typeof Uint8Array&&f?f(Uint8Array):r,g={"%AggregateError%":"undefined"==typeof AggregateError?r:AggregateError,"%Array%":Array,"%ArrayBuffer%":"undefined"==typeof ArrayBuffer?r:ArrayBuffer,"%ArrayIteratorPrototype%":p&&f?f([][Symbol.iterator]()):r,"%AsyncFromSyncIteratorPrototype%":r,"%AsyncFunction%":d,"%AsyncGenerator%":d,"%AsyncGeneratorFunction%":d,"%AsyncIteratorPrototype%":d,"%Atomics%":"undefined"==typeof Atomics?r:Atomics,"%BigInt%":"undefined"==typeof BigInt?r:BigInt,"%BigInt64Array%":"undefined"==typeof BigInt64Array?r:BigInt64Array,"%BigUint64Array%":"undefined"==typeof BigUint64Array?r:BigUint64Array,"%Boolean%":Boolean,"%DataView%":"undefined"==typeof DataView?r:DataView,"%Date%":Date,"%decodeURI%":decodeURI,"%decodeURIComponent%":decodeURIComponent,"%encodeURI%":encodeURI,"%encodeURIComponent%":encodeURIComponent,"%Error%":Error,"%eval%":eval,"%EvalError%":EvalError,"%Float32Array%":"undefined"==typeof Float32Array?r:Float32Array,"%Float64Array%":"undefined"==typeof Float64Array?r:Float64Array,"%FinalizationRegistry%":"undefined"==typeof FinalizationRegistry?r:FinalizationRegistry,"%Function%":s,"%GeneratorFunction%":d,"%Int8Array%":"undefined"==typeof Int8Array?r:Int8Array,"%Int16Array%":"undefined"==typeof Int16Array?r:Int16Array,"%Int32Array%":"undefined"==typeof Int32Array?r:Int32Array,"%isFinite%":isFinite,"%isNaN%":isNaN,"%IteratorPrototype%":p&&f?f(f([][Symbol.iterator]())):r,"%JSON%":"object"==typeof JSON?JSON:r,"%Map%":"undefined"==typeof Map?r:Map,"%MapIteratorPrototype%":"undefined"!=typeof Map&&p&&f?f((new Map)[Symbol.iterator]()):r,"%Math%":Math,"%Number%":Number,"%Object%":Object,"%parseFloat%":parseFloat,"%parseInt%":parseInt,"%Promise%":"undefined"==typeof Promise?r:Promise,"%Proxy%":"undefined"==typeof Proxy?r:Proxy,"%RangeError%":RangeError,"%ReferenceError%":ReferenceError,"%Reflect%":"undefined"==typeof Reflect?r:Reflect,"%RegExp%":RegExp,"%Set%":"undefined"==typeof Set?r:Set,"%SetIteratorPrototype%":"undefined"!=typeof Set&&p&&f?f((new Set)[Symbol.iterator]()):r,"%SharedArrayBuffer%":"undefined"==typeof SharedArrayBuffer?r:SharedArrayBuffer,"%String%":String,"%StringIteratorPrototype%":p&&f?f(""[Symbol.iterator]()):r,"%Symbol%":p?Symbol:r,"%SyntaxError%":o,"%ThrowTypeError%":u,"%TypedArray%":m,"%TypeError%":i,"%Uint8Array%":"undefined"==typeof Uint8Array?r:Uint8Array,"%Uint8ClampedArray%":"undefined"==typeof Uint8ClampedArray?r:Uint8ClampedArray,"%Uint16Array%":"undefined"==typeof Uint16Array?r:Uint16Array,"%Uint32Array%":"undefined"==typeof Uint32Array?r:Uint32Array,"%URIError%":URIError,"%WeakMap%":"undefined"==typeof WeakMap?r:WeakMap,"%WeakRef%":"undefined"==typeof WeakRef?r:WeakRef,"%WeakSet%":"undefined"==typeof WeakSet?r:WeakSet};if(f)try{null.error}catch(e){var y=f(f(e));g["%Error.prototype%"]=y}var v=function e(t){var n;if("%AsyncFunction%"===t)n=a("async function () {}");else if("%GeneratorFunction%"===t)n=a("function* () {}");else if("%AsyncGeneratorFunction%"===t)n=a("async function* () {}");else if("%AsyncGenerator%"===t){var r=e("%AsyncGeneratorFunction%");r&&(n=r.prototype)}else if("%AsyncIteratorPrototype%"===t){var o=e("%AsyncGenerator%");o&&f&&(n=f(o.prototype))}return g[t]=n,n},b={"%ArrayBufferPrototype%":["ArrayBuffer","prototype"],"%ArrayPrototype%":["Array","prototype"],"%ArrayProto_entries%":["Array","prototype","entries"],"%ArrayProto_forEach%":["Array","prototype","forEach"],"%ArrayProto_keys%":["Array","prototype","keys"],"%ArrayProto_values%":["Array","prototype","values"],"%AsyncFunctionPrototype%":["AsyncFunction","prototype"],"%AsyncGenerator%":["AsyncGeneratorFunction","prototype"],"%AsyncGeneratorPrototype%":["AsyncGeneratorFunction","prototype","prototype"],"%BooleanPrototype%":["Boolean","prototype"],"%DataViewPrototype%":["DataView","prototype"],"%DatePrototype%":["Date","prototype"],"%ErrorPrototype%":["Error","prototype"],"%EvalErrorPrototype%":["EvalError","prototype"],"%Float32ArrayPrototype%":["Float32Array","prototype"],"%Float64ArrayPrototype%":["Float64Array","prototype"],"%FunctionPrototype%":["Function","prototype"],"%Generator%":["GeneratorFunction","prototype"],"%GeneratorPrototype%":["GeneratorFunction","prototype","prototype"],"%Int8ArrayPrototype%":["Int8Array","prototype"],"%Int16ArrayPrototype%":["Int16Array","prototype"],"%Int32ArrayPrototype%":["Int32Array","prototype"],"%JSONParse%":["JSON","parse"],"%JSONStringify%":["JSON","stringify"],"%MapPrototype%":["Map","prototype"],"%NumberPrototype%":["Number","prototype"],"%ObjectPrototype%":["Object","prototype"],"%ObjProto_toString%":["Object","prototype","toString"],"%ObjProto_valueOf%":["Object","prototype","valueOf"],"%PromisePrototype%":["Promise","prototype"],"%PromiseProto_then%":["Promise","prototype","then"],"%Promise_all%":["Promise","all"],"%Promise_reject%":["Promise","reject"],"%Promise_resolve%":["Promise","resolve"],"%RangeErrorPrototype%":["RangeError","prototype"],"%ReferenceErrorPrototype%":["ReferenceError","prototype"],"%RegExpPrototype%":["RegExp","prototype"],"%SetPrototype%":["Set","prototype"],"%SharedArrayBufferPrototype%":["SharedArrayBuffer","prototype"],"%StringPrototype%":["String","prototype"],"%SymbolPrototype%":["Symbol","prototype"],"%SyntaxErrorPrototype%":["SyntaxError","prototype"],"%TypedArrayPrototype%":["TypedArray","prototype"],"%TypeErrorPrototype%":["TypeError","prototype"],"%Uint8ArrayPrototype%":["Uint8Array","prototype"],"%Uint8ClampedArrayPrototype%":["Uint8ClampedArray","prototype"],"%Uint16ArrayPrototype%":["Uint16Array","prototype"],"%Uint32ArrayPrototype%":["Uint32Array","prototype"],"%URIErrorPrototype%":["URIError","prototype"],"%WeakMapPrototype%":["WeakMap","prototype"],"%WeakSetPrototype%":["WeakSet","prototype"]},w=n(58612),E=n(17642),x=w.call(Function.call,Array.prototype.concat),S=w.call(Function.apply,Array.prototype.splice),_=w.call(Function.call,String.prototype.replace),j=w.call(Function.call,String.prototype.slice),O=w.call(Function.call,RegExp.prototype.exec),k=/[^%.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|%$))/g,A=/\\(\\)?/g,C=function(e,t){var n,r=e;if(E(b,r)&&(r="%"+(n=b[r])[0]+"%"),E(g,r)){var s=g[r];if(s===d&&(s=v(r)),void 0===s&&!t)throw new i("intrinsic "+e+" exists, but is not available. Please file an issue!");return{alias:n,name:r,value:s}}throw new o("intrinsic "+e+" does not exist!")};e.exports=function(e,t){if("string"!=typeof e||0===e.length)throw new i("intrinsic name must be a non-empty string");if(arguments.length>1&&"boolean"!=typeof t)throw new i('"allowMissing" argument must be a boolean');if(null===O(/^%?[^%]*%?$/,e))throw new o("`%` may not be present anywhere but at the beginning and end of the intrinsic name");var n=function(e){var t=j(e,0,1),n=j(e,-1);if("%"===t&&"%"!==n)throw new o("invalid intrinsic syntax, expected closing `%`");if("%"===n&&"%"!==t)throw new o("invalid intrinsic syntax, expected opening `%`");var r=[];return _(e,k,(function(e,t,n,o){r[r.length]=n?_(o,A,"$1"):t||e})),r}(e),r=n.length>0?n[0]:"",s=C("%"+r+"%",t),a=s.name,c=s.value,u=!1,p=s.alias;p&&(r=p[0],S(n,x([0,1],p)));for(var h=1,f=!0;h=n.length){var v=l(c,d);c=(f=!!v)&&"get"in v&&!("originalValue"in v.get)?v.get:c[d]}else f=E(c,d),c=c[d];f&&!u&&(g[a]=c)}}return c}},28185:e=>{"use strict";var t={foo:{}},n=Object;e.exports=function(){return{__proto__:t}.foo===t.foo&&!({__proto__:null}instanceof n)}},41405:(e,t,n)=>{"use strict";var r="undefined"!=typeof Symbol&&Symbol,o=n(55419);e.exports=function(){return"function"==typeof r&&("function"==typeof Symbol&&("symbol"==typeof r("foo")&&("symbol"==typeof Symbol("bar")&&o())))}},55419:e=>{"use strict";e.exports=function(){if("function"!=typeof Symbol||"function"!=typeof Object.getOwnPropertySymbols)return!1;if("symbol"==typeof Symbol.iterator)return!0;var e={},t=Symbol("test"),n=Object(t);if("string"==typeof t)return!1;if("[object Symbol]"!==Object.prototype.toString.call(t))return!1;if("[object Symbol]"!==Object.prototype.toString.call(n))return!1;for(t in e[t]=42,e)return!1;if("function"==typeof Object.keys&&0!==Object.keys(e).length)return!1;if("function"==typeof Object.getOwnPropertyNames&&0!==Object.getOwnPropertyNames(e).length)return!1;var r=Object.getOwnPropertySymbols(e);if(1!==r.length||r[0]!==t)return!1;if(!Object.prototype.propertyIsEnumerable.call(e,t))return!1;if("function"==typeof Object.getOwnPropertyDescriptor){var o=Object.getOwnPropertyDescriptor(e,t);if(42!==o.value||!0!==o.enumerable)return!1}return!0}},17642:(e,t,n)=>{"use strict";var r=n(58612);e.exports=r.call(Function.call,Object.prototype.hasOwnProperty)},47802:e=>{function t(e){return e instanceof Map?e.clear=e.delete=e.set=function(){throw new Error("map is read-only")}:e instanceof Set&&(e.add=e.clear=e.delete=function(){throw new Error("set is read-only")}),Object.freeze(e),Object.getOwnPropertyNames(e).forEach((function(n){var r=e[n];"object"!=typeof r||Object.isFrozen(r)||t(r)})),e}var n=t,r=t;n.default=r;class o{constructor(e){void 0===e.data&&(e.data={}),this.data=e.data,this.isMatchIgnored=!1}ignoreMatch(){this.isMatchIgnored=!0}}function s(e){return e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function i(e,...t){const n=Object.create(null);for(const t in e)n[t]=e[t];return t.forEach((function(e){for(const t in e)n[t]=e[t]})),n}const a=e=>!!e.kind;class l{constructor(e,t){this.buffer="",this.classPrefix=t.classPrefix,e.walk(this)}addText(e){this.buffer+=s(e)}openNode(e){if(!a(e))return;let t=e.kind;e.sublanguage||(t=`${this.classPrefix}${t}`),this.span(t)}closeNode(e){a(e)&&(this.buffer+="")}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const t={kind:e,children:[]};this.add(t),this.stack.push(t)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{c._collapse(e)})))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function p(e){return e?"string"==typeof e?e:e.source:null}const h=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;const f="[a-zA-Z]\\w*",d="[a-zA-Z_]\\w*",m="\\b\\d+(\\.\\d+)?",g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",y="\\b(0b[01]+)",v={begin:"\\\\[\\s\\S]",relevance:0},b={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[v]},w={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[v]},E={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},x=function(e,t,n={}){const r=i({className:"comment",begin:e,end:t,contains:[]},n);return r.contains.push(E),r.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),r},S=x("//","$"),_=x("/\\*","\\*/"),j=x("#","$"),O={className:"number",begin:m,relevance:0},k={className:"number",begin:g,relevance:0},A={className:"number",begin:y,relevance:0},C={className:"number",begin:m+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},P={begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}]},N={className:"title",begin:f,relevance:0},I={className:"title",begin:d,relevance:0},T={begin:"\\.\\s*"+d,relevance:0};var R=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:f,UNDERSCORE_IDENT_RE:d,NUMBER_RE:m,C_NUMBER_RE:g,BINARY_NUMBER_RE:y,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map((e=>p(e))).join("")}(t,/.*\b/,e.binary,/\b.*/)),i({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:v,APOS_STRING_MODE:b,QUOTE_STRING_MODE:w,PHRASAL_WORDS_MODE:E,COMMENT:x,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:_,HASH_COMMENT_MODE:j,NUMBER_MODE:O,C_NUMBER_MODE:k,BINARY_NUMBER_MODE:A,CSS_NUMBER_MODE:C,REGEXP_MODE:P,TITLE_MODE:N,UNDERSCORE_TITLE_MODE:I,METHOD_GUARD:T,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{t.data._beginMatch!==e[1]&&t.ignoreMatch()}})}});function M(e,t){"."===e.input[e.index-1]&&t.ignoreMatch()}function D(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=M,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function F(e,t){Array.isArray(e.illegal)&&(e.illegal=function(...e){return"("+e.map((e=>p(e))).join("|")+")"}(...e.illegal))}function L(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function B(e,t){void 0===e.relevance&&(e.relevance=1)}const $=["of","and","for","in","not","or","if","then","parent","list","value"],q="keyword";function U(e,t,n=q){const r={};return"string"==typeof e?o(n,e.split(" ")):Array.isArray(e)?o(n,e):Object.keys(e).forEach((function(n){Object.assign(r,U(e[n],t,n))})),r;function o(e,n){t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((function(t){const n=t.split("|");r[n[0]]=[e,z(n[0],n[1])]}))}}function z(e,t){return t?Number(t):function(e){return $.includes(e.toLowerCase())}(e)?0:1}function V(e,{plugins:t}){function n(t,n){return new RegExp(p(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class r{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,t){t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),this.matchAt+=function(e){return new RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map((e=>e[1]));this.matcherRe=n(function(e,t="|"){let n=0;return e.map((e=>{n+=1;const t=n;let r=p(e),o="";for(;r.length>0;){const e=h.exec(r);if(!e){o+=r;break}o+=r.substring(0,e.index),r=r.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?o+="\\"+String(Number(e[1])+t):(o+=e[0],"("===e[0]&&n++)}return o})).map((e=>`(${e})`)).join(t)}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e);if(!t)return null;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),r=this.matchIndexes[n];return t.splice(0,n),Object.assign(t,r)}}class o{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const t=new r;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex;let n=t.exec(e);if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}return n&&(this.regexIndex+=n.position+1,this.regexIndex===this.count&&this.considerAll()),n}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=i(e.classNameAliases||{}),function t(r,s){const a=r;if(r.isCompiled)return a;[L].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))),r.__beforeBegin=null,[D,F,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null;if("object"==typeof r.keywords&&(l=r.keywords.$pattern,delete r.keywords.$pattern),r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)),r.lexemes&&l)throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l=l||r.lexemes||/\w+/,a.keywordPatternRe=n(l,!0),s&&(r.begin||(r.begin=/\B|\b/),a.beginRe=n(r.begin),r.endSameAsBegin&&(r.end=r.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),r.end&&(a.endRe=n(r.end)),a.terminatorEnd=p(r.end)||"",r.endsWithParent&&s.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)),r.illegal&&(a.illegalRe=n(r.illegal)),r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((function(e){return function(e){e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((function(t){return i(e,{variants:null},t)})));if(e.cachedVariants)return e.cachedVariants;if(W(e))return i(e,{starts:e.starts?i(e.starts):null});if(Object.isFrozen(e))return i(e);return e}("self"===e?r:e)}))),r.contains.forEach((function(e){t(e,a)})),r.starts&&t(r.starts,s),a.matcher=function(e){const t=new o;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t}(a),a}(e)}function W(e){return!!e&&(e.endsWithParent||W(e.starts))}function J(e){const t={props:["language","code","autodetect"],data:function(){return{detectedLanguage:"",unknownLanguage:!1}},computed:{className(){return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),this.unknownLanguage=!0,s(this.code);let t={};return this.autoDetect?(t=e.highlightAuto(this.code),this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals),this.detectedLanguage=this.language),t.value},autoDetect(){return!this.language||(e=this.autodetect,Boolean(e||""===e));var e},ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const K={"after:highlightElement":({el:e,result:t,text:n})=>{const r=G(e);if(!r.length)return;const o=document.createElement("div");o.innerHTML=t.value,t.value=function(e,t,n){let r=0,o="";const i=[];function a(){return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset"}function c(e){o+=""}function u(e){("start"===e.event?l:c)(e.node)}for(;e.length||t.length;){let t=a();if(o+=s(n.substring(r,t[0].offset)),r=t[0].offset,t===e){i.reverse().forEach(c);do{u(t.splice(0,1)[0]),t=a()}while(t===e&&t.length&&t[0].offset===r);i.reverse().forEach(l)}else"start"===t[0].event?i.push(t[0].node):i.pop(),u(t.splice(0,1)[0])}return o+s(n.substr(r))}(r,G(o),n)}};function H(e){return e.nodeName.toLowerCase()}function G(e){const t=[];return function e(n,r){for(let o=n.firstChild;o;o=o.nextSibling)3===o.nodeType?r+=o.nodeValue.length:1===o.nodeType&&(t.push({event:"start",offset:r,node:o}),r=e(o,r),H(o).match(/br|hr|img|input/)||t.push({event:"stop",offset:r,node:o}));return r}(e,0),t}const Z={},Y=e=>{console.error(e)},X=(e,...t)=>{console.log(`WARN: ${e}`,...t)},Q=(e,t)=>{Z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Z[`${e}/${t}`]=!0)},ee=s,te=i,ne=Symbol("nomatch");var re=function(e){const t=Object.create(null),r=Object.create(null),s=[];let i=!0;const a=/(^(<[^>]+>|\t|)+|\n)/gm,l="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let p={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function h(e){return p.noHighlightRe.test(e)}function f(e,t,n,r){let o="",s="";"object"==typeof t?(o=e,n=t.ignoreIllegals,s=t.language,r=void 0):(Q("10.7.0","highlight(lang, code, ...args) has been deprecated."),Q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),s=e,o=t);const i={code:o,language:s};O("before:highlight",i);const a=i.result?i.result:d(i.language,i.code,n,r);return a.code=i.code,O("after:highlight",a),a}function d(e,n,r,a){function c(e,t){const n=E.case_insensitive?t[0].toLowerCase():t[0];return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]}function u(){null!=j.subLanguage?function(){if(""===A)return;let e=null;if("string"==typeof j.subLanguage){if(!t[j.subLanguage])return void k.addText(A);e=d(j.subLanguage,A,!0,O[j.subLanguage]),O[j.subLanguage]=e.top}else e=m(A,j.subLanguage.length?j.subLanguage:null);j.relevance>0&&(C+=e.relevance),k.addSublanguage(e.emitter,e.language)}():function(){if(!j.keywords)return void k.addText(A);let e=0;j.keywordPatternRe.lastIndex=0;let t=j.keywordPatternRe.exec(A),n="";for(;t;){n+=A.substring(e,t.index);const r=c(j,t);if(r){const[e,o]=r;if(k.addText(n),n="",C+=o,e.startsWith("_"))n+=t[0];else{const n=E.classNameAliases[e]||e;k.addKeyword(t[0],n)}}else n+=t[0];e=j.keywordPatternRe.lastIndex,t=j.keywordPatternRe.exec(A)}n+=A.substr(e),k.addText(n)}(),A=""}function h(e){return e.className&&k.openNode(E.classNameAliases[e.className]||e.className),j=Object.create(e,{parent:{value:j}}),j}function f(e,t,n){let r=function(e,t){const n=e&&e.exec(t);return n&&0===n.index}(e.endRe,n);if(r){if(e["on:end"]){const n=new o(e);e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return f(e.parent,t,n)}function g(e){return 0===j.matcher.regexIndex?(A+=e[0],1):(I=!0,0)}function y(e){const t=e[0],n=e.rule,r=new o(n),s=[n.__beforeBegin,n["on:begin"]];for(const n of s)if(n&&(n(e,r),r.isMatchIgnored))return g(t);return n&&n.endSameAsBegin&&(n.endRe=new RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),n.skip?A+=t:(n.excludeBegin&&(A+=t),u(),n.returnBegin||n.excludeBegin||(A=t)),h(n),n.returnBegin?0:t.length}function v(e){const t=e[0],r=n.substr(e.index),o=f(j,e,r);if(!o)return ne;const s=j;s.skip?A+=t:(s.returnEnd||s.excludeEnd||(A+=t),u(),s.excludeEnd&&(A=t));do{j.className&&k.closeNode(),j.skip||j.subLanguage||(C+=j.relevance),j=j.parent}while(j!==o.parent);return o.starts&&(o.endSameAsBegin&&(o.starts.endRe=o.endRe),h(o.starts)),s.returnEnd?0:t.length}let b={};function w(t,o){const s=o&&o[0];if(A+=t,null==s)return u(),0;if("begin"===b.type&&"end"===o.type&&b.index===o.index&&""===s){if(A+=n.slice(o.index,o.index+1),!i){const t=new Error("0 width match regex");throw t.languageName=e,t.badRule=b.rule,t}return 1}if(b=o,"begin"===o.type)return y(o);if("illegal"===o.type&&!r){const e=new Error('Illegal lexeme "'+s+'" for mode "'+(j.className||"")+'"');throw e.mode=j,e}if("end"===o.type){const e=v(o);if(e!==ne)return e}if("illegal"===o.type&&""===s)return 1;if(N>1e5&&N>3*o.index){throw new Error("potential infinite loop, way more iterations than matches")}return A+=s,s.length}const E=S(e);if(!E)throw Y(l.replace("{}",e)),new Error('Unknown language: "'+e+'"');const x=V(E,{plugins:s});let _="",j=a||x;const O={},k=new p.__emitter(p);!function(){const e=[];for(let t=j;t!==E;t=t.parent)t.className&&e.unshift(t.className);e.forEach((e=>k.openNode(e)))}();let A="",C=0,P=0,N=0,I=!1;try{for(j.matcher.considerAll();;){N++,I?I=!1:j.matcher.considerAll(),j.matcher.lastIndex=P;const e=j.matcher.exec(n);if(!e)break;const t=w(n.substring(P,e.index),e);P=e.index+t}return w(n.substr(P)),k.closeAllNodes(),k.finalize(),_=k.toHTML(),{relevance:Math.floor(C),value:_,language:e,illegal:!1,emitter:k,top:j}}catch(t){if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:t.message,context:n.slice(P-100,P+100),mode:t.mode},sofar:_,relevance:0,value:ee(n),emitter:k};if(i)return{illegal:!1,relevance:0,value:ee(n),emitter:k,language:e,top:j,errorRaised:t};throw t}}function m(e,n){n=n||p.languages||Object.keys(t);const r=function(e){const t={relevance:0,emitter:new p.__emitter(p),value:ee(e),illegal:!1,top:c};return t.emitter.addText(e),t}(e),o=n.filter(S).filter(j).map((t=>d(t,e,!1)));o.unshift(r);const s=o.sort(((e,t)=>{if(e.relevance!==t.relevance)return t.relevance-e.relevance;if(e.language&&t.language){if(S(e.language).supersetOf===t.language)return 1;if(S(t.language).supersetOf===e.language)return-1}return 0})),[i,a]=s,l=i;return l.second_best=a,l}const g={"before:highlightElement":({el:e})=>{p.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"))},"after:highlightElement":({result:e})=>{p.useBR&&(e.value=e.value.replace(/\n/g,"
"))}},y=/^(<[^>]+>|\t)+/gm,v={"after:highlightElement":({result:e})=>{p.tabReplace&&(e.value=e.value.replace(y,(e=>e.replace(/\t/g,p.tabReplace))))}};function b(e){let t=null;const n=function(e){let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"";const n=p.languageDetectRe.exec(t);if(n){const t=S(n[1]);return t||(X(l.replace("{}",n[1])),X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>h(e)||S(e)))}(e);if(h(n))return;O("before:highlightElement",{el:e,language:n}),t=e;const o=t.textContent,s=n?f(o,{language:n,ignoreIllegals:!0}):m(o);O("after:highlightElement",{el:e,result:s,text:o}),e.innerHTML=s.value,function(e,t,n){const o=t?r[t]:n;e.classList.add("hljs"),o&&e.classList.add(o)}(e,n,s.language),e.result={language:s.language,re:s.relevance,relavance:s.relevance},s.second_best&&(e.second_best={language:s.second_best.language,re:s.second_best.relevance,relavance:s.second_best.relevance})}const w=()=>{if(w.called)return;w.called=!0,Q("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead.");document.querySelectorAll("pre code").forEach(b)};let E=!1;function x(){if("loading"===document.readyState)return void(E=!0);document.querySelectorAll("pre code").forEach(b)}function S(e){return e=(e||"").toLowerCase(),t[e]||t[r[e]]}function _(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{r[e.toLowerCase()]=t}))}function j(e){const t=S(e);return t&&!t.disableAutodetect}function O(e,t){const n=e;s.forEach((function(e){e[n]&&e[n](t)}))}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){E&&x()}),!1),Object.assign(e,{highlight:f,highlightAuto:m,highlightAll:x,fixMarkup:function(e){return Q("10.2.0","fixMarkup will be removed entirely in v11.0"),Q("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),t=e,p.tabReplace||p.useBR?t.replace(a,(e=>"\n"===e?p.useBR?"
":e:p.tabReplace?e.replace(/\t/g,p.tabReplace):e)):t;var t},highlightElement:b,highlightBlock:function(e){return Q("10.7.0","highlightBlock will be removed entirely in v12.0"),Q("10.7.0","Please use highlightElement now."),b(e)},configure:function(e){e.useBR&&(Q("10.3.0","'useBR' will be removed entirely in v11.0"),Q("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),p=te(p,e)},initHighlighting:w,initHighlightingOnLoad:function(){Q("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),E=!0},registerLanguage:function(n,r){let o=null;try{o=r(e)}catch(e){if(Y("Language definition for '{}' could not be registered.".replace("{}",n)),!i)throw e;Y(e),o=c}o.name||(o.name=n),t[n]=o,o.rawDefinition=r.bind(null,e),o.aliases&&_(o.aliases,{languageName:n})},unregisterLanguage:function(e){delete t[e];for(const t of Object.keys(r))r[t]===e&&delete r[t]},listLanguages:function(){return Object.keys(t)},getLanguage:S,registerAliases:_,requireLanguage:function(e){Q("10.4.0","requireLanguage will be removed entirely in v11."),Q("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");const t=S(e);if(t)return t;throw new Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:j,inherit:te,addPlugin:function(e){!function(e){e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{e["before:highlightBlock"](Object.assign({block:t.el},t))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{e["after:highlightBlock"](Object.assign({block:t.el},t))})}(e),s.push(e)},vuePlugin:J(e).VuePlugin}),e.debugMode=function(){i=!1},e.safeMode=function(){i=!0},e.versionString="10.7.3";for(const e in R)"object"==typeof R[e]&&n(R[e]);return Object.assign(e,R),e.addPlugin(g),e.addPlugin(K),e.addPlugin(v),e}({});e.exports=re},61519:e=>{function t(...e){return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}e.exports=function(e){const n={},r={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{begin:t(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},r]});const o={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},s={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},i={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,n,o]};o.contains.push(i);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,n]},l=e.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[l,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,s,i,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},n]}}},30786:e=>{function t(...e){return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}e.exports=function(e){const n="HTTP/(2|1\\.[01])",r={className:"attribute",begin:t("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},o=[r,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+n+" \\d{3})",end:/$/,contains:[{className:"meta",begin:n},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:o}},{begin:"(?=^[A-Z]+ (.*?) "+n+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:n},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:o}},e.inherit(r,{relevance:0})]}}},96344:e=>{const t="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],r=["true","false","null","undefined","NaN","Infinity"],o=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}e.exports=function(e){const a=t,l="<>",c="",u={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,t)=>{const n=e[0].length+e.index,r=e.input[n];"<"!==r?">"===r&&(((e,{after:t})=>{const n="",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:p,contains:S}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:l,end:c},{begin:u.begin,"on:begin":u.isTrulyOpeningTag,end:u.end}],subLanguage:"xml",contains:[{begin:u.begin,end:u.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:p,contains:["self",e.inherit(e.TITLE_MODE,{begin:a}),_],illegal:/%/},{beginKeywords:"while if switch catch for"},{className:"function",begin:e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,contains:[_,e.inherit(e.TITLE_MODE,{begin:a})]},{variants:[{begin:"\\."+a},{begin:"\\$"+a}],relevance:0},{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{beginKeywords:"extends"},e.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,end:/[{;]/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:a}),"self",_]},{begin:"(get|set)\\s+(?="+a+"\\()",end:/\{/,keywords:"get set",contains:[e.inherit(e.TITLE_MODE,{begin:a}),{begin:/\(\)/},_]},{begin:/\$[(.]/}]}}},82026:e=>{e.exports=function(e){const t={literal:"true false null"},n=[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],r=[e.QUOTE_STRING_MODE,e.C_NUMBER_MODE],o={end:",",endsWithParent:!0,excludeEnd:!0,contains:r,keywords:t},s={begin:/\{/,end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE],illegal:"\\n"},e.inherit(o,{begin:/:/})].concat(n),illegal:"\\S"},i={begin:"\\[",end:"\\]",contains:[e.inherit(o)],illegal:"\\S"};return r.push(s,i),n.forEach((function(e){r.push(e)})),{name:"JSON",contains:r,keywords:t,illegal:"\\S"}}},66336:e=>{e.exports=function(e){const t={$pattern:/-?[A-z\.\-]+\b/,keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter",built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write"},n={begin:"`[\\s\\S]",relevance:0},r={className:"variable",variants:[{begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}]},o={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}],contains:[n,r,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},s={className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}]},i=e.inherit(e.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/,end:/#>/}],contains:[{className:"doctag",variants:[{begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/},{begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/}]}]}),a={className:"built_in",variants:[{begin:"(".concat("Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where",")+(-)[\\w\\d]+")}]},l={className:"class",beginKeywords:"class enum",end:/\s*[{]/,excludeEnd:!0,relevance:0,contains:[e.TITLE_MODE]},c={className:"function",begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0,contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title",begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/,className:"params",relevance:0,contains:[r]}]},u={begin:/using\s/,end:/$/,returnBegin:!0,contains:[o,s,{className:"keyword",begin:/(using|assembly|command|module|namespace|type)/}]},p={variants:[{className:"operator",begin:"(".concat("-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor",")\\b")},{className:"literal",begin:/(-)[\w\d]+/,relevance:0}]},h={className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0,relevance:0,contains:[{className:"keyword",begin:"(".concat(t.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,relevance:0},e.inherit(e.TITLE_MODE,{endsParent:!0})]},f=[h,i,n,e.NUMBER_MODE,o,s,a,r,{className:"literal",begin:/\$(null|true|false)\b/},{className:"selector-tag",begin:/@\B/,relevance:0}],d={begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",f,{begin:"("+["string","char","byte","int","long","bool","decimal","single","double","DateTime","xml","array","hashtable","void"].join("|")+")",className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/,relevance:0})};return h.contains.unshift(d),{name:"PowerShell",aliases:["ps","ps1"],case_insensitive:!0,keywords:t,contains:f.concat(l,c,u,p,d)}}},42157:e=>{function t(e){return e?"string"==typeof e?e:e.source:null}function n(e){return r("(?=",e,")")}function r(...e){return e.map((e=>t(e))).join("")}function o(...e){return"("+e.map((e=>t(e))).join("|")+")"}e.exports=function(e){const t=r(/[A-Z_]/,r("(",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),s={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},a=e.inherit(i,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{className:"meta-string"}),c=e.inherit(e.QUOTE_STRING_MODE,{className:"meta-string"}),u={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,c,l,a,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[i,a,c,l]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},s,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[u],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[u],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:r(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:u}]},{className:"tag",begin:r(/<\//,n(r(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},54587:e=>{e.exports=function(e){var t="true false yes no null",n="[\\w#;/?:@&=+$,.~*'()[\\]]+",r={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},o=e.inherit(r,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),s={className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},i={end:",",endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},a={begin:/\{/,end:/\}/,contains:[i],illegal:"\\n",relevance:0},l={begin:"\\[",end:"\\]",contains:[i],illegal:"\\n",relevance:0},c=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+n},{className:"type",begin:"!<"+n+">"},{className:"type",begin:"!"+n},{className:"type",begin:"!!"+n},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},s,{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},a,l,r],u=[...c];return u.pop(),u.push(o),i.contains=u,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:c}}},8679:(e,t,n)=>{"use strict";var r=n(59864),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},s={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},a={};function l(e){return r.isMemo(e)?i:a[e.$$typeof]||o}a[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},a[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,p=Object.getOwnPropertySymbols,h=Object.getOwnPropertyDescriptor,f=Object.getPrototypeOf,d=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(d){var o=f(n);o&&o!==d&&e(t,o,r)}var i=u(n);p&&(i=i.concat(p(n)));for(var a=l(t),m=l(n),g=0;g{t.read=function(e,t,n,r,o){var s,i,a=8*o-r-1,l=(1<>1,u=-7,p=n?o-1:0,h=n?-1:1,f=e[t+p];for(p+=h,s=f&(1<<-u)-1,f>>=-u,u+=a;u>0;s=256*s+e[t+p],p+=h,u-=8);for(i=s&(1<<-u)-1,s>>=-u,u+=r;u>0;i=256*i+e[t+p],p+=h,u-=8);if(0===s)s=1-c;else{if(s===l)return i?NaN:1/0*(f?-1:1);i+=Math.pow(2,r),s-=c}return(f?-1:1)*i*Math.pow(2,s-r)},t.write=function(e,t,n,r,o,s){var i,a,l,c=8*s-o-1,u=(1<>1,h=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,f=r?0:s-1,d=r?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,i=u):(i=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-i))<1&&(i--,l*=2),(t+=i+p>=1?h/l:h*Math.pow(2,1-p))*l>=2&&(i++,l/=2),i+p>=u?(a=0,i=u):i+p>=1?(a=(t*l-1)*Math.pow(2,o),i+=p):(a=t*Math.pow(2,p-1)*Math.pow(2,o),i=0));o>=8;e[n+f]=255&a,f+=d,a/=256,o-=8);for(i=i<0;e[n+f]=255&i,f+=d,i/=256,c-=8);e[n+f-d]|=128*m}},43393:function(e){e.exports=function(){"use strict";var e=Array.prototype.slice;function t(e,t){t&&(e.prototype=Object.create(t.prototype)),e.prototype.constructor=e}function n(e){return i(e)?e:K(e)}function r(e){return a(e)?e:H(e)}function o(e){return l(e)?e:G(e)}function s(e){return i(e)&&!c(e)?e:Z(e)}function i(e){return!(!e||!e[p])}function a(e){return!(!e||!e[h])}function l(e){return!(!e||!e[f])}function c(e){return a(e)||l(e)}function u(e){return!(!e||!e[d])}t(r,n),t(o,n),t(s,n),n.isIterable=i,n.isKeyed=a,n.isIndexed=l,n.isAssociative=c,n.isOrdered=u,n.Keyed=r,n.Indexed=o,n.Set=s;var p="@@__IMMUTABLE_ITERABLE__@@",h="@@__IMMUTABLE_KEYED__@@",f="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",m="delete",g=5,y=1<>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?O(e)+t:t}function A(){return!0}function C(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function P(e,t){return I(e,t,0)}function N(e,t){return I(e,t,t)}function I(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var T=0,R=1,M=2,D="function"==typeof Symbol&&Symbol.iterator,F="@@iterator",L=D||F;function B(e){this.next=e}function $(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function q(){return{value:void 0,done:!0}}function U(e){return!!W(e)}function z(e){return e&&"function"==typeof e.next}function V(e){var t=W(e);return t&&t.call(e)}function W(e){var t=e&&(D&&e[D]||e[F]);if("function"==typeof t)return t}function J(e){return e&&"number"==typeof e.length}function K(e){return null==e?ie():i(e)?e.toSeq():ce(e)}function H(e){return null==e?ie().toKeyedSeq():i(e)?a(e)?e.toSeq():e.fromEntrySeq():ae(e)}function G(e){return null==e?ie():i(e)?a(e)?e.entrySeq():e.toIndexedSeq():le(e)}function Z(e){return(null==e?ie():i(e)?a(e)?e.entrySeq():e:le(e)).toSetSeq()}B.prototype.toString=function(){return"[Iterator]"},B.KEYS=T,B.VALUES=R,B.ENTRIES=M,B.prototype.inspect=B.prototype.toSource=function(){return this.toString()},B.prototype[L]=function(){return this},t(K,n),K.of=function(){return K(arguments)},K.prototype.toSeq=function(){return this},K.prototype.toString=function(){return this.__toString("Seq {","}")},K.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},K.prototype.__iterate=function(e,t){return pe(this,e,t,!0)},K.prototype.__iterator=function(e,t){return he(this,e,t,!0)},t(H,K),H.prototype.toKeyedSeq=function(){return this},t(G,K),G.of=function(){return G(arguments)},G.prototype.toIndexedSeq=function(){return this},G.prototype.toString=function(){return this.__toString("Seq [","]")},G.prototype.__iterate=function(e,t){return pe(this,e,t,!1)},G.prototype.__iterator=function(e,t){return he(this,e,t,!1)},t(Z,K),Z.of=function(){return Z(arguments)},Z.prototype.toSetSeq=function(){return this},K.isSeq=se,K.Keyed=H,K.Set=Z,K.Indexed=G;var Y,X,Q,ee="@@__IMMUTABLE_SEQ__@@";function te(e){this._array=e,this.size=e.length}function ne(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function re(e){this._iterable=e,this.size=e.length||e.size}function oe(e){this._iterator=e,this._iteratorCache=[]}function se(e){return!(!e||!e[ee])}function ie(){return Y||(Y=new te([]))}function ae(e){var t=Array.isArray(e)?new te(e).fromEntrySeq():z(e)?new oe(e).fromEntrySeq():U(e)?new re(e).fromEntrySeq():"object"==typeof e?new ne(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function le(e){var t=ue(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function ce(e){var t=ue(e)||"object"==typeof e&&new ne(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}function ue(e){return J(e)?new te(e):z(e)?new oe(e):U(e)?new re(e):void 0}function pe(e,t,n,r){var o=e._cache;if(o){for(var s=o.length-1,i=0;i<=s;i++){var a=o[n?s-i:i];if(!1===t(a[1],r?a[0]:i,e))return i+1}return i}return e.__iterateUncached(t,n)}function he(e,t,n,r){var o=e._cache;if(o){var s=o.length-1,i=0;return new B((function(){var e=o[n?s-i:i];return i++>s?q():$(t,r?e[0]:i-1,e[1])}))}return e.__iteratorUncached(t,n)}function fe(e,t){return t?de(t,e,"",{"":e}):me(e)}function de(e,t,n,r){return Array.isArray(t)?e.call(r,n,G(t).map((function(n,r){return de(e,n,r,t)}))):ge(t)?e.call(r,n,H(t).map((function(n,r){return de(e,n,r,t)}))):t}function me(e){return Array.isArray(e)?G(e).map(me).toList():ge(e)?H(e).map(me).toMap():e}function ge(e){return e&&(e.constructor===Object||void 0===e.constructor)}function ye(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function ve(e,t){if(e===t)return!0;if(!i(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||a(e)!==a(t)||l(e)!==l(t)||u(e)!==u(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!c(e);if(u(e)){var r=e.entries();return t.every((function(e,t){var o=r.next().value;return o&&ye(o[1],e)&&(n||ye(o[0],t))}))&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var s=e;e=t,t=s}var p=!0,h=t.__iterate((function(t,r){if(n?!e.has(t):o?!ye(t,e.get(r,b)):!ye(e.get(r,b),t))return p=!1,!1}));return p&&e.size===h}function be(e,t){if(!(this instanceof be))return new be(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(X)return X;X=this}}function we(e,t){if(!e)throw new Error(t)}function Ee(e,t,n){if(!(this instanceof Ee))return new Ee(e,t,n);if(we(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?q():$(e,o,n[t?r-o++:o++])}))},t(ne,H),ne.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},ne.prototype.has=function(e){return this._object.hasOwnProperty(e)},ne.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,s=0;s<=o;s++){var i=r[t?o-s:s];if(!1===e(n[i],i,this))return s+1}return s},ne.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,s=0;return new B((function(){var i=r[t?o-s:s];return s++>o?q():$(e,i,n[i])}))},ne.prototype[d]=!0,t(re,G),re.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=V(this._iterable),r=0;if(z(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},re.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=V(this._iterable);if(!z(n))return new B(q);var r=0;return new B((function(){var t=n.next();return t.done?t:$(e,r++,t.value)}))},t(oe,G),oe.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,s=0;s=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return $(e,o,r[o++])}))},t(be,G),be.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},be.prototype.get=function(e,t){return this.has(e)?this._value:t},be.prototype.includes=function(e){return ye(this._value,e)},be.prototype.slice=function(e,t){var n=this.size;return C(e,t,n)?this:new be(this._value,N(t,n)-P(e,n))},be.prototype.reverse=function(){return this},be.prototype.indexOf=function(e){return ye(this._value,e)?0:-1},be.prototype.lastIndexOf=function(e){return ye(this._value,e)?this.size:-1},be.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?q():$(e,s++,i)}))},Ee.prototype.equals=function(e){return e instanceof Ee?this._start===e._start&&this._end===e._end&&this._step===e._step:ve(this,e)},t(xe,n),t(Se,xe),t(_e,xe),t(je,xe),xe.Keyed=Se,xe.Indexed=_e,xe.Set=je;var Oe="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function ke(e){return e>>>1&1073741824|3221225471&e}function Ae(e){if(!1===e||null==e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null==e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return ke(n)}if("string"===t)return e.length>Be?Ce(e):Pe(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return Ne(e);if("function"==typeof e.toString)return Pe(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function Ce(e){var t=Ue[e];return void 0===t&&(t=Pe(e),qe===$e&&(qe=0,Ue={}),qe++,Ue[e]=t),t}function Pe(e){for(var t=0,n=0;n0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}var Me,De="function"==typeof WeakMap;De&&(Me=new WeakMap);var Fe=0,Le="__immutablehash__";"function"==typeof Symbol&&(Le=Symbol(Le));var Be=16,$e=255,qe=0,Ue={};function ze(e){we(e!==1/0,"Cannot perform this action with an infinite size.")}function Ve(e){return null==e?ot():We(e)&&!u(e)?e:ot().withMutations((function(t){var n=r(e);ze(n.size),n.forEach((function(e,n){return t.set(n,e)}))}))}function We(e){return!(!e||!e[Ke])}t(Ve,Se),Ve.of=function(){var t=e.call(arguments,0);return ot().withMutations((function(e){for(var n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},Ve.prototype.toString=function(){return this.__toString("Map {","}")},Ve.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Ve.prototype.set=function(e,t){return st(this,e,t)},Ve.prototype.setIn=function(e,t){return this.updateIn(e,b,(function(){return t}))},Ve.prototype.remove=function(e){return st(this,e,b)},Ve.prototype.deleteIn=function(e){return this.updateIn(e,(function(){return b}))},Ve.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},Ve.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=gt(this,xn(e),t,n);return r===b?void 0:r},Ve.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):ot()},Ve.prototype.merge=function(){return ht(this,void 0,arguments)},Ve.prototype.mergeWith=function(t){return ht(this,t,e.call(arguments,1))},Ve.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,ot(),(function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]}))},Ve.prototype.mergeDeep=function(){return ht(this,ft,arguments)},Ve.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return ht(this,dt(t),n)},Ve.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,ot(),(function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]}))},Ve.prototype.sort=function(e){return Ut(pn(this,e))},Ve.prototype.sortBy=function(e,t){return Ut(pn(this,t,e))},Ve.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Ve.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new _)},Ve.prototype.asImmutable=function(){return this.__ensureOwner()},Ve.prototype.wasAltered=function(){return this.__altered},Ve.prototype.__iterator=function(e,t){return new et(this,e,t)},Ve.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate((function(t){return r++,e(t[1],t[0],n)}),t),r},Ve.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?rt(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Ve.isMap=We;var Je,Ke="@@__IMMUTABLE_MAP__@@",He=Ve.prototype;function Ge(e,t){this.ownerID=e,this.entries=t}function Ze(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function Ye(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Xe(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Qe(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function et(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&nt(e._root)}function tt(e,t){return $(e,t[0],t[1])}function nt(e,t){return{node:e,index:0,__prev:t}}function rt(e,t,n,r){var o=Object.create(He);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function ot(){return Je||(Je=rt(0))}function st(e,t,n){var r,o;if(e._root){var s=x(w),i=x(E);if(r=it(e._root,e.__ownerID,0,void 0,t,n,s,i),!i.value)return e;o=e.size+(s.value?n===b?-1:1:0)}else{if(n===b)return e;o=1,r=new Ge(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?rt(o,r):ot()}function it(e,t,n,r,o,s,i,a){return e?e.update(t,n,r,o,s,i,a):s===b?e:(S(a),S(i),new Qe(t,r,[o,s]))}function at(e){return e.constructor===Qe||e.constructor===Xe}function lt(e,t,n,r,o){if(e.keyHash===r)return new Xe(t,r,[e.entry,o]);var s,i=(0===n?e.keyHash:e.keyHash>>>n)&v,a=(0===n?r:r>>>n)&v;return new Ze(t,1<>>=1)i[a]=1&n?t[s++]:void 0;return i[r]=o,new Ye(e,s+1,i)}function ht(e,t,n){for(var o=[],s=0;s>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function vt(e,t,n,r){var o=r?e:j(e);return o[t]=n,o}function bt(e,t,n,r){var o=e.length+1;if(r&&t+1===o)return e[t]=n,e;for(var s=new Array(o),i=0,a=0;a=Et)return ct(e,l,r,o);var h=e&&e===this.ownerID,f=h?l:j(l);return p?a?c===u-1?f.pop():f[c]=f.pop():f[c]=[r,o]:f.push([r,o]),h?(this.entries=f,this):new Ge(e,f)}},Ze.prototype.get=function(e,t,n,r){void 0===t&&(t=Ae(n));var o=1<<((0===e?t:t>>>e)&v),s=this.bitmap;return 0==(s&o)?r:this.nodes[yt(s&o-1)].get(e+g,t,n,r)},Ze.prototype.update=function(e,t,n,r,o,s,i){void 0===n&&(n=Ae(r));var a=(0===t?n:n>>>t)&v,l=1<=xt)return pt(e,h,c,a,d);if(u&&!d&&2===h.length&&at(h[1^p]))return h[1^p];if(u&&d&&1===h.length&&at(d))return d;var m=e&&e===this.ownerID,y=u?d?c:c^l:c|l,w=u?d?vt(h,p,d,m):wt(h,p,m):bt(h,p,d,m);return m?(this.bitmap=y,this.nodes=w,this):new Ze(e,y,w)},Ye.prototype.get=function(e,t,n,r){void 0===t&&(t=Ae(n));var o=(0===e?t:t>>>e)&v,s=this.nodes[o];return s?s.get(e+g,t,n,r):r},Ye.prototype.update=function(e,t,n,r,o,s,i){void 0===n&&(n=Ae(r));var a=(0===t?n:n>>>t)&v,l=o===b,c=this.nodes,u=c[a];if(l&&!u)return this;var p=it(u,e,t+g,n,r,o,s,i);if(p===u)return this;var h=this.count;if(u){if(!p&&--h0&&r=0&&e>>t&v;if(r>=this.array.length)return new At([],e);var o,s=0===r;if(t>0){var i=this.array[r];if((o=i&&i.removeBefore(e,t-g,n))===i&&s)return this}if(s&&!o)return this;var a=Ft(this,e);if(!s)for(var l=0;l>>t&v;if(o>=this.array.length)return this;if(t>0){var s=this.array[o];if((r=s&&s.removeAfter(e,t-g,n))===s&&o===this.array.length-1)return this}var i=Ft(this,e);return i.array.splice(o+1),r&&(i.array[o]=r),i};var Ct,Pt,Nt={};function It(e,t){var n=e._origin,r=e._capacity,o=qt(r),s=e._tail;return i(e._root,e._level,0);function i(e,t,n){return 0===t?a(e,n):l(e,t,n)}function a(e,i){var a=i===o?s&&s.array:e&&e.array,l=i>n?0:n-i,c=r-i;return c>y&&(c=y),function(){if(l===c)return Nt;var e=t?--c:l++;return a&&a[e]}}function l(e,o,s){var a,l=e&&e.array,c=s>n?0:n-s>>o,u=1+(r-s>>o);return u>y&&(u=y),function(){for(;;){if(a){var e=a();if(e!==Nt)return e;a=null}if(c===u)return Nt;var n=t?--u:c++;a=i(l&&l[n],o-g,s+(n<=e.size||t<0)return e.withMutations((function(e){t<0?Bt(e,t).set(0,n):Bt(e,0,t+1).set(t,n)}));t+=e._origin;var r=e._tail,o=e._root,s=x(E);return t>=qt(e._capacity)?r=Dt(r,e.__ownerID,0,t,n,s):o=Dt(o,e.__ownerID,e._level,t,n,s),s.value?e.__ownerID?(e._root=o,e._tail=r,e.__hash=void 0,e.__altered=!0,e):Tt(e._origin,e._capacity,e._level,o,r):e}function Dt(e,t,n,r,o,s){var i,a=r>>>n&v,l=e&&a0){var c=e&&e.array[a],u=Dt(c,t,n-g,r,o,s);return u===c?e:((i=Ft(e,t)).array[a]=u,i)}return l&&e.array[a]===o?e:(S(s),i=Ft(e,t),void 0===o&&a===i.array.length-1?i.array.pop():i.array[a]=o,i)}function Ft(e,t){return t&&e&&t===e.ownerID?e:new At(e?e.array.slice():[],t)}function Lt(e,t){if(t>=qt(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&v],r-=g;return n}}function Bt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new _,o=e._origin,s=e._capacity,i=o+t,a=void 0===n?s:n<0?s+n:o+n;if(i===o&&a===s)return e;if(i>=a)return e.clear();for(var l=e._level,c=e._root,u=0;i+u<0;)c=new At(c&&c.array.length?[void 0,c]:[],r),u+=1<<(l+=g);u&&(i+=u,o+=u,a+=u,s+=u);for(var p=qt(s),h=qt(a);h>=1<p?new At([],r):f;if(f&&h>p&&ig;y-=g){var b=p>>>y&v;m=m.array[b]=Ft(m.array[b],r)}m.array[p>>>g&v]=f}if(a=h)i-=h,a-=h,l=g,c=null,d=d&&d.removeBefore(r,0,i);else if(i>o||h>>l&v;if(w!==h>>>l&v)break;w&&(u+=(1<o&&(c=c.removeBefore(r,l,i-u)),c&&hs&&(s=c.size),i(l)||(c=c.map((function(e){return fe(e)}))),r.push(c)}return s>e.size&&(e=e.setSize(s)),mt(e,t,r)}function qt(e){return e>>g<=y&&i.size>=2*s.size?(r=(o=i.filter((function(e,t){return void 0!==e&&a!==t}))).toKeyedSeq().map((function(e){return e[0]})).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=s.remove(t),o=a===i.size-1?i.pop():i.set(a,void 0))}else if(l){if(n===i.get(a)[1])return e;r=s,o=i.set(a,[t,n])}else r=s.set(t,i.size),o=i.set(i.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Vt(r,o)}function Kt(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function Ht(e){this._iter=e,this.size=e.size}function Gt(e){this._iter=e,this.size=e.size}function Zt(e){this._iter=e,this.size=e.size}function Yt(e){var t=bn(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=wn,t.__iterateUncached=function(t,n){var r=this;return e.__iterate((function(e,n){return!1!==t(n,e,r)}),n)},t.__iteratorUncached=function(t,n){if(t===M){var r=e.__iterator(t,n);return new B((function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e}))}return e.__iterator(t===R?T:R,n)},t}function Xt(e,t,n){var r=bn(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,o){var s=e.get(r,b);return s===b?o:t.call(n,s,r,e)},r.__iterateUncached=function(r,o){var s=this;return e.__iterate((function(e,o,i){return!1!==r(t.call(n,e,o,i),o,s)}),o)},r.__iteratorUncached=function(r,o){var s=e.__iterator(M,o);return new B((function(){var o=s.next();if(o.done)return o;var i=o.value,a=i[0];return $(r,a,t.call(n,i[1],a,e),o)}))},r}function Qt(e,t){var n=bn(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=Yt(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=wn,n.__iterate=function(t,n){var r=this;return e.__iterate((function(e,n){return t(e,n,r)}),!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function en(e,t,n,r){var o=bn(e);return r&&(o.has=function(r){var o=e.get(r,b);return o!==b&&!!t.call(n,o,r,e)},o.get=function(r,o){var s=e.get(r,b);return s!==b&&t.call(n,s,r,e)?s:o}),o.__iterateUncached=function(o,s){var i=this,a=0;return e.__iterate((function(e,s,l){if(t.call(n,e,s,l))return a++,o(e,r?s:a-1,i)}),s),a},o.__iteratorUncached=function(o,s){var i=e.__iterator(M,s),a=0;return new B((function(){for(;;){var s=i.next();if(s.done)return s;var l=s.value,c=l[0],u=l[1];if(t.call(n,u,c,e))return $(o,r?c:a++,u,s)}}))},o}function tn(e,t,n){var r=Ve().asMutable();return e.__iterate((function(o,s){r.update(t.call(n,o,s,e),0,(function(e){return e+1}))})),r.asImmutable()}function nn(e,t,n){var r=a(e),o=(u(e)?Ut():Ve()).asMutable();e.__iterate((function(s,i){o.update(t.call(n,s,i,e),(function(e){return(e=e||[]).push(r?[i,s]:s),e}))}));var s=vn(e);return o.map((function(t){return mn(e,s(t))}))}function rn(e,t,n,r){var o=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=o:n|=0),C(t,n,o))return e;var s=P(t,o),i=N(n,o);if(s!=s||i!=i)return rn(e.toSeq().cacheResult(),t,n,r);var a,l=i-s;l==l&&(a=l<0?0:l);var c=bn(e);return c.size=0===a?a:e.size&&a||void 0,!r&&se(e)&&a>=0&&(c.get=function(t,n){return(t=k(this,t))>=0&&ta)return q();var e=o.next();return r||t===R?e:$(t,l-1,t===T?void 0:e.value[1],e)}))},c}function on(e,t,n){var r=bn(e);return r.__iterateUncached=function(r,o){var s=this;if(o)return this.cacheResult().__iterate(r,o);var i=0;return e.__iterate((function(e,o,a){return t.call(n,e,o,a)&&++i&&r(e,o,s)})),i},r.__iteratorUncached=function(r,o){var s=this;if(o)return this.cacheResult().__iterator(r,o);var i=e.__iterator(M,o),a=!0;return new B((function(){if(!a)return q();var e=i.next();if(e.done)return e;var o=e.value,l=o[0],c=o[1];return t.call(n,c,l,s)?r===M?e:$(r,l,c,e):(a=!1,q())}))},r}function sn(e,t,n,r){var o=bn(e);return o.__iterateUncached=function(o,s){var i=this;if(s)return this.cacheResult().__iterate(o,s);var a=!0,l=0;return e.__iterate((function(e,s,c){if(!a||!(a=t.call(n,e,s,c)))return l++,o(e,r?s:l-1,i)})),l},o.__iteratorUncached=function(o,s){var i=this;if(s)return this.cacheResult().__iterator(o,s);var a=e.__iterator(M,s),l=!0,c=0;return new B((function(){var e,s,u;do{if((e=a.next()).done)return r||o===R?e:$(o,c++,o===T?void 0:e.value[1],e);var p=e.value;s=p[0],u=p[1],l&&(l=t.call(n,u,s,i))}while(l);return o===M?e:$(o,s,u,e)}))},o}function an(e,t){var n=a(e),o=[e].concat(t).map((function(e){return i(e)?n&&(e=r(e)):e=n?ae(e):le(Array.isArray(e)?e:[e]),e})).filter((function(e){return 0!==e.size}));if(0===o.length)return e;if(1===o.length){var s=o[0];if(s===e||n&&a(s)||l(e)&&l(s))return s}var c=new te(o);return n?c=c.toKeyedSeq():l(e)||(c=c.toSetSeq()),(c=c.flatten(!0)).size=o.reduce((function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}}),0),c}function ln(e,t,n){var r=bn(e);return r.__iterateUncached=function(r,o){var s=0,a=!1;function l(e,c){var u=this;e.__iterate((function(e,o){return(!t||c0}function dn(e,t,r){var o=bn(e);return o.size=new te(r).map((function(e){return e.size})).min(),o.__iterate=function(e,t){for(var n,r=this.__iterator(R,t),o=0;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},o.__iteratorUncached=function(e,o){var s=r.map((function(e){return e=n(e),V(o?e.reverse():e)})),i=0,a=!1;return new B((function(){var n;return a||(n=s.map((function(e){return e.next()})),a=n.some((function(e){return e.done}))),a?q():$(e,i++,t.apply(null,n.map((function(e){return e.value}))))}))},o}function mn(e,t){return se(e)?t:e.constructor(t)}function gn(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function yn(e){return ze(e.size),O(e)}function vn(e){return a(e)?r:l(e)?o:s}function bn(e){return Object.create((a(e)?H:l(e)?G:Z).prototype)}function wn(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):K.prototype.cacheResult.call(this)}function En(e,t){return e>t?1:e=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):Hn(e,t)},zn.prototype.pushAll=function(e){if(0===(e=o(e)).size)return this;ze(e.size);var t=this.size,n=this._head;return e.reverse().forEach((function(e){t++,n={value:e,next:n}})),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):Hn(t,n)},zn.prototype.pop=function(){return this.slice(1)},zn.prototype.unshift=function(){return this.push.apply(this,arguments)},zn.prototype.unshiftAll=function(e){return this.pushAll(e)},zn.prototype.shift=function(){return this.pop.apply(this,arguments)},zn.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Gn()},zn.prototype.slice=function(e,t){if(C(e,t,this.size))return this;var n=P(e,this.size);if(N(t,this.size)!==this.size)return _e.prototype.slice.call(this,e,t);for(var r=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=r,this._head=o,this.__hash=void 0,this.__altered=!0,this):Hn(r,o)},zn.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Hn(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},zn.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},zn.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new B((function(){if(r){var t=r.value;return r=r.next,$(e,n++,t)}return q()}))},zn.isStack=Vn;var Wn,Jn="@@__IMMUTABLE_STACK__@@",Kn=zn.prototype;function Hn(e,t,n,r){var o=Object.create(Kn);return o.size=e,o._head=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function Gn(){return Wn||(Wn=Hn(0))}function Zn(e,t){var n=function(n){e.prototype[n]=t[n]};return Object.keys(t).forEach(n),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(n),e}Kn[Jn]=!0,Kn.withMutations=He.withMutations,Kn.asMutable=He.asMutable,Kn.asImmutable=He.asImmutable,Kn.wasAltered=He.wasAltered,n.Iterator=B,Zn(n,{toArray:function(){ze(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate((function(t,n){e[n]=t})),e},toIndexedSeq:function(){return new Ht(this)},toJS:function(){return this.toSeq().map((function(e){return e&&"function"==typeof e.toJS?e.toJS():e})).__toJS()},toJSON:function(){return this.toSeq().map((function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e})).__toJS()},toKeyedSeq:function(){return new Kt(this,!0)},toMap:function(){return Ve(this.toKeyedSeq())},toObject:function(){ze(this.size);var e={};return this.__iterate((function(t,n){e[n]=t})),e},toOrderedMap:function(){return Ut(this.toKeyedSeq())},toOrderedSet:function(){return Fn(a(this)?this.valueSeq():this)},toSet:function(){return Cn(a(this)?this.valueSeq():this)},toSetSeq:function(){return new Gt(this)},toSeq:function(){return l(this)?this.toIndexedSeq():a(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return zn(a(this)?this.valueSeq():this)},toList:function(){return _t(a(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){return mn(this,an(this,e.call(arguments,0)))},includes:function(e){return this.some((function(t){return ye(t,e)}))},entries:function(){return this.__iterator(M)},every:function(e,t){ze(this.size);var n=!0;return this.__iterate((function(r,o,s){if(!e.call(t,r,o,s))return n=!1,!1})),n},filter:function(e,t){return mn(this,en(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return ze(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){ze(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate((function(r){n?n=!1:t+=e,t+=null!=r?r.toString():""})),t},keys:function(){return this.__iterator(T)},map:function(e,t){return mn(this,Xt(this,e,t))},reduce:function(e,t,n){var r,o;return ze(this.size),arguments.length<2?o=!0:r=t,this.__iterate((function(t,s,i){o?(o=!1,r=t):r=e.call(n,r,t,s,i)})),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return mn(this,Qt(this,!0))},slice:function(e,t){return mn(this,rn(this,e,t,!0))},some:function(e,t){return!this.every(tr(e),t)},sort:function(e){return mn(this,pn(this,e))},values:function(){return this.__iterator(R)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(e,t){return O(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return tn(this,e,t)},equals:function(e){return ve(this,e)},entrySeq:function(){var e=this;if(e._cache)return new te(e._cache);var t=e.toSeq().map(er).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(tr(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate((function(n,o,s){if(e.call(t,n,o,s))return r=[o,n],!1})),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(A)},flatMap:function(e,t){return mn(this,cn(this,e,t))},flatten:function(e){return mn(this,ln(this,e,!0))},fromEntrySeq:function(){return new Zt(this)},get:function(e,t){return this.find((function(t,n){return ye(n,e)}),void 0,t)},getIn:function(e,t){for(var n,r=this,o=xn(e);!(n=o.next()).done;){var s=n.value;if((r=r&&r.get?r.get(s,b):b)===b)return t}return r},groupBy:function(e,t){return nn(this,e,t)},has:function(e){return this.get(e,b)!==b},hasIn:function(e){return this.getIn(e,b)!==b},isSubset:function(e){return e="function"==typeof e.includes?e:n(e),this.every((function(t){return e.includes(t)}))},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:n(e)).isSubset(this)},keyOf:function(e){return this.findKey((function(t){return ye(t,e)}))},keySeq:function(){return this.toSeq().map(Qn).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return hn(this,e)},maxBy:function(e,t){return hn(this,t,e)},min:function(e){return hn(this,e?nr(e):sr)},minBy:function(e,t){return hn(this,t?nr(t):sr,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return mn(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return mn(this,sn(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(tr(e),t)},sortBy:function(e,t){return mn(this,pn(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return mn(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return mn(this,on(this,e,t))},takeUntil:function(e,t){return this.takeWhile(tr(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=ir(this))}});var Yn=n.prototype;Yn[p]=!0,Yn[L]=Yn.values,Yn.__toJS=Yn.toArray,Yn.__toStringMapper=rr,Yn.inspect=Yn.toSource=function(){return this.toString()},Yn.chain=Yn.flatMap,Yn.contains=Yn.includes,Zn(r,{flip:function(){return mn(this,Yt(this))},mapEntries:function(e,t){var n=this,r=0;return mn(this,this.toSeq().map((function(o,s){return e.call(t,[s,o],r++,n)})).fromEntrySeq())},mapKeys:function(e,t){var n=this;return mn(this,this.toSeq().flip().map((function(r,o){return e.call(t,r,o,n)})).flip())}});var Xn=r.prototype;function Qn(e,t){return t}function er(e,t){return[t,e]}function tr(e){return function(){return!e.apply(this,arguments)}}function nr(e){return function(){return-e.apply(this,arguments)}}function rr(e){return"string"==typeof e?JSON.stringify(e):String(e)}function or(){return j(arguments)}function sr(e,t){return et?-1:0}function ir(e){if(e.size===1/0)return 0;var t=u(e),n=a(e),r=t?1:0;return ar(e.__iterate(n?t?function(e,t){r=31*r+lr(Ae(e),Ae(t))|0}:function(e,t){r=r+lr(Ae(e),Ae(t))|0}:t?function(e){r=31*r+Ae(e)|0}:function(e){r=r+Ae(e)|0}),r)}function ar(e,t){return t=Oe(t,3432918353),t=Oe(t<<15|t>>>-15,461845907),t=Oe(t<<13|t>>>-13,5),t=Oe((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=ke((t=Oe(t^t>>>13,3266489909))^t>>>16)}function lr(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return Xn[h]=!0,Xn[L]=Yn.entries,Xn.__toJS=Yn.toObject,Xn.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+rr(e)},Zn(o,{toKeyedSeq:function(){return new Kt(this,!1)},filter:function(e,t){return mn(this,en(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return mn(this,Qt(this,!1))},slice:function(e,t){return mn(this,rn(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=P(e,e<0?this.count():this.size);var r=this.slice(0,e);return mn(this,1===n?r:r.concat(j(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return mn(this,ln(this,e,!1))},get:function(e,t){return(e=k(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find((function(t,n){return n===e}),void 0,t)},has:function(e){return(e=k(this,e))>=0&&(void 0!==this.size?this.size===1/0||e{"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},35823:e=>{e.exports=function(e,t,n,r){var o=new Blob(void 0!==r?[r,e]:[e],{type:n||"application/octet-stream"});if(void 0!==window.navigator.msSaveBlob)window.navigator.msSaveBlob(o,t);else{var s=window.URL&&window.URL.createObjectURL?window.URL.createObjectURL(o):window.webkitURL.createObjectURL(o),i=document.createElement("a");i.style.display="none",i.href=s,i.setAttribute("download",t),void 0===i.download&&i.setAttribute("target","_blank"),document.body.appendChild(i),i.click(),setTimeout((function(){document.body.removeChild(i),window.URL.revokeObjectURL(s)}),200)}}},91296:(e,t,n)=>{var r=NaN,o="[object Symbol]",s=/^\s+|\s+$/g,i=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,l=/^0o[0-7]+$/i,c=parseInt,u="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,p="object"==typeof self&&self&&self.Object===Object&&self,h=u||p||Function("return this")(),f=Object.prototype.toString,d=Math.max,m=Math.min,g=function(){return h.Date.now()};function y(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function v(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&f.call(e)==o}(e))return r;if(y(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=y(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(s,"");var n=a.test(e);return n||l.test(e)?c(e.slice(2),n?2:8):i.test(e)?r:+e}e.exports=function(e,t,n){var r,o,s,i,a,l,c=0,u=!1,p=!1,h=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function f(t){var n=r,s=o;return r=o=void 0,c=t,i=e.apply(s,n)}function b(e){var n=e-l;return void 0===l||n>=t||n<0||p&&e-c>=s}function w(){var e=g();if(b(e))return E(e);a=setTimeout(w,function(e){var n=t-(e-l);return p?m(n,s-(e-c)):n}(e))}function E(e){return a=void 0,h&&r?f(e):(r=o=void 0,i)}function x(){var e=g(),n=b(e);if(r=arguments,o=this,l=e,n){if(void 0===a)return function(e){return c=e,a=setTimeout(w,t),u?f(e):i}(l);if(p)return a=setTimeout(w,t),f(l)}return void 0===a&&(a=setTimeout(w,t)),i}return t=v(t)||0,y(n)&&(u=!!n.leading,s=(p="maxWait"in n)?d(v(n.maxWait)||0,t):s,h="trailing"in n?!!n.trailing:h),x.cancel=function(){void 0!==a&&clearTimeout(a),c=0,r=l=o=a=void 0},x.flush=function(){return void 0===a?i:E(g())},x}},18552:(e,t,n)=>{var r=n(10852)(n(55639),"DataView");e.exports=r},1989:(e,t,n)=>{var r=n(51789),o=n(80401),s=n(57667),i=n(21327),a=n(81866);function l(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(3118),o=n(9435);function s(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}s.prototype=r(o.prototype),s.prototype.constructor=s,e.exports=s},38407:(e,t,n)=>{var r=n(27040),o=n(14125),s=n(82117),i=n(67518),a=n(54705);function l(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(3118),o=n(9435);function s(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=void 0}s.prototype=r(o.prototype),s.prototype.constructor=s,e.exports=s},57071:(e,t,n)=>{var r=n(10852)(n(55639),"Map");e.exports=r},83369:(e,t,n)=>{var r=n(24785),o=n(11285),s=n(96e3),i=n(49916),a=n(95265);function l(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(10852)(n(55639),"Promise");e.exports=r},58525:(e,t,n)=>{var r=n(10852)(n(55639),"Set");e.exports=r},88668:(e,t,n)=>{var r=n(83369),o=n(90619),s=n(72385);function i(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new r;++t{var r=n(38407),o=n(37465),s=n(63779),i=n(67599),a=n(44758),l=n(34309);function c(e){var t=this.__data__=new r(e);this.size=t.size}c.prototype.clear=o,c.prototype.delete=s,c.prototype.get=i,c.prototype.has=a,c.prototype.set=l,e.exports=c},62705:(e,t,n)=>{var r=n(55639).Symbol;e.exports=r},11149:(e,t,n)=>{var r=n(55639).Uint8Array;e.exports=r},70577:(e,t,n)=>{var r=n(10852)(n(55639),"WeakMap");e.exports=r},96874:e=>{e.exports=function(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}},77412:e=>{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=0,s=[];++n{var r=n(42118);e.exports=function(e,t){return!!(null==e?0:e.length)&&r(e,t,0)>-1}},14636:(e,t,n)=>{var r=n(22545),o=n(35694),s=n(1469),i=n(44144),a=n(65776),l=n(36719),c=Object.prototype.hasOwnProperty;e.exports=function(e,t){var n=s(e),u=!n&&o(e),p=!n&&!u&&i(e),h=!n&&!u&&!p&&l(e),f=n||u||p||h,d=f?r(e.length,String):[],m=d.length;for(var g in e)!t&&!c.call(e,g)||f&&("length"==g||p&&("offset"==g||"parent"==g)||h&&("buffer"==g||"byteLength"==g||"byteOffset"==g)||a(g,m))||d.push(g);return d}},29932:e=>{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=Array(r);++n{e.exports=function(e,t){for(var n=-1,r=t.length,o=e.length;++n{e.exports=function(e,t,n,r){var o=-1,s=null==e?0:e.length;for(r&&s&&(n=e[++o]);++o{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n{e.exports=function(e){return e.split("")}},49029:e=>{var t=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;e.exports=function(e){return e.match(t)||[]}},86556:(e,t,n)=>{var r=n(89465),o=n(77813);e.exports=function(e,t,n){(void 0!==n&&!o(e[t],n)||void 0===n&&!(t in e))&&r(e,t,n)}},34865:(e,t,n)=>{var r=n(89465),o=n(77813),s=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var i=e[t];s.call(e,t)&&o(i,n)&&(void 0!==n||t in e)||r(e,t,n)}},18470:(e,t,n)=>{var r=n(77813);e.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},44037:(e,t,n)=>{var r=n(98363),o=n(3674);e.exports=function(e,t){return e&&r(t,o(t),e)}},63886:(e,t,n)=>{var r=n(98363),o=n(81704);e.exports=function(e,t){return e&&r(t,o(t),e)}},89465:(e,t,n)=>{var r=n(38777);e.exports=function(e,t,n){"__proto__"==t&&r?r(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0}):e[t]=n}},85990:(e,t,n)=>{var r=n(46384),o=n(77412),s=n(34865),i=n(44037),a=n(63886),l=n(64626),c=n(278),u=n(18805),p=n(1911),h=n(58234),f=n(46904),d=n(98882),m=n(43824),g=n(29148),y=n(38517),v=n(1469),b=n(44144),w=n(56688),E=n(13218),x=n(72928),S=n(3674),_=n(81704),j="[object Arguments]",O="[object Function]",k="[object Object]",A={};A[j]=A["[object Array]"]=A["[object ArrayBuffer]"]=A["[object DataView]"]=A["[object Boolean]"]=A["[object Date]"]=A["[object Float32Array]"]=A["[object Float64Array]"]=A["[object Int8Array]"]=A["[object Int16Array]"]=A["[object Int32Array]"]=A["[object Map]"]=A["[object Number]"]=A[k]=A["[object RegExp]"]=A["[object Set]"]=A["[object String]"]=A["[object Symbol]"]=A["[object Uint8Array]"]=A["[object Uint8ClampedArray]"]=A["[object Uint16Array]"]=A["[object Uint32Array]"]=!0,A["[object Error]"]=A[O]=A["[object WeakMap]"]=!1,e.exports=function e(t,n,C,P,N,I){var T,R=1&n,M=2&n,D=4&n;if(C&&(T=N?C(t,P,N,I):C(t)),void 0!==T)return T;if(!E(t))return t;var F=v(t);if(F){if(T=m(t),!R)return c(t,T)}else{var L=d(t),B=L==O||"[object GeneratorFunction]"==L;if(b(t))return l(t,R);if(L==k||L==j||B&&!N){if(T=M||B?{}:y(t),!R)return M?p(t,a(T,t)):u(t,i(T,t))}else{if(!A[L])return N?t:{};T=g(t,L,R)}}I||(I=new r);var $=I.get(t);if($)return $;I.set(t,T),x(t)?t.forEach((function(r){T.add(e(r,n,C,r,t,I))})):w(t)&&t.forEach((function(r,o){T.set(o,e(r,n,C,o,t,I))}));var q=F?void 0:(D?M?f:h:M?_:S)(t);return o(q||t,(function(r,o){q&&(r=t[o=r]),s(T,o,e(r,n,C,o,t,I))})),T}},3118:(e,t,n)=>{var r=n(13218),o=Object.create,s=function(){function e(){}return function(t){if(!r(t))return{};if(o)return o(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();e.exports=s},89881:(e,t,n)=>{var r=n(47816),o=n(99291)(r);e.exports=o},41848:e=>{e.exports=function(e,t,n,r){for(var o=e.length,s=n+(r?1:-1);r?s--:++s{var r=n(62488),o=n(37285);e.exports=function e(t,n,s,i,a){var l=-1,c=t.length;for(s||(s=o),a||(a=[]);++l0&&s(u)?n>1?e(u,n-1,s,i,a):r(a,u):i||(a[a.length]=u)}return a}},28483:(e,t,n)=>{var r=n(25063)();e.exports=r},47816:(e,t,n)=>{var r=n(28483),o=n(3674);e.exports=function(e,t){return e&&r(e,t,o)}},97786:(e,t,n)=>{var r=n(71811),o=n(40327);e.exports=function(e,t){for(var n=0,s=(t=r(t,e)).length;null!=e&&n{var r=n(62488),o=n(1469);e.exports=function(e,t,n){var s=t(e);return o(e)?s:r(s,n(e))}},44239:(e,t,n)=>{var r=n(62705),o=n(89607),s=n(2333),i=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":i&&i in Object(e)?o(e):s(e)}},13:e=>{e.exports=function(e,t){return null!=e&&t in Object(e)}},42118:(e,t,n)=>{var r=n(41848),o=n(62722),s=n(42351);e.exports=function(e,t,n){return t==t?s(e,t,n):r(e,o,n)}},9454:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return o(e)&&"[object Arguments]"==r(e)}},90939:(e,t,n)=>{var r=n(2492),o=n(37005);e.exports=function e(t,n,s,i,a){return t===n||(null==t||null==n||!o(t)&&!o(n)?t!=t&&n!=n:r(t,n,s,i,e,a))}},2492:(e,t,n)=>{var r=n(46384),o=n(67114),s=n(18351),i=n(16096),a=n(98882),l=n(1469),c=n(44144),u=n(36719),p="[object Arguments]",h="[object Array]",f="[object Object]",d=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,m,g,y){var v=l(e),b=l(t),w=v?h:a(e),E=b?h:a(t),x=(w=w==p?f:w)==f,S=(E=E==p?f:E)==f,_=w==E;if(_&&c(e)){if(!c(t))return!1;v=!0,x=!1}if(_&&!x)return y||(y=new r),v||u(e)?o(e,t,n,m,g,y):s(e,t,w,n,m,g,y);if(!(1&n)){var j=x&&d.call(e,"__wrapped__"),O=S&&d.call(t,"__wrapped__");if(j||O){var k=j?e.value():e,A=O?t.value():t;return y||(y=new r),g(k,A,n,m,y)}}return!!_&&(y||(y=new r),i(e,t,n,m,g,y))}},25588:(e,t,n)=>{var r=n(98882),o=n(37005);e.exports=function(e){return o(e)&&"[object Map]"==r(e)}},2958:(e,t,n)=>{var r=n(46384),o=n(90939);e.exports=function(e,t,n,s){var i=n.length,a=i,l=!s;if(null==e)return!a;for(e=Object(e);i--;){var c=n[i];if(l&&c[2]?c[1]!==e[c[0]]:!(c[0]in e))return!1}for(;++i{e.exports=function(e){return e!=e}},28458:(e,t,n)=>{var r=n(23560),o=n(15346),s=n(13218),i=n(80346),a=/^\[object .+?Constructor\]$/,l=Function.prototype,c=Object.prototype,u=l.toString,p=c.hasOwnProperty,h=RegExp("^"+u.call(p).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");e.exports=function(e){return!(!s(e)||o(e))&&(r(e)?h:a).test(i(e))}},29221:(e,t,n)=>{var r=n(98882),o=n(37005);e.exports=function(e){return o(e)&&"[object Set]"==r(e)}},38749:(e,t,n)=>{var r=n(44239),o=n(41780),s=n(37005),i={};i["[object Float32Array]"]=i["[object Float64Array]"]=i["[object Int8Array]"]=i["[object Int16Array]"]=i["[object Int32Array]"]=i["[object Uint8Array]"]=i["[object Uint8ClampedArray]"]=i["[object Uint16Array]"]=i["[object Uint32Array]"]=!0,i["[object Arguments]"]=i["[object Array]"]=i["[object ArrayBuffer]"]=i["[object Boolean]"]=i["[object DataView]"]=i["[object Date]"]=i["[object Error]"]=i["[object Function]"]=i["[object Map]"]=i["[object Number]"]=i["[object Object]"]=i["[object RegExp]"]=i["[object Set]"]=i["[object String]"]=i["[object WeakMap]"]=!1,e.exports=function(e){return s(e)&&o(e.length)&&!!i[r(e)]}},67206:(e,t,n)=>{var r=n(91573),o=n(16432),s=n(6557),i=n(1469),a=n(39601);e.exports=function(e){return"function"==typeof e?e:null==e?s:"object"==typeof e?i(e)?o(e[0],e[1]):r(e):a(e)}},280:(e,t,n)=>{var r=n(25726),o=n(86916),s=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return o(e);var t=[];for(var n in Object(e))s.call(e,n)&&"constructor"!=n&&t.push(n);return t}},10313:(e,t,n)=>{var r=n(13218),o=n(25726),s=n(33498),i=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return s(e);var t=o(e),n=[];for(var a in e)("constructor"!=a||!t&&i.call(e,a))&&n.push(a);return n}},9435:e=>{e.exports=function(){}},91573:(e,t,n)=>{var r=n(2958),o=n(1499),s=n(42634);e.exports=function(e){var t=o(e);return 1==t.length&&t[0][2]?s(t[0][0],t[0][1]):function(n){return n===e||r(n,e,t)}}},16432:(e,t,n)=>{var r=n(90939),o=n(27361),s=n(79095),i=n(15403),a=n(89162),l=n(42634),c=n(40327);e.exports=function(e,t){return i(e)&&a(t)?l(c(e),t):function(n){var i=o(n,e);return void 0===i&&i===t?s(n,e):r(t,i,3)}}},42980:(e,t,n)=>{var r=n(46384),o=n(86556),s=n(28483),i=n(59783),a=n(13218),l=n(81704),c=n(36390);e.exports=function e(t,n,u,p,h){t!==n&&s(n,(function(s,l){if(h||(h=new r),a(s))i(t,n,l,u,e,p,h);else{var f=p?p(c(t,l),s,l+"",t,n,h):void 0;void 0===f&&(f=s),o(t,l,f)}}),l)}},59783:(e,t,n)=>{var r=n(86556),o=n(64626),s=n(77133),i=n(278),a=n(38517),l=n(35694),c=n(1469),u=n(29246),p=n(44144),h=n(23560),f=n(13218),d=n(68630),m=n(36719),g=n(36390),y=n(59881);e.exports=function(e,t,n,v,b,w,E){var x=g(e,n),S=g(t,n),_=E.get(S);if(_)r(e,n,_);else{var j=w?w(x,S,n+"",e,t,E):void 0,O=void 0===j;if(O){var k=c(S),A=!k&&p(S),C=!k&&!A&&m(S);j=S,k||A||C?c(x)?j=x:u(x)?j=i(x):A?(O=!1,j=o(S,!0)):C?(O=!1,j=s(S,!0)):j=[]:d(S)||l(S)?(j=x,l(x)?j=y(x):f(x)&&!h(x)||(j=a(S))):O=!1}O&&(E.set(S,j),b(j,S,v,w,E),E.delete(S)),r(e,n,j)}}},40371:e=>{e.exports=function(e){return function(t){return null==t?void 0:t[e]}}},79152:(e,t,n)=>{var r=n(97786);e.exports=function(e){return function(t){return r(t,e)}}},18674:e=>{e.exports=function(e){return function(t){return null==e?void 0:e[t]}}},10107:e=>{e.exports=function(e,t,n,r,o){return o(e,(function(e,o,s){n=r?(r=!1,e):t(n,e,o,s)})),n}},5976:(e,t,n)=>{var r=n(6557),o=n(45357),s=n(30061);e.exports=function(e,t){return s(o(e,t,r),e+"")}},10611:(e,t,n)=>{var r=n(34865),o=n(71811),s=n(65776),i=n(13218),a=n(40327);e.exports=function(e,t,n,l){if(!i(e))return e;for(var c=-1,u=(t=o(t,e)).length,p=u-1,h=e;null!=h&&++c{var r=n(6557),o=n(89250),s=o?function(e,t){return o.set(e,t),e}:r;e.exports=s},56560:(e,t,n)=>{var r=n(75703),o=n(38777),s=n(6557),i=o?function(e,t){return o(e,"toString",{configurable:!0,enumerable:!1,value:r(t),writable:!0})}:s;e.exports=i},14259:e=>{e.exports=function(e,t,n){var r=-1,o=e.length;t<0&&(t=-t>o?0:o+t),(n=n>o?o:n)<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var s=Array(o);++r{var r=n(89881);e.exports=function(e,t){var n;return r(e,(function(e,r,o){return!(n=t(e,r,o))})),!!n}},22545:e=>{e.exports=function(e,t){for(var n=-1,r=Array(e);++n{var r=n(62705),o=n(29932),s=n(1469),i=n(33448),a=r?r.prototype:void 0,l=a?a.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(s(t))return o(t,e)+"";if(i(t))return l?l.call(t):"";var n=t+"";return"0"==n&&1/t==-Infinity?"-0":n}},27561:(e,t,n)=>{var r=n(67990),o=/^\s+/;e.exports=function(e){return e?e.slice(0,r(e)+1).replace(o,""):e}},7518:e=>{e.exports=function(e){return function(t){return e(t)}}},57406:(e,t,n)=>{var r=n(71811),o=n(10928),s=n(40292),i=n(40327);e.exports=function(e,t){return t=r(t,e),null==(e=s(e,t))||delete e[i(o(t))]}},1757:e=>{e.exports=function(e,t,n){for(var r=-1,o=e.length,s=t.length,i={};++r{e.exports=function(e,t){return e.has(t)}},71811:(e,t,n)=>{var r=n(1469),o=n(15403),s=n(55514),i=n(79833);e.exports=function(e,t){return r(e)?e:o(e,t)?[e]:s(i(e))}},40180:(e,t,n)=>{var r=n(14259);e.exports=function(e,t,n){var o=e.length;return n=void 0===n?o:n,!t&&n>=o?e:r(e,t,n)}},74318:(e,t,n)=>{var r=n(11149);e.exports=function(e){var t=new e.constructor(e.byteLength);return new r(t).set(new r(e)),t}},64626:(e,t,n)=>{e=n.nmd(e);var r=n(55639),o=t&&!t.nodeType&&t,s=o&&e&&!e.nodeType&&e,i=s&&s.exports===o?r.Buffer:void 0,a=i?i.allocUnsafe:void 0;e.exports=function(e,t){if(t)return e.slice();var n=e.length,r=a?a(n):new e.constructor(n);return e.copy(r),r}},57157:(e,t,n)=>{var r=n(74318);e.exports=function(e,t){var n=t?r(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}},93147:e=>{var t=/\w*$/;e.exports=function(e){var n=new e.constructor(e.source,t.exec(e));return n.lastIndex=e.lastIndex,n}},40419:(e,t,n)=>{var r=n(62705),o=r?r.prototype:void 0,s=o?o.valueOf:void 0;e.exports=function(e){return s?Object(s.call(e)):{}}},77133:(e,t,n)=>{var r=n(74318);e.exports=function(e,t){var n=t?r(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}},52157:e=>{var t=Math.max;e.exports=function(e,n,r,o){for(var s=-1,i=e.length,a=r.length,l=-1,c=n.length,u=t(i-a,0),p=Array(c+u),h=!o;++l{var t=Math.max;e.exports=function(e,n,r,o){for(var s=-1,i=e.length,a=-1,l=r.length,c=-1,u=n.length,p=t(i-l,0),h=Array(p+u),f=!o;++s{e.exports=function(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n{var r=n(34865),o=n(89465);e.exports=function(e,t,n,s){var i=!n;n||(n={});for(var a=-1,l=t.length;++a{var r=n(98363),o=n(99551);e.exports=function(e,t){return r(e,o(e),t)}},1911:(e,t,n)=>{var r=n(98363),o=n(51442);e.exports=function(e,t){return r(e,o(e),t)}},14429:(e,t,n)=>{var r=n(55639)["__core-js_shared__"];e.exports=r},97991:e=>{e.exports=function(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}},21463:(e,t,n)=>{var r=n(5976),o=n(16612);e.exports=function(e){return r((function(t,n){var r=-1,s=n.length,i=s>1?n[s-1]:void 0,a=s>2?n[2]:void 0;for(i=e.length>3&&"function"==typeof i?(s--,i):void 0,a&&o(n[0],n[1],a)&&(i=s<3?void 0:i,s=1),t=Object(t);++r{var r=n(98612);e.exports=function(e,t){return function(n,o){if(null==n)return n;if(!r(n))return e(n,o);for(var s=n.length,i=t?s:-1,a=Object(n);(t?i--:++i{e.exports=function(e){return function(t,n,r){for(var o=-1,s=Object(t),i=r(t),a=i.length;a--;){var l=i[e?a:++o];if(!1===n(s[l],l,s))break}return t}}},22402:(e,t,n)=>{var r=n(71774),o=n(55639);e.exports=function(e,t,n){var s=1&t,i=r(e);return function t(){return(this&&this!==o&&this instanceof t?i:e).apply(s?n:this,arguments)}}},98805:(e,t,n)=>{var r=n(40180),o=n(62689),s=n(83140),i=n(79833);e.exports=function(e){return function(t){t=i(t);var n=o(t)?s(t):void 0,a=n?n[0]:t.charAt(0),l=n?r(n,1).join(""):t.slice(1);return a[e]()+l}}},35393:(e,t,n)=>{var r=n(62663),o=n(53816),s=n(58748),i=RegExp("['’]","g");e.exports=function(e){return function(t){return r(s(o(t).replace(i,"")),e,"")}}},71774:(e,t,n)=>{var r=n(3118),o=n(13218);e.exports=function(e){return function(){var t=arguments;switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3]);case 5:return new e(t[0],t[1],t[2],t[3],t[4]);case 6:return new e(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new e(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var n=r(e.prototype),s=e.apply(n,t);return o(s)?s:n}}},46347:(e,t,n)=>{var r=n(96874),o=n(71774),s=n(86935),i=n(94487),a=n(20893),l=n(46460),c=n(55639);e.exports=function(e,t,n){var u=o(e);return function o(){for(var p=arguments.length,h=Array(p),f=p,d=a(o);f--;)h[f]=arguments[f];var m=p<3&&h[0]!==d&&h[p-1]!==d?[]:l(h,d);return(p-=m.length){var r=n(67206),o=n(98612),s=n(3674);e.exports=function(e){return function(t,n,i){var a=Object(t);if(!o(t)){var l=r(n,3);t=s(t),n=function(e){return l(a[e],e,a)}}var c=e(t,n,i);return c>-1?a[l?t[c]:c]:void 0}}},86935:(e,t,n)=>{var r=n(52157),o=n(14054),s=n(97991),i=n(71774),a=n(94487),l=n(20893),c=n(90451),u=n(46460),p=n(55639);e.exports=function e(t,n,h,f,d,m,g,y,v,b){var w=128&n,E=1&n,x=2&n,S=24&n,_=512&n,j=x?void 0:i(t);return function O(){for(var k=arguments.length,A=Array(k),C=k;C--;)A[C]=arguments[C];if(S)var P=l(O),N=s(A,P);if(f&&(A=r(A,f,d,S)),m&&(A=o(A,m,g,S)),k-=N,S&&k1&&A.reverse(),w&&v{var r=n(96874),o=n(71774),s=n(55639);e.exports=function(e,t,n,i){var a=1&t,l=o(e);return function t(){for(var o=-1,c=arguments.length,u=-1,p=i.length,h=Array(p+c),f=this&&this!==s&&this instanceof t?l:e;++u{var r=n(86528),o=n(258),s=n(69255);e.exports=function(e,t,n,i,a,l,c,u,p,h){var f=8&t;t|=f?32:64,4&(t&=~(f?64:32))||(t&=-4);var d=[e,t,a,f?l:void 0,f?c:void 0,f?void 0:l,f?void 0:c,u,p,h],m=n.apply(void 0,d);return r(e)&&o(m,d),m.placeholder=i,s(m,e,t)}},97727:(e,t,n)=>{var r=n(28045),o=n(22402),s=n(46347),i=n(86935),a=n(84375),l=n(66833),c=n(63833),u=n(258),p=n(69255),h=n(40554),f=Math.max;e.exports=function(e,t,n,d,m,g,y,v){var b=2&t;if(!b&&"function"!=typeof e)throw new TypeError("Expected a function");var w=d?d.length:0;if(w||(t&=-97,d=m=void 0),y=void 0===y?y:f(h(y),0),v=void 0===v?v:h(v),w-=m?m.length:0,64&t){var E=d,x=m;d=m=void 0}var S=b?void 0:l(e),_=[e,t,n,d,m,E,x,g,y,v];if(S&&c(_,S),e=_[0],t=_[1],n=_[2],d=_[3],m=_[4],!(v=_[9]=void 0===_[9]?b?0:e.length:f(_[9]-w,0))&&24&t&&(t&=-25),t&&1!=t)j=8==t||16==t?s(e,t,v):32!=t&&33!=t||m.length?i.apply(void 0,_):a(e,t,n,d);else var j=o(e,t,n);return p((S?r:u)(j,_),e,t)}},60696:(e,t,n)=>{var r=n(68630);e.exports=function(e){return r(e)?void 0:e}},69389:(e,t,n)=>{var r=n(18674)({À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"});e.exports=r},38777:(e,t,n)=>{var r=n(10852),o=function(){try{var e=r(Object,"defineProperty");return e({},"",{}),e}catch(e){}}();e.exports=o},67114:(e,t,n)=>{var r=n(88668),o=n(82908),s=n(74757);e.exports=function(e,t,n,i,a,l){var c=1&n,u=e.length,p=t.length;if(u!=p&&!(c&&p>u))return!1;var h=l.get(e),f=l.get(t);if(h&&f)return h==t&&f==e;var d=-1,m=!0,g=2&n?new r:void 0;for(l.set(e,t),l.set(t,e);++d{var r=n(62705),o=n(11149),s=n(77813),i=n(67114),a=n(68776),l=n(21814),c=r?r.prototype:void 0,u=c?c.valueOf:void 0;e.exports=function(e,t,n,r,c,p,h){switch(n){case"[object DataView]":if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case"[object ArrayBuffer]":return!(e.byteLength!=t.byteLength||!p(new o(e),new o(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return s(+e,+t);case"[object Error]":return e.name==t.name&&e.message==t.message;case"[object RegExp]":case"[object String]":return e==t+"";case"[object Map]":var f=a;case"[object Set]":var d=1&r;if(f||(f=l),e.size!=t.size&&!d)return!1;var m=h.get(e);if(m)return m==t;r|=2,h.set(e,t);var g=i(f(e),f(t),r,c,p,h);return h.delete(e),g;case"[object Symbol]":if(u)return u.call(e)==u.call(t)}return!1}},16096:(e,t,n)=>{var r=n(58234),o=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,s,i,a){var l=1&n,c=r(e),u=c.length;if(u!=r(t).length&&!l)return!1;for(var p=u;p--;){var h=c[p];if(!(l?h in t:o.call(t,h)))return!1}var f=a.get(e),d=a.get(t);if(f&&d)return f==t&&d==e;var m=!0;a.set(e,t),a.set(t,e);for(var g=l;++p{var r=n(85564),o=n(45357),s=n(30061);e.exports=function(e){return s(o(e,void 0,r),e+"")}},31957:(e,t,n)=>{var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;e.exports=r},58234:(e,t,n)=>{var r=n(68866),o=n(99551),s=n(3674);e.exports=function(e){return r(e,s,o)}},46904:(e,t,n)=>{var r=n(68866),o=n(51442),s=n(81704);e.exports=function(e){return r(e,s,o)}},66833:(e,t,n)=>{var r=n(89250),o=n(50308),s=r?function(e){return r.get(e)}:o;e.exports=s},97658:(e,t,n)=>{var r=n(52060),o=Object.prototype.hasOwnProperty;e.exports=function(e){for(var t=e.name+"",n=r[t],s=o.call(r,t)?n.length:0;s--;){var i=n[s],a=i.func;if(null==a||a==e)return i.name}return t}},20893:e=>{e.exports=function(e){return e.placeholder}},45050:(e,t,n)=>{var r=n(37019);e.exports=function(e,t){var n=e.__data__;return r(t)?n["string"==typeof t?"string":"hash"]:n.map}},1499:(e,t,n)=>{var r=n(89162),o=n(3674);e.exports=function(e){for(var t=o(e),n=t.length;n--;){var s=t[n],i=e[s];t[n]=[s,i,r(i)]}return t}},10852:(e,t,n)=>{var r=n(28458),o=n(47801);e.exports=function(e,t){var n=o(e,t);return r(n)?n:void 0}},85924:(e,t,n)=>{var r=n(5569)(Object.getPrototypeOf,Object);e.exports=r},89607:(e,t,n)=>{var r=n(62705),o=Object.prototype,s=o.hasOwnProperty,i=o.toString,a=r?r.toStringTag:void 0;e.exports=function(e){var t=s.call(e,a),n=e[a];try{e[a]=void 0;var r=!0}catch(e){}var o=i.call(e);return r&&(t?e[a]=n:delete e[a]),o}},99551:(e,t,n)=>{var r=n(34963),o=n(70479),s=Object.prototype.propertyIsEnumerable,i=Object.getOwnPropertySymbols,a=i?function(e){return null==e?[]:(e=Object(e),r(i(e),(function(t){return s.call(e,t)})))}:o;e.exports=a},51442:(e,t,n)=>{var r=n(62488),o=n(85924),s=n(99551),i=n(70479),a=Object.getOwnPropertySymbols?function(e){for(var t=[];e;)r(t,s(e)),e=o(e);return t}:i;e.exports=a},98882:(e,t,n)=>{var r=n(18552),o=n(57071),s=n(53818),i=n(58525),a=n(70577),l=n(44239),c=n(80346),u="[object Map]",p="[object Promise]",h="[object Set]",f="[object WeakMap]",d="[object DataView]",m=c(r),g=c(o),y=c(s),v=c(i),b=c(a),w=l;(r&&w(new r(new ArrayBuffer(1)))!=d||o&&w(new o)!=u||s&&w(s.resolve())!=p||i&&w(new i)!=h||a&&w(new a)!=f)&&(w=function(e){var t=l(e),n="[object Object]"==t?e.constructor:void 0,r=n?c(n):"";if(r)switch(r){case m:return d;case g:return u;case y:return p;case v:return h;case b:return f}return t}),e.exports=w},47801:e=>{e.exports=function(e,t){return null==e?void 0:e[t]}},58775:e=>{var t=/\{\n\/\* \[wrapped with (.+)\] \*/,n=/,? & /;e.exports=function(e){var r=e.match(t);return r?r[1].split(n):[]}},222:(e,t,n)=>{var r=n(71811),o=n(35694),s=n(1469),i=n(65776),a=n(41780),l=n(40327);e.exports=function(e,t,n){for(var c=-1,u=(t=r(t,e)).length,p=!1;++c{var t=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");e.exports=function(e){return t.test(e)}},93157:e=>{var t=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;e.exports=function(e){return t.test(e)}},51789:(e,t,n)=>{var r=n(94536);e.exports=function(){this.__data__=r?r(null):{},this.size=0}},80401:e=>{e.exports=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}},57667:(e,t,n)=>{var r=n(94536),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;if(r){var n=t[e];return"__lodash_hash_undefined__"===n?void 0:n}return o.call(t,e)?t[e]:void 0}},21327:(e,t,n)=>{var r=n(94536),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;return r?void 0!==t[e]:o.call(t,e)}},81866:(e,t,n)=>{var r=n(94536);e.exports=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=r&&void 0===t?"__lodash_hash_undefined__":t,this}},43824:e=>{var t=Object.prototype.hasOwnProperty;e.exports=function(e){var n=e.length,r=new e.constructor(n);return n&&"string"==typeof e[0]&&t.call(e,"index")&&(r.index=e.index,r.input=e.input),r}},29148:(e,t,n)=>{var r=n(74318),o=n(57157),s=n(93147),i=n(40419),a=n(77133);e.exports=function(e,t,n){var l=e.constructor;switch(t){case"[object ArrayBuffer]":return r(e);case"[object Boolean]":case"[object Date]":return new l(+e);case"[object DataView]":return o(e,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return a(e,n);case"[object Map]":case"[object Set]":return new l;case"[object Number]":case"[object String]":return new l(e);case"[object RegExp]":return s(e);case"[object Symbol]":return i(e)}}},38517:(e,t,n)=>{var r=n(3118),o=n(85924),s=n(25726);e.exports=function(e){return"function"!=typeof e.constructor||s(e)?{}:r(o(e))}},83112:e=>{var t=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/;e.exports=function(e,n){var r=n.length;if(!r)return e;var o=r-1;return n[o]=(r>1?"& ":"")+n[o],n=n.join(r>2?", ":" "),e.replace(t,"{\n/* [wrapped with "+n+"] */\n")}},37285:(e,t,n)=>{var r=n(62705),o=n(35694),s=n(1469),i=r?r.isConcatSpreadable:void 0;e.exports=function(e){return s(e)||o(e)||!!(i&&e&&e[i])}},65776:e=>{var t=/^(?:0|[1-9]\d*)$/;e.exports=function(e,n){var r=typeof e;return!!(n=null==n?9007199254740991:n)&&("number"==r||"symbol"!=r&&t.test(e))&&e>-1&&e%1==0&&e{var r=n(77813),o=n(98612),s=n(65776),i=n(13218);e.exports=function(e,t,n){if(!i(n))return!1;var a=typeof t;return!!("number"==a?o(n)&&s(t,n.length):"string"==a&&t in n)&&r(n[t],e)}},15403:(e,t,n)=>{var r=n(1469),o=n(33448),s=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,i=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||(i.test(e)||!s.test(e)||null!=t&&e in Object(t))}},37019:e=>{e.exports=function(e){var t=typeof e;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e}},86528:(e,t,n)=>{var r=n(96425),o=n(66833),s=n(97658),i=n(8111);e.exports=function(e){var t=s(e),n=i[t];if("function"!=typeof n||!(t in r.prototype))return!1;if(e===n)return!0;var a=o(n);return!!a&&e===a[0]}},15346:(e,t,n)=>{var r,o=n(14429),s=(r=/[^.]+$/.exec(o&&o.keys&&o.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";e.exports=function(e){return!!s&&s in e}},25726:e=>{var t=Object.prototype;e.exports=function(e){var n=e&&e.constructor;return e===("function"==typeof n&&n.prototype||t)}},89162:(e,t,n)=>{var r=n(13218);e.exports=function(e){return e==e&&!r(e)}},27040:e=>{e.exports=function(){this.__data__=[],this.size=0}},14125:(e,t,n)=>{var r=n(18470),o=Array.prototype.splice;e.exports=function(e){var t=this.__data__,n=r(t,e);return!(n<0)&&(n==t.length-1?t.pop():o.call(t,n,1),--this.size,!0)}},82117:(e,t,n)=>{var r=n(18470);e.exports=function(e){var t=this.__data__,n=r(t,e);return n<0?void 0:t[n][1]}},67518:(e,t,n)=>{var r=n(18470);e.exports=function(e){return r(this.__data__,e)>-1}},54705:(e,t,n)=>{var r=n(18470);e.exports=function(e,t){var n=this.__data__,o=r(n,e);return o<0?(++this.size,n.push([e,t])):n[o][1]=t,this}},24785:(e,t,n)=>{var r=n(1989),o=n(38407),s=n(57071);e.exports=function(){this.size=0,this.__data__={hash:new r,map:new(s||o),string:new r}}},11285:(e,t,n)=>{var r=n(45050);e.exports=function(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}},96e3:(e,t,n)=>{var r=n(45050);e.exports=function(e){return r(this,e).get(e)}},49916:(e,t,n)=>{var r=n(45050);e.exports=function(e){return r(this,e).has(e)}},95265:(e,t,n)=>{var r=n(45050);e.exports=function(e,t){var n=r(this,e),o=n.size;return n.set(e,t),this.size+=n.size==o?0:1,this}},68776:e=>{e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}},42634:e=>{e.exports=function(e,t){return function(n){return null!=n&&(n[e]===t&&(void 0!==t||e in Object(n)))}}},24523:(e,t,n)=>{var r=n(88306);e.exports=function(e){var t=r(e,(function(e){return 500===n.size&&n.clear(),e})),n=t.cache;return t}},63833:(e,t,n)=>{var r=n(52157),o=n(14054),s=n(46460),i="__lodash_placeholder__",a=128,l=Math.min;e.exports=function(e,t){var n=e[1],c=t[1],u=n|c,p=u<131,h=c==a&&8==n||c==a&&256==n&&e[7].length<=t[8]||384==c&&t[7].length<=t[8]&&8==n;if(!p&&!h)return e;1&c&&(e[2]=t[2],u|=1&n?0:4);var f=t[3];if(f){var d=e[3];e[3]=d?r(d,f,t[4]):f,e[4]=d?s(e[3],i):t[4]}return(f=t[5])&&(d=e[5],e[5]=d?o(d,f,t[6]):f,e[6]=d?s(e[5],i):t[6]),(f=t[7])&&(e[7]=f),c&a&&(e[8]=null==e[8]?t[8]:l(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=u,e}},89250:(e,t,n)=>{var r=n(70577),o=r&&new r;e.exports=o},94536:(e,t,n)=>{var r=n(10852)(Object,"create");e.exports=r},86916:(e,t,n)=>{var r=n(5569)(Object.keys,Object);e.exports=r},33498:e=>{e.exports=function(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}},31167:(e,t,n)=>{e=n.nmd(e);var r=n(31957),o=t&&!t.nodeType&&t,s=o&&e&&!e.nodeType&&e,i=s&&s.exports===o&&r.process,a=function(){try{var e=s&&s.require&&s.require("util").types;return e||i&&i.binding&&i.binding("util")}catch(e){}}();e.exports=a},2333:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},5569:e=>{e.exports=function(e,t){return function(n){return e(t(n))}}},45357:(e,t,n)=>{var r=n(96874),o=Math.max;e.exports=function(e,t,n){return t=o(void 0===t?e.length-1:t,0),function(){for(var s=arguments,i=-1,a=o(s.length-t,0),l=Array(a);++i{var r=n(97786),o=n(14259);e.exports=function(e,t){return t.length<2?e:r(e,o(t,0,-1))}},52060:e=>{e.exports={}},90451:(e,t,n)=>{var r=n(278),o=n(65776),s=Math.min;e.exports=function(e,t){for(var n=e.length,i=s(t.length,n),a=r(e);i--;){var l=t[i];e[i]=o(l,n)?a[l]:void 0}return e}},46460:e=>{var t="__lodash_placeholder__";e.exports=function(e,n){for(var r=-1,o=e.length,s=0,i=[];++r{var r=n(31957),o="object"==typeof self&&self&&self.Object===Object&&self,s=r||o||Function("return this")();e.exports=s},36390:e=>{e.exports=function(e,t){if(("constructor"!==t||"function"!=typeof e[t])&&"__proto__"!=t)return e[t]}},90619:e=>{e.exports=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this}},72385:e=>{e.exports=function(e){return this.__data__.has(e)}},258:(e,t,n)=>{var r=n(28045),o=n(21275)(r);e.exports=o},21814:e=>{e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=e})),n}},30061:(e,t,n)=>{var r=n(56560),o=n(21275)(r);e.exports=o},69255:(e,t,n)=>{var r=n(58775),o=n(83112),s=n(30061),i=n(87241);e.exports=function(e,t,n){var a=t+"";return s(e,o(a,i(r(a),n)))}},21275:e=>{var t=Date.now;e.exports=function(e){var n=0,r=0;return function(){var o=t(),s=16-(o-r);if(r=o,s>0){if(++n>=800)return arguments[0]}else n=0;return e.apply(void 0,arguments)}}},37465:(e,t,n)=>{var r=n(38407);e.exports=function(){this.__data__=new r,this.size=0}},63779:e=>{e.exports=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}},67599:e=>{e.exports=function(e){return this.__data__.get(e)}},44758:e=>{e.exports=function(e){return this.__data__.has(e)}},34309:(e,t,n)=>{var r=n(38407),o=n(57071),s=n(83369);e.exports=function(e,t){var n=this.__data__;if(n instanceof r){var i=n.__data__;if(!o||i.length<199)return i.push([e,t]),this.size=++n.size,this;n=this.__data__=new s(i)}return n.set(e,t),this.size=n.size,this}},42351:e=>{e.exports=function(e,t,n){for(var r=n-1,o=e.length;++r{var r=n(44286),o=n(62689),s=n(676);e.exports=function(e){return o(e)?s(e):r(e)}},55514:(e,t,n)=>{var r=n(24523),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,s=/\\(\\)?/g,i=r((function(e){var t=[];return 46===e.charCodeAt(0)&&t.push(""),e.replace(o,(function(e,n,r,o){t.push(r?o.replace(s,"$1"):n||e)})),t}));e.exports=i},40327:(e,t,n)=>{var r=n(33448);e.exports=function(e){if("string"==typeof e||r(e))return e;var t=e+"";return"0"==t&&1/e==-Infinity?"-0":t}},80346:e=>{var t=Function.prototype.toString;e.exports=function(e){if(null!=e){try{return t.call(e)}catch(e){}try{return e+""}catch(e){}}return""}},67990:e=>{var t=/\s/;e.exports=function(e){for(var n=e.length;n--&&t.test(e.charAt(n)););return n}},676:e=>{var t="\\ud800-\\udfff",n="["+t+"]",r="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",o="\\ud83c[\\udffb-\\udfff]",s="[^"+t+"]",i="(?:\\ud83c[\\udde6-\\uddff]){2}",a="[\\ud800-\\udbff][\\udc00-\\udfff]",l="(?:"+r+"|"+o+")"+"?",c="[\\ufe0e\\ufe0f]?",u=c+l+("(?:\\u200d(?:"+[s,i,a].join("|")+")"+c+l+")*"),p="(?:"+[s+r+"?",r,i,a,n].join("|")+")",h=RegExp(o+"(?="+o+")|"+p+u,"g");e.exports=function(e){return e.match(h)||[]}},2757:e=>{var t="\\ud800-\\udfff",n="\\u2700-\\u27bf",r="a-z\\xdf-\\xf6\\xf8-\\xff",o="A-Z\\xc0-\\xd6\\xd8-\\xde",s="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",i="["+s+"]",a="\\d+",l="["+n+"]",c="["+r+"]",u="[^"+t+s+a+n+r+o+"]",p="(?:\\ud83c[\\udde6-\\uddff]){2}",h="[\\ud800-\\udbff][\\udc00-\\udfff]",f="["+o+"]",d="(?:"+c+"|"+u+")",m="(?:"+f+"|"+u+")",g="(?:['’](?:d|ll|m|re|s|t|ve))?",y="(?:['’](?:D|LL|M|RE|S|T|VE))?",v="(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?",b="[\\ufe0e\\ufe0f]?",w=b+v+("(?:\\u200d(?:"+["[^"+t+"]",p,h].join("|")+")"+b+v+")*"),E="(?:"+[l,p,h].join("|")+")"+w,x=RegExp([f+"?"+c+"+"+g+"(?="+[i,f,"$"].join("|")+")",m+"+"+y+"(?="+[i,f+d,"$"].join("|")+")",f+"?"+d+"+"+g,f+"+"+y,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",a,E].join("|"),"g");e.exports=function(e){return e.match(x)||[]}},87241:(e,t,n)=>{var r=n(77412),o=n(47443),s=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]];e.exports=function(e,t){return r(s,(function(n){var r="_."+n[0];t&n[1]&&!o(e,r)&&e.push(r)})),e.sort()}},21913:(e,t,n)=>{var r=n(96425),o=n(7548),s=n(278);e.exports=function(e){if(e instanceof r)return e.clone();var t=new o(e.__wrapped__,e.__chain__);return t.__actions__=s(e.__actions__),t.__index__=e.__index__,t.__values__=e.__values__,t}},39514:(e,t,n)=>{var r=n(97727);e.exports=function(e,t,n){return t=n?void 0:t,t=e&&null==t?e.length:t,r(e,128,void 0,void 0,void 0,void 0,t)}},68929:(e,t,n)=>{var r=n(48403),o=n(35393)((function(e,t,n){return t=t.toLowerCase(),e+(n?r(t):t)}));e.exports=o},48403:(e,t,n)=>{var r=n(79833),o=n(11700);e.exports=function(e){return o(r(e).toLowerCase())}},66678:(e,t,n)=>{var r=n(85990);e.exports=function(e){return r(e,4)}},75703:e=>{e.exports=function(e){return function(){return e}}},40087:(e,t,n)=>{var r=n(97727);function o(e,t,n){var s=r(e,8,void 0,void 0,void 0,void 0,void 0,t=n?void 0:t);return s.placeholder=o.placeholder,s}o.placeholder={},e.exports=o},23279:(e,t,n)=>{var r=n(13218),o=n(7771),s=n(14841),i=Math.max,a=Math.min;e.exports=function(e,t,n){var l,c,u,p,h,f,d=0,m=!1,g=!1,y=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function v(t){var n=l,r=c;return l=c=void 0,d=t,p=e.apply(r,n)}function b(e){var n=e-f;return void 0===f||n>=t||n<0||g&&e-d>=u}function w(){var e=o();if(b(e))return E(e);h=setTimeout(w,function(e){var n=t-(e-f);return g?a(n,u-(e-d)):n}(e))}function E(e){return h=void 0,y&&l?v(e):(l=c=void 0,p)}function x(){var e=o(),n=b(e);if(l=arguments,c=this,f=e,n){if(void 0===h)return function(e){return d=e,h=setTimeout(w,t),m?v(e):p}(f);if(g)return clearTimeout(h),h=setTimeout(w,t),v(f)}return void 0===h&&(h=setTimeout(w,t)),p}return t=s(t)||0,r(n)&&(m=!!n.leading,u=(g="maxWait"in n)?i(s(n.maxWait)||0,t):u,y="trailing"in n?!!n.trailing:y),x.cancel=function(){void 0!==h&&clearTimeout(h),d=0,l=f=c=h=void 0},x.flush=function(){return void 0===h?p:E(o())},x}},53816:(e,t,n)=>{var r=n(69389),o=n(79833),s=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,i=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");e.exports=function(e){return(e=o(e))&&e.replace(s,r).replace(i,"")}},77813:e=>{e.exports=function(e,t){return e===t||e!=e&&t!=t}},13311:(e,t,n)=>{var r=n(67740)(n(30998));e.exports=r},30998:(e,t,n)=>{var r=n(41848),o=n(67206),s=n(40554),i=Math.max;e.exports=function(e,t,n){var a=null==e?0:e.length;if(!a)return-1;var l=null==n?0:s(n);return l<0&&(l=i(a+l,0)),r(e,o(t,3),l)}},85564:(e,t,n)=>{var r=n(21078);e.exports=function(e){return(null==e?0:e.length)?r(e,1):[]}},84599:(e,t,n)=>{var r=n(68836),o=n(69306),s=Array.prototype.push;function i(e,t){return 2==t?function(t,n){return e(t,n)}:function(t){return e(t)}}function a(e){for(var t=e?e.length:0,n=Array(t);t--;)n[t]=e[t];return n}function l(e,t){return function(){var n=arguments.length;if(n){for(var r=Array(n);n--;)r[n]=arguments[n];var o=r[0]=t.apply(void 0,r);return e.apply(void 0,r),o}}}e.exports=function e(t,n,c,u){var p="function"==typeof n,h=n===Object(n);if(h&&(u=c,c=n,n=void 0),null==c)throw new TypeError;u||(u={});var f={cap:!("cap"in u)||u.cap,curry:!("curry"in u)||u.curry,fixed:!("fixed"in u)||u.fixed,immutable:!("immutable"in u)||u.immutable,rearg:!("rearg"in u)||u.rearg},d=p?c:o,m="curry"in u&&u.curry,g="fixed"in u&&u.fixed,y="rearg"in u&&u.rearg,v=p?c.runInContext():void 0,b=p?c:{ary:t.ary,assign:t.assign,clone:t.clone,curry:t.curry,forEach:t.forEach,isArray:t.isArray,isError:t.isError,isFunction:t.isFunction,isWeakMap:t.isWeakMap,iteratee:t.iteratee,keys:t.keys,rearg:t.rearg,toInteger:t.toInteger,toPath:t.toPath},w=b.ary,E=b.assign,x=b.clone,S=b.curry,_=b.forEach,j=b.isArray,O=b.isError,k=b.isFunction,A=b.isWeakMap,C=b.keys,P=b.rearg,N=b.toInteger,I=b.toPath,T=C(r.aryMethod),R={castArray:function(e){return function(){var t=arguments[0];return j(t)?e(a(t)):e.apply(void 0,arguments)}},iteratee:function(e){return function(){var t=arguments[1],n=e(arguments[0],t),r=n.length;return f.cap&&"number"==typeof t?(t=t>2?t-2:1,r&&r<=t?n:i(n,t)):n}},mixin:function(e){return function(t){var n=this;if(!k(n))return e(n,Object(t));var r=[];return _(C(t),(function(e){k(t[e])&&r.push([e,n.prototype[e]])})),e(n,Object(t)),_(r,(function(e){var t=e[1];k(t)?n.prototype[e[0]]=t:delete n.prototype[e[0]]})),n}},nthArg:function(e){return function(t){var n=t<0?1:N(t)+1;return S(e(t),n)}},rearg:function(e){return function(t,n){var r=n?n.length:0;return S(e(t,n),r)}},runInContext:function(n){return function(r){return e(t,n(r),u)}}};function M(e,t){if(f.cap){var n=r.iterateeRearg[e];if(n)return function(e,t){return $(e,(function(e){var n=t.length;return function(e,t){return 2==t?function(t,n){return e.apply(void 0,arguments)}:function(t){return e.apply(void 0,arguments)}}(P(i(e,n),t),n)}))}(t,n);var o=!p&&r.iterateeAry[e];if(o)return function(e,t){return $(e,(function(e){return"function"==typeof e?i(e,t):e}))}(t,o)}return t}function D(e,t,n){if(f.fixed&&(g||!r.skipFixed[e])){var o=r.methodSpread[e],i=o&&o.start;return void 0===i?w(t,n):function(e,t){return function(){for(var n=arguments.length,r=n-1,o=Array(n);n--;)o[n]=arguments[n];var i=o[t],a=o.slice(0,t);return i&&s.apply(a,i),t!=r&&s.apply(a,o.slice(t+1)),e.apply(this,a)}}(t,i)}return t}function F(e,t,n){return f.rearg&&n>1&&(y||!r.skipRearg[e])?P(t,r.methodRearg[e]||r.aryRearg[n]):t}function L(e,t){for(var n=-1,r=(t=I(t)).length,o=r-1,s=x(Object(e)),i=s;null!=i&&++n1?S(t,n):t}(0,o=M(s,o),e),!1}})),!o})),o||(o=i),o==t&&(o=m?S(o,1):function(){return t.apply(this,arguments)}),o.convert=B(s,t),o.placeholder=t.placeholder=n,o}if(!h)return q(n,c,d);var U=c,z=[];return _(T,(function(e){_(r.aryMethod[e],(function(e){var t=U[r.remap[e]||e];t&&z.push([e,q(e,t,U)])}))})),_(C(U),(function(e){var t=U[e];if("function"==typeof t){for(var n=z.length;n--;)if(z[n][0]==e)return;t.convert=B(e,t),z.push([e,t])}})),_(z,(function(e){U[e[0]]=e[1]})),U.convert=function(e){return U.runInContext.convert(e)(void 0)},U.placeholder=U,_(C(U),(function(e){_(r.realToAlias[e]||[],(function(t){U[t]=U[e]}))})),U}},68836:(e,t)=>{t.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},t.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},t.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},t.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},t.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},t.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},t.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},t.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},t.realToAlias=function(){var e=Object.prototype.hasOwnProperty,n=t.aliasToReal,r={};for(var o in n){var s=n[o];e.call(r,s)?r[s].push(o):r[s]=[o]}return r}(),t.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},t.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},t.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},4269:(e,t,n)=>{e.exports={ary:n(39514),assign:n(44037),clone:n(66678),curry:n(40087),forEach:n(77412),isArray:n(1469),isError:n(64647),isFunction:n(23560),isWeakMap:n(81018),iteratee:n(72594),keys:n(280),rearg:n(4963),toInteger:n(40554),toPath:n(30084)}},72700:(e,t,n)=>{e.exports=n(28252)},92822:(e,t,n)=>{var r=n(84599),o=n(4269);e.exports=function(e,t,n){return r(o,e,t,n)}},69306:e=>{e.exports={}},28252:(e,t,n)=>{var r=n(92822)("set",n(36968));r.placeholder=n(69306),e.exports=r},27361:(e,t,n)=>{var r=n(97786);e.exports=function(e,t,n){var o=null==e?void 0:r(e,t);return void 0===o?n:o}},79095:(e,t,n)=>{var r=n(13),o=n(222);e.exports=function(e,t){return null!=e&&o(e,t,r)}},6557:e=>{e.exports=function(e){return e}},35694:(e,t,n)=>{var r=n(9454),o=n(37005),s=Object.prototype,i=s.hasOwnProperty,a=s.propertyIsEnumerable,l=r(function(){return arguments}())?r:function(e){return o(e)&&i.call(e,"callee")&&!a.call(e,"callee")};e.exports=l},1469:e=>{var t=Array.isArray;e.exports=t},98612:(e,t,n)=>{var r=n(23560),o=n(41780);e.exports=function(e){return null!=e&&o(e.length)&&!r(e)}},29246:(e,t,n)=>{var r=n(98612),o=n(37005);e.exports=function(e){return o(e)&&r(e)}},51584:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return!0===e||!1===e||o(e)&&"[object Boolean]"==r(e)}},44144:(e,t,n)=>{e=n.nmd(e);var r=n(55639),o=n(95062),s=t&&!t.nodeType&&t,i=s&&e&&!e.nodeType&&e,a=i&&i.exports===s?r.Buffer:void 0,l=(a?a.isBuffer:void 0)||o;e.exports=l},41609:(e,t,n)=>{var r=n(280),o=n(98882),s=n(35694),i=n(1469),a=n(98612),l=n(44144),c=n(25726),u=n(36719),p=Object.prototype.hasOwnProperty;e.exports=function(e){if(null==e)return!0;if(a(e)&&(i(e)||"string"==typeof e||"function"==typeof e.splice||l(e)||u(e)||s(e)))return!e.length;var t=o(e);if("[object Map]"==t||"[object Set]"==t)return!e.size;if(c(e))return!r(e).length;for(var n in e)if(p.call(e,n))return!1;return!0}},18446:(e,t,n)=>{var r=n(90939);e.exports=function(e,t){return r(e,t)}},64647:(e,t,n)=>{var r=n(44239),o=n(37005),s=n(68630);e.exports=function(e){if(!o(e))return!1;var t=r(e);return"[object Error]"==t||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!s(e)}},23560:(e,t,n)=>{var r=n(44239),o=n(13218);e.exports=function(e){if(!o(e))return!1;var t=r(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},41780:e=>{e.exports=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}},56688:(e,t,n)=>{var r=n(25588),o=n(7518),s=n(31167),i=s&&s.isMap,a=i?o(i):r;e.exports=a},45220:e=>{e.exports=function(e){return null===e}},81763:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return"number"==typeof e||o(e)&&"[object Number]"==r(e)}},13218:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},37005:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},68630:(e,t,n)=>{var r=n(44239),o=n(85924),s=n(37005),i=Function.prototype,a=Object.prototype,l=i.toString,c=a.hasOwnProperty,u=l.call(Object);e.exports=function(e){if(!s(e)||"[object Object]"!=r(e))return!1;var t=o(e);if(null===t)return!0;var n=c.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&l.call(n)==u}},72928:(e,t,n)=>{var r=n(29221),o=n(7518),s=n(31167),i=s&&s.isSet,a=i?o(i):r;e.exports=a},47037:(e,t,n)=>{var r=n(44239),o=n(1469),s=n(37005);e.exports=function(e){return"string"==typeof e||!o(e)&&s(e)&&"[object String]"==r(e)}},33448:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},36719:(e,t,n)=>{var r=n(38749),o=n(7518),s=n(31167),i=s&&s.isTypedArray,a=i?o(i):r;e.exports=a},81018:(e,t,n)=>{var r=n(98882),o=n(37005);e.exports=function(e){return o(e)&&"[object WeakMap]"==r(e)}},72594:(e,t,n)=>{var r=n(85990),o=n(67206);e.exports=function(e){return o("function"==typeof e?e:r(e,1))}},3674:(e,t,n)=>{var r=n(14636),o=n(280),s=n(98612);e.exports=function(e){return s(e)?r(e):o(e)}},81704:(e,t,n)=>{var r=n(14636),o=n(10313),s=n(98612);e.exports=function(e){return s(e)?r(e,!0):o(e)}},10928:e=>{e.exports=function(e){var t=null==e?0:e.length;return t?e[t-1]:void 0}},88306:(e,t,n)=>{var r=n(83369);function o(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new TypeError("Expected a function");var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],s=n.cache;if(s.has(o))return s.get(o);var i=e.apply(this,r);return n.cache=s.set(o,i)||s,i};return n.cache=new(o.Cache||r),n}o.Cache=r,e.exports=o},82492:(e,t,n)=>{var r=n(42980),o=n(21463)((function(e,t,n){r(e,t,n)}));e.exports=o},94885:e=>{e.exports=function(e){if("function"!=typeof e)throw new TypeError("Expected a function");return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}},50308:e=>{e.exports=function(){}},7771:(e,t,n)=>{var r=n(55639);e.exports=function(){return r.Date.now()}},57557:(e,t,n)=>{var r=n(29932),o=n(85990),s=n(57406),i=n(71811),a=n(98363),l=n(60696),c=n(99021),u=n(46904),p=c((function(e,t){var n={};if(null==e)return n;var c=!1;t=r(t,(function(t){return t=i(t,e),c||(c=t.length>1),t})),a(e,u(e),n),c&&(n=o(n,7,l));for(var p=t.length;p--;)s(n,t[p]);return n}));e.exports=p},39601:(e,t,n)=>{var r=n(40371),o=n(79152),s=n(15403),i=n(40327);e.exports=function(e){return s(e)?r(i(e)):o(e)}},4963:(e,t,n)=>{var r=n(97727),o=n(99021),s=o((function(e,t){return r(e,256,void 0,void 0,void 0,t)}));e.exports=s},54061:(e,t,n)=>{var r=n(62663),o=n(89881),s=n(67206),i=n(10107),a=n(1469);e.exports=function(e,t,n){var l=a(e)?r:i,c=arguments.length<3;return l(e,s(t,4),n,c,o)}},36968:(e,t,n)=>{var r=n(10611);e.exports=function(e,t,n){return null==e?e:r(e,t,n)}},59704:(e,t,n)=>{var r=n(82908),o=n(67206),s=n(5076),i=n(1469),a=n(16612);e.exports=function(e,t,n){var l=i(e)?r:s;return n&&a(e,t,n)&&(t=void 0),l(e,o(t,3))}},70479:e=>{e.exports=function(){return[]}},95062:e=>{e.exports=function(){return!1}},18601:(e,t,n)=>{var r=n(14841),o=1/0;e.exports=function(e){return e?(e=r(e))===o||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}},40554:(e,t,n)=>{var r=n(18601);e.exports=function(e){var t=r(e),n=t%1;return t==t?n?t-n:t:0}},7334:(e,t,n)=>{var r=n(79833);e.exports=function(e){return r(e).toLowerCase()}},14841:(e,t,n)=>{var r=n(27561),o=n(13218),s=n(33448),i=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,l=/^0o[0-7]+$/i,c=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(s(e))return NaN;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var n=a.test(e);return n||l.test(e)?c(e.slice(2),n?2:8):i.test(e)?NaN:+e}},30084:(e,t,n)=>{var r=n(29932),o=n(278),s=n(1469),i=n(33448),a=n(55514),l=n(40327),c=n(79833);e.exports=function(e){return s(e)?r(e,l):i(e)?[e]:o(a(c(e)))}},59881:(e,t,n)=>{var r=n(98363),o=n(81704);e.exports=function(e){return r(e,o(e))}},79833:(e,t,n)=>{var r=n(80531);e.exports=function(e){return null==e?"":r(e)}},11700:(e,t,n)=>{var r=n(98805)("toUpperCase");e.exports=r},58748:(e,t,n)=>{var r=n(49029),o=n(93157),s=n(79833),i=n(2757);e.exports=function(e,t,n){return e=s(e),void 0===(t=n?void 0:t)?o(e)?i(e):r(e):e.match(t)||[]}},8111:(e,t,n)=>{var r=n(96425),o=n(7548),s=n(9435),i=n(1469),a=n(37005),l=n(21913),c=Object.prototype.hasOwnProperty;function u(e){if(a(e)&&!i(e)&&!(e instanceof r)){if(e instanceof o)return e;if(c.call(e,"__wrapped__"))return l(e)}return new o(e)}u.prototype=s.prototype,u.prototype.constructor=u,e.exports=u},7287:(e,t,n)=>{var r=n(34865),o=n(1757);e.exports=function(e,t){return o(e||[],t||[],r)}},96470:(e,t,n)=>{"use strict";var r=n(47802),o=n(21102);t.highlight=i,t.highlightAuto=function(e,t){var n,a,l,c,u=t||{},p=u.subset||r.listLanguages(),h=u.prefix,f=p.length,d=-1;null==h&&(h=s);if("string"!=typeof e)throw o("Expected `string` for value, got `%s`",e);a={relevance:0,language:null,value:[]},n={relevance:0,language:null,value:[]};for(;++da.relevance&&(a=l),l.relevance>n.relevance&&(a=n,n=l));a.language&&(n.secondBest=a);return n},t.registerLanguage=function(e,t){r.registerLanguage(e,t)},t.listLanguages=function(){return r.listLanguages()},t.registerAlias=function(e,t){var n,o=e;t&&((o={})[e]=t);for(n in o)r.registerAliases(o[n],{languageName:n})},a.prototype.addText=function(e){var t,n,r=this.stack;if(""===e)return;t=r[r.length-1],(n=t.children[t.children.length-1])&&"text"===n.type?n.value+=e:t.children.push({type:"text",value:e})},a.prototype.addKeyword=function(e,t){this.openNode(t),this.addText(e),this.closeNode()},a.prototype.addSublanguage=function(e,t){var n=this.stack,r=n[n.length-1],o=e.rootNode.children,s=t?{type:"element",tagName:"span",properties:{className:[t]},children:o}:o;r.children=r.children.concat(s)},a.prototype.openNode=function(e){var t=this.stack,n=this.options.classPrefix+e,r=t[t.length-1],o={type:"element",tagName:"span",properties:{className:[n]},children:[]};r.children.push(o),t.push(o)},a.prototype.closeNode=function(){this.stack.pop()},a.prototype.closeAllNodes=l,a.prototype.finalize=l,a.prototype.toHTML=function(){return""};var s="hljs-";function i(e,t,n){var i,l=r.configure({}),c=(n||{}).prefix;if("string"!=typeof e)throw o("Expected `string` for name, got `%s`",e);if(!r.getLanguage(e))throw o("Unknown language: `%s` is not registered",e);if("string"!=typeof t)throw o("Expected `string` for value, got `%s`",t);if(null==c&&(c=s),r.configure({__emitter:a,classPrefix:c}),i=r.highlight(t,{language:e,ignoreIllegals:!0}),r.configure(l||{}),i.errorRaised)throw i.errorRaised;return{relevance:i.relevance,language:i.language,value:i.emitter.rootNode.children}}function a(e){this.options=e,this.rootNode={children:[]},this.stack=[this.rootNode]}function l(){}},42566:(e,t,n)=>{const r=n(94885);function o(e){return"string"==typeof e?t=>t.element===e:e.constructor&&e.extend?t=>t instanceof e:e}class s{constructor(e){this.elements=e||[]}toValue(){return this.elements.map((e=>e.toValue()))}map(e,t){return this.elements.map(e,t)}flatMap(e,t){return this.map(e,t).reduce(((e,t)=>e.concat(t)),[])}compactMap(e,t){const n=[];return this.forEach((r=>{const o=e.bind(t)(r);o&&n.push(o)})),n}filter(e,t){return e=o(e),new s(this.elements.filter(e,t))}reject(e,t){return e=o(e),new s(this.elements.filter(r(e),t))}find(e,t){return e=o(e),this.elements.find(e,t)}forEach(e,t){this.elements.forEach(e,t)}reduce(e,t){return this.elements.reduce(e,t)}includes(e){return this.elements.some((t=>t.equals(e)))}shift(){return this.elements.shift()}unshift(e){this.elements.unshift(this.refract(e))}push(e){return this.elements.push(this.refract(e)),this}add(e){this.push(e)}get(e){return this.elements[e]}getValue(e){const t=this.elements[e];if(t)return t.toValue()}get length(){return this.elements.length}get isEmpty(){return 0===this.elements.length}get first(){return this.elements[0]}}"undefined"!=typeof Symbol&&(s.prototype[Symbol.iterator]=function(){return this.elements[Symbol.iterator]()}),e.exports=s},17645:e=>{class t{constructor(e,t){this.key=e,this.value=t}clone(){const e=new t;return this.key&&(e.key=this.key.clone()),this.value&&(e.value=this.value.clone()),e}}e.exports=t},78520:(e,t,n)=>{const r=n(45220),o=n(47037),s=n(81763),i=n(51584),a=n(13218),l=n(28219),c=n(99829);class u{constructor(e){this.elementMap={},this.elementDetection=[],this.Element=c.Element,this.KeyValuePair=c.KeyValuePair,e&&e.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(e){return e.namespace&&e.namespace({base:this}),e.load&&e.load({base:this}),this}useDefault(){return this.register("null",c.NullElement).register("string",c.StringElement).register("number",c.NumberElement).register("boolean",c.BooleanElement).register("array",c.ArrayElement).register("object",c.ObjectElement).register("member",c.MemberElement).register("ref",c.RefElement).register("link",c.LinkElement),this.detect(r,c.NullElement,!1).detect(o,c.StringElement,!1).detect(s,c.NumberElement,!1).detect(i,c.BooleanElement,!1).detect(Array.isArray,c.ArrayElement,!1).detect(a,c.ObjectElement,!1),this}register(e,t){return this._elements=void 0,this.elementMap[e]=t,this}unregister(e){return this._elements=void 0,delete this.elementMap[e],this}detect(e,t,n){return void 0===n||n?this.elementDetection.unshift([e,t]):this.elementDetection.push([e,t]),this}toElement(e){if(e instanceof this.Element)return e;let t;for(let n=0;n{const t=e[0].toUpperCase()+e.substr(1);this._elements[t]=this.elementMap[e]}))),this._elements}get serialiser(){return new l(this)}}l.prototype.Namespace=u,e.exports=u},87526:(e,t,n)=>{const r=n(94885),o=n(42566);class s extends o{map(e,t){return this.elements.map((n=>e.bind(t)(n.value,n.key,n)))}filter(e,t){return new s(this.elements.filter((n=>e.bind(t)(n.value,n.key,n))))}reject(e,t){return this.filter(r(e.bind(t)))}forEach(e,t){return this.elements.forEach(((n,r)=>{e.bind(t)(n.value,n.key,n,r)}))}keys(){return this.map(((e,t)=>t.toValue()))}values(){return this.map((e=>e.toValue()))}}e.exports=s},99829:(e,t,n)=>{const r=n(3079),o=n(96295),s=n(16036),i=n(91090),a=n(18866),l=n(35804),c=n(5946),u=n(76735),p=n(59964),h=n(38588),f=n(42566),d=n(87526),m=n(17645);function g(e){if(e instanceof r)return e;if("string"==typeof e)return new s(e);if("number"==typeof e)return new i(e);if("boolean"==typeof e)return new a(e);if(null===e)return new o;if(Array.isArray(e))return new l(e.map(g));if("object"==typeof e){return new u(e)}return e}r.prototype.ObjectElement=u,r.prototype.RefElement=h,r.prototype.MemberElement=c,r.prototype.refract=g,f.prototype.refract=g,e.exports={Element:r,NullElement:o,StringElement:s,NumberElement:i,BooleanElement:a,ArrayElement:l,MemberElement:c,ObjectElement:u,LinkElement:p,RefElement:h,refract:g,ArraySlice:f,ObjectSlice:d,KeyValuePair:m}},59964:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e||[],t,n),this.element="link"}get relation(){return this.attributes.get("relation")}set relation(e){this.attributes.set("relation",e)}get href(){return this.attributes.get("href")}set href(e){this.attributes.set("href",e)}}},38588:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e||[],t,n),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(e){this.attributes.set("path",e)}}},43500:(e,t,n)=>{const r=n(78520),o=n(99829);t.lS=r,n(17645),t.O4=o.ArraySlice,o.ObjectSlice,t.W_=o.Element,t.RP=o.StringElement,t.VL=o.NumberElement,t.hh=o.BooleanElement,t.zr=o.NullElement,t.ON=o.ArrayElement,t.Sb=o.ObjectElement,t.c6=o.MemberElement,t.tK=o.RefElement,t.EA=o.LinkElement,t.Qc=o.refract,n(28219),n(3414)},35804:(e,t,n)=>{const r=n(94885),o=n(3079),s=n(42566);class i extends o{constructor(e,t,n){super(e||[],t,n),this.element="array"}primitive(){return"array"}get(e){return this.content[e]}getValue(e){const t=this.get(e);if(t)return t.toValue()}getIndex(e){return this.content[e]}set(e,t){return this.content[e]=this.refract(t),this}remove(e){const t=this.content.splice(e,1);return t.length?t[0]:null}map(e,t){return this.content.map(e,t)}flatMap(e,t){return this.map(e,t).reduce(((e,t)=>e.concat(t)),[])}compactMap(e,t){const n=[];return this.forEach((r=>{const o=e.bind(t)(r);o&&n.push(o)})),n}filter(e,t){return new s(this.content.filter(e,t))}reject(e,t){return this.filter(r(e),t)}reduce(e,t){let n,r;void 0!==t?(n=0,r=this.refract(t)):(n=1,r="object"===this.primitive()?this.first.value:this.first);for(let t=n;t{e.bind(t)(n,this.refract(r))}))}shift(){return this.content.shift()}unshift(e){this.content.unshift(this.refract(e))}push(e){return this.content.push(this.refract(e)),this}add(e){this.push(e)}findElements(e,t){const n=t||{},r=!!n.recursive,o=void 0===n.results?[]:n.results;return this.forEach(((t,n,s)=>{r&&void 0!==t.findElements&&t.findElements(e,{results:o,recursive:r}),e(t,n,s)&&o.push(t)})),o}find(e){return new s(this.findElements(e,{recursive:!0}))}findByElement(e){return this.find((t=>t.element===e))}findByClass(e){return this.find((t=>t.classes.includes(e)))}getById(e){return this.find((t=>t.id.toValue()===e)).first}includes(e){return this.content.some((t=>t.equals(e)))}contains(e){return this.includes(e)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(e){return new this.constructor(this.content.concat(e.content))}"fantasy-land/concat"(e){return this.concat(e)}"fantasy-land/map"(e){return new this.constructor(this.map(e))}"fantasy-land/chain"(e){return this.map((t=>e(t)),this).reduce(((e,t)=>e.concat(t)),this.empty())}"fantasy-land/filter"(e){return new this.constructor(this.content.filter(e))}"fantasy-land/reduce"(e,t){return this.content.reduce(e,t)}get length(){return this.content.length}get isEmpty(){return 0===this.content.length}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}i.empty=function(){return new this},i["fantasy-land/empty"]=i.empty,"undefined"!=typeof Symbol&&(i.prototype[Symbol.iterator]=function(){return this.content[Symbol.iterator]()}),e.exports=i},18866:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e,t,n),this.element="boolean"}primitive(){return"boolean"}}},3079:(e,t,n)=>{const r=n(18446),o=n(17645),s=n(42566);class i{constructor(e,t,n){t&&(this.meta=t),n&&(this.attributes=n),this.content=e}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this,this.meta.freeze()),this._attributes&&(this.attributes.parent=this,this.attributes.freeze()),this.children.forEach((e=>{e.parent=this,e.freeze()}),this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const e=new this.constructor;return e.element=this.element,this.meta.length&&(e._meta=this.meta.clone()),this.attributes.length&&(e._attributes=this.attributes.clone()),this.content?this.content.clone?e.content=this.content.clone():Array.isArray(this.content)?e.content=this.content.map((e=>e.clone())):e.content=this.content:e.content=this.content,e}toValue(){return this.content instanceof i?this.content.toValue():this.content instanceof o?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map((e=>e.toValue()),this):this.content}toRef(e){if(""===this.id.toValue())throw Error("Cannot create reference to an element that does not contain an ID");const t=new this.RefElement(this.id.toValue());return e&&(t.path=e),t}findRecursive(...e){if(arguments.length>1&&!this.isFrozen)throw new Error("Cannot find recursive with multiple element names without first freezing the element. Call `element.freeze()`");const t=e.pop();let n=new s;const r=(e,t)=>(e.push(t),e),i=(e,n)=>{n.element===t&&e.push(n);const s=n.findRecursive(t);return s&&s.reduce(r,e),n.content instanceof o&&(n.content.key&&i(e,n.content.key),n.content.value&&i(e,n.content.value)),e};return this.content&&(this.content.element&&i(n,this.content),Array.isArray(this.content)&&this.content.reduce(i,n)),e.isEmpty||(n=n.filter((t=>{let n=t.parents.map((e=>e.element));for(const t in e){const r=e[t],o=n.indexOf(r);if(-1===o)return!1;n=n.splice(0,o)}return!0}))),n}set(e){return this.content=e,this}equals(e){return r(this.toValue(),e)}getMetaProperty(e,t){if(!this.meta.hasKey(e)){if(this.isFrozen){const e=this.refract(t);return e.freeze(),e}this.meta.set(e,t)}return this.meta.get(e)}setMetaProperty(e,t){this.meta.set(e,t)}get element(){return this._storedElement||"element"}set element(e){this._storedElement=e}get content(){return this._content}set content(e){if(e instanceof i)this._content=e;else if(e instanceof s)this.content=e.elements;else if("string"==typeof e||"number"==typeof e||"boolean"==typeof e||"null"===e||null==e)this._content=e;else if(e instanceof o)this._content=e;else if(Array.isArray(e))this._content=e.map(this.refract);else{if("object"!=typeof e)throw new Error("Cannot set content to given value");this._content=Object.keys(e).map((t=>new this.MemberElement(t,e[t])))}}get meta(){if(!this._meta){if(this.isFrozen){const e=new this.ObjectElement;return e.freeze(),e}this._meta=new this.ObjectElement}return this._meta}set meta(e){e instanceof this.ObjectElement?this._meta=e:this.meta.set(e||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const e=new this.ObjectElement;return e.freeze(),e}this._attributes=new this.ObjectElement}return this._attributes}set attributes(e){e instanceof this.ObjectElement?this._attributes=e:this.attributes.set(e||{})}get id(){return this.getMetaProperty("id","")}set id(e){this.setMetaProperty("id",e)}get classes(){return this.getMetaProperty("classes",[])}set classes(e){this.setMetaProperty("classes",e)}get title(){return this.getMetaProperty("title","")}set title(e){this.setMetaProperty("title",e)}get description(){return this.getMetaProperty("description","")}set description(e){this.setMetaProperty("description",e)}get links(){return this.getMetaProperty("links",[])}set links(e){this.setMetaProperty("links",e)}get isFrozen(){return Object.isFrozen(this)}get parents(){let{parent:e}=this;const t=new s;for(;e;)t.push(e),e=e.parent;return t}get children(){if(Array.isArray(this.content))return new s(this.content);if(this.content instanceof o){const e=new s([this.content.key]);return this.content.value&&e.push(this.content.value),e}return this.content instanceof i?new s([this.content]):new s}get recursiveChildren(){const e=new s;return this.children.forEach((t=>{e.push(t),t.recursiveChildren.forEach((t=>{e.push(t)}))})),e}}e.exports=i},5946:(e,t,n)=>{const r=n(17645),o=n(3079);e.exports=class extends o{constructor(e,t,n,o){super(new r,n,o),this.element="member",this.key=e,this.value=t}get key(){return this.content.key}set key(e){this.content.key=this.refract(e)}get value(){return this.content.value}set value(e){this.content.value=this.refract(e)}}},96295:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e||null,t,n),this.element="null"}primitive(){return"null"}set(){return new Error("Cannot set the value of null")}}},91090:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e,t,n),this.element="number"}primitive(){return"number"}}},76735:(e,t,n)=>{const r=n(94885),o=n(13218),s=n(35804),i=n(5946),a=n(87526);e.exports=class extends s{constructor(e,t,n){super(e||[],t,n),this.element="object"}primitive(){return"object"}toValue(){return this.content.reduce(((e,t)=>(e[t.key.toValue()]=t.value?t.value.toValue():void 0,e)),{})}get(e){const t=this.getMember(e);if(t)return t.value}getMember(e){if(void 0!==e)return this.content.find((t=>t.key.toValue()===e))}remove(e){let t=null;return this.content=this.content.filter((n=>n.key.toValue()!==e||(t=n,!1))),t}getKey(e){const t=this.getMember(e);if(t)return t.key}set(e,t){if(o(e))return Object.keys(e).forEach((t=>{this.set(t,e[t])})),this;const n=e,r=this.getMember(n);return r?r.value=t:this.content.push(new i(n,t)),this}keys(){return this.content.map((e=>e.key.toValue()))}values(){return this.content.map((e=>e.value.toValue()))}hasKey(e){return this.content.some((t=>t.key.equals(e)))}items(){return this.content.map((e=>[e.key.toValue(),e.value.toValue()]))}map(e,t){return this.content.map((n=>e.bind(t)(n.value,n.key,n)))}compactMap(e,t){const n=[];return this.forEach(((r,o,s)=>{const i=e.bind(t)(r,o,s);i&&n.push(i)})),n}filter(e,t){return new a(this.content).filter(e,t)}reject(e,t){return this.filter(r(e),t)}forEach(e,t){return this.content.forEach((n=>e.bind(t)(n.value,n.key,n)))}}},16036:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e,t,n),this.element="string"}primitive(){return"string"}get length(){return this.content.length}}},3414:(e,t,n)=>{const r=n(28219);e.exports=class extends r{serialise(e){if(!(e instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${e}\` is not an Element instance`);let t;e._attributes&&e.attributes.get("variable")&&(t=e.attributes.get("variable"));const n={element:e.element};e._meta&&e._meta.length>0&&(n.meta=this.serialiseObject(e.meta));const r="enum"===e.element||-1!==e.attributes.keys().indexOf("enumerations");if(r){const t=this.enumSerialiseAttributes(e);t&&(n.attributes=t)}else if(e._attributes&&e._attributes.length>0){let{attributes:r}=e;r.get("metadata")&&(r=r.clone(),r.set("meta",r.get("metadata")),r.remove("metadata")),"member"===e.element&&t&&(r=r.clone(),r.remove("variable")),r.length>0&&(n.attributes=this.serialiseObject(r))}if(r)n.content=this.enumSerialiseContent(e,n);else if(this[`${e.element}SerialiseContent`])n.content=this[`${e.element}SerialiseContent`](e,n);else if(void 0!==e.content){let r;t&&e.content.key?(r=e.content.clone(),r.key.attributes.set("variable",t),r=this.serialiseContent(r)):r=this.serialiseContent(e.content),this.shouldSerialiseContent(e,r)&&(n.content=r)}else this.shouldSerialiseContent(e,e.content)&&e instanceof this.namespace.elements.Array&&(n.content=[]);return n}shouldSerialiseContent(e,t){return"parseResult"===e.element||"httpRequest"===e.element||"httpResponse"===e.element||"category"===e.element||"link"===e.element||void 0!==t&&(!Array.isArray(t)||0!==t.length)}refSerialiseContent(e,t){return delete t.attributes,{href:e.toValue(),path:e.path.toValue()}}sourceMapSerialiseContent(e){return e.toValue()}dataStructureSerialiseContent(e){return[this.serialiseContent(e.content)]}enumSerialiseAttributes(e){const t=e.attributes.clone(),n=t.remove("enumerations")||new this.namespace.elements.Array([]),r=t.get("default");let o=t.get("samples")||new this.namespace.elements.Array([]);if(r&&r.content&&(r.content.attributes&&r.content.attributes.remove("typeAttributes"),t.set("default",new this.namespace.elements.Array([r.content]))),o.forEach((e=>{e.content&&e.content.element&&e.content.attributes.remove("typeAttributes")})),e.content&&0!==n.length&&o.unshift(e.content),o=o.map((e=>e instanceof this.namespace.elements.Array?[e]:new this.namespace.elements.Array([e.content]))),o.length&&t.set("samples",o),t.length>0)return this.serialiseObject(t)}enumSerialiseContent(e){if(e._attributes){const t=e.attributes.get("enumerations");if(t&&t.length>0)return t.content.map((e=>{const t=e.clone();return t.attributes.remove("typeAttributes"),this.serialise(t)}))}if(e.content){const t=e.content.clone();return t.attributes.remove("typeAttributes"),[this.serialise(t)]}return[]}deserialise(e){if("string"==typeof e)return new this.namespace.elements.String(e);if("number"==typeof e)return new this.namespace.elements.Number(e);if("boolean"==typeof e)return new this.namespace.elements.Boolean(e);if(null===e)return new this.namespace.elements.Null;if(Array.isArray(e))return new this.namespace.elements.Array(e.map(this.deserialise,this));const t=this.namespace.getElementClass(e.element),n=new t;n.element!==e.element&&(n.element=e.element),e.meta&&this.deserialiseObject(e.meta,n.meta),e.attributes&&this.deserialiseObject(e.attributes,n.attributes);const r=this.deserialiseContent(e.content);if(void 0===r&&null!==n.content||(n.content=r),"enum"===n.element){n.content&&n.attributes.set("enumerations",n.content);let e=n.attributes.get("samples");if(n.attributes.remove("samples"),e){const r=e;e=new this.namespace.elements.Array,r.forEach((r=>{r.forEach((r=>{const o=new t(r);o.element=n.element,e.push(o)}))}));const o=e.shift();n.content=o?o.content:void 0,n.attributes.set("samples",e)}else n.content=void 0;let r=n.attributes.get("default");if(r&&r.length>0){r=r.get(0);const e=new t(r);e.element=n.element,n.attributes.set("default",e)}}else if("dataStructure"===n.element&&Array.isArray(n.content))[n.content]=n.content;else if("category"===n.element){const e=n.attributes.get("meta");e&&(n.attributes.set("metadata",e),n.attributes.remove("meta"))}else"member"===n.element&&n.key&&n.key._attributes&&n.key._attributes.getValue("variable")&&(n.attributes.set("variable",n.key.attributes.get("variable")),n.key.attributes.remove("variable"));return n}serialiseContent(e){if(e instanceof this.namespace.elements.Element)return this.serialise(e);if(e instanceof this.namespace.KeyValuePair){const t={key:this.serialise(e.key)};return e.value&&(t.value=this.serialise(e.value)),t}return e&&e.map?e.map(this.serialise,this):e}deserialiseContent(e){if(e){if(e.element)return this.deserialise(e);if(e.key){const t=new this.namespace.KeyValuePair(this.deserialise(e.key));return e.value&&(t.value=this.deserialise(e.value)),t}if(e.map)return e.map(this.deserialise,this)}return e}shouldRefract(e){return!!(e._attributes&&e.attributes.keys().length||e._meta&&e.meta.keys().length)||"enum"!==e.element&&(e.element!==e.primitive()||"member"===e.element)}convertKeyToRefract(e,t){return this.shouldRefract(t)?this.serialise(t):"enum"===t.element?this.serialiseEnum(t):"array"===t.element?t.map((t=>this.shouldRefract(t)||"default"===e?this.serialise(t):"array"===t.element||"object"===t.element||"enum"===t.element?t.children.map((e=>this.serialise(e))):t.toValue())):"object"===t.element?(t.content||[]).map(this.serialise,this):t.toValue()}serialiseEnum(e){return e.children.map((e=>this.serialise(e)))}serialiseObject(e){const t={};return e.forEach(((e,n)=>{if(e){const r=n.toValue();t[r]=this.convertKeyToRefract(r,e)}})),t}deserialiseObject(e,t){Object.keys(e).forEach((n=>{t.set(n,this.deserialise(e[n]))}))}}},28219:e=>{e.exports=class{constructor(e){this.namespace=e||new this.Namespace}serialise(e){if(!(e instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${e}\` is not an Element instance`);const t={element:e.element};e._meta&&e._meta.length>0&&(t.meta=this.serialiseObject(e.meta)),e._attributes&&e._attributes.length>0&&(t.attributes=this.serialiseObject(e.attributes));const n=this.serialiseContent(e.content);return void 0!==n&&(t.content=n),t}deserialise(e){if(!e.element)throw new Error("Given value is not an object containing an element name");const t=new(this.namespace.getElementClass(e.element));t.element!==e.element&&(t.element=e.element),e.meta&&this.deserialiseObject(e.meta,t.meta),e.attributes&&this.deserialiseObject(e.attributes,t.attributes);const n=this.deserialiseContent(e.content);return void 0===n&&null!==t.content||(t.content=n),t}serialiseContent(e){if(e instanceof this.namespace.elements.Element)return this.serialise(e);if(e instanceof this.namespace.KeyValuePair){const t={key:this.serialise(e.key)};return e.value&&(t.value=this.serialise(e.value)),t}if(e&&e.map){if(0===e.length)return;return e.map(this.serialise,this)}return e}deserialiseContent(e){if(e){if(e.element)return this.deserialise(e);if(e.key){const t=new this.namespace.KeyValuePair(this.deserialise(e.key));return e.value&&(t.value=this.deserialise(e.value)),t}if(e.map)return e.map(this.deserialise,this)}return e}serialiseObject(e){const t={};if(e.forEach(((e,n)=>{e&&(t[n.toValue()]=this.serialise(e))})),0!==Object.keys(t).length)return t}deserialiseObject(e,t){Object.keys(e).forEach((n=>{t.set(n,this.deserialise(e[n]))}))}}},27418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,o){for(var s,i,a=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),l=1;l{var r="function"==typeof Map&&Map.prototype,o=Object.getOwnPropertyDescriptor&&r?Object.getOwnPropertyDescriptor(Map.prototype,"size"):null,s=r&&o&&"function"==typeof o.get?o.get:null,i=r&&Map.prototype.forEach,a="function"==typeof Set&&Set.prototype,l=Object.getOwnPropertyDescriptor&&a?Object.getOwnPropertyDescriptor(Set.prototype,"size"):null,c=a&&l&&"function"==typeof l.get?l.get:null,u=a&&Set.prototype.forEach,p="function"==typeof WeakMap&&WeakMap.prototype?WeakMap.prototype.has:null,h="function"==typeof WeakSet&&WeakSet.prototype?WeakSet.prototype.has:null,f="function"==typeof WeakRef&&WeakRef.prototype?WeakRef.prototype.deref:null,d=Boolean.prototype.valueOf,m=Object.prototype.toString,g=Function.prototype.toString,y=String.prototype.match,v=String.prototype.slice,b=String.prototype.replace,w=String.prototype.toUpperCase,E=String.prototype.toLowerCase,x=RegExp.prototype.test,S=Array.prototype.concat,_=Array.prototype.join,j=Array.prototype.slice,O=Math.floor,k="function"==typeof BigInt?BigInt.prototype.valueOf:null,A=Object.getOwnPropertySymbols,C="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?Symbol.prototype.toString:null,P="function"==typeof Symbol&&"object"==typeof Symbol.iterator,N="function"==typeof Symbol&&Symbol.toStringTag&&(typeof Symbol.toStringTag===P||"symbol")?Symbol.toStringTag:null,I=Object.prototype.propertyIsEnumerable,T=("function"==typeof Reflect?Reflect.getPrototypeOf:Object.getPrototypeOf)||([].__proto__===Array.prototype?function(e){return e.__proto__}:null);function R(e,t){if(e===1/0||e===-1/0||e!=e||e&&e>-1e3&&e<1e3||x.call(/e/,t))return t;var n=/[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;if("number"==typeof e){var r=e<0?-O(-e):O(e);if(r!==e){var o=String(r),s=v.call(t,o.length+1);return b.call(o,n,"$&_")+"."+b.call(b.call(s,/([0-9]{3})/g,"$&_"),/_$/,"")}}return b.call(t,n,"$&_")}var M=n(24654),D=M.custom,F=U(D)?D:null;function L(e,t,n){var r="double"===(n.quoteStyle||t)?'"':"'";return r+e+r}function B(e){return b.call(String(e),/"/g,""")}function $(e){return!("[object Array]"!==W(e)||N&&"object"==typeof e&&N in e)}function q(e){return!("[object RegExp]"!==W(e)||N&&"object"==typeof e&&N in e)}function U(e){if(P)return e&&"object"==typeof e&&e instanceof Symbol;if("symbol"==typeof e)return!0;if(!e||"object"!=typeof e||!C)return!1;try{return C.call(e),!0}catch(e){}return!1}e.exports=function e(t,n,r,o){var a=n||{};if(V(a,"quoteStyle")&&"single"!==a.quoteStyle&&"double"!==a.quoteStyle)throw new TypeError('option "quoteStyle" must be "single" or "double"');if(V(a,"maxStringLength")&&("number"==typeof a.maxStringLength?a.maxStringLength<0&&a.maxStringLength!==1/0:null!==a.maxStringLength))throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`');var l=!V(a,"customInspect")||a.customInspect;if("boolean"!=typeof l&&"symbol"!==l)throw new TypeError("option \"customInspect\", if provided, must be `true`, `false`, or `'symbol'`");if(V(a,"indent")&&null!==a.indent&&"\t"!==a.indent&&!(parseInt(a.indent,10)===a.indent&&a.indent>0))throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`');if(V(a,"numericSeparator")&&"boolean"!=typeof a.numericSeparator)throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var m=a.numericSeparator;if(void 0===t)return"undefined";if(null===t)return"null";if("boolean"==typeof t)return t?"true":"false";if("string"==typeof t)return K(t,a);if("number"==typeof t){if(0===t)return 1/0/t>0?"0":"-0";var w=String(t);return m?R(t,w):w}if("bigint"==typeof t){var x=String(t)+"n";return m?R(t,x):x}var O=void 0===a.depth?5:a.depth;if(void 0===r&&(r=0),r>=O&&O>0&&"object"==typeof t)return $(t)?"[Array]":"[Object]";var A=function(e,t){var n;if("\t"===e.indent)n="\t";else{if(!("number"==typeof e.indent&&e.indent>0))return null;n=_.call(Array(e.indent+1)," ")}return{base:n,prev:_.call(Array(t+1),n)}}(a,r);if(void 0===o)o=[];else if(J(o,t)>=0)return"[Circular]";function D(t,n,s){if(n&&(o=j.call(o)).push(n),s){var i={depth:a.depth};return V(a,"quoteStyle")&&(i.quoteStyle=a.quoteStyle),e(t,i,r+1,o)}return e(t,a,r+1,o)}if("function"==typeof t&&!q(t)){var z=function(e){if(e.name)return e.name;var t=y.call(g.call(e),/^function\s*([\w$]+)/);if(t)return t[1];return null}(t),H=Q(t,D);return"[Function"+(z?": "+z:" (anonymous)")+"]"+(H.length>0?" { "+_.call(H,", ")+" }":"")}if(U(t)){var ee=P?b.call(String(t),/^(Symbol\(.*\))_[^)]*$/,"$1"):C.call(t);return"object"!=typeof t||P?ee:G(ee)}if(function(e){if(!e||"object"!=typeof e)return!1;if("undefined"!=typeof HTMLElement&&e instanceof HTMLElement)return!0;return"string"==typeof e.nodeName&&"function"==typeof e.getAttribute}(t)){for(var te="<"+E.call(String(t.nodeName)),ne=t.attributes||[],re=0;re"}if($(t)){if(0===t.length)return"[]";var oe=Q(t,D);return A&&!function(e){for(var t=0;t=0)return!1;return!0}(oe)?"["+X(oe,A)+"]":"[ "+_.call(oe,", ")+" ]"}if(function(e){return!("[object Error]"!==W(e)||N&&"object"==typeof e&&N in e)}(t)){var se=Q(t,D);return"cause"in Error.prototype||!("cause"in t)||I.call(t,"cause")?0===se.length?"["+String(t)+"]":"{ ["+String(t)+"] "+_.call(se,", ")+" }":"{ ["+String(t)+"] "+_.call(S.call("[cause]: "+D(t.cause),se),", ")+" }"}if("object"==typeof t&&l){if(F&&"function"==typeof t[F]&&M)return M(t,{depth:O-r});if("symbol"!==l&&"function"==typeof t.inspect)return t.inspect()}if(function(e){if(!s||!e||"object"!=typeof e)return!1;try{s.call(e);try{c.call(e)}catch(e){return!0}return e instanceof Map}catch(e){}return!1}(t)){var ie=[];return i&&i.call(t,(function(e,n){ie.push(D(n,t,!0)+" => "+D(e,t))})),Y("Map",s.call(t),ie,A)}if(function(e){if(!c||!e||"object"!=typeof e)return!1;try{c.call(e);try{s.call(e)}catch(e){return!0}return e instanceof Set}catch(e){}return!1}(t)){var ae=[];return u&&u.call(t,(function(e){ae.push(D(e,t))})),Y("Set",c.call(t),ae,A)}if(function(e){if(!p||!e||"object"!=typeof e)return!1;try{p.call(e,p);try{h.call(e,h)}catch(e){return!0}return e instanceof WeakMap}catch(e){}return!1}(t))return Z("WeakMap");if(function(e){if(!h||!e||"object"!=typeof e)return!1;try{h.call(e,h);try{p.call(e,p)}catch(e){return!0}return e instanceof WeakSet}catch(e){}return!1}(t))return Z("WeakSet");if(function(e){if(!f||!e||"object"!=typeof e)return!1;try{return f.call(e),!0}catch(e){}return!1}(t))return Z("WeakRef");if(function(e){return!("[object Number]"!==W(e)||N&&"object"==typeof e&&N in e)}(t))return G(D(Number(t)));if(function(e){if(!e||"object"!=typeof e||!k)return!1;try{return k.call(e),!0}catch(e){}return!1}(t))return G(D(k.call(t)));if(function(e){return!("[object Boolean]"!==W(e)||N&&"object"==typeof e&&N in e)}(t))return G(d.call(t));if(function(e){return!("[object String]"!==W(e)||N&&"object"==typeof e&&N in e)}(t))return G(D(String(t)));if(!function(e){return!("[object Date]"!==W(e)||N&&"object"==typeof e&&N in e)}(t)&&!q(t)){var le=Q(t,D),ce=T?T(t)===Object.prototype:t instanceof Object||t.constructor===Object,ue=t instanceof Object?"":"null prototype",pe=!ce&&N&&Object(t)===t&&N in t?v.call(W(t),8,-1):ue?"Object":"",he=(ce||"function"!=typeof t.constructor?"":t.constructor.name?t.constructor.name+" ":"")+(pe||ue?"["+_.call(S.call([],pe||[],ue||[]),": ")+"] ":"");return 0===le.length?he+"{}":A?he+"{"+X(le,A)+"}":he+"{ "+_.call(le,", ")+" }"}return String(t)};var z=Object.prototype.hasOwnProperty||function(e){return e in this};function V(e,t){return z.call(e,t)}function W(e){return m.call(e)}function J(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,r=e.length;nt.maxStringLength){var n=e.length-t.maxStringLength,r="... "+n+" more character"+(n>1?"s":"");return K(v.call(e,0,t.maxStringLength),t)+r}return L(b.call(b.call(e,/(['\\])/g,"\\$1"),/[\x00-\x1f]/g,H),"single",t)}function H(e){var t=e.charCodeAt(0),n={8:"b",9:"t",10:"n",12:"f",13:"r"}[t];return n?"\\"+n:"\\x"+(t<16?"0":"")+w.call(t.toString(16))}function G(e){return"Object("+e+")"}function Z(e){return e+" { ? }"}function Y(e,t,n,r){return e+" ("+t+") {"+(r?X(n,r):_.call(n,", "))+"}"}function X(e,t){if(0===e.length)return"";var n="\n"+t.prev+t.base;return n+_.call(e,","+n)+"\n"+t.prev}function Q(e,t){var n=$(e),r=[];if(n){r.length=e.length;for(var o=0;o{var t,n,r=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function i(e){if(t===setTimeout)return setTimeout(e,0);if((t===o||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(n){try{return t.call(null,e,0)}catch(n){return t.call(this,e,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:o}catch(e){t=o}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(e){n=s}}();var a,l=[],c=!1,u=-1;function p(){c&&a&&(c=!1,a.length?l=a.concat(l):u=-1,l.length&&h())}function h(){if(!c){var e=i(p);c=!0;for(var t=l.length;t;){for(a=l,l=[];++u1)for(var n=1;n{"use strict";var r=n(50414);function o(){}function s(){}s.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,s,i){if(i!==r){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:s,resetWarningCache:o};return n.PropTypes=n,n}},45697:(e,t,n)=>{e.exports=n(92703)()},50414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},55798:e=>{"use strict";var t=String.prototype.replace,n=/%20/g,r="RFC1738",o="RFC3986";e.exports={default:o,formatters:{RFC1738:function(e){return t.call(e,n,"+")},RFC3986:function(e){return String(e)}},RFC1738:r,RFC3986:o}},80129:(e,t,n)=>{"use strict";var r=n(58261),o=n(55235),s=n(55798);e.exports={formats:s,parse:o,stringify:r}},55235:(e,t,n)=>{"use strict";var r=n(12769),o=Object.prototype.hasOwnProperty,s=Array.isArray,i={allowDots:!1,allowPrototypes:!1,allowSparse:!1,arrayLimit:20,charset:"utf-8",charsetSentinel:!1,comma:!1,decoder:r.decode,delimiter:"&",depth:5,ignoreQueryPrefix:!1,interpretNumericEntities:!1,parameterLimit:1e3,parseArrays:!0,plainObjects:!1,strictNullHandling:!1},a=function(e){return e.replace(/&#(\d+);/g,(function(e,t){return String.fromCharCode(parseInt(t,10))}))},l=function(e,t){return e&&"string"==typeof e&&t.comma&&e.indexOf(",")>-1?e.split(","):e},c=function(e,t,n,r){if(e){var s=n.allowDots?e.replace(/\.([^.[]+)/g,"[$1]"):e,i=/(\[[^[\]]*])/g,a=n.depth>0&&/(\[[^[\]]*])/.exec(s),c=a?s.slice(0,a.index):s,u=[];if(c){if(!n.plainObjects&&o.call(Object.prototype,c)&&!n.allowPrototypes)return;u.push(c)}for(var p=0;n.depth>0&&null!==(a=i.exec(s))&&p=0;--s){var i,a=e[s];if("[]"===a&&n.parseArrays)i=[].concat(o);else{i=n.plainObjects?Object.create(null):{};var c="["===a.charAt(0)&&"]"===a.charAt(a.length-1)?a.slice(1,-1):a,u=parseInt(c,10);n.parseArrays||""!==c?!isNaN(u)&&a!==c&&String(u)===c&&u>=0&&n.parseArrays&&u<=n.arrayLimit?(i=[])[u]=o:"__proto__"!==c&&(i[c]=o):i={0:o}}o=i}return o}(u,t,n,r)}};e.exports=function(e,t){var n=function(e){if(!e)return i;if(null!==e.decoder&&void 0!==e.decoder&&"function"!=typeof e.decoder)throw new TypeError("Decoder has to be a function.");if(void 0!==e.charset&&"utf-8"!==e.charset&&"iso-8859-1"!==e.charset)throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var t=void 0===e.charset?i.charset:e.charset;return{allowDots:void 0===e.allowDots?i.allowDots:!!e.allowDots,allowPrototypes:"boolean"==typeof e.allowPrototypes?e.allowPrototypes:i.allowPrototypes,allowSparse:"boolean"==typeof e.allowSparse?e.allowSparse:i.allowSparse,arrayLimit:"number"==typeof e.arrayLimit?e.arrayLimit:i.arrayLimit,charset:t,charsetSentinel:"boolean"==typeof e.charsetSentinel?e.charsetSentinel:i.charsetSentinel,comma:"boolean"==typeof e.comma?e.comma:i.comma,decoder:"function"==typeof e.decoder?e.decoder:i.decoder,delimiter:"string"==typeof e.delimiter||r.isRegExp(e.delimiter)?e.delimiter:i.delimiter,depth:"number"==typeof e.depth||!1===e.depth?+e.depth:i.depth,ignoreQueryPrefix:!0===e.ignoreQueryPrefix,interpretNumericEntities:"boolean"==typeof e.interpretNumericEntities?e.interpretNumericEntities:i.interpretNumericEntities,parameterLimit:"number"==typeof e.parameterLimit?e.parameterLimit:i.parameterLimit,parseArrays:!1!==e.parseArrays,plainObjects:"boolean"==typeof e.plainObjects?e.plainObjects:i.plainObjects,strictNullHandling:"boolean"==typeof e.strictNullHandling?e.strictNullHandling:i.strictNullHandling}}(t);if(""===e||null==e)return n.plainObjects?Object.create(null):{};for(var u="string"==typeof e?function(e,t){var n,c={},u=t.ignoreQueryPrefix?e.replace(/^\?/,""):e,p=t.parameterLimit===1/0?void 0:t.parameterLimit,h=u.split(t.delimiter,p),f=-1,d=t.charset;if(t.charsetSentinel)for(n=0;n-1&&(g=s(g)?[g]:g),o.call(c,m)?c[m]=r.combine(c[m],g):c[m]=g}return c}(e,n):e,p=n.plainObjects?Object.create(null):{},h=Object.keys(u),f=0;f{"use strict";var r=n(37478),o=n(12769),s=n(55798),i=Object.prototype.hasOwnProperty,a={brackets:function(e){return e+"[]"},comma:"comma",indices:function(e,t){return e+"["+t+"]"},repeat:function(e){return e}},l=Array.isArray,c=String.prototype.split,u=Array.prototype.push,p=function(e,t){u.apply(e,l(t)?t:[t])},h=Date.prototype.toISOString,f=s.default,d={addQueryPrefix:!1,allowDots:!1,charset:"utf-8",charsetSentinel:!1,delimiter:"&",encode:!0,encoder:o.encode,encodeValuesOnly:!1,format:f,formatter:s.formatters[f],indices:!1,serializeDate:function(e){return h.call(e)},skipNulls:!1,strictNullHandling:!1},m={},g=function e(t,n,s,i,a,u,h,f,g,y,v,b,w,E,x,S){for(var _,j=t,O=S,k=0,A=!1;void 0!==(O=O.get(m))&&!A;){var C=O.get(t);if(k+=1,void 0!==C){if(C===k)throw new RangeError("Cyclic object value");A=!0}void 0===O.get(m)&&(k=0)}if("function"==typeof f?j=f(n,j):j instanceof Date?j=v(j):"comma"===s&&l(j)&&(j=o.maybeMap(j,(function(e){return e instanceof Date?v(e):e}))),null===j){if(a)return h&&!E?h(n,d.encoder,x,"key",b):n;j=""}if("string"==typeof(_=j)||"number"==typeof _||"boolean"==typeof _||"symbol"==typeof _||"bigint"==typeof _||o.isBuffer(j)){if(h){var P=E?n:h(n,d.encoder,x,"key",b);if("comma"===s&&E){for(var N=c.call(String(j),","),I="",T=0;T0?j.join(",")||null:void 0}];else if(l(f))R=f;else{var D=Object.keys(j);R=g?D.sort(g):D}for(var F=i&&l(j)&&1===j.length?n+"[]":n,L=0;L0?E+w:""}},12769:(e,t,n)=>{"use strict";var r=n(55798),o=Object.prototype.hasOwnProperty,s=Array.isArray,i=function(){for(var e=[],t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e}(),a=function(e,t){for(var n=t&&t.plainObjects?Object.create(null):{},r=0;r1;){var t=e.pop(),n=t.obj[t.prop];if(s(n)){for(var r=[],o=0;o=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122||s===r.RFC1738&&(40===u||41===u)?l+=a.charAt(c):u<128?l+=i[u]:u<2048?l+=i[192|u>>6]+i[128|63&u]:u<55296||u>=57344?l+=i[224|u>>12]+i[128|u>>6&63]+i[128|63&u]:(c+=1,u=65536+((1023&u)<<10|1023&a.charCodeAt(c)),l+=i[240|u>>18]+i[128|u>>12&63]+i[128|u>>6&63]+i[128|63&u])}return l},isBuffer:function(e){return!(!e||"object"!=typeof e)&&!!(e.constructor&&e.constructor.isBuffer&&e.constructor.isBuffer(e))},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},maybeMap:function(e,t){if(s(e)){for(var n=[],r=0;r{"use strict";var n=Object.prototype.hasOwnProperty;function r(e){try{return decodeURIComponent(e.replace(/\+/g," "))}catch(e){return null}}function o(e){try{return encodeURIComponent(e)}catch(e){return null}}t.stringify=function(e,t){t=t||"";var r,s,i=[];for(s in"string"!=typeof t&&(t="?"),e)if(n.call(e,s)){if((r=e[s])||null!=r&&!isNaN(r)||(r=""),s=o(s),r=o(r),null===s||null===r)continue;i.push(s+"="+r)}return i.length?t+i.join("&"):""},t.parse=function(e){for(var t,n=/([^=?#&]+)=?([^&]*)/g,o={};t=n.exec(e);){var s=r(t[1]),i=r(t[2]);null===s||null===i||s in o||(o[s]=i)}return o}},14419:(e,t,n)=>{const r=n(60697),o=n(69450),s=r.types;e.exports=class e{constructor(e,t){if(this._setDefaults(e),e instanceof RegExp)this.ignoreCase=e.ignoreCase,this.multiline=e.multiline,e=e.source;else{if("string"!=typeof e)throw new Error("Expected a regexp or string");this.ignoreCase=t&&-1!==t.indexOf("i"),this.multiline=t&&-1!==t.indexOf("m")}this.tokens=r(e)}_setDefaults(t){this.max=null!=t.max?t.max:null!=e.prototype.max?e.prototype.max:100,this.defaultRange=t.defaultRange?t.defaultRange:this.defaultRange.clone(),t.randInt&&(this.randInt=t.randInt)}gen(){return this._gen(this.tokens,[])}_gen(e,t){var n,r,o,i,a;switch(e.type){case s.ROOT:case s.GROUP:if(e.followedBy||e.notFollowedBy)return"";for(e.remember&&void 0===e.groupNumber&&(e.groupNumber=t.push(null)-1),r="",i=0,a=(n=e.options?this._randSelect(e.options):e.stack).length;i{"use strict";var r=n(34155),o=65536,s=4294967295;var i=n(89509).Buffer,a=n.g.crypto||n.g.msCrypto;a&&a.getRandomValues?e.exports=function(e,t){if(e>s)throw new RangeError("requested too many random bytes");var n=i.allocUnsafe(e);if(e>0)if(e>o)for(var l=0;l{"use strict";function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.CopyToClipboard=void 0;var o=a(n(67294)),s=a(n(20640)),i=["text","onCopy","options","children"];function a(e){return e&&e.__esModule?e:{default:e}}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function c(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function p(e,t){for(var n=0;n{"use strict";var r=n(74300).CopyToClipboard;r.CopyToClipboard=r,e.exports=r},53441:(e,t,n)=>{"use strict";function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.DebounceInput=void 0;var o=a(n(67294)),s=a(n(91296)),i=["element","onChange","value","minLength","debounceTimeout","forceNotifyByEnter","forceNotifyOnBlur","onKeyDown","onBlur","inputRef"];function a(e){return e&&e.__esModule?e:{default:e}}function l(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},s=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function c(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function u(e){for(var t=1;t=r?t.notify(e):n.length>o.length&&t.notify(u(u({},e),{},{target:u(u({},e.target),{},{value:""})}))}))})),g(d(t),"onKeyDown",(function(e){"Enter"===e.key&&t.forceNotify(e);var n=t.props.onKeyDown;n&&(e.persist(),n(e))})),g(d(t),"onBlur",(function(e){t.forceNotify(e);var n=t.props.onBlur;n&&(e.persist(),n(e))})),g(d(t),"createNotifier",(function(e){if(e<0)t.notify=function(){return null};else if(0===e)t.notify=t.doNotify;else{var n=(0,s.default)((function(e){t.isDebouncing=!1,t.doNotify(e)}),e);t.notify=function(e){t.isDebouncing=!0,n(e)},t.flush=function(){return n.flush()},t.cancel=function(){t.isDebouncing=!1,n.cancel()}}})),g(d(t),"doNotify",(function(){t.props.onChange.apply(void 0,arguments)})),g(d(t),"forceNotify",(function(e){var n=t.props.debounceTimeout;if(t.isDebouncing||!(n>0)){t.cancel&&t.cancel();var r=t.state.value,o=t.props.minLength;r.length>=o?t.doNotify(e):t.doNotify(u(u({},e),{},{target:u(u({},e.target),{},{value:r})}))}})),t.isDebouncing=!1,t.state={value:void 0===e.value||null===e.value?"":e.value};var n=t.props.debounceTimeout;return t.createNotifier(n),t}return t=c,(n=[{key:"componentDidUpdate",value:function(e){if(!this.isDebouncing){var t=this.props,n=t.value,r=t.debounceTimeout,o=e.debounceTimeout,s=e.value,i=this.state.value;void 0!==n&&s!==n&&i!==n&&this.setState({value:n}),r!==o&&this.createNotifier(r)}}},{key:"componentWillUnmount",value:function(){this.flush&&this.flush()}},{key:"render",value:function(){var e,t,n=this.props,r=n.element,s=(n.onChange,n.value,n.minLength,n.debounceTimeout,n.forceNotifyByEnter),a=n.forceNotifyOnBlur,c=n.onKeyDown,p=n.onBlur,h=n.inputRef,f=l(n,i),d=this.state.value;e=s?{onKeyDown:this.onKeyDown}:c?{onKeyDown:c}:{},t=a?{onBlur:this.onBlur}:p?{onBlur:p}:{};var m=h?{ref:h}:{};return o.default.createElement(r,u(u(u(u({},f),{},{onChange:this.onChange,value:d},e),t),m))}}])&&p(t.prototype,n),r&&p(t,r),Object.defineProperty(t,"prototype",{writable:!1}),c}(o.default.PureComponent);t.DebounceInput=y,g(y,"defaultProps",{element:"input",type:"text",onKeyDown:void 0,onBlur:void 0,value:void 0,minLength:0,debounceTimeout:100,forceNotifyByEnter:!0,forceNotifyOnBlur:!0,inputRef:void 0})},775:(e,t,n)=>{"use strict";var r=n(53441).DebounceInput;r.DebounceInput=r,e.exports=r},64448:(e,t,n)=>{"use strict";var r=n(67294),o=n(27418),s=n(63840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n
")}value(){return this.buffer}span(e){this.buffer+=``}}class c{constructor(){this.rootNode={children:[]},this.stack=[this.rootNode]}get top(){return this.stack[this.stack.length-1]}get root(){return this.rootNode}add(e){this.top.children.push(e)}openNode(e){const t={kind:e,children:[]};this.add(t),this.stack.push(t)}closeNode(){if(this.stack.length>1)return this.stack.pop()}closeAllNodes(){for(;this.closeNode(););}toJSON(){return JSON.stringify(this.rootNode,null,4)}walk(e){return this.constructor._walk(e,this.rootNode)}static _walk(e,t){return"string"==typeof t?e.addText(t):t.children&&(e.openNode(t),t.children.forEach((t=>this._walk(e,t))),e.closeNode(t)),e}static _collapse(e){"string"!=typeof e&&e.children&&(e.children.every((e=>"string"==typeof e))?e.children=[e.children.join("")]:e.children.forEach((e=>{c._collapse(e)})))}}class u extends c{constructor(e){super(),this.options=e}addKeyword(e,t){""!==e&&(this.openNode(t),this.addText(e),this.closeNode())}addText(e){""!==e&&this.add(e)}addSublanguage(e,t){const n=e.root;n.kind=t,n.sublanguage=!0,this.add(n)}toHTML(){return new l(this,this.options).value()}finalize(){return!0}}function p(e){return e?"string"==typeof e?e:e.source:null}const h=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./;const f="[a-zA-Z]\\w*",d="[a-zA-Z_]\\w*",m="\\b\\d+(\\.\\d+)?",g="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",y="\\b(0b[01]+)",v={begin:"\\\\[\\s\\S]",relevance:0},b={className:"string",begin:"'",end:"'",illegal:"\\n",contains:[v]},w={className:"string",begin:'"',end:'"',illegal:"\\n",contains:[v]},E={begin:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},x=function(e,t,n={}){const r=i({className:"comment",begin:e,end:t,contains:[]},n);return r.contains.push(E),r.contains.push({className:"doctag",begin:"(?:TODO|FIXME|NOTE|BUG|OPTIMIZE|HACK|XXX):",relevance:0}),r},S=x("//","$"),_=x("/\\*","\\*/"),j=x("#","$"),O={className:"number",begin:m,relevance:0},k={className:"number",begin:g,relevance:0},A={className:"number",begin:y,relevance:0},C={className:"number",begin:m+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",relevance:0},P={begin:/(?=\/[^/\n]*\/)/,contains:[{className:"regexp",begin:/\//,end:/\/[gimuy]*/,illegal:/\n/,contains:[v,{begin:/\[/,end:/\]/,relevance:0,contains:[v]}]}]},N={className:"title",begin:f,relevance:0},I={className:"title",begin:d,relevance:0},T={begin:"\\.\\s*"+d,relevance:0};var R=Object.freeze({__proto__:null,MATCH_NOTHING_RE:/\b\B/,IDENT_RE:f,UNDERSCORE_IDENT_RE:d,NUMBER_RE:m,C_NUMBER_RE:g,BINARY_NUMBER_RE:y,RE_STARTERS_RE:"!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",SHEBANG:(e={})=>{const t=/^#![ ]*\//;return e.binary&&(e.begin=function(...e){return e.map((e=>p(e))).join("")}(t,/.*\b/,e.binary,/\b.*/)),i({className:"meta",begin:t,end:/$/,relevance:0,"on:begin":(e,t)=>{0!==e.index&&t.ignoreMatch()}},e)},BACKSLASH_ESCAPE:v,APOS_STRING_MODE:b,QUOTE_STRING_MODE:w,PHRASAL_WORDS_MODE:E,COMMENT:x,C_LINE_COMMENT_MODE:S,C_BLOCK_COMMENT_MODE:_,HASH_COMMENT_MODE:j,NUMBER_MODE:O,C_NUMBER_MODE:k,BINARY_NUMBER_MODE:A,CSS_NUMBER_MODE:C,REGEXP_MODE:P,TITLE_MODE:N,UNDERSCORE_TITLE_MODE:I,METHOD_GUARD:T,END_SAME_AS_BEGIN:function(e){return Object.assign(e,{"on:begin":(e,t)=>{t.data._beginMatch=e[1]},"on:end":(e,t)=>{t.data._beginMatch!==e[1]&&t.ignoreMatch()}})}});function M(e,t){"."===e.input[e.index-1]&&t.ignoreMatch()}function D(e,t){t&&e.beginKeywords&&(e.begin="\\b("+e.beginKeywords.split(" ").join("|")+")(?!\\.)(?=\\b|\\s)",e.__beforeBegin=M,e.keywords=e.keywords||e.beginKeywords,delete e.beginKeywords,void 0===e.relevance&&(e.relevance=0))}function F(e,t){Array.isArray(e.illegal)&&(e.illegal=function(...e){return"("+e.map((e=>p(e))).join("|")+")"}(...e.illegal))}function L(e,t){if(e.match){if(e.begin||e.end)throw new Error("begin & end are not supported with match");e.begin=e.match,delete e.match}}function B(e,t){void 0===e.relevance&&(e.relevance=1)}const $=["of","and","for","in","not","or","if","then","parent","list","value"],q="keyword";function U(e,t,n=q){const r={};return"string"==typeof e?o(n,e.split(" ")):Array.isArray(e)?o(n,e):Object.keys(e).forEach((function(n){Object.assign(r,U(e[n],t,n))})),r;function o(e,n){t&&(n=n.map((e=>e.toLowerCase()))),n.forEach((function(t){const n=t.split("|");r[n[0]]=[e,z(n[0],n[1])]}))}}function z(e,t){return t?Number(t):function(e){return $.includes(e.toLowerCase())}(e)?0:1}function V(e,{plugins:t}){function n(t,n){return new RegExp(p(t),"m"+(e.case_insensitive?"i":"")+(n?"g":""))}class r{constructor(){this.matchIndexes={},this.regexes=[],this.matchAt=1,this.position=0}addRule(e,t){t.position=this.position++,this.matchIndexes[this.matchAt]=t,this.regexes.push([t,e]),this.matchAt+=function(e){return new RegExp(e.toString()+"|").exec("").length-1}(e)+1}compile(){0===this.regexes.length&&(this.exec=()=>null);const e=this.regexes.map((e=>e[1]));this.matcherRe=n(function(e,t="|"){let n=0;return e.map((e=>{n+=1;const t=n;let r=p(e),o="";for(;r.length>0;){const e=h.exec(r);if(!e){o+=r;break}o+=r.substring(0,e.index),r=r.substring(e.index+e[0].length),"\\"===e[0][0]&&e[1]?o+="\\"+String(Number(e[1])+t):(o+=e[0],"("===e[0]&&n++)}return o})).map((e=>`(${e})`)).join(t)}(e),!0),this.lastIndex=0}exec(e){this.matcherRe.lastIndex=this.lastIndex;const t=this.matcherRe.exec(e);if(!t)return null;const n=t.findIndex(((e,t)=>t>0&&void 0!==e)),r=this.matchIndexes[n];return t.splice(0,n),Object.assign(t,r)}}class o{constructor(){this.rules=[],this.multiRegexes=[],this.count=0,this.lastIndex=0,this.regexIndex=0}getMatcher(e){if(this.multiRegexes[e])return this.multiRegexes[e];const t=new r;return this.rules.slice(e).forEach((([e,n])=>t.addRule(e,n))),t.compile(),this.multiRegexes[e]=t,t}resumingScanAtSamePosition(){return 0!==this.regexIndex}considerAll(){this.regexIndex=0}addRule(e,t){this.rules.push([e,t]),"begin"===t.type&&this.count++}exec(e){const t=this.getMatcher(this.regexIndex);t.lastIndex=this.lastIndex;let n=t.exec(e);if(this.resumingScanAtSamePosition())if(n&&n.index===this.lastIndex);else{const t=this.getMatcher(0);t.lastIndex=this.lastIndex+1,n=t.exec(e)}return n&&(this.regexIndex+=n.position+1,this.regexIndex===this.count&&this.considerAll()),n}}if(e.compilerExtensions||(e.compilerExtensions=[]),e.contains&&e.contains.includes("self"))throw new Error("ERR: contains `self` is not supported at the top-level of a language. See documentation.");return e.classNameAliases=i(e.classNameAliases||{}),function t(r,s){const a=r;if(r.isCompiled)return a;[L].forEach((e=>e(r,s))),e.compilerExtensions.forEach((e=>e(r,s))),r.__beforeBegin=null,[D,F,B].forEach((e=>e(r,s))),r.isCompiled=!0;let l=null;if("object"==typeof r.keywords&&(l=r.keywords.$pattern,delete r.keywords.$pattern),r.keywords&&(r.keywords=U(r.keywords,e.case_insensitive)),r.lexemes&&l)throw new Error("ERR: Prefer `keywords.$pattern` to `mode.lexemes`, BOTH are not allowed. (see mode reference) ");return l=l||r.lexemes||/\w+/,a.keywordPatternRe=n(l,!0),s&&(r.begin||(r.begin=/\B|\b/),a.beginRe=n(r.begin),r.endSameAsBegin&&(r.end=r.begin),r.end||r.endsWithParent||(r.end=/\B|\b/),r.end&&(a.endRe=n(r.end)),a.terminatorEnd=p(r.end)||"",r.endsWithParent&&s.terminatorEnd&&(a.terminatorEnd+=(r.end?"|":"")+s.terminatorEnd)),r.illegal&&(a.illegalRe=n(r.illegal)),r.contains||(r.contains=[]),r.contains=[].concat(...r.contains.map((function(e){return function(e){e.variants&&!e.cachedVariants&&(e.cachedVariants=e.variants.map((function(t){return i(e,{variants:null},t)})));if(e.cachedVariants)return e.cachedVariants;if(W(e))return i(e,{starts:e.starts?i(e.starts):null});if(Object.isFrozen(e))return i(e);return e}("self"===e?r:e)}))),r.contains.forEach((function(e){t(e,a)})),r.starts&&t(r.starts,s),a.matcher=function(e){const t=new o;return e.contains.forEach((e=>t.addRule(e.begin,{rule:e,type:"begin"}))),e.terminatorEnd&&t.addRule(e.terminatorEnd,{type:"end"}),e.illegal&&t.addRule(e.illegal,{type:"illegal"}),t}(a),a}(e)}function W(e){return!!e&&(e.endsWithParent||W(e.starts))}function J(e){const t={props:["language","code","autodetect"],data:function(){return{detectedLanguage:"",unknownLanguage:!1}},computed:{className(){return this.unknownLanguage?"":"hljs "+this.detectedLanguage},highlighted(){if(!this.autoDetect&&!e.getLanguage(this.language))return console.warn(`The language "${this.language}" you specified could not be found.`),this.unknownLanguage=!0,s(this.code);let t={};return this.autoDetect?(t=e.highlightAuto(this.code),this.detectedLanguage=t.language):(t=e.highlight(this.language,this.code,this.ignoreIllegals),this.detectedLanguage=this.language),t.value},autoDetect(){return!this.language||(e=this.autodetect,Boolean(e||""===e));var e},ignoreIllegals:()=>!0},render(e){return e("pre",{},[e("code",{class:this.className,domProps:{innerHTML:this.highlighted}})])}};return{Component:t,VuePlugin:{install(e){e.component("highlightjs",t)}}}}const K={"after:highlightElement":({el:e,result:t,text:n})=>{const r=G(e);if(!r.length)return;const o=document.createElement("div");o.innerHTML=t.value,t.value=function(e,t,n){let r=0,o="";const i=[];function a(){return e.length&&t.length?e[0].offset!==t[0].offset?e[0].offset"}function c(e){o+=""}function u(e){("start"===e.event?l:c)(e.node)}for(;e.length||t.length;){let t=a();if(o+=s(n.substring(r,t[0].offset)),r=t[0].offset,t===e){i.reverse().forEach(c);do{u(t.splice(0,1)[0]),t=a()}while(t===e&&t.length&&t[0].offset===r);i.reverse().forEach(l)}else"start"===t[0].event?i.push(t[0].node):i.pop(),u(t.splice(0,1)[0])}return o+s(n.substr(r))}(r,G(o),n)}};function H(e){return e.nodeName.toLowerCase()}function G(e){const t=[];return function e(n,r){for(let o=n.firstChild;o;o=o.nextSibling)3===o.nodeType?r+=o.nodeValue.length:1===o.nodeType&&(t.push({event:"start",offset:r,node:o}),r=e(o,r),H(o).match(/br|hr|img|input/)||t.push({event:"stop",offset:r,node:o}));return r}(e,0),t}const Z={},Y=e=>{console.error(e)},X=(e,...t)=>{console.log(`WARN: ${e}`,...t)},Q=(e,t)=>{Z[`${e}/${t}`]||(console.log(`Deprecated as of ${e}. ${t}`),Z[`${e}/${t}`]=!0)},ee=s,te=i,ne=Symbol("nomatch");var re=function(e){const t=Object.create(null),r=Object.create(null),s=[];let i=!0;const a=/(^(<[^>]+>|\t|)+|\n)/gm,l="Could not find the language '{}', did you forget to load/include a language module?",c={disableAutodetect:!0,name:"Plain text",contains:[]};let p={noHighlightRe:/^(no-?highlight)$/i,languageDetectRe:/\blang(?:uage)?-([\w-]+)\b/i,classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:null,__emitter:u};function h(e){return p.noHighlightRe.test(e)}function f(e,t,n,r){let o="",s="";"object"==typeof t?(o=e,n=t.ignoreIllegals,s=t.language,r=void 0):(Q("10.7.0","highlight(lang, code, ...args) has been deprecated."),Q("10.7.0","Please use highlight(code, options) instead.\nhttps://github.com/highlightjs/highlight.js/issues/2277"),s=e,o=t);const i={code:o,language:s};O("before:highlight",i);const a=i.result?i.result:d(i.language,i.code,n,r);return a.code=i.code,O("after:highlight",a),a}function d(e,n,r,a){function c(e,t){const n=E.case_insensitive?t[0].toLowerCase():t[0];return Object.prototype.hasOwnProperty.call(e.keywords,n)&&e.keywords[n]}function u(){null!=j.subLanguage?function(){if(""===A)return;let e=null;if("string"==typeof j.subLanguage){if(!t[j.subLanguage])return void k.addText(A);e=d(j.subLanguage,A,!0,O[j.subLanguage]),O[j.subLanguage]=e.top}else e=m(A,j.subLanguage.length?j.subLanguage:null);j.relevance>0&&(C+=e.relevance),k.addSublanguage(e.emitter,e.language)}():function(){if(!j.keywords)return void k.addText(A);let e=0;j.keywordPatternRe.lastIndex=0;let t=j.keywordPatternRe.exec(A),n="";for(;t;){n+=A.substring(e,t.index);const r=c(j,t);if(r){const[e,o]=r;if(k.addText(n),n="",C+=o,e.startsWith("_"))n+=t[0];else{const n=E.classNameAliases[e]||e;k.addKeyword(t[0],n)}}else n+=t[0];e=j.keywordPatternRe.lastIndex,t=j.keywordPatternRe.exec(A)}n+=A.substr(e),k.addText(n)}(),A=""}function h(e){return e.className&&k.openNode(E.classNameAliases[e.className]||e.className),j=Object.create(e,{parent:{value:j}}),j}function f(e,t,n){let r=function(e,t){const n=e&&e.exec(t);return n&&0===n.index}(e.endRe,n);if(r){if(e["on:end"]){const n=new o(e);e["on:end"](t,n),n.isMatchIgnored&&(r=!1)}if(r){for(;e.endsParent&&e.parent;)e=e.parent;return e}}if(e.endsWithParent)return f(e.parent,t,n)}function g(e){return 0===j.matcher.regexIndex?(A+=e[0],1):(I=!0,0)}function y(e){const t=e[0],n=e.rule,r=new o(n),s=[n.__beforeBegin,n["on:begin"]];for(const n of s)if(n&&(n(e,r),r.isMatchIgnored))return g(t);return n&&n.endSameAsBegin&&(n.endRe=new RegExp(t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),"m")),n.skip?A+=t:(n.excludeBegin&&(A+=t),u(),n.returnBegin||n.excludeBegin||(A=t)),h(n),n.returnBegin?0:t.length}function v(e){const t=e[0],r=n.substr(e.index),o=f(j,e,r);if(!o)return ne;const s=j;s.skip?A+=t:(s.returnEnd||s.excludeEnd||(A+=t),u(),s.excludeEnd&&(A=t));do{j.className&&k.closeNode(),j.skip||j.subLanguage||(C+=j.relevance),j=j.parent}while(j!==o.parent);return o.starts&&(o.endSameAsBegin&&(o.starts.endRe=o.endRe),h(o.starts)),s.returnEnd?0:t.length}let b={};function w(t,o){const s=o&&o[0];if(A+=t,null==s)return u(),0;if("begin"===b.type&&"end"===o.type&&b.index===o.index&&""===s){if(A+=n.slice(o.index,o.index+1),!i){const t=new Error("0 width match regex");throw t.languageName=e,t.badRule=b.rule,t}return 1}if(b=o,"begin"===o.type)return y(o);if("illegal"===o.type&&!r){const e=new Error('Illegal lexeme "'+s+'" for mode "'+(j.className||"")+'"');throw e.mode=j,e}if("end"===o.type){const e=v(o);if(e!==ne)return e}if("illegal"===o.type&&""===s)return 1;if(N>1e5&&N>3*o.index){throw new Error("potential infinite loop, way more iterations than matches")}return A+=s,s.length}const E=S(e);if(!E)throw Y(l.replace("{}",e)),new Error('Unknown language: "'+e+'"');const x=V(E,{plugins:s});let _="",j=a||x;const O={},k=new p.__emitter(p);!function(){const e=[];for(let t=j;t!==E;t=t.parent)t.className&&e.unshift(t.className);e.forEach((e=>k.openNode(e)))}();let A="",C=0,P=0,N=0,I=!1;try{for(j.matcher.considerAll();;){N++,I?I=!1:j.matcher.considerAll(),j.matcher.lastIndex=P;const e=j.matcher.exec(n);if(!e)break;const t=w(n.substring(P,e.index),e);P=e.index+t}return w(n.substr(P)),k.closeAllNodes(),k.finalize(),_=k.toHTML(),{relevance:Math.floor(C),value:_,language:e,illegal:!1,emitter:k,top:j}}catch(t){if(t.message&&t.message.includes("Illegal"))return{illegal:!0,illegalBy:{msg:t.message,context:n.slice(P-100,P+100),mode:t.mode},sofar:_,relevance:0,value:ee(n),emitter:k};if(i)return{illegal:!1,relevance:0,value:ee(n),emitter:k,language:e,top:j,errorRaised:t};throw t}}function m(e,n){n=n||p.languages||Object.keys(t);const r=function(e){const t={relevance:0,emitter:new p.__emitter(p),value:ee(e),illegal:!1,top:c};return t.emitter.addText(e),t}(e),o=n.filter(S).filter(j).map((t=>d(t,e,!1)));o.unshift(r);const s=o.sort(((e,t)=>{if(e.relevance!==t.relevance)return t.relevance-e.relevance;if(e.language&&t.language){if(S(e.language).supersetOf===t.language)return 1;if(S(t.language).supersetOf===e.language)return-1}return 0})),[i,a]=s,l=i;return l.second_best=a,l}const g={"before:highlightElement":({el:e})=>{p.useBR&&(e.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n"))},"after:highlightElement":({result:e})=>{p.useBR&&(e.value=e.value.replace(/\n/g,"
"))}},y=/^(<[^>]+>|\t)+/gm,v={"after:highlightElement":({result:e})=>{p.tabReplace&&(e.value=e.value.replace(y,(e=>e.replace(/\t/g,p.tabReplace))))}};function b(e){let t=null;const n=function(e){let t=e.className+" ";t+=e.parentNode?e.parentNode.className:"";const n=p.languageDetectRe.exec(t);if(n){const t=S(n[1]);return t||(X(l.replace("{}",n[1])),X("Falling back to no-highlight mode for this block.",e)),t?n[1]:"no-highlight"}return t.split(/\s+/).find((e=>h(e)||S(e)))}(e);if(h(n))return;O("before:highlightElement",{el:e,language:n}),t=e;const o=t.textContent,s=n?f(o,{language:n,ignoreIllegals:!0}):m(o);O("after:highlightElement",{el:e,result:s,text:o}),e.innerHTML=s.value,function(e,t,n){const o=t?r[t]:n;e.classList.add("hljs"),o&&e.classList.add(o)}(e,n,s.language),e.result={language:s.language,re:s.relevance,relavance:s.relevance},s.second_best&&(e.second_best={language:s.second_best.language,re:s.second_best.relevance,relavance:s.second_best.relevance})}const w=()=>{if(w.called)return;w.called=!0,Q("10.6.0","initHighlighting() is deprecated. Use highlightAll() instead.");document.querySelectorAll("pre code").forEach(b)};let E=!1;function x(){if("loading"===document.readyState)return void(E=!0);document.querySelectorAll("pre code").forEach(b)}function S(e){return e=(e||"").toLowerCase(),t[e]||t[r[e]]}function _(e,{languageName:t}){"string"==typeof e&&(e=[e]),e.forEach((e=>{r[e.toLowerCase()]=t}))}function j(e){const t=S(e);return t&&!t.disableAutodetect}function O(e,t){const n=e;s.forEach((function(e){e[n]&&e[n](t)}))}"undefined"!=typeof window&&window.addEventListener&&window.addEventListener("DOMContentLoaded",(function(){E&&x()}),!1),Object.assign(e,{highlight:f,highlightAuto:m,highlightAll:x,fixMarkup:function(e){return Q("10.2.0","fixMarkup will be removed entirely in v11.0"),Q("10.2.0","Please see https://github.com/highlightjs/highlight.js/issues/2534"),t=e,p.tabReplace||p.useBR?t.replace(a,(e=>"\n"===e?p.useBR?"
":e:p.tabReplace?e.replace(/\t/g,p.tabReplace):e)):t;var t},highlightElement:b,highlightBlock:function(e){return Q("10.7.0","highlightBlock will be removed entirely in v12.0"),Q("10.7.0","Please use highlightElement now."),b(e)},configure:function(e){e.useBR&&(Q("10.3.0","'useBR' will be removed entirely in v11.0"),Q("10.3.0","Please see https://github.com/highlightjs/highlight.js/issues/2559")),p=te(p,e)},initHighlighting:w,initHighlightingOnLoad:function(){Q("10.6.0","initHighlightingOnLoad() is deprecated. Use highlightAll() instead."),E=!0},registerLanguage:function(n,r){let o=null;try{o=r(e)}catch(e){if(Y("Language definition for '{}' could not be registered.".replace("{}",n)),!i)throw e;Y(e),o=c}o.name||(o.name=n),t[n]=o,o.rawDefinition=r.bind(null,e),o.aliases&&_(o.aliases,{languageName:n})},unregisterLanguage:function(e){delete t[e];for(const t of Object.keys(r))r[t]===e&&delete r[t]},listLanguages:function(){return Object.keys(t)},getLanguage:S,registerAliases:_,requireLanguage:function(e){Q("10.4.0","requireLanguage will be removed entirely in v11."),Q("10.4.0","Please see https://github.com/highlightjs/highlight.js/pull/2844");const t=S(e);if(t)return t;throw new Error("The '{}' language is required, but not loaded.".replace("{}",e))},autoDetection:j,inherit:te,addPlugin:function(e){!function(e){e["before:highlightBlock"]&&!e["before:highlightElement"]&&(e["before:highlightElement"]=t=>{e["before:highlightBlock"](Object.assign({block:t.el},t))}),e["after:highlightBlock"]&&!e["after:highlightElement"]&&(e["after:highlightElement"]=t=>{e["after:highlightBlock"](Object.assign({block:t.el},t))})}(e),s.push(e)},vuePlugin:J(e).VuePlugin}),e.debugMode=function(){i=!1},e.safeMode=function(){i=!0},e.versionString="10.7.3";for(const e in R)"object"==typeof R[e]&&n(R[e]);return Object.assign(e,R),e.addPlugin(g),e.addPlugin(K),e.addPlugin(v),e}({});e.exports=re},61519:e=>{function t(...e){return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}e.exports=function(e){const n={},r={begin:/\$\{/,end:/\}/,contains:["self",{begin:/:-/,contains:[n]}]};Object.assign(n,{className:"variable",variants:[{begin:t(/\$[\w\d#@][\w\d_]*/,"(?![\\w\\d])(?![$])")},r]});const o={className:"subst",begin:/\$\(/,end:/\)/,contains:[e.BACKSLASH_ESCAPE]},s={begin:/<<-?\s*(?=\w+)/,starts:{contains:[e.END_SAME_AS_BEGIN({begin:/(\w+)/,end:/(\w+)/,className:"string"})]}},i={className:"string",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE,n,o]};o.contains.push(i);const a={begin:/\$\(\(/,end:/\)\)/,contains:[{begin:/\d+#[0-9a-f]+/,className:"number"},e.NUMBER_MODE,n]},l=e.SHEBANG({binary:`(${["fish","bash","zsh","sh","csh","ksh","tcsh","dash","scsh"].join("|")})`,relevance:10}),c={className:"function",begin:/\w[\w\d_]*\s*\(\s*\)\s*\{/,returnBegin:!0,contains:[e.inherit(e.TITLE_MODE,{begin:/\w[\w\d_]*/})],relevance:0};return{name:"Bash",aliases:["sh","zsh"],keywords:{$pattern:/\b[a-z._-]+\b/,keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp"},contains:[l,e.SHEBANG(),c,a,e.HASH_COMMENT_MODE,s,i,{className:"",begin:/\\"/},{className:"string",begin:/'/,end:/'/},n]}}},30786:e=>{function t(...e){return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}e.exports=function(e){const n="HTTP/(2|1\\.[01])",r={className:"attribute",begin:t("^",/[A-Za-z][A-Za-z0-9-]*/,"(?=\\:\\s)"),starts:{contains:[{className:"punctuation",begin:/: /,relevance:0,starts:{end:"$",relevance:0}}]}},o=[r,{begin:"\\n\\n",starts:{subLanguage:[],endsWithParent:!0}}];return{name:"HTTP",aliases:["https"],illegal:/\S/,contains:[{begin:"^(?="+n+" \\d{3})",end:/$/,contains:[{className:"meta",begin:n},{className:"number",begin:"\\b\\d{3}\\b"}],starts:{end:/\b\B/,illegal:/\S/,contains:o}},{begin:"(?=^[A-Z]+ (.*?) "+n+"$)",end:/$/,contains:[{className:"string",begin:" ",end:" ",excludeBegin:!0,excludeEnd:!0},{className:"meta",begin:n},{className:"keyword",begin:"[A-Z]+"}],starts:{end:/\b\B/,illegal:/\S/,contains:o}},e.inherit(r,{relevance:0})]}}},96344:e=>{const t="[A-Za-z$_][0-9A-Za-z$_]*",n=["as","in","of","if","for","while","finally","var","new","function","do","return","void","else","break","catch","instanceof","with","throw","case","default","try","switch","continue","typeof","delete","let","yield","const","class","debugger","async","await","static","import","from","export","extends"],r=["true","false","null","undefined","NaN","Infinity"],o=[].concat(["setInterval","setTimeout","clearInterval","clearTimeout","require","exports","eval","isFinite","isNaN","parseFloat","parseInt","decodeURI","decodeURIComponent","encodeURI","encodeURIComponent","escape","unescape"],["arguments","this","super","console","window","document","localStorage","module","global"],["Intl","DataView","Number","Math","Date","String","RegExp","Object","Function","Boolean","Error","Symbol","Set","Map","WeakSet","WeakMap","Proxy","Reflect","JSON","Promise","Float64Array","Int16Array","Int32Array","Int8Array","Uint16Array","Uint32Array","Float32Array","Array","Uint8Array","Uint8ClampedArray","ArrayBuffer","BigInt64Array","BigUint64Array","BigInt"],["EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]);function s(e){return i("(?=",e,")")}function i(...e){return e.map((e=>{return(t=e)?"string"==typeof t?t:t.source:null;var t})).join("")}e.exports=function(e){const a=t,l="<>",c="",u={begin:/<[A-Za-z0-9\\._:-]+/,end:/\/[A-Za-z0-9\\._:-]+>|\/>/,isTrulyOpeningTag:(e,t)=>{const n=e[0].length+e.index,r=e.input[n];"<"!==r?">"===r&&(((e,{after:t})=>{const n="",returnBegin:!0,end:"\\s*=>",contains:[{className:"params",variants:[{begin:e.UNDERSCORE_IDENT_RE,relevance:0},{className:null,begin:/\(\s*\)/,skip:!0},{begin:/\(/,end:/\)/,excludeBegin:!0,excludeEnd:!0,keywords:p,contains:S}]}]},{begin:/,/,relevance:0},{className:"",begin:/\s/,end:/\s*/,skip:!0},{variants:[{begin:l,end:c},{begin:u.begin,"on:begin":u.isTrulyOpeningTag,end:u.end}],subLanguage:"xml",contains:[{begin:u.begin,end:u.end,skip:!0,contains:["self"]}]}],relevance:0},{className:"function",beginKeywords:"function",end:/[{;]/,excludeEnd:!0,keywords:p,contains:["self",e.inherit(e.TITLE_MODE,{begin:a}),_],illegal:/%/},{beginKeywords:"while if switch catch for"},{className:"function",begin:e.UNDERSCORE_IDENT_RE+"\\([^()]*(\\([^()]*(\\([^()]*\\)[^()]*)*\\)[^()]*)*\\)\\s*\\{",returnBegin:!0,contains:[_,e.inherit(e.TITLE_MODE,{begin:a})]},{variants:[{begin:"\\."+a},{begin:"\\$"+a}],relevance:0},{className:"class",beginKeywords:"class",end:/[{;=]/,excludeEnd:!0,illegal:/[:"[\]]/,contains:[{beginKeywords:"extends"},e.UNDERSCORE_TITLE_MODE]},{begin:/\b(?=constructor)/,end:/[{;]/,excludeEnd:!0,contains:[e.inherit(e.TITLE_MODE,{begin:a}),"self",_]},{begin:"(get|set)\\s+(?="+a+"\\()",end:/\{/,keywords:"get set",contains:[e.inherit(e.TITLE_MODE,{begin:a}),{begin:/\(\)/},_]},{begin:/\$[(.]/}]}}},82026:e=>{e.exports=function(e){const t={literal:"true false null"},n=[e.C_LINE_COMMENT_MODE,e.C_BLOCK_COMMENT_MODE],r=[e.QUOTE_STRING_MODE,e.C_NUMBER_MODE],o={end:",",endsWithParent:!0,excludeEnd:!0,contains:r,keywords:t},s={begin:/\{/,end:/\}/,contains:[{className:"attr",begin:/"/,end:/"/,contains:[e.BACKSLASH_ESCAPE],illegal:"\\n"},e.inherit(o,{begin:/:/})].concat(n),illegal:"\\S"},i={begin:"\\[",end:"\\]",contains:[e.inherit(o)],illegal:"\\S"};return r.push(s,i),n.forEach((function(e){r.push(e)})),{name:"JSON",contains:r,keywords:t,illegal:"\\S"}}},66336:e=>{e.exports=function(e){const t={$pattern:/-?[A-z\.\-]+\b/,keyword:"if else foreach return do while until elseif begin for trap data dynamicparam end break throw param continue finally in switch exit filter try process catch hidden static parameter",built_in:"ac asnp cat cd CFS chdir clc clear clhy cli clp cls clv cnsn compare copy cp cpi cpp curl cvpa dbp del diff dir dnsn ebp echo|0 epal epcsv epsn erase etsn exsn fc fhx fl ft fw gal gbp gc gcb gci gcm gcs gdr gerr ghy gi gin gjb gl gm gmo gp gps gpv group gsn gsnp gsv gtz gu gv gwmi h history icm iex ihy ii ipal ipcsv ipmo ipsn irm ise iwmi iwr kill lp ls man md measure mi mount move mp mv nal ndr ni nmo npssc nsn nv ogv oh popd ps pushd pwd r rbp rcjb rcsn rd rdr ren ri rjb rm rmdir rmo rni rnp rp rsn rsnp rujb rv rvpa rwmi sajb sal saps sasv sbp sc scb select set shcm si sl sleep sls sort sp spjb spps spsv start stz sujb sv swmi tee trcm type wget where wjb write"},n={begin:"`[\\s\\S]",relevance:0},r={className:"variable",variants:[{begin:/\$\B/},{className:"keyword",begin:/\$this/},{begin:/\$[\w\d][\w\d_:]*/}]},o={className:"string",variants:[{begin:/"/,end:/"/},{begin:/@"/,end:/^"@/}],contains:[n,r,{className:"variable",begin:/\$[A-z]/,end:/[^A-z]/}]},s={className:"string",variants:[{begin:/'/,end:/'/},{begin:/@'/,end:/^'@/}]},i=e.inherit(e.COMMENT(null,null),{variants:[{begin:/#/,end:/$/},{begin:/<#/,end:/#>/}],contains:[{className:"doctag",variants:[{begin:/\.(synopsis|description|example|inputs|outputs|notes|link|component|role|functionality)/},{begin:/\.(parameter|forwardhelptargetname|forwardhelpcategory|remotehelprunspace|externalhelp)\s+\S+/}]}]}),a={className:"built_in",variants:[{begin:"(".concat("Add|Clear|Close|Copy|Enter|Exit|Find|Format|Get|Hide|Join|Lock|Move|New|Open|Optimize|Pop|Push|Redo|Remove|Rename|Reset|Resize|Search|Select|Set|Show|Skip|Split|Step|Switch|Undo|Unlock|Watch|Backup|Checkpoint|Compare|Compress|Convert|ConvertFrom|ConvertTo|Dismount|Edit|Expand|Export|Group|Import|Initialize|Limit|Merge|Mount|Out|Publish|Restore|Save|Sync|Unpublish|Update|Approve|Assert|Build|Complete|Confirm|Deny|Deploy|Disable|Enable|Install|Invoke|Register|Request|Restart|Resume|Start|Stop|Submit|Suspend|Uninstall|Unregister|Wait|Debug|Measure|Ping|Repair|Resolve|Test|Trace|Connect|Disconnect|Read|Receive|Send|Write|Block|Grant|Protect|Revoke|Unblock|Unprotect|Use|ForEach|Sort|Tee|Where",")+(-)[\\w\\d]+")}]},l={className:"class",beginKeywords:"class enum",end:/\s*[{]/,excludeEnd:!0,relevance:0,contains:[e.TITLE_MODE]},c={className:"function",begin:/function\s+/,end:/\s*\{|$/,excludeEnd:!0,returnBegin:!0,relevance:0,contains:[{begin:"function",relevance:0,className:"keyword"},{className:"title",begin:/\w[\w\d]*((-)[\w\d]+)*/,relevance:0},{begin:/\(/,end:/\)/,className:"params",relevance:0,contains:[r]}]},u={begin:/using\s/,end:/$/,returnBegin:!0,contains:[o,s,{className:"keyword",begin:/(using|assembly|command|module|namespace|type)/}]},p={variants:[{className:"operator",begin:"(".concat("-and|-as|-band|-bnot|-bor|-bxor|-casesensitive|-ccontains|-ceq|-cge|-cgt|-cle|-clike|-clt|-cmatch|-cne|-cnotcontains|-cnotlike|-cnotmatch|-contains|-creplace|-csplit|-eq|-exact|-f|-file|-ge|-gt|-icontains|-ieq|-ige|-igt|-ile|-ilike|-ilt|-imatch|-in|-ine|-inotcontains|-inotlike|-inotmatch|-ireplace|-is|-isnot|-isplit|-join|-le|-like|-lt|-match|-ne|-not|-notcontains|-notin|-notlike|-notmatch|-or|-regex|-replace|-shl|-shr|-split|-wildcard|-xor",")\\b")},{className:"literal",begin:/(-)[\w\d]+/,relevance:0}]},h={className:"function",begin:/\[.*\]\s*[\w]+[ ]??\(/,end:/$/,returnBegin:!0,relevance:0,contains:[{className:"keyword",begin:"(".concat(t.keyword.toString().replace(/\s/g,"|"),")\\b"),endsParent:!0,relevance:0},e.inherit(e.TITLE_MODE,{endsParent:!0})]},f=[h,i,n,e.NUMBER_MODE,o,s,a,r,{className:"literal",begin:/\$(null|true|false)\b/},{className:"selector-tag",begin:/@\B/,relevance:0}],d={begin:/\[/,end:/\]/,excludeBegin:!0,excludeEnd:!0,relevance:0,contains:[].concat("self",f,{begin:"("+["string","char","byte","int","long","bool","decimal","single","double","DateTime","xml","array","hashtable","void"].join("|")+")",className:"built_in",relevance:0},{className:"type",begin:/[\.\w\d]+/,relevance:0})};return h.contains.unshift(d),{name:"PowerShell",aliases:["ps","ps1"],case_insensitive:!0,keywords:t,contains:f.concat(l,c,u,p,d)}}},42157:e=>{function t(e){return e?"string"==typeof e?e:e.source:null}function n(e){return r("(?=",e,")")}function r(...e){return e.map((e=>t(e))).join("")}function o(...e){return"("+e.map((e=>t(e))).join("|")+")"}e.exports=function(e){const t=r(/[A-Z_]/,r("(",/[A-Z0-9_.-]*:/,")?"),/[A-Z0-9_.-]*/),s={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},i={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},a=e.inherit(i,{begin:/\(/,end:/\)/}),l=e.inherit(e.APOS_STRING_MODE,{className:"meta-string"}),c=e.inherit(e.QUOTE_STRING_MODE,{className:"meta-string"}),u={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[i,c,l,a,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[i,a,c,l]}]}]},e.COMMENT(//,{relevance:10}),{begin://,relevance:10},s,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[u],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[u],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:r(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:t,relevance:0,starts:u}]},{className:"tag",begin:r(/<\//,n(r(t,/>/))),contains:[{className:"name",begin:t,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},54587:e=>{e.exports=function(e){var t="true false yes no null",n="[\\w#;/?:@&=+$,.~*'()[\\]]+",r={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[e.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},o=e.inherit(r,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),s={className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},i={end:",",endsWithParent:!0,excludeEnd:!0,keywords:t,relevance:0},a={begin:/\{/,end:/\}/,contains:[i],illegal:"\\n",relevance:0},l={begin:"\\[",end:"\\]",contains:[i],illegal:"\\n",relevance:0},c=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+n},{className:"type",begin:"!<"+n+">"},{className:"type",begin:"!"+n},{className:"type",begin:"!!"+n},{className:"meta",begin:"&"+e.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+e.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},e.HASH_COMMENT_MODE,{beginKeywords:t,keywords:{literal:t}},s,{className:"number",begin:e.C_NUMBER_RE+"\\b",relevance:0},a,l,r],u=[...c];return u.pop(),u.push(o),i.contains=u,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:c}}},8679:(e,t,n)=>{"use strict";var r=n(59864),o={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},s={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},i={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},a={};function l(e){return r.isMemo(e)?i:a[e.$$typeof]||o}a[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},a[r.Memo]=i;var c=Object.defineProperty,u=Object.getOwnPropertyNames,p=Object.getOwnPropertySymbols,h=Object.getOwnPropertyDescriptor,f=Object.getPrototypeOf,d=Object.prototype;e.exports=function e(t,n,r){if("string"!=typeof n){if(d){var o=f(n);o&&o!==d&&e(t,o,r)}var i=u(n);p&&(i=i.concat(p(n)));for(var a=l(t),m=l(n),g=0;g{t.read=function(e,t,n,r,o){var s,i,a=8*o-r-1,l=(1<>1,u=-7,p=n?o-1:0,h=n?-1:1,f=e[t+p];for(p+=h,s=f&(1<<-u)-1,f>>=-u,u+=a;u>0;s=256*s+e[t+p],p+=h,u-=8);for(i=s&(1<<-u)-1,s>>=-u,u+=r;u>0;i=256*i+e[t+p],p+=h,u-=8);if(0===s)s=1-c;else{if(s===l)return i?NaN:1/0*(f?-1:1);i+=Math.pow(2,r),s-=c}return(f?-1:1)*i*Math.pow(2,s-r)},t.write=function(e,t,n,r,o,s){var i,a,l,c=8*s-o-1,u=(1<>1,h=23===o?Math.pow(2,-24)-Math.pow(2,-77):0,f=r?0:s-1,d=r?1:-1,m=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(a=isNaN(t)?1:0,i=u):(i=Math.floor(Math.log(t)/Math.LN2),t*(l=Math.pow(2,-i))<1&&(i--,l*=2),(t+=i+p>=1?h/l:h*Math.pow(2,1-p))*l>=2&&(i++,l/=2),i+p>=u?(a=0,i=u):i+p>=1?(a=(t*l-1)*Math.pow(2,o),i+=p):(a=t*Math.pow(2,p-1)*Math.pow(2,o),i=0));o>=8;e[n+f]=255&a,f+=d,a/=256,o-=8);for(i=i<0;e[n+f]=255&i,f+=d,i/=256,c-=8);e[n+f-d]|=128*m}},43393:function(e){e.exports=function(){"use strict";var e=Array.prototype.slice;function t(e,t){t&&(e.prototype=Object.create(t.prototype)),e.prototype.constructor=e}function n(e){return i(e)?e:K(e)}function r(e){return a(e)?e:H(e)}function o(e){return l(e)?e:G(e)}function s(e){return i(e)&&!c(e)?e:Z(e)}function i(e){return!(!e||!e[p])}function a(e){return!(!e||!e[h])}function l(e){return!(!e||!e[f])}function c(e){return a(e)||l(e)}function u(e){return!(!e||!e[d])}t(r,n),t(o,n),t(s,n),n.isIterable=i,n.isKeyed=a,n.isIndexed=l,n.isAssociative=c,n.isOrdered=u,n.Keyed=r,n.Indexed=o,n.Set=s;var p="@@__IMMUTABLE_ITERABLE__@@",h="@@__IMMUTABLE_KEYED__@@",f="@@__IMMUTABLE_INDEXED__@@",d="@@__IMMUTABLE_ORDERED__@@",m="delete",g=5,y=1<>>0;if(""+n!==t||4294967295===n)return NaN;t=n}return t<0?O(e)+t:t}function A(){return!0}function C(e,t,n){return(0===e||void 0!==n&&e<=-n)&&(void 0===t||void 0!==n&&t>=n)}function P(e,t){return I(e,t,0)}function N(e,t){return I(e,t,t)}function I(e,t,n){return void 0===e?n:e<0?Math.max(0,t+e):void 0===t?e:Math.min(t,e)}var T=0,R=1,M=2,D="function"==typeof Symbol&&Symbol.iterator,F="@@iterator",L=D||F;function B(e){this.next=e}function $(e,t,n,r){var o=0===e?t:1===e?n:[t,n];return r?r.value=o:r={value:o,done:!1},r}function q(){return{value:void 0,done:!0}}function U(e){return!!W(e)}function z(e){return e&&"function"==typeof e.next}function V(e){var t=W(e);return t&&t.call(e)}function W(e){var t=e&&(D&&e[D]||e[F]);if("function"==typeof t)return t}function J(e){return e&&"number"==typeof e.length}function K(e){return null==e?ie():i(e)?e.toSeq():ce(e)}function H(e){return null==e?ie().toKeyedSeq():i(e)?a(e)?e.toSeq():e.fromEntrySeq():ae(e)}function G(e){return null==e?ie():i(e)?a(e)?e.entrySeq():e.toIndexedSeq():le(e)}function Z(e){return(null==e?ie():i(e)?a(e)?e.entrySeq():e:le(e)).toSetSeq()}B.prototype.toString=function(){return"[Iterator]"},B.KEYS=T,B.VALUES=R,B.ENTRIES=M,B.prototype.inspect=B.prototype.toSource=function(){return this.toString()},B.prototype[L]=function(){return this},t(K,n),K.of=function(){return K(arguments)},K.prototype.toSeq=function(){return this},K.prototype.toString=function(){return this.__toString("Seq {","}")},K.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},K.prototype.__iterate=function(e,t){return pe(this,e,t,!0)},K.prototype.__iterator=function(e,t){return he(this,e,t,!0)},t(H,K),H.prototype.toKeyedSeq=function(){return this},t(G,K),G.of=function(){return G(arguments)},G.prototype.toIndexedSeq=function(){return this},G.prototype.toString=function(){return this.__toString("Seq [","]")},G.prototype.__iterate=function(e,t){return pe(this,e,t,!1)},G.prototype.__iterator=function(e,t){return he(this,e,t,!1)},t(Z,K),Z.of=function(){return Z(arguments)},Z.prototype.toSetSeq=function(){return this},K.isSeq=se,K.Keyed=H,K.Set=Z,K.Indexed=G;var Y,X,Q,ee="@@__IMMUTABLE_SEQ__@@";function te(e){this._array=e,this.size=e.length}function ne(e){var t=Object.keys(e);this._object=e,this._keys=t,this.size=t.length}function re(e){this._iterable=e,this.size=e.length||e.size}function oe(e){this._iterator=e,this._iteratorCache=[]}function se(e){return!(!e||!e[ee])}function ie(){return Y||(Y=new te([]))}function ae(e){var t=Array.isArray(e)?new te(e).fromEntrySeq():z(e)?new oe(e).fromEntrySeq():U(e)?new re(e).fromEntrySeq():"object"==typeof e?new ne(e):void 0;if(!t)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+e);return t}function le(e){var t=ue(e);if(!t)throw new TypeError("Expected Array or iterable object of values: "+e);return t}function ce(e){var t=ue(e)||"object"==typeof e&&new ne(e);if(!t)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+e);return t}function ue(e){return J(e)?new te(e):z(e)?new oe(e):U(e)?new re(e):void 0}function pe(e,t,n,r){var o=e._cache;if(o){for(var s=o.length-1,i=0;i<=s;i++){var a=o[n?s-i:i];if(!1===t(a[1],r?a[0]:i,e))return i+1}return i}return e.__iterateUncached(t,n)}function he(e,t,n,r){var o=e._cache;if(o){var s=o.length-1,i=0;return new B((function(){var e=o[n?s-i:i];return i++>s?q():$(t,r?e[0]:i-1,e[1])}))}return e.__iteratorUncached(t,n)}function fe(e,t){return t?de(t,e,"",{"":e}):me(e)}function de(e,t,n,r){return Array.isArray(t)?e.call(r,n,G(t).map((function(n,r){return de(e,n,r,t)}))):ge(t)?e.call(r,n,H(t).map((function(n,r){return de(e,n,r,t)}))):t}function me(e){return Array.isArray(e)?G(e).map(me).toList():ge(e)?H(e).map(me).toMap():e}function ge(e){return e&&(e.constructor===Object||void 0===e.constructor)}function ye(e,t){if(e===t||e!=e&&t!=t)return!0;if(!e||!t)return!1;if("function"==typeof e.valueOf&&"function"==typeof t.valueOf){if((e=e.valueOf())===(t=t.valueOf())||e!=e&&t!=t)return!0;if(!e||!t)return!1}return!("function"!=typeof e.equals||"function"!=typeof t.equals||!e.equals(t))}function ve(e,t){if(e===t)return!0;if(!i(t)||void 0!==e.size&&void 0!==t.size&&e.size!==t.size||void 0!==e.__hash&&void 0!==t.__hash&&e.__hash!==t.__hash||a(e)!==a(t)||l(e)!==l(t)||u(e)!==u(t))return!1;if(0===e.size&&0===t.size)return!0;var n=!c(e);if(u(e)){var r=e.entries();return t.every((function(e,t){var o=r.next().value;return o&&ye(o[1],e)&&(n||ye(o[0],t))}))&&r.next().done}var o=!1;if(void 0===e.size)if(void 0===t.size)"function"==typeof e.cacheResult&&e.cacheResult();else{o=!0;var s=e;e=t,t=s}var p=!0,h=t.__iterate((function(t,r){if(n?!e.has(t):o?!ye(t,e.get(r,b)):!ye(e.get(r,b),t))return p=!1,!1}));return p&&e.size===h}function be(e,t){if(!(this instanceof be))return new be(e,t);if(this._value=e,this.size=void 0===t?1/0:Math.max(0,t),0===this.size){if(X)return X;X=this}}function we(e,t){if(!e)throw new Error(t)}function Ee(e,t,n){if(!(this instanceof Ee))return new Ee(e,t,n);if(we(0!==n,"Cannot step a Range by 0"),e=e||0,void 0===t&&(t=1/0),n=void 0===n?1:Math.abs(n),tr?q():$(e,o,n[t?r-o++:o++])}))},t(ne,H),ne.prototype.get=function(e,t){return void 0===t||this.has(e)?this._object[e]:t},ne.prototype.has=function(e){return this._object.hasOwnProperty(e)},ne.prototype.__iterate=function(e,t){for(var n=this._object,r=this._keys,o=r.length-1,s=0;s<=o;s++){var i=r[t?o-s:s];if(!1===e(n[i],i,this))return s+1}return s},ne.prototype.__iterator=function(e,t){var n=this._object,r=this._keys,o=r.length-1,s=0;return new B((function(){var i=r[t?o-s:s];return s++>o?q():$(e,i,n[i])}))},ne.prototype[d]=!0,t(re,G),re.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);var n=V(this._iterable),r=0;if(z(n))for(var o;!(o=n.next()).done&&!1!==e(o.value,r++,this););return r},re.prototype.__iteratorUncached=function(e,t){if(t)return this.cacheResult().__iterator(e,t);var n=V(this._iterable);if(!z(n))return new B(q);var r=0;return new B((function(){var t=n.next();return t.done?t:$(e,r++,t.value)}))},t(oe,G),oe.prototype.__iterateUncached=function(e,t){if(t)return this.cacheResult().__iterate(e,t);for(var n,r=this._iterator,o=this._iteratorCache,s=0;s=r.length){var t=n.next();if(t.done)return t;r[o]=t.value}return $(e,o,r[o++])}))},t(be,G),be.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},be.prototype.get=function(e,t){return this.has(e)?this._value:t},be.prototype.includes=function(e){return ye(this._value,e)},be.prototype.slice=function(e,t){var n=this.size;return C(e,t,n)?this:new be(this._value,N(t,n)-P(e,n))},be.prototype.reverse=function(){return this},be.prototype.indexOf=function(e){return ye(this._value,e)?0:-1},be.prototype.lastIndexOf=function(e){return ye(this._value,e)?this.size:-1},be.prototype.__iterate=function(e,t){for(var n=0;n=0&&t=0&&nn?q():$(e,s++,i)}))},Ee.prototype.equals=function(e){return e instanceof Ee?this._start===e._start&&this._end===e._end&&this._step===e._step:ve(this,e)},t(xe,n),t(Se,xe),t(_e,xe),t(je,xe),xe.Keyed=Se,xe.Indexed=_e,xe.Set=je;var Oe="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(e,t){var n=65535&(e|=0),r=65535&(t|=0);return n*r+((e>>>16)*r+n*(t>>>16)<<16>>>0)|0};function ke(e){return e>>>1&1073741824|3221225471&e}function Ae(e){if(!1===e||null==e)return 0;if("function"==typeof e.valueOf&&(!1===(e=e.valueOf())||null==e))return 0;if(!0===e)return 1;var t=typeof e;if("number"===t){if(e!=e||e===1/0)return 0;var n=0|e;for(n!==e&&(n^=4294967295*e);e>4294967295;)n^=e/=4294967295;return ke(n)}if("string"===t)return e.length>Be?Ce(e):Pe(e);if("function"==typeof e.hashCode)return e.hashCode();if("object"===t)return Ne(e);if("function"==typeof e.toString)return Pe(e.toString());throw new Error("Value type "+t+" cannot be hashed.")}function Ce(e){var t=Ue[e];return void 0===t&&(t=Pe(e),qe===$e&&(qe=0,Ue={}),qe++,Ue[e]=t),t}function Pe(e){for(var t=0,n=0;n0)switch(e.nodeType){case 1:return e.uniqueID;case 9:return e.documentElement&&e.documentElement.uniqueID}}var Me,De="function"==typeof WeakMap;De&&(Me=new WeakMap);var Fe=0,Le="__immutablehash__";"function"==typeof Symbol&&(Le=Symbol(Le));var Be=16,$e=255,qe=0,Ue={};function ze(e){we(e!==1/0,"Cannot perform this action with an infinite size.")}function Ve(e){return null==e?ot():We(e)&&!u(e)?e:ot().withMutations((function(t){var n=r(e);ze(n.size),n.forEach((function(e,n){return t.set(n,e)}))}))}function We(e){return!(!e||!e[Ke])}t(Ve,Se),Ve.of=function(){var t=e.call(arguments,0);return ot().withMutations((function(e){for(var n=0;n=t.length)throw new Error("Missing value for key: "+t[n]);e.set(t[n],t[n+1])}}))},Ve.prototype.toString=function(){return this.__toString("Map {","}")},Ve.prototype.get=function(e,t){return this._root?this._root.get(0,void 0,e,t):t},Ve.prototype.set=function(e,t){return st(this,e,t)},Ve.prototype.setIn=function(e,t){return this.updateIn(e,b,(function(){return t}))},Ve.prototype.remove=function(e){return st(this,e,b)},Ve.prototype.deleteIn=function(e){return this.updateIn(e,(function(){return b}))},Ve.prototype.update=function(e,t,n){return 1===arguments.length?e(this):this.updateIn([e],t,n)},Ve.prototype.updateIn=function(e,t,n){n||(n=t,t=void 0);var r=gt(this,xn(e),t,n);return r===b?void 0:r},Ve.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):ot()},Ve.prototype.merge=function(){return ht(this,void 0,arguments)},Ve.prototype.mergeWith=function(t){return ht(this,t,e.call(arguments,1))},Ve.prototype.mergeIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,ot(),(function(e){return"function"==typeof e.merge?e.merge.apply(e,n):n[n.length-1]}))},Ve.prototype.mergeDeep=function(){return ht(this,ft,arguments)},Ve.prototype.mergeDeepWith=function(t){var n=e.call(arguments,1);return ht(this,dt(t),n)},Ve.prototype.mergeDeepIn=function(t){var n=e.call(arguments,1);return this.updateIn(t,ot(),(function(e){return"function"==typeof e.mergeDeep?e.mergeDeep.apply(e,n):n[n.length-1]}))},Ve.prototype.sort=function(e){return Ut(pn(this,e))},Ve.prototype.sortBy=function(e,t){return Ut(pn(this,t,e))},Ve.prototype.withMutations=function(e){var t=this.asMutable();return e(t),t.wasAltered()?t.__ensureOwner(this.__ownerID):this},Ve.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new _)},Ve.prototype.asImmutable=function(){return this.__ensureOwner()},Ve.prototype.wasAltered=function(){return this.__altered},Ve.prototype.__iterator=function(e,t){return new et(this,e,t)},Ve.prototype.__iterate=function(e,t){var n=this,r=0;return this._root&&this._root.iterate((function(t){return r++,e(t[1],t[0],n)}),t),r},Ve.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?rt(this.size,this._root,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},Ve.isMap=We;var Je,Ke="@@__IMMUTABLE_MAP__@@",He=Ve.prototype;function Ge(e,t){this.ownerID=e,this.entries=t}function Ze(e,t,n){this.ownerID=e,this.bitmap=t,this.nodes=n}function Ye(e,t,n){this.ownerID=e,this.count=t,this.nodes=n}function Xe(e,t,n){this.ownerID=e,this.keyHash=t,this.entries=n}function Qe(e,t,n){this.ownerID=e,this.keyHash=t,this.entry=n}function et(e,t,n){this._type=t,this._reverse=n,this._stack=e._root&&nt(e._root)}function tt(e,t){return $(e,t[0],t[1])}function nt(e,t){return{node:e,index:0,__prev:t}}function rt(e,t,n,r){var o=Object.create(He);return o.size=e,o._root=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function ot(){return Je||(Je=rt(0))}function st(e,t,n){var r,o;if(e._root){var s=x(w),i=x(E);if(r=it(e._root,e.__ownerID,0,void 0,t,n,s,i),!i.value)return e;o=e.size+(s.value?n===b?-1:1:0)}else{if(n===b)return e;o=1,r=new Ge(e.__ownerID,[[t,n]])}return e.__ownerID?(e.size=o,e._root=r,e.__hash=void 0,e.__altered=!0,e):r?rt(o,r):ot()}function it(e,t,n,r,o,s,i,a){return e?e.update(t,n,r,o,s,i,a):s===b?e:(S(a),S(i),new Qe(t,r,[o,s]))}function at(e){return e.constructor===Qe||e.constructor===Xe}function lt(e,t,n,r,o){if(e.keyHash===r)return new Xe(t,r,[e.entry,o]);var s,i=(0===n?e.keyHash:e.keyHash>>>n)&v,a=(0===n?r:r>>>n)&v;return new Ze(t,1<>>=1)i[a]=1&n?t[s++]:void 0;return i[r]=o,new Ye(e,s+1,i)}function ht(e,t,n){for(var o=[],s=0;s>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135,e+=e>>8,127&(e+=e>>16)}function vt(e,t,n,r){var o=r?e:j(e);return o[t]=n,o}function bt(e,t,n,r){var o=e.length+1;if(r&&t+1===o)return e[t]=n,e;for(var s=new Array(o),i=0,a=0;a=Et)return ct(e,l,r,o);var h=e&&e===this.ownerID,f=h?l:j(l);return p?a?c===u-1?f.pop():f[c]=f.pop():f[c]=[r,o]:f.push([r,o]),h?(this.entries=f,this):new Ge(e,f)}},Ze.prototype.get=function(e,t,n,r){void 0===t&&(t=Ae(n));var o=1<<((0===e?t:t>>>e)&v),s=this.bitmap;return 0==(s&o)?r:this.nodes[yt(s&o-1)].get(e+g,t,n,r)},Ze.prototype.update=function(e,t,n,r,o,s,i){void 0===n&&(n=Ae(r));var a=(0===t?n:n>>>t)&v,l=1<=xt)return pt(e,h,c,a,d);if(u&&!d&&2===h.length&&at(h[1^p]))return h[1^p];if(u&&d&&1===h.length&&at(d))return d;var m=e&&e===this.ownerID,y=u?d?c:c^l:c|l,w=u?d?vt(h,p,d,m):wt(h,p,m):bt(h,p,d,m);return m?(this.bitmap=y,this.nodes=w,this):new Ze(e,y,w)},Ye.prototype.get=function(e,t,n,r){void 0===t&&(t=Ae(n));var o=(0===e?t:t>>>e)&v,s=this.nodes[o];return s?s.get(e+g,t,n,r):r},Ye.prototype.update=function(e,t,n,r,o,s,i){void 0===n&&(n=Ae(r));var a=(0===t?n:n>>>t)&v,l=o===b,c=this.nodes,u=c[a];if(l&&!u)return this;var p=it(u,e,t+g,n,r,o,s,i);if(p===u)return this;var h=this.count;if(u){if(!p&&--h0&&r=0&&e>>t&v;if(r>=this.array.length)return new At([],e);var o,s=0===r;if(t>0){var i=this.array[r];if((o=i&&i.removeBefore(e,t-g,n))===i&&s)return this}if(s&&!o)return this;var a=Ft(this,e);if(!s)for(var l=0;l>>t&v;if(o>=this.array.length)return this;if(t>0){var s=this.array[o];if((r=s&&s.removeAfter(e,t-g,n))===s&&o===this.array.length-1)return this}var i=Ft(this,e);return i.array.splice(o+1),r&&(i.array[o]=r),i};var Ct,Pt,Nt={};function It(e,t){var n=e._origin,r=e._capacity,o=qt(r),s=e._tail;return i(e._root,e._level,0);function i(e,t,n){return 0===t?a(e,n):l(e,t,n)}function a(e,i){var a=i===o?s&&s.array:e&&e.array,l=i>n?0:n-i,c=r-i;return c>y&&(c=y),function(){if(l===c)return Nt;var e=t?--c:l++;return a&&a[e]}}function l(e,o,s){var a,l=e&&e.array,c=s>n?0:n-s>>o,u=1+(r-s>>o);return u>y&&(u=y),function(){for(;;){if(a){var e=a();if(e!==Nt)return e;a=null}if(c===u)return Nt;var n=t?--u:c++;a=i(l&&l[n],o-g,s+(n<=e.size||t<0)return e.withMutations((function(e){t<0?Bt(e,t).set(0,n):Bt(e,0,t+1).set(t,n)}));t+=e._origin;var r=e._tail,o=e._root,s=x(E);return t>=qt(e._capacity)?r=Dt(r,e.__ownerID,0,t,n,s):o=Dt(o,e.__ownerID,e._level,t,n,s),s.value?e.__ownerID?(e._root=o,e._tail=r,e.__hash=void 0,e.__altered=!0,e):Tt(e._origin,e._capacity,e._level,o,r):e}function Dt(e,t,n,r,o,s){var i,a=r>>>n&v,l=e&&a0){var c=e&&e.array[a],u=Dt(c,t,n-g,r,o,s);return u===c?e:((i=Ft(e,t)).array[a]=u,i)}return l&&e.array[a]===o?e:(S(s),i=Ft(e,t),void 0===o&&a===i.array.length-1?i.array.pop():i.array[a]=o,i)}function Ft(e,t){return t&&e&&t===e.ownerID?e:new At(e?e.array.slice():[],t)}function Lt(e,t){if(t>=qt(e._capacity))return e._tail;if(t<1<0;)n=n.array[t>>>r&v],r-=g;return n}}function Bt(e,t,n){void 0!==t&&(t|=0),void 0!==n&&(n|=0);var r=e.__ownerID||new _,o=e._origin,s=e._capacity,i=o+t,a=void 0===n?s:n<0?s+n:o+n;if(i===o&&a===s)return e;if(i>=a)return e.clear();for(var l=e._level,c=e._root,u=0;i+u<0;)c=new At(c&&c.array.length?[void 0,c]:[],r),u+=1<<(l+=g);u&&(i+=u,o+=u,a+=u,s+=u);for(var p=qt(s),h=qt(a);h>=1<p?new At([],r):f;if(f&&h>p&&ig;y-=g){var b=p>>>y&v;m=m.array[b]=Ft(m.array[b],r)}m.array[p>>>g&v]=f}if(a=h)i-=h,a-=h,l=g,c=null,d=d&&d.removeBefore(r,0,i);else if(i>o||h>>l&v;if(w!==h>>>l&v)break;w&&(u+=(1<o&&(c=c.removeBefore(r,l,i-u)),c&&hs&&(s=c.size),i(l)||(c=c.map((function(e){return fe(e)}))),r.push(c)}return s>e.size&&(e=e.setSize(s)),mt(e,t,r)}function qt(e){return e>>g<=y&&i.size>=2*s.size?(r=(o=i.filter((function(e,t){return void 0!==e&&a!==t}))).toKeyedSeq().map((function(e){return e[0]})).flip().toMap(),e.__ownerID&&(r.__ownerID=o.__ownerID=e.__ownerID)):(r=s.remove(t),o=a===i.size-1?i.pop():i.set(a,void 0))}else if(l){if(n===i.get(a)[1])return e;r=s,o=i.set(a,[t,n])}else r=s.set(t,i.size),o=i.set(i.size,[t,n]);return e.__ownerID?(e.size=r.size,e._map=r,e._list=o,e.__hash=void 0,e):Vt(r,o)}function Kt(e,t){this._iter=e,this._useKeys=t,this.size=e.size}function Ht(e){this._iter=e,this.size=e.size}function Gt(e){this._iter=e,this.size=e.size}function Zt(e){this._iter=e,this.size=e.size}function Yt(e){var t=bn(e);return t._iter=e,t.size=e.size,t.flip=function(){return e},t.reverse=function(){var t=e.reverse.apply(this);return t.flip=function(){return e.reverse()},t},t.has=function(t){return e.includes(t)},t.includes=function(t){return e.has(t)},t.cacheResult=wn,t.__iterateUncached=function(t,n){var r=this;return e.__iterate((function(e,n){return!1!==t(n,e,r)}),n)},t.__iteratorUncached=function(t,n){if(t===M){var r=e.__iterator(t,n);return new B((function(){var e=r.next();if(!e.done){var t=e.value[0];e.value[0]=e.value[1],e.value[1]=t}return e}))}return e.__iterator(t===R?T:R,n)},t}function Xt(e,t,n){var r=bn(e);return r.size=e.size,r.has=function(t){return e.has(t)},r.get=function(r,o){var s=e.get(r,b);return s===b?o:t.call(n,s,r,e)},r.__iterateUncached=function(r,o){var s=this;return e.__iterate((function(e,o,i){return!1!==r(t.call(n,e,o,i),o,s)}),o)},r.__iteratorUncached=function(r,o){var s=e.__iterator(M,o);return new B((function(){var o=s.next();if(o.done)return o;var i=o.value,a=i[0];return $(r,a,t.call(n,i[1],a,e),o)}))},r}function Qt(e,t){var n=bn(e);return n._iter=e,n.size=e.size,n.reverse=function(){return e},e.flip&&(n.flip=function(){var t=Yt(e);return t.reverse=function(){return e.flip()},t}),n.get=function(n,r){return e.get(t?n:-1-n,r)},n.has=function(n){return e.has(t?n:-1-n)},n.includes=function(t){return e.includes(t)},n.cacheResult=wn,n.__iterate=function(t,n){var r=this;return e.__iterate((function(e,n){return t(e,n,r)}),!n)},n.__iterator=function(t,n){return e.__iterator(t,!n)},n}function en(e,t,n,r){var o=bn(e);return r&&(o.has=function(r){var o=e.get(r,b);return o!==b&&!!t.call(n,o,r,e)},o.get=function(r,o){var s=e.get(r,b);return s!==b&&t.call(n,s,r,e)?s:o}),o.__iterateUncached=function(o,s){var i=this,a=0;return e.__iterate((function(e,s,l){if(t.call(n,e,s,l))return a++,o(e,r?s:a-1,i)}),s),a},o.__iteratorUncached=function(o,s){var i=e.__iterator(M,s),a=0;return new B((function(){for(;;){var s=i.next();if(s.done)return s;var l=s.value,c=l[0],u=l[1];if(t.call(n,u,c,e))return $(o,r?c:a++,u,s)}}))},o}function tn(e,t,n){var r=Ve().asMutable();return e.__iterate((function(o,s){r.update(t.call(n,o,s,e),0,(function(e){return e+1}))})),r.asImmutable()}function nn(e,t,n){var r=a(e),o=(u(e)?Ut():Ve()).asMutable();e.__iterate((function(s,i){o.update(t.call(n,s,i,e),(function(e){return(e=e||[]).push(r?[i,s]:s),e}))}));var s=vn(e);return o.map((function(t){return mn(e,s(t))}))}function rn(e,t,n,r){var o=e.size;if(void 0!==t&&(t|=0),void 0!==n&&(n===1/0?n=o:n|=0),C(t,n,o))return e;var s=P(t,o),i=N(n,o);if(s!=s||i!=i)return rn(e.toSeq().cacheResult(),t,n,r);var a,l=i-s;l==l&&(a=l<0?0:l);var c=bn(e);return c.size=0===a?a:e.size&&a||void 0,!r&&se(e)&&a>=0&&(c.get=function(t,n){return(t=k(this,t))>=0&&ta)return q();var e=o.next();return r||t===R?e:$(t,l-1,t===T?void 0:e.value[1],e)}))},c}function on(e,t,n){var r=bn(e);return r.__iterateUncached=function(r,o){var s=this;if(o)return this.cacheResult().__iterate(r,o);var i=0;return e.__iterate((function(e,o,a){return t.call(n,e,o,a)&&++i&&r(e,o,s)})),i},r.__iteratorUncached=function(r,o){var s=this;if(o)return this.cacheResult().__iterator(r,o);var i=e.__iterator(M,o),a=!0;return new B((function(){if(!a)return q();var e=i.next();if(e.done)return e;var o=e.value,l=o[0],c=o[1];return t.call(n,c,l,s)?r===M?e:$(r,l,c,e):(a=!1,q())}))},r}function sn(e,t,n,r){var o=bn(e);return o.__iterateUncached=function(o,s){var i=this;if(s)return this.cacheResult().__iterate(o,s);var a=!0,l=0;return e.__iterate((function(e,s,c){if(!a||!(a=t.call(n,e,s,c)))return l++,o(e,r?s:l-1,i)})),l},o.__iteratorUncached=function(o,s){var i=this;if(s)return this.cacheResult().__iterator(o,s);var a=e.__iterator(M,s),l=!0,c=0;return new B((function(){var e,s,u;do{if((e=a.next()).done)return r||o===R?e:$(o,c++,o===T?void 0:e.value[1],e);var p=e.value;s=p[0],u=p[1],l&&(l=t.call(n,u,s,i))}while(l);return o===M?e:$(o,s,u,e)}))},o}function an(e,t){var n=a(e),o=[e].concat(t).map((function(e){return i(e)?n&&(e=r(e)):e=n?ae(e):le(Array.isArray(e)?e:[e]),e})).filter((function(e){return 0!==e.size}));if(0===o.length)return e;if(1===o.length){var s=o[0];if(s===e||n&&a(s)||l(e)&&l(s))return s}var c=new te(o);return n?c=c.toKeyedSeq():l(e)||(c=c.toSetSeq()),(c=c.flatten(!0)).size=o.reduce((function(e,t){if(void 0!==e){var n=t.size;if(void 0!==n)return e+n}}),0),c}function ln(e,t,n){var r=bn(e);return r.__iterateUncached=function(r,o){var s=0,a=!1;function l(e,c){var u=this;e.__iterate((function(e,o){return(!t||c0}function dn(e,t,r){var o=bn(e);return o.size=new te(r).map((function(e){return e.size})).min(),o.__iterate=function(e,t){for(var n,r=this.__iterator(R,t),o=0;!(n=r.next()).done&&!1!==e(n.value,o++,this););return o},o.__iteratorUncached=function(e,o){var s=r.map((function(e){return e=n(e),V(o?e.reverse():e)})),i=0,a=!1;return new B((function(){var n;return a||(n=s.map((function(e){return e.next()})),a=n.some((function(e){return e.done}))),a?q():$(e,i++,t.apply(null,n.map((function(e){return e.value}))))}))},o}function mn(e,t){return se(e)?t:e.constructor(t)}function gn(e){if(e!==Object(e))throw new TypeError("Expected [K, V] tuple: "+e)}function yn(e){return ze(e.size),O(e)}function vn(e){return a(e)?r:l(e)?o:s}function bn(e){return Object.create((a(e)?H:l(e)?G:Z).prototype)}function wn(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):K.prototype.cacheResult.call(this)}function En(e,t){return e>t?1:e=0;n--)t={value:arguments[n],next:t};return this.__ownerID?(this.size=e,this._head=t,this.__hash=void 0,this.__altered=!0,this):Hn(e,t)},zn.prototype.pushAll=function(e){if(0===(e=o(e)).size)return this;ze(e.size);var t=this.size,n=this._head;return e.reverse().forEach((function(e){t++,n={value:e,next:n}})),this.__ownerID?(this.size=t,this._head=n,this.__hash=void 0,this.__altered=!0,this):Hn(t,n)},zn.prototype.pop=function(){return this.slice(1)},zn.prototype.unshift=function(){return this.push.apply(this,arguments)},zn.prototype.unshiftAll=function(e){return this.pushAll(e)},zn.prototype.shift=function(){return this.pop.apply(this,arguments)},zn.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):Gn()},zn.prototype.slice=function(e,t){if(C(e,t,this.size))return this;var n=P(e,this.size);if(N(t,this.size)!==this.size)return _e.prototype.slice.call(this,e,t);for(var r=this.size-n,o=this._head;n--;)o=o.next;return this.__ownerID?(this.size=r,this._head=o,this.__hash=void 0,this.__altered=!0,this):Hn(r,o)},zn.prototype.__ensureOwner=function(e){return e===this.__ownerID?this:e?Hn(this.size,this._head,e,this.__hash):(this.__ownerID=e,this.__altered=!1,this)},zn.prototype.__iterate=function(e,t){if(t)return this.reverse().__iterate(e);for(var n=0,r=this._head;r&&!1!==e(r.value,n++,this);)r=r.next;return n},zn.prototype.__iterator=function(e,t){if(t)return this.reverse().__iterator(e);var n=0,r=this._head;return new B((function(){if(r){var t=r.value;return r=r.next,$(e,n++,t)}return q()}))},zn.isStack=Vn;var Wn,Jn="@@__IMMUTABLE_STACK__@@",Kn=zn.prototype;function Hn(e,t,n,r){var o=Object.create(Kn);return o.size=e,o._head=t,o.__ownerID=n,o.__hash=r,o.__altered=!1,o}function Gn(){return Wn||(Wn=Hn(0))}function Zn(e,t){var n=function(n){e.prototype[n]=t[n]};return Object.keys(t).forEach(n),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(t).forEach(n),e}Kn[Jn]=!0,Kn.withMutations=He.withMutations,Kn.asMutable=He.asMutable,Kn.asImmutable=He.asImmutable,Kn.wasAltered=He.wasAltered,n.Iterator=B,Zn(n,{toArray:function(){ze(this.size);var e=new Array(this.size||0);return this.valueSeq().__iterate((function(t,n){e[n]=t})),e},toIndexedSeq:function(){return new Ht(this)},toJS:function(){return this.toSeq().map((function(e){return e&&"function"==typeof e.toJS?e.toJS():e})).__toJS()},toJSON:function(){return this.toSeq().map((function(e){return e&&"function"==typeof e.toJSON?e.toJSON():e})).__toJS()},toKeyedSeq:function(){return new Kt(this,!0)},toMap:function(){return Ve(this.toKeyedSeq())},toObject:function(){ze(this.size);var e={};return this.__iterate((function(t,n){e[n]=t})),e},toOrderedMap:function(){return Ut(this.toKeyedSeq())},toOrderedSet:function(){return Fn(a(this)?this.valueSeq():this)},toSet:function(){return Cn(a(this)?this.valueSeq():this)},toSetSeq:function(){return new Gt(this)},toSeq:function(){return l(this)?this.toIndexedSeq():a(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return zn(a(this)?this.valueSeq():this)},toList:function(){return _t(a(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(e,t){return 0===this.size?e+t:e+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+t},concat:function(){return mn(this,an(this,e.call(arguments,0)))},includes:function(e){return this.some((function(t){return ye(t,e)}))},entries:function(){return this.__iterator(M)},every:function(e,t){ze(this.size);var n=!0;return this.__iterate((function(r,o,s){if(!e.call(t,r,o,s))return n=!1,!1})),n},filter:function(e,t){return mn(this,en(this,e,t,!0))},find:function(e,t,n){var r=this.findEntry(e,t);return r?r[1]:n},forEach:function(e,t){return ze(this.size),this.__iterate(t?e.bind(t):e)},join:function(e){ze(this.size),e=void 0!==e?""+e:",";var t="",n=!0;return this.__iterate((function(r){n?n=!1:t+=e,t+=null!=r?r.toString():""})),t},keys:function(){return this.__iterator(T)},map:function(e,t){return mn(this,Xt(this,e,t))},reduce:function(e,t,n){var r,o;return ze(this.size),arguments.length<2?o=!0:r=t,this.__iterate((function(t,s,i){o?(o=!1,r=t):r=e.call(n,r,t,s,i)})),r},reduceRight:function(e,t,n){var r=this.toKeyedSeq().reverse();return r.reduce.apply(r,arguments)},reverse:function(){return mn(this,Qt(this,!0))},slice:function(e,t){return mn(this,rn(this,e,t,!0))},some:function(e,t){return!this.every(tr(e),t)},sort:function(e){return mn(this,pn(this,e))},values:function(){return this.__iterator(R)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(e,t){return O(e?this.toSeq().filter(e,t):this)},countBy:function(e,t){return tn(this,e,t)},equals:function(e){return ve(this,e)},entrySeq:function(){var e=this;if(e._cache)return new te(e._cache);var t=e.toSeq().map(er).toIndexedSeq();return t.fromEntrySeq=function(){return e.toSeq()},t},filterNot:function(e,t){return this.filter(tr(e),t)},findEntry:function(e,t,n){var r=n;return this.__iterate((function(n,o,s){if(e.call(t,n,o,s))return r=[o,n],!1})),r},findKey:function(e,t){var n=this.findEntry(e,t);return n&&n[0]},findLast:function(e,t,n){return this.toKeyedSeq().reverse().find(e,t,n)},findLastEntry:function(e,t,n){return this.toKeyedSeq().reverse().findEntry(e,t,n)},findLastKey:function(e,t){return this.toKeyedSeq().reverse().findKey(e,t)},first:function(){return this.find(A)},flatMap:function(e,t){return mn(this,cn(this,e,t))},flatten:function(e){return mn(this,ln(this,e,!0))},fromEntrySeq:function(){return new Zt(this)},get:function(e,t){return this.find((function(t,n){return ye(n,e)}),void 0,t)},getIn:function(e,t){for(var n,r=this,o=xn(e);!(n=o.next()).done;){var s=n.value;if((r=r&&r.get?r.get(s,b):b)===b)return t}return r},groupBy:function(e,t){return nn(this,e,t)},has:function(e){return this.get(e,b)!==b},hasIn:function(e){return this.getIn(e,b)!==b},isSubset:function(e){return e="function"==typeof e.includes?e:n(e),this.every((function(t){return e.includes(t)}))},isSuperset:function(e){return(e="function"==typeof e.isSubset?e:n(e)).isSubset(this)},keyOf:function(e){return this.findKey((function(t){return ye(t,e)}))},keySeq:function(){return this.toSeq().map(Qn).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(e){return this.toKeyedSeq().reverse().keyOf(e)},max:function(e){return hn(this,e)},maxBy:function(e,t){return hn(this,t,e)},min:function(e){return hn(this,e?nr(e):sr)},minBy:function(e,t){return hn(this,t?nr(t):sr,e)},rest:function(){return this.slice(1)},skip:function(e){return this.slice(Math.max(0,e))},skipLast:function(e){return mn(this,this.toSeq().reverse().skip(e).reverse())},skipWhile:function(e,t){return mn(this,sn(this,e,t,!0))},skipUntil:function(e,t){return this.skipWhile(tr(e),t)},sortBy:function(e,t){return mn(this,pn(this,t,e))},take:function(e){return this.slice(0,Math.max(0,e))},takeLast:function(e){return mn(this,this.toSeq().reverse().take(e).reverse())},takeWhile:function(e,t){return mn(this,on(this,e,t))},takeUntil:function(e,t){return this.takeWhile(tr(e),t)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=ir(this))}});var Yn=n.prototype;Yn[p]=!0,Yn[L]=Yn.values,Yn.__toJS=Yn.toArray,Yn.__toStringMapper=rr,Yn.inspect=Yn.toSource=function(){return this.toString()},Yn.chain=Yn.flatMap,Yn.contains=Yn.includes,Zn(r,{flip:function(){return mn(this,Yt(this))},mapEntries:function(e,t){var n=this,r=0;return mn(this,this.toSeq().map((function(o,s){return e.call(t,[s,o],r++,n)})).fromEntrySeq())},mapKeys:function(e,t){var n=this;return mn(this,this.toSeq().flip().map((function(r,o){return e.call(t,r,o,n)})).flip())}});var Xn=r.prototype;function Qn(e,t){return t}function er(e,t){return[t,e]}function tr(e){return function(){return!e.apply(this,arguments)}}function nr(e){return function(){return-e.apply(this,arguments)}}function rr(e){return"string"==typeof e?JSON.stringify(e):String(e)}function or(){return j(arguments)}function sr(e,t){return et?-1:0}function ir(e){if(e.size===1/0)return 0;var t=u(e),n=a(e),r=t?1:0;return ar(e.__iterate(n?t?function(e,t){r=31*r+lr(Ae(e),Ae(t))|0}:function(e,t){r=r+lr(Ae(e),Ae(t))|0}:t?function(e){r=31*r+Ae(e)|0}:function(e){r=r+Ae(e)|0}),r)}function ar(e,t){return t=Oe(t,3432918353),t=Oe(t<<15|t>>>-15,461845907),t=Oe(t<<13|t>>>-13,5),t=Oe((t=(t+3864292196|0)^e)^t>>>16,2246822507),t=ke((t=Oe(t^t>>>13,3266489909))^t>>>16)}function lr(e,t){return e^t+2654435769+(e<<6)+(e>>2)|0}return Xn[h]=!0,Xn[L]=Yn.entries,Xn.__toJS=Yn.toObject,Xn.__toStringMapper=function(e,t){return JSON.stringify(t)+": "+rr(e)},Zn(o,{toKeyedSeq:function(){return new Kt(this,!1)},filter:function(e,t){return mn(this,en(this,e,t,!1))},findIndex:function(e,t){var n=this.findEntry(e,t);return n?n[0]:-1},indexOf:function(e){var t=this.keyOf(e);return void 0===t?-1:t},lastIndexOf:function(e){var t=this.lastKeyOf(e);return void 0===t?-1:t},reverse:function(){return mn(this,Qt(this,!1))},slice:function(e,t){return mn(this,rn(this,e,t,!1))},splice:function(e,t){var n=arguments.length;if(t=Math.max(0|t,0),0===n||2===n&&!t)return this;e=P(e,e<0?this.count():this.size);var r=this.slice(0,e);return mn(this,1===n?r:r.concat(j(arguments,2),this.slice(e+t)))},findLastIndex:function(e,t){var n=this.findLastEntry(e,t);return n?n[0]:-1},first:function(){return this.get(0)},flatten:function(e){return mn(this,ln(this,e,!1))},get:function(e,t){return(e=k(this,e))<0||this.size===1/0||void 0!==this.size&&e>this.size?t:this.find((function(t,n){return n===e}),void 0,t)},has:function(e){return(e=k(this,e))>=0&&(void 0!==this.size?this.size===1/0||e{"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},35823:e=>{e.exports=function(e,t,n,r){var o=new Blob(void 0!==r?[r,e]:[e],{type:n||"application/octet-stream"});if(void 0!==window.navigator.msSaveBlob)window.navigator.msSaveBlob(o,t);else{var s=window.URL&&window.URL.createObjectURL?window.URL.createObjectURL(o):window.webkitURL.createObjectURL(o),i=document.createElement("a");i.style.display="none",i.href=s,i.setAttribute("download",t),void 0===i.download&&i.setAttribute("target","_blank"),document.body.appendChild(i),i.click(),setTimeout((function(){document.body.removeChild(i),window.URL.revokeObjectURL(s)}),200)}}},91296:(e,t,n)=>{var r=NaN,o="[object Symbol]",s=/^\s+|\s+$/g,i=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,l=/^0o[0-7]+$/i,c=parseInt,u="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,p="object"==typeof self&&self&&self.Object===Object&&self,h=u||p||Function("return this")(),f=Object.prototype.toString,d=Math.max,m=Math.min,g=function(){return h.Date.now()};function y(e){var t=typeof e;return!!e&&("object"==t||"function"==t)}function v(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&f.call(e)==o}(e))return r;if(y(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=y(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(s,"");var n=a.test(e);return n||l.test(e)?c(e.slice(2),n?2:8):i.test(e)?r:+e}e.exports=function(e,t,n){var r,o,s,i,a,l,c=0,u=!1,p=!1,h=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function f(t){var n=r,s=o;return r=o=void 0,c=t,i=e.apply(s,n)}function b(e){var n=e-l;return void 0===l||n>=t||n<0||p&&e-c>=s}function w(){var e=g();if(b(e))return E(e);a=setTimeout(w,function(e){var n=t-(e-l);return p?m(n,s-(e-c)):n}(e))}function E(e){return a=void 0,h&&r?f(e):(r=o=void 0,i)}function x(){var e=g(),n=b(e);if(r=arguments,o=this,l=e,n){if(void 0===a)return function(e){return c=e,a=setTimeout(w,t),u?f(e):i}(l);if(p)return a=setTimeout(w,t),f(l)}return void 0===a&&(a=setTimeout(w,t)),i}return t=v(t)||0,y(n)&&(u=!!n.leading,s=(p="maxWait"in n)?d(v(n.maxWait)||0,t):s,h="trailing"in n?!!n.trailing:h),x.cancel=function(){void 0!==a&&clearTimeout(a),c=0,r=l=o=a=void 0},x.flush=function(){return void 0===a?i:E(g())},x}},18552:(e,t,n)=>{var r=n(10852)(n(55639),"DataView");e.exports=r},1989:(e,t,n)=>{var r=n(51789),o=n(80401),s=n(57667),i=n(21327),a=n(81866);function l(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(3118),o=n(9435);function s(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=4294967295,this.__views__=[]}s.prototype=r(o.prototype),s.prototype.constructor=s,e.exports=s},38407:(e,t,n)=>{var r=n(27040),o=n(14125),s=n(82117),i=n(67518),a=n(54705);function l(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(3118),o=n(9435);function s(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=void 0}s.prototype=r(o.prototype),s.prototype.constructor=s,e.exports=s},57071:(e,t,n)=>{var r=n(10852)(n(55639),"Map");e.exports=r},83369:(e,t,n)=>{var r=n(24785),o=n(11285),s=n(96e3),i=n(49916),a=n(95265);function l(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t{var r=n(10852)(n(55639),"Promise");e.exports=r},58525:(e,t,n)=>{var r=n(10852)(n(55639),"Set");e.exports=r},88668:(e,t,n)=>{var r=n(83369),o=n(90619),s=n(72385);function i(e){var t=-1,n=null==e?0:e.length;for(this.__data__=new r;++t{var r=n(38407),o=n(37465),s=n(63779),i=n(67599),a=n(44758),l=n(34309);function c(e){var t=this.__data__=new r(e);this.size=t.size}c.prototype.clear=o,c.prototype.delete=s,c.prototype.get=i,c.prototype.has=a,c.prototype.set=l,e.exports=c},62705:(e,t,n)=>{var r=n(55639).Symbol;e.exports=r},11149:(e,t,n)=>{var r=n(55639).Uint8Array;e.exports=r},70577:(e,t,n)=>{var r=n(10852)(n(55639),"WeakMap");e.exports=r},96874:e=>{e.exports=function(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}},77412:e=>{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=0,s=[];++n{var r=n(42118);e.exports=function(e,t){return!!(null==e?0:e.length)&&r(e,t,0)>-1}},14636:(e,t,n)=>{var r=n(22545),o=n(35694),s=n(1469),i=n(44144),a=n(65776),l=n(36719),c=Object.prototype.hasOwnProperty;e.exports=function(e,t){var n=s(e),u=!n&&o(e),p=!n&&!u&&i(e),h=!n&&!u&&!p&&l(e),f=n||u||p||h,d=f?r(e.length,String):[],m=d.length;for(var g in e)!t&&!c.call(e,g)||f&&("length"==g||p&&("offset"==g||"parent"==g)||h&&("buffer"==g||"byteLength"==g||"byteOffset"==g)||a(g,m))||d.push(g);return d}},29932:e=>{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length,o=Array(r);++n{e.exports=function(e,t){for(var n=-1,r=t.length,o=e.length;++n{e.exports=function(e,t,n,r){var o=-1,s=null==e?0:e.length;for(r&&s&&(n=e[++o]);++o{e.exports=function(e,t){for(var n=-1,r=null==e?0:e.length;++n{e.exports=function(e){return e.split("")}},49029:e=>{var t=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;e.exports=function(e){return e.match(t)||[]}},86556:(e,t,n)=>{var r=n(89465),o=n(77813);e.exports=function(e,t,n){(void 0!==n&&!o(e[t],n)||void 0===n&&!(t in e))&&r(e,t,n)}},34865:(e,t,n)=>{var r=n(89465),o=n(77813),s=Object.prototype.hasOwnProperty;e.exports=function(e,t,n){var i=e[t];s.call(e,t)&&o(i,n)&&(void 0!==n||t in e)||r(e,t,n)}},18470:(e,t,n)=>{var r=n(77813);e.exports=function(e,t){for(var n=e.length;n--;)if(r(e[n][0],t))return n;return-1}},44037:(e,t,n)=>{var r=n(98363),o=n(3674);e.exports=function(e,t){return e&&r(t,o(t),e)}},63886:(e,t,n)=>{var r=n(98363),o=n(81704);e.exports=function(e,t){return e&&r(t,o(t),e)}},89465:(e,t,n)=>{var r=n(38777);e.exports=function(e,t,n){"__proto__"==t&&r?r(e,t,{configurable:!0,enumerable:!0,value:n,writable:!0}):e[t]=n}},85990:(e,t,n)=>{var r=n(46384),o=n(77412),s=n(34865),i=n(44037),a=n(63886),l=n(64626),c=n(278),u=n(18805),p=n(1911),h=n(58234),f=n(46904),d=n(98882),m=n(43824),g=n(29148),y=n(38517),v=n(1469),b=n(44144),w=n(56688),E=n(13218),x=n(72928),S=n(3674),_=n(81704),j="[object Arguments]",O="[object Function]",k="[object Object]",A={};A[j]=A["[object Array]"]=A["[object ArrayBuffer]"]=A["[object DataView]"]=A["[object Boolean]"]=A["[object Date]"]=A["[object Float32Array]"]=A["[object Float64Array]"]=A["[object Int8Array]"]=A["[object Int16Array]"]=A["[object Int32Array]"]=A["[object Map]"]=A["[object Number]"]=A[k]=A["[object RegExp]"]=A["[object Set]"]=A["[object String]"]=A["[object Symbol]"]=A["[object Uint8Array]"]=A["[object Uint8ClampedArray]"]=A["[object Uint16Array]"]=A["[object Uint32Array]"]=!0,A["[object Error]"]=A[O]=A["[object WeakMap]"]=!1,e.exports=function e(t,n,C,P,N,I){var T,R=1&n,M=2&n,D=4&n;if(C&&(T=N?C(t,P,N,I):C(t)),void 0!==T)return T;if(!E(t))return t;var F=v(t);if(F){if(T=m(t),!R)return c(t,T)}else{var L=d(t),B=L==O||"[object GeneratorFunction]"==L;if(b(t))return l(t,R);if(L==k||L==j||B&&!N){if(T=M||B?{}:y(t),!R)return M?p(t,a(T,t)):u(t,i(T,t))}else{if(!A[L])return N?t:{};T=g(t,L,R)}}I||(I=new r);var $=I.get(t);if($)return $;I.set(t,T),x(t)?t.forEach((function(r){T.add(e(r,n,C,r,t,I))})):w(t)&&t.forEach((function(r,o){T.set(o,e(r,n,C,o,t,I))}));var q=F?void 0:(D?M?f:h:M?_:S)(t);return o(q||t,(function(r,o){q&&(r=t[o=r]),s(T,o,e(r,n,C,o,t,I))})),T}},3118:(e,t,n)=>{var r=n(13218),o=Object.create,s=function(){function e(){}return function(t){if(!r(t))return{};if(o)return o(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();e.exports=s},89881:(e,t,n)=>{var r=n(47816),o=n(99291)(r);e.exports=o},41848:e=>{e.exports=function(e,t,n,r){for(var o=e.length,s=n+(r?1:-1);r?s--:++s{var r=n(62488),o=n(37285);e.exports=function e(t,n,s,i,a){var l=-1,c=t.length;for(s||(s=o),a||(a=[]);++l0&&s(u)?n>1?e(u,n-1,s,i,a):r(a,u):i||(a[a.length]=u)}return a}},28483:(e,t,n)=>{var r=n(25063)();e.exports=r},47816:(e,t,n)=>{var r=n(28483),o=n(3674);e.exports=function(e,t){return e&&r(e,t,o)}},97786:(e,t,n)=>{var r=n(71811),o=n(40327);e.exports=function(e,t){for(var n=0,s=(t=r(t,e)).length;null!=e&&n{var r=n(62488),o=n(1469);e.exports=function(e,t,n){var s=t(e);return o(e)?s:r(s,n(e))}},44239:(e,t,n)=>{var r=n(62705),o=n(89607),s=n(2333),i=r?r.toStringTag:void 0;e.exports=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":i&&i in Object(e)?o(e):s(e)}},13:e=>{e.exports=function(e,t){return null!=e&&t in Object(e)}},42118:(e,t,n)=>{var r=n(41848),o=n(62722),s=n(42351);e.exports=function(e,t,n){return t==t?s(e,t,n):r(e,o,n)}},9454:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return o(e)&&"[object Arguments]"==r(e)}},90939:(e,t,n)=>{var r=n(2492),o=n(37005);e.exports=function e(t,n,s,i,a){return t===n||(null==t||null==n||!o(t)&&!o(n)?t!=t&&n!=n:r(t,n,s,i,e,a))}},2492:(e,t,n)=>{var r=n(46384),o=n(67114),s=n(18351),i=n(16096),a=n(98882),l=n(1469),c=n(44144),u=n(36719),p="[object Arguments]",h="[object Array]",f="[object Object]",d=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,m,g,y){var v=l(e),b=l(t),w=v?h:a(e),E=b?h:a(t),x=(w=w==p?f:w)==f,S=(E=E==p?f:E)==f,_=w==E;if(_&&c(e)){if(!c(t))return!1;v=!0,x=!1}if(_&&!x)return y||(y=new r),v||u(e)?o(e,t,n,m,g,y):s(e,t,w,n,m,g,y);if(!(1&n)){var j=x&&d.call(e,"__wrapped__"),O=S&&d.call(t,"__wrapped__");if(j||O){var k=j?e.value():e,A=O?t.value():t;return y||(y=new r),g(k,A,n,m,y)}}return!!_&&(y||(y=new r),i(e,t,n,m,g,y))}},25588:(e,t,n)=>{var r=n(98882),o=n(37005);e.exports=function(e){return o(e)&&"[object Map]"==r(e)}},2958:(e,t,n)=>{var r=n(46384),o=n(90939);e.exports=function(e,t,n,s){var i=n.length,a=i,l=!s;if(null==e)return!a;for(e=Object(e);i--;){var c=n[i];if(l&&c[2]?c[1]!==e[c[0]]:!(c[0]in e))return!1}for(;++i{e.exports=function(e){return e!=e}},28458:(e,t,n)=>{var r=n(23560),o=n(15346),s=n(13218),i=n(80346),a=/^\[object .+?Constructor\]$/,l=Function.prototype,c=Object.prototype,u=l.toString,p=c.hasOwnProperty,h=RegExp("^"+u.call(p).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");e.exports=function(e){return!(!s(e)||o(e))&&(r(e)?h:a).test(i(e))}},29221:(e,t,n)=>{var r=n(98882),o=n(37005);e.exports=function(e){return o(e)&&"[object Set]"==r(e)}},38749:(e,t,n)=>{var r=n(44239),o=n(41780),s=n(37005),i={};i["[object Float32Array]"]=i["[object Float64Array]"]=i["[object Int8Array]"]=i["[object Int16Array]"]=i["[object Int32Array]"]=i["[object Uint8Array]"]=i["[object Uint8ClampedArray]"]=i["[object Uint16Array]"]=i["[object Uint32Array]"]=!0,i["[object Arguments]"]=i["[object Array]"]=i["[object ArrayBuffer]"]=i["[object Boolean]"]=i["[object DataView]"]=i["[object Date]"]=i["[object Error]"]=i["[object Function]"]=i["[object Map]"]=i["[object Number]"]=i["[object Object]"]=i["[object RegExp]"]=i["[object Set]"]=i["[object String]"]=i["[object WeakMap]"]=!1,e.exports=function(e){return s(e)&&o(e.length)&&!!i[r(e)]}},67206:(e,t,n)=>{var r=n(91573),o=n(16432),s=n(6557),i=n(1469),a=n(39601);e.exports=function(e){return"function"==typeof e?e:null==e?s:"object"==typeof e?i(e)?o(e[0],e[1]):r(e):a(e)}},280:(e,t,n)=>{var r=n(25726),o=n(86916),s=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return o(e);var t=[];for(var n in Object(e))s.call(e,n)&&"constructor"!=n&&t.push(n);return t}},10313:(e,t,n)=>{var r=n(13218),o=n(25726),s=n(33498),i=Object.prototype.hasOwnProperty;e.exports=function(e){if(!r(e))return s(e);var t=o(e),n=[];for(var a in e)("constructor"!=a||!t&&i.call(e,a))&&n.push(a);return n}},9435:e=>{e.exports=function(){}},91573:(e,t,n)=>{var r=n(2958),o=n(1499),s=n(42634);e.exports=function(e){var t=o(e);return 1==t.length&&t[0][2]?s(t[0][0],t[0][1]):function(n){return n===e||r(n,e,t)}}},16432:(e,t,n)=>{var r=n(90939),o=n(27361),s=n(79095),i=n(15403),a=n(89162),l=n(42634),c=n(40327);e.exports=function(e,t){return i(e)&&a(t)?l(c(e),t):function(n){var i=o(n,e);return void 0===i&&i===t?s(n,e):r(t,i,3)}}},42980:(e,t,n)=>{var r=n(46384),o=n(86556),s=n(28483),i=n(59783),a=n(13218),l=n(81704),c=n(36390);e.exports=function e(t,n,u,p,h){t!==n&&s(n,(function(s,l){if(h||(h=new r),a(s))i(t,n,l,u,e,p,h);else{var f=p?p(c(t,l),s,l+"",t,n,h):void 0;void 0===f&&(f=s),o(t,l,f)}}),l)}},59783:(e,t,n)=>{var r=n(86556),o=n(64626),s=n(77133),i=n(278),a=n(38517),l=n(35694),c=n(1469),u=n(29246),p=n(44144),h=n(23560),f=n(13218),d=n(68630),m=n(36719),g=n(36390),y=n(59881);e.exports=function(e,t,n,v,b,w,E){var x=g(e,n),S=g(t,n),_=E.get(S);if(_)r(e,n,_);else{var j=w?w(x,S,n+"",e,t,E):void 0,O=void 0===j;if(O){var k=c(S),A=!k&&p(S),C=!k&&!A&&m(S);j=S,k||A||C?c(x)?j=x:u(x)?j=i(x):A?(O=!1,j=o(S,!0)):C?(O=!1,j=s(S,!0)):j=[]:d(S)||l(S)?(j=x,l(x)?j=y(x):f(x)&&!h(x)||(j=a(S))):O=!1}O&&(E.set(S,j),b(j,S,v,w,E),E.delete(S)),r(e,n,j)}}},40371:e=>{e.exports=function(e){return function(t){return null==t?void 0:t[e]}}},79152:(e,t,n)=>{var r=n(97786);e.exports=function(e){return function(t){return r(t,e)}}},18674:e=>{e.exports=function(e){return function(t){return null==e?void 0:e[t]}}},10107:e=>{e.exports=function(e,t,n,r,o){return o(e,(function(e,o,s){n=r?(r=!1,e):t(n,e,o,s)})),n}},5976:(e,t,n)=>{var r=n(6557),o=n(45357),s=n(30061);e.exports=function(e,t){return s(o(e,t,r),e+"")}},10611:(e,t,n)=>{var r=n(34865),o=n(71811),s=n(65776),i=n(13218),a=n(40327);e.exports=function(e,t,n,l){if(!i(e))return e;for(var c=-1,u=(t=o(t,e)).length,p=u-1,h=e;null!=h&&++c{var r=n(6557),o=n(89250),s=o?function(e,t){return o.set(e,t),e}:r;e.exports=s},56560:(e,t,n)=>{var r=n(75703),o=n(38777),s=n(6557),i=o?function(e,t){return o(e,"toString",{configurable:!0,enumerable:!1,value:r(t),writable:!0})}:s;e.exports=i},14259:e=>{e.exports=function(e,t,n){var r=-1,o=e.length;t<0&&(t=-t>o?0:o+t),(n=n>o?o:n)<0&&(n+=o),o=t>n?0:n-t>>>0,t>>>=0;for(var s=Array(o);++r{var r=n(89881);e.exports=function(e,t){var n;return r(e,(function(e,r,o){return!(n=t(e,r,o))})),!!n}},22545:e=>{e.exports=function(e,t){for(var n=-1,r=Array(e);++n{var r=n(62705),o=n(29932),s=n(1469),i=n(33448),a=r?r.prototype:void 0,l=a?a.toString:void 0;e.exports=function e(t){if("string"==typeof t)return t;if(s(t))return o(t,e)+"";if(i(t))return l?l.call(t):"";var n=t+"";return"0"==n&&1/t==-Infinity?"-0":n}},27561:(e,t,n)=>{var r=n(67990),o=/^\s+/;e.exports=function(e){return e?e.slice(0,r(e)+1).replace(o,""):e}},7518:e=>{e.exports=function(e){return function(t){return e(t)}}},57406:(e,t,n)=>{var r=n(71811),o=n(10928),s=n(40292),i=n(40327);e.exports=function(e,t){return t=r(t,e),null==(e=s(e,t))||delete e[i(o(t))]}},1757:e=>{e.exports=function(e,t,n){for(var r=-1,o=e.length,s=t.length,i={};++r{e.exports=function(e,t){return e.has(t)}},71811:(e,t,n)=>{var r=n(1469),o=n(15403),s=n(55514),i=n(79833);e.exports=function(e,t){return r(e)?e:o(e,t)?[e]:s(i(e))}},40180:(e,t,n)=>{var r=n(14259);e.exports=function(e,t,n){var o=e.length;return n=void 0===n?o:n,!t&&n>=o?e:r(e,t,n)}},74318:(e,t,n)=>{var r=n(11149);e.exports=function(e){var t=new e.constructor(e.byteLength);return new r(t).set(new r(e)),t}},64626:(e,t,n)=>{e=n.nmd(e);var r=n(55639),o=t&&!t.nodeType&&t,s=o&&e&&!e.nodeType&&e,i=s&&s.exports===o?r.Buffer:void 0,a=i?i.allocUnsafe:void 0;e.exports=function(e,t){if(t)return e.slice();var n=e.length,r=a?a(n):new e.constructor(n);return e.copy(r),r}},57157:(e,t,n)=>{var r=n(74318);e.exports=function(e,t){var n=t?r(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}},93147:e=>{var t=/\w*$/;e.exports=function(e){var n=new e.constructor(e.source,t.exec(e));return n.lastIndex=e.lastIndex,n}},40419:(e,t,n)=>{var r=n(62705),o=r?r.prototype:void 0,s=o?o.valueOf:void 0;e.exports=function(e){return s?Object(s.call(e)):{}}},77133:(e,t,n)=>{var r=n(74318);e.exports=function(e,t){var n=t?r(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}},52157:e=>{var t=Math.max;e.exports=function(e,n,r,o){for(var s=-1,i=e.length,a=r.length,l=-1,c=n.length,u=t(i-a,0),p=Array(c+u),h=!o;++l{var t=Math.max;e.exports=function(e,n,r,o){for(var s=-1,i=e.length,a=-1,l=r.length,c=-1,u=n.length,p=t(i-l,0),h=Array(p+u),f=!o;++s{e.exports=function(e,t){var n=-1,r=e.length;for(t||(t=Array(r));++n{var r=n(34865),o=n(89465);e.exports=function(e,t,n,s){var i=!n;n||(n={});for(var a=-1,l=t.length;++a{var r=n(98363),o=n(99551);e.exports=function(e,t){return r(e,o(e),t)}},1911:(e,t,n)=>{var r=n(98363),o=n(51442);e.exports=function(e,t){return r(e,o(e),t)}},14429:(e,t,n)=>{var r=n(55639)["__core-js_shared__"];e.exports=r},97991:e=>{e.exports=function(e,t){for(var n=e.length,r=0;n--;)e[n]===t&&++r;return r}},21463:(e,t,n)=>{var r=n(5976),o=n(16612);e.exports=function(e){return r((function(t,n){var r=-1,s=n.length,i=s>1?n[s-1]:void 0,a=s>2?n[2]:void 0;for(i=e.length>3&&"function"==typeof i?(s--,i):void 0,a&&o(n[0],n[1],a)&&(i=s<3?void 0:i,s=1),t=Object(t);++r{var r=n(98612);e.exports=function(e,t){return function(n,o){if(null==n)return n;if(!r(n))return e(n,o);for(var s=n.length,i=t?s:-1,a=Object(n);(t?i--:++i{e.exports=function(e){return function(t,n,r){for(var o=-1,s=Object(t),i=r(t),a=i.length;a--;){var l=i[e?a:++o];if(!1===n(s[l],l,s))break}return t}}},22402:(e,t,n)=>{var r=n(71774),o=n(55639);e.exports=function(e,t,n){var s=1&t,i=r(e);return function t(){return(this&&this!==o&&this instanceof t?i:e).apply(s?n:this,arguments)}}},98805:(e,t,n)=>{var r=n(40180),o=n(62689),s=n(83140),i=n(79833);e.exports=function(e){return function(t){t=i(t);var n=o(t)?s(t):void 0,a=n?n[0]:t.charAt(0),l=n?r(n,1).join(""):t.slice(1);return a[e]()+l}}},35393:(e,t,n)=>{var r=n(62663),o=n(53816),s=n(58748),i=RegExp("['’]","g");e.exports=function(e){return function(t){return r(s(o(t).replace(i,"")),e,"")}}},71774:(e,t,n)=>{var r=n(3118),o=n(13218);e.exports=function(e){return function(){var t=arguments;switch(t.length){case 0:return new e;case 1:return new e(t[0]);case 2:return new e(t[0],t[1]);case 3:return new e(t[0],t[1],t[2]);case 4:return new e(t[0],t[1],t[2],t[3]);case 5:return new e(t[0],t[1],t[2],t[3],t[4]);case 6:return new e(t[0],t[1],t[2],t[3],t[4],t[5]);case 7:return new e(t[0],t[1],t[2],t[3],t[4],t[5],t[6])}var n=r(e.prototype),s=e.apply(n,t);return o(s)?s:n}}},46347:(e,t,n)=>{var r=n(96874),o=n(71774),s=n(86935),i=n(94487),a=n(20893),l=n(46460),c=n(55639);e.exports=function(e,t,n){var u=o(e);return function o(){for(var p=arguments.length,h=Array(p),f=p,d=a(o);f--;)h[f]=arguments[f];var m=p<3&&h[0]!==d&&h[p-1]!==d?[]:l(h,d);return(p-=m.length){var r=n(67206),o=n(98612),s=n(3674);e.exports=function(e){return function(t,n,i){var a=Object(t);if(!o(t)){var l=r(n,3);t=s(t),n=function(e){return l(a[e],e,a)}}var c=e(t,n,i);return c>-1?a[l?t[c]:c]:void 0}}},86935:(e,t,n)=>{var r=n(52157),o=n(14054),s=n(97991),i=n(71774),a=n(94487),l=n(20893),c=n(90451),u=n(46460),p=n(55639);e.exports=function e(t,n,h,f,d,m,g,y,v,b){var w=128&n,E=1&n,x=2&n,S=24&n,_=512&n,j=x?void 0:i(t);return function O(){for(var k=arguments.length,A=Array(k),C=k;C--;)A[C]=arguments[C];if(S)var P=l(O),N=s(A,P);if(f&&(A=r(A,f,d,S)),m&&(A=o(A,m,g,S)),k-=N,S&&k1&&A.reverse(),w&&v{var r=n(96874),o=n(71774),s=n(55639);e.exports=function(e,t,n,i){var a=1&t,l=o(e);return function t(){for(var o=-1,c=arguments.length,u=-1,p=i.length,h=Array(p+c),f=this&&this!==s&&this instanceof t?l:e;++u{var r=n(86528),o=n(258),s=n(69255);e.exports=function(e,t,n,i,a,l,c,u,p,h){var f=8&t;t|=f?32:64,4&(t&=~(f?64:32))||(t&=-4);var d=[e,t,a,f?l:void 0,f?c:void 0,f?void 0:l,f?void 0:c,u,p,h],m=n.apply(void 0,d);return r(e)&&o(m,d),m.placeholder=i,s(m,e,t)}},97727:(e,t,n)=>{var r=n(28045),o=n(22402),s=n(46347),i=n(86935),a=n(84375),l=n(66833),c=n(63833),u=n(258),p=n(69255),h=n(40554),f=Math.max;e.exports=function(e,t,n,d,m,g,y,v){var b=2&t;if(!b&&"function"!=typeof e)throw new TypeError("Expected a function");var w=d?d.length:0;if(w||(t&=-97,d=m=void 0),y=void 0===y?y:f(h(y),0),v=void 0===v?v:h(v),w-=m?m.length:0,64&t){var E=d,x=m;d=m=void 0}var S=b?void 0:l(e),_=[e,t,n,d,m,E,x,g,y,v];if(S&&c(_,S),e=_[0],t=_[1],n=_[2],d=_[3],m=_[4],!(v=_[9]=void 0===_[9]?b?0:e.length:f(_[9]-w,0))&&24&t&&(t&=-25),t&&1!=t)j=8==t||16==t?s(e,t,v):32!=t&&33!=t||m.length?i.apply(void 0,_):a(e,t,n,d);else var j=o(e,t,n);return p((S?r:u)(j,_),e,t)}},60696:(e,t,n)=>{var r=n(68630);e.exports=function(e){return r(e)?void 0:e}},69389:(e,t,n)=>{var r=n(18674)({À:"A",Á:"A",Â:"A",Ã:"A",Ä:"A",Å:"A",à:"a",á:"a",â:"a",ã:"a",ä:"a",å:"a",Ç:"C",ç:"c",Ð:"D",ð:"d",È:"E",É:"E",Ê:"E",Ë:"E",è:"e",é:"e",ê:"e",ë:"e",Ì:"I",Í:"I",Î:"I",Ï:"I",ì:"i",í:"i",î:"i",ï:"i",Ñ:"N",ñ:"n",Ò:"O",Ó:"O",Ô:"O",Õ:"O",Ö:"O",Ø:"O",ò:"o",ó:"o",ô:"o",õ:"o",ö:"o",ø:"o",Ù:"U",Ú:"U",Û:"U",Ü:"U",ù:"u",ú:"u",û:"u",ü:"u",Ý:"Y",ý:"y",ÿ:"y",Æ:"Ae",æ:"ae",Þ:"Th",þ:"th",ß:"ss",Ā:"A",Ă:"A",Ą:"A",ā:"a",ă:"a",ą:"a",Ć:"C",Ĉ:"C",Ċ:"C",Č:"C",ć:"c",ĉ:"c",ċ:"c",č:"c",Ď:"D",Đ:"D",ď:"d",đ:"d",Ē:"E",Ĕ:"E",Ė:"E",Ę:"E",Ě:"E",ē:"e",ĕ:"e",ė:"e",ę:"e",ě:"e",Ĝ:"G",Ğ:"G",Ġ:"G",Ģ:"G",ĝ:"g",ğ:"g",ġ:"g",ģ:"g",Ĥ:"H",Ħ:"H",ĥ:"h",ħ:"h",Ĩ:"I",Ī:"I",Ĭ:"I",Į:"I",İ:"I",ĩ:"i",ī:"i",ĭ:"i",į:"i",ı:"i",Ĵ:"J",ĵ:"j",Ķ:"K",ķ:"k",ĸ:"k",Ĺ:"L",Ļ:"L",Ľ:"L",Ŀ:"L",Ł:"L",ĺ:"l",ļ:"l",ľ:"l",ŀ:"l",ł:"l",Ń:"N",Ņ:"N",Ň:"N",Ŋ:"N",ń:"n",ņ:"n",ň:"n",ŋ:"n",Ō:"O",Ŏ:"O",Ő:"O",ō:"o",ŏ:"o",ő:"o",Ŕ:"R",Ŗ:"R",Ř:"R",ŕ:"r",ŗ:"r",ř:"r",Ś:"S",Ŝ:"S",Ş:"S",Š:"S",ś:"s",ŝ:"s",ş:"s",š:"s",Ţ:"T",Ť:"T",Ŧ:"T",ţ:"t",ť:"t",ŧ:"t",Ũ:"U",Ū:"U",Ŭ:"U",Ů:"U",Ű:"U",Ų:"U",ũ:"u",ū:"u",ŭ:"u",ů:"u",ű:"u",ų:"u",Ŵ:"W",ŵ:"w",Ŷ:"Y",ŷ:"y",Ÿ:"Y",Ź:"Z",Ż:"Z",Ž:"Z",ź:"z",ż:"z",ž:"z",IJ:"IJ",ij:"ij",Œ:"Oe",œ:"oe",ʼn:"'n",ſ:"s"});e.exports=r},38777:(e,t,n)=>{var r=n(10852),o=function(){try{var e=r(Object,"defineProperty");return e({},"",{}),e}catch(e){}}();e.exports=o},67114:(e,t,n)=>{var r=n(88668),o=n(82908),s=n(74757);e.exports=function(e,t,n,i,a,l){var c=1&n,u=e.length,p=t.length;if(u!=p&&!(c&&p>u))return!1;var h=l.get(e),f=l.get(t);if(h&&f)return h==t&&f==e;var d=-1,m=!0,g=2&n?new r:void 0;for(l.set(e,t),l.set(t,e);++d{var r=n(62705),o=n(11149),s=n(77813),i=n(67114),a=n(68776),l=n(21814),c=r?r.prototype:void 0,u=c?c.valueOf:void 0;e.exports=function(e,t,n,r,c,p,h){switch(n){case"[object DataView]":if(e.byteLength!=t.byteLength||e.byteOffset!=t.byteOffset)return!1;e=e.buffer,t=t.buffer;case"[object ArrayBuffer]":return!(e.byteLength!=t.byteLength||!p(new o(e),new o(t)));case"[object Boolean]":case"[object Date]":case"[object Number]":return s(+e,+t);case"[object Error]":return e.name==t.name&&e.message==t.message;case"[object RegExp]":case"[object String]":return e==t+"";case"[object Map]":var f=a;case"[object Set]":var d=1&r;if(f||(f=l),e.size!=t.size&&!d)return!1;var m=h.get(e);if(m)return m==t;r|=2,h.set(e,t);var g=i(f(e),f(t),r,c,p,h);return h.delete(e),g;case"[object Symbol]":if(u)return u.call(e)==u.call(t)}return!1}},16096:(e,t,n)=>{var r=n(58234),o=Object.prototype.hasOwnProperty;e.exports=function(e,t,n,s,i,a){var l=1&n,c=r(e),u=c.length;if(u!=r(t).length&&!l)return!1;for(var p=u;p--;){var h=c[p];if(!(l?h in t:o.call(t,h)))return!1}var f=a.get(e),d=a.get(t);if(f&&d)return f==t&&d==e;var m=!0;a.set(e,t),a.set(t,e);for(var g=l;++p{var r=n(85564),o=n(45357),s=n(30061);e.exports=function(e){return s(o(e,void 0,r),e+"")}},31957:(e,t,n)=>{var r="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g;e.exports=r},58234:(e,t,n)=>{var r=n(68866),o=n(99551),s=n(3674);e.exports=function(e){return r(e,s,o)}},46904:(e,t,n)=>{var r=n(68866),o=n(51442),s=n(81704);e.exports=function(e){return r(e,s,o)}},66833:(e,t,n)=>{var r=n(89250),o=n(50308),s=r?function(e){return r.get(e)}:o;e.exports=s},97658:(e,t,n)=>{var r=n(52060),o=Object.prototype.hasOwnProperty;e.exports=function(e){for(var t=e.name+"",n=r[t],s=o.call(r,t)?n.length:0;s--;){var i=n[s],a=i.func;if(null==a||a==e)return i.name}return t}},20893:e=>{e.exports=function(e){return e.placeholder}},45050:(e,t,n)=>{var r=n(37019);e.exports=function(e,t){var n=e.__data__;return r(t)?n["string"==typeof t?"string":"hash"]:n.map}},1499:(e,t,n)=>{var r=n(89162),o=n(3674);e.exports=function(e){for(var t=o(e),n=t.length;n--;){var s=t[n],i=e[s];t[n]=[s,i,r(i)]}return t}},10852:(e,t,n)=>{var r=n(28458),o=n(47801);e.exports=function(e,t){var n=o(e,t);return r(n)?n:void 0}},85924:(e,t,n)=>{var r=n(5569)(Object.getPrototypeOf,Object);e.exports=r},89607:(e,t,n)=>{var r=n(62705),o=Object.prototype,s=o.hasOwnProperty,i=o.toString,a=r?r.toStringTag:void 0;e.exports=function(e){var t=s.call(e,a),n=e[a];try{e[a]=void 0;var r=!0}catch(e){}var o=i.call(e);return r&&(t?e[a]=n:delete e[a]),o}},99551:(e,t,n)=>{var r=n(34963),o=n(70479),s=Object.prototype.propertyIsEnumerable,i=Object.getOwnPropertySymbols,a=i?function(e){return null==e?[]:(e=Object(e),r(i(e),(function(t){return s.call(e,t)})))}:o;e.exports=a},51442:(e,t,n)=>{var r=n(62488),o=n(85924),s=n(99551),i=n(70479),a=Object.getOwnPropertySymbols?function(e){for(var t=[];e;)r(t,s(e)),e=o(e);return t}:i;e.exports=a},98882:(e,t,n)=>{var r=n(18552),o=n(57071),s=n(53818),i=n(58525),a=n(70577),l=n(44239),c=n(80346),u="[object Map]",p="[object Promise]",h="[object Set]",f="[object WeakMap]",d="[object DataView]",m=c(r),g=c(o),y=c(s),v=c(i),b=c(a),w=l;(r&&w(new r(new ArrayBuffer(1)))!=d||o&&w(new o)!=u||s&&w(s.resolve())!=p||i&&w(new i)!=h||a&&w(new a)!=f)&&(w=function(e){var t=l(e),n="[object Object]"==t?e.constructor:void 0,r=n?c(n):"";if(r)switch(r){case m:return d;case g:return u;case y:return p;case v:return h;case b:return f}return t}),e.exports=w},47801:e=>{e.exports=function(e,t){return null==e?void 0:e[t]}},58775:e=>{var t=/\{\n\/\* \[wrapped with (.+)\] \*/,n=/,? & /;e.exports=function(e){var r=e.match(t);return r?r[1].split(n):[]}},222:(e,t,n)=>{var r=n(71811),o=n(35694),s=n(1469),i=n(65776),a=n(41780),l=n(40327);e.exports=function(e,t,n){for(var c=-1,u=(t=r(t,e)).length,p=!1;++c{var t=RegExp("[\\u200d\\ud800-\\udfff\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\ufe0e\\ufe0f]");e.exports=function(e){return t.test(e)}},93157:e=>{var t=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;e.exports=function(e){return t.test(e)}},51789:(e,t,n)=>{var r=n(94536);e.exports=function(){this.__data__=r?r(null):{},this.size=0}},80401:e=>{e.exports=function(e){var t=this.has(e)&&delete this.__data__[e];return this.size-=t?1:0,t}},57667:(e,t,n)=>{var r=n(94536),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;if(r){var n=t[e];return"__lodash_hash_undefined__"===n?void 0:n}return o.call(t,e)?t[e]:void 0}},21327:(e,t,n)=>{var r=n(94536),o=Object.prototype.hasOwnProperty;e.exports=function(e){var t=this.__data__;return r?void 0!==t[e]:o.call(t,e)}},81866:(e,t,n)=>{var r=n(94536);e.exports=function(e,t){var n=this.__data__;return this.size+=this.has(e)?0:1,n[e]=r&&void 0===t?"__lodash_hash_undefined__":t,this}},43824:e=>{var t=Object.prototype.hasOwnProperty;e.exports=function(e){var n=e.length,r=new e.constructor(n);return n&&"string"==typeof e[0]&&t.call(e,"index")&&(r.index=e.index,r.input=e.input),r}},29148:(e,t,n)=>{var r=n(74318),o=n(57157),s=n(93147),i=n(40419),a=n(77133);e.exports=function(e,t,n){var l=e.constructor;switch(t){case"[object ArrayBuffer]":return r(e);case"[object Boolean]":case"[object Date]":return new l(+e);case"[object DataView]":return o(e,n);case"[object Float32Array]":case"[object Float64Array]":case"[object Int8Array]":case"[object Int16Array]":case"[object Int32Array]":case"[object Uint8Array]":case"[object Uint8ClampedArray]":case"[object Uint16Array]":case"[object Uint32Array]":return a(e,n);case"[object Map]":case"[object Set]":return new l;case"[object Number]":case"[object String]":return new l(e);case"[object RegExp]":return s(e);case"[object Symbol]":return i(e)}}},38517:(e,t,n)=>{var r=n(3118),o=n(85924),s=n(25726);e.exports=function(e){return"function"!=typeof e.constructor||s(e)?{}:r(o(e))}},83112:e=>{var t=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/;e.exports=function(e,n){var r=n.length;if(!r)return e;var o=r-1;return n[o]=(r>1?"& ":"")+n[o],n=n.join(r>2?", ":" "),e.replace(t,"{\n/* [wrapped with "+n+"] */\n")}},37285:(e,t,n)=>{var r=n(62705),o=n(35694),s=n(1469),i=r?r.isConcatSpreadable:void 0;e.exports=function(e){return s(e)||o(e)||!!(i&&e&&e[i])}},65776:e=>{var t=/^(?:0|[1-9]\d*)$/;e.exports=function(e,n){var r=typeof e;return!!(n=null==n?9007199254740991:n)&&("number"==r||"symbol"!=r&&t.test(e))&&e>-1&&e%1==0&&e{var r=n(77813),o=n(98612),s=n(65776),i=n(13218);e.exports=function(e,t,n){if(!i(n))return!1;var a=typeof t;return!!("number"==a?o(n)&&s(t,n.length):"string"==a&&t in n)&&r(n[t],e)}},15403:(e,t,n)=>{var r=n(1469),o=n(33448),s=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,i=/^\w*$/;e.exports=function(e,t){if(r(e))return!1;var n=typeof e;return!("number"!=n&&"symbol"!=n&&"boolean"!=n&&null!=e&&!o(e))||(i.test(e)||!s.test(e)||null!=t&&e in Object(t))}},37019:e=>{e.exports=function(e){var t=typeof e;return"string"==t||"number"==t||"symbol"==t||"boolean"==t?"__proto__"!==e:null===e}},86528:(e,t,n)=>{var r=n(96425),o=n(66833),s=n(97658),i=n(8111);e.exports=function(e){var t=s(e),n=i[t];if("function"!=typeof n||!(t in r.prototype))return!1;if(e===n)return!0;var a=o(n);return!!a&&e===a[0]}},15346:(e,t,n)=>{var r,o=n(14429),s=(r=/[^.]+$/.exec(o&&o.keys&&o.keys.IE_PROTO||""))?"Symbol(src)_1."+r:"";e.exports=function(e){return!!s&&s in e}},25726:e=>{var t=Object.prototype;e.exports=function(e){var n=e&&e.constructor;return e===("function"==typeof n&&n.prototype||t)}},89162:(e,t,n)=>{var r=n(13218);e.exports=function(e){return e==e&&!r(e)}},27040:e=>{e.exports=function(){this.__data__=[],this.size=0}},14125:(e,t,n)=>{var r=n(18470),o=Array.prototype.splice;e.exports=function(e){var t=this.__data__,n=r(t,e);return!(n<0)&&(n==t.length-1?t.pop():o.call(t,n,1),--this.size,!0)}},82117:(e,t,n)=>{var r=n(18470);e.exports=function(e){var t=this.__data__,n=r(t,e);return n<0?void 0:t[n][1]}},67518:(e,t,n)=>{var r=n(18470);e.exports=function(e){return r(this.__data__,e)>-1}},54705:(e,t,n)=>{var r=n(18470);e.exports=function(e,t){var n=this.__data__,o=r(n,e);return o<0?(++this.size,n.push([e,t])):n[o][1]=t,this}},24785:(e,t,n)=>{var r=n(1989),o=n(38407),s=n(57071);e.exports=function(){this.size=0,this.__data__={hash:new r,map:new(s||o),string:new r}}},11285:(e,t,n)=>{var r=n(45050);e.exports=function(e){var t=r(this,e).delete(e);return this.size-=t?1:0,t}},96e3:(e,t,n)=>{var r=n(45050);e.exports=function(e){return r(this,e).get(e)}},49916:(e,t,n)=>{var r=n(45050);e.exports=function(e){return r(this,e).has(e)}},95265:(e,t,n)=>{var r=n(45050);e.exports=function(e,t){var n=r(this,e),o=n.size;return n.set(e,t),this.size+=n.size==o?0:1,this}},68776:e=>{e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}},42634:e=>{e.exports=function(e,t){return function(n){return null!=n&&(n[e]===t&&(void 0!==t||e in Object(n)))}}},24523:(e,t,n)=>{var r=n(88306);e.exports=function(e){var t=r(e,(function(e){return 500===n.size&&n.clear(),e})),n=t.cache;return t}},63833:(e,t,n)=>{var r=n(52157),o=n(14054),s=n(46460),i="__lodash_placeholder__",a=128,l=Math.min;e.exports=function(e,t){var n=e[1],c=t[1],u=n|c,p=u<131,h=c==a&&8==n||c==a&&256==n&&e[7].length<=t[8]||384==c&&t[7].length<=t[8]&&8==n;if(!p&&!h)return e;1&c&&(e[2]=t[2],u|=1&n?0:4);var f=t[3];if(f){var d=e[3];e[3]=d?r(d,f,t[4]):f,e[4]=d?s(e[3],i):t[4]}return(f=t[5])&&(d=e[5],e[5]=d?o(d,f,t[6]):f,e[6]=d?s(e[5],i):t[6]),(f=t[7])&&(e[7]=f),c&a&&(e[8]=null==e[8]?t[8]:l(e[8],t[8])),null==e[9]&&(e[9]=t[9]),e[0]=t[0],e[1]=u,e}},89250:(e,t,n)=>{var r=n(70577),o=r&&new r;e.exports=o},94536:(e,t,n)=>{var r=n(10852)(Object,"create");e.exports=r},86916:(e,t,n)=>{var r=n(5569)(Object.keys,Object);e.exports=r},33498:e=>{e.exports=function(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}},31167:(e,t,n)=>{e=n.nmd(e);var r=n(31957),o=t&&!t.nodeType&&t,s=o&&e&&!e.nodeType&&e,i=s&&s.exports===o&&r.process,a=function(){try{var e=s&&s.require&&s.require("util").types;return e||i&&i.binding&&i.binding("util")}catch(e){}}();e.exports=a},2333:e=>{var t=Object.prototype.toString;e.exports=function(e){return t.call(e)}},5569:e=>{e.exports=function(e,t){return function(n){return e(t(n))}}},45357:(e,t,n)=>{var r=n(96874),o=Math.max;e.exports=function(e,t,n){return t=o(void 0===t?e.length-1:t,0),function(){for(var s=arguments,i=-1,a=o(s.length-t,0),l=Array(a);++i{var r=n(97786),o=n(14259);e.exports=function(e,t){return t.length<2?e:r(e,o(t,0,-1))}},52060:e=>{e.exports={}},90451:(e,t,n)=>{var r=n(278),o=n(65776),s=Math.min;e.exports=function(e,t){for(var n=e.length,i=s(t.length,n),a=r(e);i--;){var l=t[i];e[i]=o(l,n)?a[l]:void 0}return e}},46460:e=>{var t="__lodash_placeholder__";e.exports=function(e,n){for(var r=-1,o=e.length,s=0,i=[];++r{var r=n(31957),o="object"==typeof self&&self&&self.Object===Object&&self,s=r||o||Function("return this")();e.exports=s},36390:e=>{e.exports=function(e,t){if(("constructor"!==t||"function"!=typeof e[t])&&"__proto__"!=t)return e[t]}},90619:e=>{e.exports=function(e){return this.__data__.set(e,"__lodash_hash_undefined__"),this}},72385:e=>{e.exports=function(e){return this.__data__.has(e)}},258:(e,t,n)=>{var r=n(28045),o=n(21275)(r);e.exports=o},21814:e=>{e.exports=function(e){var t=-1,n=Array(e.size);return e.forEach((function(e){n[++t]=e})),n}},30061:(e,t,n)=>{var r=n(56560),o=n(21275)(r);e.exports=o},69255:(e,t,n)=>{var r=n(58775),o=n(83112),s=n(30061),i=n(87241);e.exports=function(e,t,n){var a=t+"";return s(e,o(a,i(r(a),n)))}},21275:e=>{var t=Date.now;e.exports=function(e){var n=0,r=0;return function(){var o=t(),s=16-(o-r);if(r=o,s>0){if(++n>=800)return arguments[0]}else n=0;return e.apply(void 0,arguments)}}},37465:(e,t,n)=>{var r=n(38407);e.exports=function(){this.__data__=new r,this.size=0}},63779:e=>{e.exports=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n}},67599:e=>{e.exports=function(e){return this.__data__.get(e)}},44758:e=>{e.exports=function(e){return this.__data__.has(e)}},34309:(e,t,n)=>{var r=n(38407),o=n(57071),s=n(83369);e.exports=function(e,t){var n=this.__data__;if(n instanceof r){var i=n.__data__;if(!o||i.length<199)return i.push([e,t]),this.size=++n.size,this;n=this.__data__=new s(i)}return n.set(e,t),this.size=n.size,this}},42351:e=>{e.exports=function(e,t,n){for(var r=n-1,o=e.length;++r{var r=n(44286),o=n(62689),s=n(676);e.exports=function(e){return o(e)?s(e):r(e)}},55514:(e,t,n)=>{var r=n(24523),o=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,s=/\\(\\)?/g,i=r((function(e){var t=[];return 46===e.charCodeAt(0)&&t.push(""),e.replace(o,(function(e,n,r,o){t.push(r?o.replace(s,"$1"):n||e)})),t}));e.exports=i},40327:(e,t,n)=>{var r=n(33448);e.exports=function(e){if("string"==typeof e||r(e))return e;var t=e+"";return"0"==t&&1/e==-Infinity?"-0":t}},80346:e=>{var t=Function.prototype.toString;e.exports=function(e){if(null!=e){try{return t.call(e)}catch(e){}try{return e+""}catch(e){}}return""}},67990:e=>{var t=/\s/;e.exports=function(e){for(var n=e.length;n--&&t.test(e.charAt(n)););return n}},676:e=>{var t="\\ud800-\\udfff",n="["+t+"]",r="[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]",o="\\ud83c[\\udffb-\\udfff]",s="[^"+t+"]",i="(?:\\ud83c[\\udde6-\\uddff]){2}",a="[\\ud800-\\udbff][\\udc00-\\udfff]",l="(?:"+r+"|"+o+")"+"?",c="[\\ufe0e\\ufe0f]?",u=c+l+("(?:\\u200d(?:"+[s,i,a].join("|")+")"+c+l+")*"),p="(?:"+[s+r+"?",r,i,a,n].join("|")+")",h=RegExp(o+"(?="+o+")|"+p+u,"g");e.exports=function(e){return e.match(h)||[]}},2757:e=>{var t="\\ud800-\\udfff",n="\\u2700-\\u27bf",r="a-z\\xdf-\\xf6\\xf8-\\xff",o="A-Z\\xc0-\\xd6\\xd8-\\xde",s="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",i="["+s+"]",a="\\d+",l="["+n+"]",c="["+r+"]",u="[^"+t+s+a+n+r+o+"]",p="(?:\\ud83c[\\udde6-\\uddff]){2}",h="[\\ud800-\\udbff][\\udc00-\\udfff]",f="["+o+"]",d="(?:"+c+"|"+u+")",m="(?:"+f+"|"+u+")",g="(?:['’](?:d|ll|m|re|s|t|ve))?",y="(?:['’](?:D|LL|M|RE|S|T|VE))?",v="(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?",b="[\\ufe0e\\ufe0f]?",w=b+v+("(?:\\u200d(?:"+["[^"+t+"]",p,h].join("|")+")"+b+v+")*"),E="(?:"+[l,p,h].join("|")+")"+w,x=RegExp([f+"?"+c+"+"+g+"(?="+[i,f,"$"].join("|")+")",m+"+"+y+"(?="+[i,f+d,"$"].join("|")+")",f+"?"+d+"+"+g,f+"+"+y,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",a,E].join("|"),"g");e.exports=function(e){return e.match(x)||[]}},87241:(e,t,n)=>{var r=n(77412),o=n(47443),s=[["ary",128],["bind",1],["bindKey",2],["curry",8],["curryRight",16],["flip",512],["partial",32],["partialRight",64],["rearg",256]];e.exports=function(e,t){return r(s,(function(n){var r="_."+n[0];t&n[1]&&!o(e,r)&&e.push(r)})),e.sort()}},21913:(e,t,n)=>{var r=n(96425),o=n(7548),s=n(278);e.exports=function(e){if(e instanceof r)return e.clone();var t=new o(e.__wrapped__,e.__chain__);return t.__actions__=s(e.__actions__),t.__index__=e.__index__,t.__values__=e.__values__,t}},39514:(e,t,n)=>{var r=n(97727);e.exports=function(e,t,n){return t=n?void 0:t,t=e&&null==t?e.length:t,r(e,128,void 0,void 0,void 0,void 0,t)}},68929:(e,t,n)=>{var r=n(48403),o=n(35393)((function(e,t,n){return t=t.toLowerCase(),e+(n?r(t):t)}));e.exports=o},48403:(e,t,n)=>{var r=n(79833),o=n(11700);e.exports=function(e){return o(r(e).toLowerCase())}},66678:(e,t,n)=>{var r=n(85990);e.exports=function(e){return r(e,4)}},75703:e=>{e.exports=function(e){return function(){return e}}},40087:(e,t,n)=>{var r=n(97727);function o(e,t,n){var s=r(e,8,void 0,void 0,void 0,void 0,void 0,t=n?void 0:t);return s.placeholder=o.placeholder,s}o.placeholder={},e.exports=o},23279:(e,t,n)=>{var r=n(13218),o=n(7771),s=n(14841),i=Math.max,a=Math.min;e.exports=function(e,t,n){var l,c,u,p,h,f,d=0,m=!1,g=!1,y=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function v(t){var n=l,r=c;return l=c=void 0,d=t,p=e.apply(r,n)}function b(e){var n=e-f;return void 0===f||n>=t||n<0||g&&e-d>=u}function w(){var e=o();if(b(e))return E(e);h=setTimeout(w,function(e){var n=t-(e-f);return g?a(n,u-(e-d)):n}(e))}function E(e){return h=void 0,y&&l?v(e):(l=c=void 0,p)}function x(){var e=o(),n=b(e);if(l=arguments,c=this,f=e,n){if(void 0===h)return function(e){return d=e,h=setTimeout(w,t),m?v(e):p}(f);if(g)return clearTimeout(h),h=setTimeout(w,t),v(f)}return void 0===h&&(h=setTimeout(w,t)),p}return t=s(t)||0,r(n)&&(m=!!n.leading,u=(g="maxWait"in n)?i(s(n.maxWait)||0,t):u,y="trailing"in n?!!n.trailing:y),x.cancel=function(){void 0!==h&&clearTimeout(h),d=0,l=f=c=h=void 0},x.flush=function(){return void 0===h?p:E(o())},x}},53816:(e,t,n)=>{var r=n(69389),o=n(79833),s=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,i=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]","g");e.exports=function(e){return(e=o(e))&&e.replace(s,r).replace(i,"")}},77813:e=>{e.exports=function(e,t){return e===t||e!=e&&t!=t}},13311:(e,t,n)=>{var r=n(67740)(n(30998));e.exports=r},30998:(e,t,n)=>{var r=n(41848),o=n(67206),s=n(40554),i=Math.max;e.exports=function(e,t,n){var a=null==e?0:e.length;if(!a)return-1;var l=null==n?0:s(n);return l<0&&(l=i(a+l,0)),r(e,o(t,3),l)}},85564:(e,t,n)=>{var r=n(21078);e.exports=function(e){return(null==e?0:e.length)?r(e,1):[]}},84599:(e,t,n)=>{var r=n(68836),o=n(69306),s=Array.prototype.push;function i(e,t){return 2==t?function(t,n){return e(t,n)}:function(t){return e(t)}}function a(e){for(var t=e?e.length:0,n=Array(t);t--;)n[t]=e[t];return n}function l(e,t){return function(){var n=arguments.length;if(n){for(var r=Array(n);n--;)r[n]=arguments[n];var o=r[0]=t.apply(void 0,r);return e.apply(void 0,r),o}}}e.exports=function e(t,n,c,u){var p="function"==typeof n,h=n===Object(n);if(h&&(u=c,c=n,n=void 0),null==c)throw new TypeError;u||(u={});var f={cap:!("cap"in u)||u.cap,curry:!("curry"in u)||u.curry,fixed:!("fixed"in u)||u.fixed,immutable:!("immutable"in u)||u.immutable,rearg:!("rearg"in u)||u.rearg},d=p?c:o,m="curry"in u&&u.curry,g="fixed"in u&&u.fixed,y="rearg"in u&&u.rearg,v=p?c.runInContext():void 0,b=p?c:{ary:t.ary,assign:t.assign,clone:t.clone,curry:t.curry,forEach:t.forEach,isArray:t.isArray,isError:t.isError,isFunction:t.isFunction,isWeakMap:t.isWeakMap,iteratee:t.iteratee,keys:t.keys,rearg:t.rearg,toInteger:t.toInteger,toPath:t.toPath},w=b.ary,E=b.assign,x=b.clone,S=b.curry,_=b.forEach,j=b.isArray,O=b.isError,k=b.isFunction,A=b.isWeakMap,C=b.keys,P=b.rearg,N=b.toInteger,I=b.toPath,T=C(r.aryMethod),R={castArray:function(e){return function(){var t=arguments[0];return j(t)?e(a(t)):e.apply(void 0,arguments)}},iteratee:function(e){return function(){var t=arguments[1],n=e(arguments[0],t),r=n.length;return f.cap&&"number"==typeof t?(t=t>2?t-2:1,r&&r<=t?n:i(n,t)):n}},mixin:function(e){return function(t){var n=this;if(!k(n))return e(n,Object(t));var r=[];return _(C(t),(function(e){k(t[e])&&r.push([e,n.prototype[e]])})),e(n,Object(t)),_(r,(function(e){var t=e[1];k(t)?n.prototype[e[0]]=t:delete n.prototype[e[0]]})),n}},nthArg:function(e){return function(t){var n=t<0?1:N(t)+1;return S(e(t),n)}},rearg:function(e){return function(t,n){var r=n?n.length:0;return S(e(t,n),r)}},runInContext:function(n){return function(r){return e(t,n(r),u)}}};function M(e,t){if(f.cap){var n=r.iterateeRearg[e];if(n)return function(e,t){return $(e,(function(e){var n=t.length;return function(e,t){return 2==t?function(t,n){return e.apply(void 0,arguments)}:function(t){return e.apply(void 0,arguments)}}(P(i(e,n),t),n)}))}(t,n);var o=!p&&r.iterateeAry[e];if(o)return function(e,t){return $(e,(function(e){return"function"==typeof e?i(e,t):e}))}(t,o)}return t}function D(e,t,n){if(f.fixed&&(g||!r.skipFixed[e])){var o=r.methodSpread[e],i=o&&o.start;return void 0===i?w(t,n):function(e,t){return function(){for(var n=arguments.length,r=n-1,o=Array(n);n--;)o[n]=arguments[n];var i=o[t],a=o.slice(0,t);return i&&s.apply(a,i),t!=r&&s.apply(a,o.slice(t+1)),e.apply(this,a)}}(t,i)}return t}function F(e,t,n){return f.rearg&&n>1&&(y||!r.skipRearg[e])?P(t,r.methodRearg[e]||r.aryRearg[n]):t}function L(e,t){for(var n=-1,r=(t=I(t)).length,o=r-1,s=x(Object(e)),i=s;null!=i&&++n1?S(t,n):t}(0,o=M(s,o),e),!1}})),!o})),o||(o=i),o==t&&(o=m?S(o,1):function(){return t.apply(this,arguments)}),o.convert=B(s,t),o.placeholder=t.placeholder=n,o}if(!h)return q(n,c,d);var U=c,z=[];return _(T,(function(e){_(r.aryMethod[e],(function(e){var t=U[r.remap[e]||e];t&&z.push([e,q(e,t,U)])}))})),_(C(U),(function(e){var t=U[e];if("function"==typeof t){for(var n=z.length;n--;)if(z[n][0]==e)return;t.convert=B(e,t),z.push([e,t])}})),_(z,(function(e){U[e[0]]=e[1]})),U.convert=function(e){return U.runInContext.convert(e)(void 0)},U.placeholder=U,_(C(U),(function(e){_(r.realToAlias[e]||[],(function(t){U[t]=U[e]}))})),U}},68836:(e,t)=>{t.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},t.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},t.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},t.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},t.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},t.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},t.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},t.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},t.realToAlias=function(){var e=Object.prototype.hasOwnProperty,n=t.aliasToReal,r={};for(var o in n){var s=n[o];e.call(r,s)?r[s].push(o):r[s]=[o]}return r}(),t.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},t.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},t.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},4269:(e,t,n)=>{e.exports={ary:n(39514),assign:n(44037),clone:n(66678),curry:n(40087),forEach:n(77412),isArray:n(1469),isError:n(64647),isFunction:n(23560),isWeakMap:n(81018),iteratee:n(72594),keys:n(280),rearg:n(4963),toInteger:n(40554),toPath:n(30084)}},72700:(e,t,n)=>{e.exports=n(28252)},92822:(e,t,n)=>{var r=n(84599),o=n(4269);e.exports=function(e,t,n){return r(o,e,t,n)}},69306:e=>{e.exports={}},28252:(e,t,n)=>{var r=n(92822)("set",n(36968));r.placeholder=n(69306),e.exports=r},27361:(e,t,n)=>{var r=n(97786);e.exports=function(e,t,n){var o=null==e?void 0:r(e,t);return void 0===o?n:o}},79095:(e,t,n)=>{var r=n(13),o=n(222);e.exports=function(e,t){return null!=e&&o(e,t,r)}},6557:e=>{e.exports=function(e){return e}},35694:(e,t,n)=>{var r=n(9454),o=n(37005),s=Object.prototype,i=s.hasOwnProperty,a=s.propertyIsEnumerable,l=r(function(){return arguments}())?r:function(e){return o(e)&&i.call(e,"callee")&&!a.call(e,"callee")};e.exports=l},1469:e=>{var t=Array.isArray;e.exports=t},98612:(e,t,n)=>{var r=n(23560),o=n(41780);e.exports=function(e){return null!=e&&o(e.length)&&!r(e)}},29246:(e,t,n)=>{var r=n(98612),o=n(37005);e.exports=function(e){return o(e)&&r(e)}},51584:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return!0===e||!1===e||o(e)&&"[object Boolean]"==r(e)}},44144:(e,t,n)=>{e=n.nmd(e);var r=n(55639),o=n(95062),s=t&&!t.nodeType&&t,i=s&&e&&!e.nodeType&&e,a=i&&i.exports===s?r.Buffer:void 0,l=(a?a.isBuffer:void 0)||o;e.exports=l},41609:(e,t,n)=>{var r=n(280),o=n(98882),s=n(35694),i=n(1469),a=n(98612),l=n(44144),c=n(25726),u=n(36719),p=Object.prototype.hasOwnProperty;e.exports=function(e){if(null==e)return!0;if(a(e)&&(i(e)||"string"==typeof e||"function"==typeof e.splice||l(e)||u(e)||s(e)))return!e.length;var t=o(e);if("[object Map]"==t||"[object Set]"==t)return!e.size;if(c(e))return!r(e).length;for(var n in e)if(p.call(e,n))return!1;return!0}},18446:(e,t,n)=>{var r=n(90939);e.exports=function(e,t){return r(e,t)}},64647:(e,t,n)=>{var r=n(44239),o=n(37005),s=n(68630);e.exports=function(e){if(!o(e))return!1;var t=r(e);return"[object Error]"==t||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!s(e)}},23560:(e,t,n)=>{var r=n(44239),o=n(13218);e.exports=function(e){if(!o(e))return!1;var t=r(e);return"[object Function]"==t||"[object GeneratorFunction]"==t||"[object AsyncFunction]"==t||"[object Proxy]"==t}},41780:e=>{e.exports=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=9007199254740991}},56688:(e,t,n)=>{var r=n(25588),o=n(7518),s=n(31167),i=s&&s.isMap,a=i?o(i):r;e.exports=a},45220:e=>{e.exports=function(e){return null===e}},81763:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return"number"==typeof e||o(e)&&"[object Number]"==r(e)}},13218:e=>{e.exports=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}},37005:e=>{e.exports=function(e){return null!=e&&"object"==typeof e}},68630:(e,t,n)=>{var r=n(44239),o=n(85924),s=n(37005),i=Function.prototype,a=Object.prototype,l=i.toString,c=a.hasOwnProperty,u=l.call(Object);e.exports=function(e){if(!s(e)||"[object Object]"!=r(e))return!1;var t=o(e);if(null===t)return!0;var n=c.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&l.call(n)==u}},72928:(e,t,n)=>{var r=n(29221),o=n(7518),s=n(31167),i=s&&s.isSet,a=i?o(i):r;e.exports=a},47037:(e,t,n)=>{var r=n(44239),o=n(1469),s=n(37005);e.exports=function(e){return"string"==typeof e||!o(e)&&s(e)&&"[object String]"==r(e)}},33448:(e,t,n)=>{var r=n(44239),o=n(37005);e.exports=function(e){return"symbol"==typeof e||o(e)&&"[object Symbol]"==r(e)}},36719:(e,t,n)=>{var r=n(38749),o=n(7518),s=n(31167),i=s&&s.isTypedArray,a=i?o(i):r;e.exports=a},81018:(e,t,n)=>{var r=n(98882),o=n(37005);e.exports=function(e){return o(e)&&"[object WeakMap]"==r(e)}},72594:(e,t,n)=>{var r=n(85990),o=n(67206);e.exports=function(e){return o("function"==typeof e?e:r(e,1))}},3674:(e,t,n)=>{var r=n(14636),o=n(280),s=n(98612);e.exports=function(e){return s(e)?r(e):o(e)}},81704:(e,t,n)=>{var r=n(14636),o=n(10313),s=n(98612);e.exports=function(e){return s(e)?r(e,!0):o(e)}},10928:e=>{e.exports=function(e){var t=null==e?0:e.length;return t?e[t-1]:void 0}},88306:(e,t,n)=>{var r=n(83369);function o(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new TypeError("Expected a function");var n=function(){var r=arguments,o=t?t.apply(this,r):r[0],s=n.cache;if(s.has(o))return s.get(o);var i=e.apply(this,r);return n.cache=s.set(o,i)||s,i};return n.cache=new(o.Cache||r),n}o.Cache=r,e.exports=o},82492:(e,t,n)=>{var r=n(42980),o=n(21463)((function(e,t,n){r(e,t,n)}));e.exports=o},94885:e=>{e.exports=function(e){if("function"!=typeof e)throw new TypeError("Expected a function");return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}},50308:e=>{e.exports=function(){}},7771:(e,t,n)=>{var r=n(55639);e.exports=function(){return r.Date.now()}},57557:(e,t,n)=>{var r=n(29932),o=n(85990),s=n(57406),i=n(71811),a=n(98363),l=n(60696),c=n(99021),u=n(46904),p=c((function(e,t){var n={};if(null==e)return n;var c=!1;t=r(t,(function(t){return t=i(t,e),c||(c=t.length>1),t})),a(e,u(e),n),c&&(n=o(n,7,l));for(var p=t.length;p--;)s(n,t[p]);return n}));e.exports=p},39601:(e,t,n)=>{var r=n(40371),o=n(79152),s=n(15403),i=n(40327);e.exports=function(e){return s(e)?r(i(e)):o(e)}},4963:(e,t,n)=>{var r=n(97727),o=n(99021),s=o((function(e,t){return r(e,256,void 0,void 0,void 0,t)}));e.exports=s},54061:(e,t,n)=>{var r=n(62663),o=n(89881),s=n(67206),i=n(10107),a=n(1469);e.exports=function(e,t,n){var l=a(e)?r:i,c=arguments.length<3;return l(e,s(t,4),n,c,o)}},36968:(e,t,n)=>{var r=n(10611);e.exports=function(e,t,n){return null==e?e:r(e,t,n)}},59704:(e,t,n)=>{var r=n(82908),o=n(67206),s=n(5076),i=n(1469),a=n(16612);e.exports=function(e,t,n){var l=i(e)?r:s;return n&&a(e,t,n)&&(t=void 0),l(e,o(t,3))}},70479:e=>{e.exports=function(){return[]}},95062:e=>{e.exports=function(){return!1}},18601:(e,t,n)=>{var r=n(14841),o=1/0;e.exports=function(e){return e?(e=r(e))===o||e===-1/0?17976931348623157e292*(e<0?-1:1):e==e?e:0:0===e?e:0}},40554:(e,t,n)=>{var r=n(18601);e.exports=function(e){var t=r(e),n=t%1;return t==t?n?t-n:t:0}},7334:(e,t,n)=>{var r=n(79833);e.exports=function(e){return r(e).toLowerCase()}},14841:(e,t,n)=>{var r=n(27561),o=n(13218),s=n(33448),i=/^[-+]0x[0-9a-f]+$/i,a=/^0b[01]+$/i,l=/^0o[0-7]+$/i,c=parseInt;e.exports=function(e){if("number"==typeof e)return e;if(s(e))return NaN;if(o(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=o(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=r(e);var n=a.test(e);return n||l.test(e)?c(e.slice(2),n?2:8):i.test(e)?NaN:+e}},30084:(e,t,n)=>{var r=n(29932),o=n(278),s=n(1469),i=n(33448),a=n(55514),l=n(40327),c=n(79833);e.exports=function(e){return s(e)?r(e,l):i(e)?[e]:o(a(c(e)))}},59881:(e,t,n)=>{var r=n(98363),o=n(81704);e.exports=function(e){return r(e,o(e))}},79833:(e,t,n)=>{var r=n(80531);e.exports=function(e){return null==e?"":r(e)}},11700:(e,t,n)=>{var r=n(98805)("toUpperCase");e.exports=r},58748:(e,t,n)=>{var r=n(49029),o=n(93157),s=n(79833),i=n(2757);e.exports=function(e,t,n){return e=s(e),void 0===(t=n?void 0:t)?o(e)?i(e):r(e):e.match(t)||[]}},8111:(e,t,n)=>{var r=n(96425),o=n(7548),s=n(9435),i=n(1469),a=n(37005),l=n(21913),c=Object.prototype.hasOwnProperty;function u(e){if(a(e)&&!i(e)&&!(e instanceof r)){if(e instanceof o)return e;if(c.call(e,"__wrapped__"))return l(e)}return new o(e)}u.prototype=s.prototype,u.prototype.constructor=u,e.exports=u},7287:(e,t,n)=>{var r=n(34865),o=n(1757);e.exports=function(e,t){return o(e||[],t||[],r)}},96470:(e,t,n)=>{"use strict";var r=n(47802),o=n(21102);t.highlight=i,t.highlightAuto=function(e,t){var n,a,l,c,u=t||{},p=u.subset||r.listLanguages(),h=u.prefix,f=p.length,d=-1;null==h&&(h=s);if("string"!=typeof e)throw o("Expected `string` for value, got `%s`",e);a={relevance:0,language:null,value:[]},n={relevance:0,language:null,value:[]};for(;++da.relevance&&(a=l),l.relevance>n.relevance&&(a=n,n=l));a.language&&(n.secondBest=a);return n},t.registerLanguage=function(e,t){r.registerLanguage(e,t)},t.listLanguages=function(){return r.listLanguages()},t.registerAlias=function(e,t){var n,o=e;t&&((o={})[e]=t);for(n in o)r.registerAliases(o[n],{languageName:n})},a.prototype.addText=function(e){var t,n,r=this.stack;if(""===e)return;t=r[r.length-1],(n=t.children[t.children.length-1])&&"text"===n.type?n.value+=e:t.children.push({type:"text",value:e})},a.prototype.addKeyword=function(e,t){this.openNode(t),this.addText(e),this.closeNode()},a.prototype.addSublanguage=function(e,t){var n=this.stack,r=n[n.length-1],o=e.rootNode.children,s=t?{type:"element",tagName:"span",properties:{className:[t]},children:o}:o;r.children=r.children.concat(s)},a.prototype.openNode=function(e){var t=this.stack,n=this.options.classPrefix+e,r=t[t.length-1],o={type:"element",tagName:"span",properties:{className:[n]},children:[]};r.children.push(o),t.push(o)},a.prototype.closeNode=function(){this.stack.pop()},a.prototype.closeAllNodes=l,a.prototype.finalize=l,a.prototype.toHTML=function(){return""};var s="hljs-";function i(e,t,n){var i,l=r.configure({}),c=(n||{}).prefix;if("string"!=typeof e)throw o("Expected `string` for name, got `%s`",e);if(!r.getLanguage(e))throw o("Unknown language: `%s` is not registered",e);if("string"!=typeof t)throw o("Expected `string` for value, got `%s`",t);if(null==c&&(c=s),r.configure({__emitter:a,classPrefix:c}),i=r.highlight(t,{language:e,ignoreIllegals:!0}),r.configure(l||{}),i.errorRaised)throw i.errorRaised;return{relevance:i.relevance,language:i.language,value:i.emitter.rootNode.children}}function a(e){this.options=e,this.rootNode={children:[]},this.stack=[this.rootNode]}function l(){}},42566:(e,t,n)=>{const r=n(94885);function o(e){return"string"==typeof e?t=>t.element===e:e.constructor&&e.extend?t=>t instanceof e:e}class s{constructor(e){this.elements=e||[]}toValue(){return this.elements.map((e=>e.toValue()))}map(e,t){return this.elements.map(e,t)}flatMap(e,t){return this.map(e,t).reduce(((e,t)=>e.concat(t)),[])}compactMap(e,t){const n=[];return this.forEach((r=>{const o=e.bind(t)(r);o&&n.push(o)})),n}filter(e,t){return e=o(e),new s(this.elements.filter(e,t))}reject(e,t){return e=o(e),new s(this.elements.filter(r(e),t))}find(e,t){return e=o(e),this.elements.find(e,t)}forEach(e,t){this.elements.forEach(e,t)}reduce(e,t){return this.elements.reduce(e,t)}includes(e){return this.elements.some((t=>t.equals(e)))}shift(){return this.elements.shift()}unshift(e){this.elements.unshift(this.refract(e))}push(e){return this.elements.push(this.refract(e)),this}add(e){this.push(e)}get(e){return this.elements[e]}getValue(e){const t=this.elements[e];if(t)return t.toValue()}get length(){return this.elements.length}get isEmpty(){return 0===this.elements.length}get first(){return this.elements[0]}}"undefined"!=typeof Symbol&&(s.prototype[Symbol.iterator]=function(){return this.elements[Symbol.iterator]()}),e.exports=s},17645:e=>{class t{constructor(e,t){this.key=e,this.value=t}clone(){const e=new t;return this.key&&(e.key=this.key.clone()),this.value&&(e.value=this.value.clone()),e}}e.exports=t},78520:(e,t,n)=>{const r=n(45220),o=n(47037),s=n(81763),i=n(51584),a=n(13218),l=n(28219),c=n(99829);class u{constructor(e){this.elementMap={},this.elementDetection=[],this.Element=c.Element,this.KeyValuePair=c.KeyValuePair,e&&e.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(e){return e.namespace&&e.namespace({base:this}),e.load&&e.load({base:this}),this}useDefault(){return this.register("null",c.NullElement).register("string",c.StringElement).register("number",c.NumberElement).register("boolean",c.BooleanElement).register("array",c.ArrayElement).register("object",c.ObjectElement).register("member",c.MemberElement).register("ref",c.RefElement).register("link",c.LinkElement),this.detect(r,c.NullElement,!1).detect(o,c.StringElement,!1).detect(s,c.NumberElement,!1).detect(i,c.BooleanElement,!1).detect(Array.isArray,c.ArrayElement,!1).detect(a,c.ObjectElement,!1),this}register(e,t){return this._elements=void 0,this.elementMap[e]=t,this}unregister(e){return this._elements=void 0,delete this.elementMap[e],this}detect(e,t,n){return void 0===n||n?this.elementDetection.unshift([e,t]):this.elementDetection.push([e,t]),this}toElement(e){if(e instanceof this.Element)return e;let t;for(let n=0;n{const t=e[0].toUpperCase()+e.substr(1);this._elements[t]=this.elementMap[e]}))),this._elements}get serialiser(){return new l(this)}}l.prototype.Namespace=u,e.exports=u},87526:(e,t,n)=>{const r=n(94885),o=n(42566);class s extends o{map(e,t){return this.elements.map((n=>e.bind(t)(n.value,n.key,n)))}filter(e,t){return new s(this.elements.filter((n=>e.bind(t)(n.value,n.key,n))))}reject(e,t){return this.filter(r(e.bind(t)))}forEach(e,t){return this.elements.forEach(((n,r)=>{e.bind(t)(n.value,n.key,n,r)}))}keys(){return this.map(((e,t)=>t.toValue()))}values(){return this.map((e=>e.toValue()))}}e.exports=s},99829:(e,t,n)=>{const r=n(3079),o=n(96295),s=n(16036),i=n(91090),a=n(18866),l=n(35804),c=n(5946),u=n(76735),p=n(59964),h=n(38588),f=n(42566),d=n(87526),m=n(17645);function g(e){if(e instanceof r)return e;if("string"==typeof e)return new s(e);if("number"==typeof e)return new i(e);if("boolean"==typeof e)return new a(e);if(null===e)return new o;if(Array.isArray(e))return new l(e.map(g));if("object"==typeof e){return new u(e)}return e}r.prototype.ObjectElement=u,r.prototype.RefElement=h,r.prototype.MemberElement=c,r.prototype.refract=g,f.prototype.refract=g,e.exports={Element:r,NullElement:o,StringElement:s,NumberElement:i,BooleanElement:a,ArrayElement:l,MemberElement:c,ObjectElement:u,LinkElement:p,RefElement:h,refract:g,ArraySlice:f,ObjectSlice:d,KeyValuePair:m}},59964:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e||[],t,n),this.element="link"}get relation(){return this.attributes.get("relation")}set relation(e){this.attributes.set("relation",e)}get href(){return this.attributes.get("href")}set href(e){this.attributes.set("href",e)}}},38588:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e||[],t,n),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(e){this.attributes.set("path",e)}}},43500:(e,t,n)=>{const r=n(78520),o=n(99829);t.lS=r,n(17645),t.O4=o.ArraySlice,o.ObjectSlice,t.W_=o.Element,t.RP=o.StringElement,t.VL=o.NumberElement,t.hh=o.BooleanElement,t.zr=o.NullElement,t.ON=o.ArrayElement,t.Sb=o.ObjectElement,t.c6=o.MemberElement,t.tK=o.RefElement,t.EA=o.LinkElement,t.Qc=o.refract,n(28219),n(3414)},35804:(e,t,n)=>{const r=n(94885),o=n(3079),s=n(42566);class i extends o{constructor(e,t,n){super(e||[],t,n),this.element="array"}primitive(){return"array"}get(e){return this.content[e]}getValue(e){const t=this.get(e);if(t)return t.toValue()}getIndex(e){return this.content[e]}set(e,t){return this.content[e]=this.refract(t),this}remove(e){const t=this.content.splice(e,1);return t.length?t[0]:null}map(e,t){return this.content.map(e,t)}flatMap(e,t){return this.map(e,t).reduce(((e,t)=>e.concat(t)),[])}compactMap(e,t){const n=[];return this.forEach((r=>{const o=e.bind(t)(r);o&&n.push(o)})),n}filter(e,t){return new s(this.content.filter(e,t))}reject(e,t){return this.filter(r(e),t)}reduce(e,t){let n,r;void 0!==t?(n=0,r=this.refract(t)):(n=1,r="object"===this.primitive()?this.first.value:this.first);for(let t=n;t{e.bind(t)(n,this.refract(r))}))}shift(){return this.content.shift()}unshift(e){this.content.unshift(this.refract(e))}push(e){return this.content.push(this.refract(e)),this}add(e){this.push(e)}findElements(e,t){const n=t||{},r=!!n.recursive,o=void 0===n.results?[]:n.results;return this.forEach(((t,n,s)=>{r&&void 0!==t.findElements&&t.findElements(e,{results:o,recursive:r}),e(t,n,s)&&o.push(t)})),o}find(e){return new s(this.findElements(e,{recursive:!0}))}findByElement(e){return this.find((t=>t.element===e))}findByClass(e){return this.find((t=>t.classes.includes(e)))}getById(e){return this.find((t=>t.id.toValue()===e)).first}includes(e){return this.content.some((t=>t.equals(e)))}contains(e){return this.includes(e)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(e){return new this.constructor(this.content.concat(e.content))}"fantasy-land/concat"(e){return this.concat(e)}"fantasy-land/map"(e){return new this.constructor(this.map(e))}"fantasy-land/chain"(e){return this.map((t=>e(t)),this).reduce(((e,t)=>e.concat(t)),this.empty())}"fantasy-land/filter"(e){return new this.constructor(this.content.filter(e))}"fantasy-land/reduce"(e,t){return this.content.reduce(e,t)}get length(){return this.content.length}get isEmpty(){return 0===this.content.length}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}i.empty=function(){return new this},i["fantasy-land/empty"]=i.empty,"undefined"!=typeof Symbol&&(i.prototype[Symbol.iterator]=function(){return this.content[Symbol.iterator]()}),e.exports=i},18866:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e,t,n),this.element="boolean"}primitive(){return"boolean"}}},3079:(e,t,n)=>{const r=n(18446),o=n(17645),s=n(42566);class i{constructor(e,t,n){t&&(this.meta=t),n&&(this.attributes=n),this.content=e}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this,this.meta.freeze()),this._attributes&&(this.attributes.parent=this,this.attributes.freeze()),this.children.forEach((e=>{e.parent=this,e.freeze()}),this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const e=new this.constructor;return e.element=this.element,this.meta.length&&(e._meta=this.meta.clone()),this.attributes.length&&(e._attributes=this.attributes.clone()),this.content?this.content.clone?e.content=this.content.clone():Array.isArray(this.content)?e.content=this.content.map((e=>e.clone())):e.content=this.content:e.content=this.content,e}toValue(){return this.content instanceof i?this.content.toValue():this.content instanceof o?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map((e=>e.toValue()),this):this.content}toRef(e){if(""===this.id.toValue())throw Error("Cannot create reference to an element that does not contain an ID");const t=new this.RefElement(this.id.toValue());return e&&(t.path=e),t}findRecursive(...e){if(arguments.length>1&&!this.isFrozen)throw new Error("Cannot find recursive with multiple element names without first freezing the element. Call `element.freeze()`");const t=e.pop();let n=new s;const r=(e,t)=>(e.push(t),e),i=(e,n)=>{n.element===t&&e.push(n);const s=n.findRecursive(t);return s&&s.reduce(r,e),n.content instanceof o&&(n.content.key&&i(e,n.content.key),n.content.value&&i(e,n.content.value)),e};return this.content&&(this.content.element&&i(n,this.content),Array.isArray(this.content)&&this.content.reduce(i,n)),e.isEmpty||(n=n.filter((t=>{let n=t.parents.map((e=>e.element));for(const t in e){const r=e[t],o=n.indexOf(r);if(-1===o)return!1;n=n.splice(0,o)}return!0}))),n}set(e){return this.content=e,this}equals(e){return r(this.toValue(),e)}getMetaProperty(e,t){if(!this.meta.hasKey(e)){if(this.isFrozen){const e=this.refract(t);return e.freeze(),e}this.meta.set(e,t)}return this.meta.get(e)}setMetaProperty(e,t){this.meta.set(e,t)}get element(){return this._storedElement||"element"}set element(e){this._storedElement=e}get content(){return this._content}set content(e){if(e instanceof i)this._content=e;else if(e instanceof s)this.content=e.elements;else if("string"==typeof e||"number"==typeof e||"boolean"==typeof e||"null"===e||null==e)this._content=e;else if(e instanceof o)this._content=e;else if(Array.isArray(e))this._content=e.map(this.refract);else{if("object"!=typeof e)throw new Error("Cannot set content to given value");this._content=Object.keys(e).map((t=>new this.MemberElement(t,e[t])))}}get meta(){if(!this._meta){if(this.isFrozen){const e=new this.ObjectElement;return e.freeze(),e}this._meta=new this.ObjectElement}return this._meta}set meta(e){e instanceof this.ObjectElement?this._meta=e:this.meta.set(e||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const e=new this.ObjectElement;return e.freeze(),e}this._attributes=new this.ObjectElement}return this._attributes}set attributes(e){e instanceof this.ObjectElement?this._attributes=e:this.attributes.set(e||{})}get id(){return this.getMetaProperty("id","")}set id(e){this.setMetaProperty("id",e)}get classes(){return this.getMetaProperty("classes",[])}set classes(e){this.setMetaProperty("classes",e)}get title(){return this.getMetaProperty("title","")}set title(e){this.setMetaProperty("title",e)}get description(){return this.getMetaProperty("description","")}set description(e){this.setMetaProperty("description",e)}get links(){return this.getMetaProperty("links",[])}set links(e){this.setMetaProperty("links",e)}get isFrozen(){return Object.isFrozen(this)}get parents(){let{parent:e}=this;const t=new s;for(;e;)t.push(e),e=e.parent;return t}get children(){if(Array.isArray(this.content))return new s(this.content);if(this.content instanceof o){const e=new s([this.content.key]);return this.content.value&&e.push(this.content.value),e}return this.content instanceof i?new s([this.content]):new s}get recursiveChildren(){const e=new s;return this.children.forEach((t=>{e.push(t),t.recursiveChildren.forEach((t=>{e.push(t)}))})),e}}e.exports=i},5946:(e,t,n)=>{const r=n(17645),o=n(3079);e.exports=class extends o{constructor(e,t,n,o){super(new r,n,o),this.element="member",this.key=e,this.value=t}get key(){return this.content.key}set key(e){this.content.key=this.refract(e)}get value(){return this.content.value}set value(e){this.content.value=this.refract(e)}}},96295:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e||null,t,n),this.element="null"}primitive(){return"null"}set(){return new Error("Cannot set the value of null")}}},91090:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e,t,n),this.element="number"}primitive(){return"number"}}},76735:(e,t,n)=>{const r=n(94885),o=n(13218),s=n(35804),i=n(5946),a=n(87526);e.exports=class extends s{constructor(e,t,n){super(e||[],t,n),this.element="object"}primitive(){return"object"}toValue(){return this.content.reduce(((e,t)=>(e[t.key.toValue()]=t.value?t.value.toValue():void 0,e)),{})}get(e){const t=this.getMember(e);if(t)return t.value}getMember(e){if(void 0!==e)return this.content.find((t=>t.key.toValue()===e))}remove(e){let t=null;return this.content=this.content.filter((n=>n.key.toValue()!==e||(t=n,!1))),t}getKey(e){const t=this.getMember(e);if(t)return t.key}set(e,t){if(o(e))return Object.keys(e).forEach((t=>{this.set(t,e[t])})),this;const n=e,r=this.getMember(n);return r?r.value=t:this.content.push(new i(n,t)),this}keys(){return this.content.map((e=>e.key.toValue()))}values(){return this.content.map((e=>e.value.toValue()))}hasKey(e){return this.content.some((t=>t.key.equals(e)))}items(){return this.content.map((e=>[e.key.toValue(),e.value.toValue()]))}map(e,t){return this.content.map((n=>e.bind(t)(n.value,n.key,n)))}compactMap(e,t){const n=[];return this.forEach(((r,o,s)=>{const i=e.bind(t)(r,o,s);i&&n.push(i)})),n}filter(e,t){return new a(this.content).filter(e,t)}reject(e,t){return this.filter(r(e),t)}forEach(e,t){return this.content.forEach((n=>e.bind(t)(n.value,n.key,n)))}}},16036:(e,t,n)=>{const r=n(3079);e.exports=class extends r{constructor(e,t,n){super(e,t,n),this.element="string"}primitive(){return"string"}get length(){return this.content.length}}},3414:(e,t,n)=>{const r=n(28219);e.exports=class extends r{serialise(e){if(!(e instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${e}\` is not an Element instance`);let t;e._attributes&&e.attributes.get("variable")&&(t=e.attributes.get("variable"));const n={element:e.element};e._meta&&e._meta.length>0&&(n.meta=this.serialiseObject(e.meta));const r="enum"===e.element||-1!==e.attributes.keys().indexOf("enumerations");if(r){const t=this.enumSerialiseAttributes(e);t&&(n.attributes=t)}else if(e._attributes&&e._attributes.length>0){let{attributes:r}=e;r.get("metadata")&&(r=r.clone(),r.set("meta",r.get("metadata")),r.remove("metadata")),"member"===e.element&&t&&(r=r.clone(),r.remove("variable")),r.length>0&&(n.attributes=this.serialiseObject(r))}if(r)n.content=this.enumSerialiseContent(e,n);else if(this[`${e.element}SerialiseContent`])n.content=this[`${e.element}SerialiseContent`](e,n);else if(void 0!==e.content){let r;t&&e.content.key?(r=e.content.clone(),r.key.attributes.set("variable",t),r=this.serialiseContent(r)):r=this.serialiseContent(e.content),this.shouldSerialiseContent(e,r)&&(n.content=r)}else this.shouldSerialiseContent(e,e.content)&&e instanceof this.namespace.elements.Array&&(n.content=[]);return n}shouldSerialiseContent(e,t){return"parseResult"===e.element||"httpRequest"===e.element||"httpResponse"===e.element||"category"===e.element||"link"===e.element||void 0!==t&&(!Array.isArray(t)||0!==t.length)}refSerialiseContent(e,t){return delete t.attributes,{href:e.toValue(),path:e.path.toValue()}}sourceMapSerialiseContent(e){return e.toValue()}dataStructureSerialiseContent(e){return[this.serialiseContent(e.content)]}enumSerialiseAttributes(e){const t=e.attributes.clone(),n=t.remove("enumerations")||new this.namespace.elements.Array([]),r=t.get("default");let o=t.get("samples")||new this.namespace.elements.Array([]);if(r&&r.content&&(r.content.attributes&&r.content.attributes.remove("typeAttributes"),t.set("default",new this.namespace.elements.Array([r.content]))),o.forEach((e=>{e.content&&e.content.element&&e.content.attributes.remove("typeAttributes")})),e.content&&0!==n.length&&o.unshift(e.content),o=o.map((e=>e instanceof this.namespace.elements.Array?[e]:new this.namespace.elements.Array([e.content]))),o.length&&t.set("samples",o),t.length>0)return this.serialiseObject(t)}enumSerialiseContent(e){if(e._attributes){const t=e.attributes.get("enumerations");if(t&&t.length>0)return t.content.map((e=>{const t=e.clone();return t.attributes.remove("typeAttributes"),this.serialise(t)}))}if(e.content){const t=e.content.clone();return t.attributes.remove("typeAttributes"),[this.serialise(t)]}return[]}deserialise(e){if("string"==typeof e)return new this.namespace.elements.String(e);if("number"==typeof e)return new this.namespace.elements.Number(e);if("boolean"==typeof e)return new this.namespace.elements.Boolean(e);if(null===e)return new this.namespace.elements.Null;if(Array.isArray(e))return new this.namespace.elements.Array(e.map(this.deserialise,this));const t=this.namespace.getElementClass(e.element),n=new t;n.element!==e.element&&(n.element=e.element),e.meta&&this.deserialiseObject(e.meta,n.meta),e.attributes&&this.deserialiseObject(e.attributes,n.attributes);const r=this.deserialiseContent(e.content);if(void 0===r&&null!==n.content||(n.content=r),"enum"===n.element){n.content&&n.attributes.set("enumerations",n.content);let e=n.attributes.get("samples");if(n.attributes.remove("samples"),e){const r=e;e=new this.namespace.elements.Array,r.forEach((r=>{r.forEach((r=>{const o=new t(r);o.element=n.element,e.push(o)}))}));const o=e.shift();n.content=o?o.content:void 0,n.attributes.set("samples",e)}else n.content=void 0;let r=n.attributes.get("default");if(r&&r.length>0){r=r.get(0);const e=new t(r);e.element=n.element,n.attributes.set("default",e)}}else if("dataStructure"===n.element&&Array.isArray(n.content))[n.content]=n.content;else if("category"===n.element){const e=n.attributes.get("meta");e&&(n.attributes.set("metadata",e),n.attributes.remove("meta"))}else"member"===n.element&&n.key&&n.key._attributes&&n.key._attributes.getValue("variable")&&(n.attributes.set("variable",n.key.attributes.get("variable")),n.key.attributes.remove("variable"));return n}serialiseContent(e){if(e instanceof this.namespace.elements.Element)return this.serialise(e);if(e instanceof this.namespace.KeyValuePair){const t={key:this.serialise(e.key)};return e.value&&(t.value=this.serialise(e.value)),t}return e&&e.map?e.map(this.serialise,this):e}deserialiseContent(e){if(e){if(e.element)return this.deserialise(e);if(e.key){const t=new this.namespace.KeyValuePair(this.deserialise(e.key));return e.value&&(t.value=this.deserialise(e.value)),t}if(e.map)return e.map(this.deserialise,this)}return e}shouldRefract(e){return!!(e._attributes&&e.attributes.keys().length||e._meta&&e.meta.keys().length)||"enum"!==e.element&&(e.element!==e.primitive()||"member"===e.element)}convertKeyToRefract(e,t){return this.shouldRefract(t)?this.serialise(t):"enum"===t.element?this.serialiseEnum(t):"array"===t.element?t.map((t=>this.shouldRefract(t)||"default"===e?this.serialise(t):"array"===t.element||"object"===t.element||"enum"===t.element?t.children.map((e=>this.serialise(e))):t.toValue())):"object"===t.element?(t.content||[]).map(this.serialise,this):t.toValue()}serialiseEnum(e){return e.children.map((e=>this.serialise(e)))}serialiseObject(e){const t={};return e.forEach(((e,n)=>{if(e){const r=n.toValue();t[r]=this.convertKeyToRefract(r,e)}})),t}deserialiseObject(e,t){Object.keys(e).forEach((n=>{t.set(n,this.deserialise(e[n]))}))}}},28219:e=>{e.exports=class{constructor(e){this.namespace=e||new this.Namespace}serialise(e){if(!(e instanceof this.namespace.elements.Element))throw new TypeError(`Given element \`${e}\` is not an Element instance`);const t={element:e.element};e._meta&&e._meta.length>0&&(t.meta=this.serialiseObject(e.meta)),e._attributes&&e._attributes.length>0&&(t.attributes=this.serialiseObject(e.attributes));const n=this.serialiseContent(e.content);return void 0!==n&&(t.content=n),t}deserialise(e){if(!e.element)throw new Error("Given value is not an object containing an element name");const t=new(this.namespace.getElementClass(e.element));t.element!==e.element&&(t.element=e.element),e.meta&&this.deserialiseObject(e.meta,t.meta),e.attributes&&this.deserialiseObject(e.attributes,t.attributes);const n=this.deserialiseContent(e.content);return void 0===n&&null!==t.content||(t.content=n),t}serialiseContent(e){if(e instanceof this.namespace.elements.Element)return this.serialise(e);if(e instanceof this.namespace.KeyValuePair){const t={key:this.serialise(e.key)};return e.value&&(t.value=this.serialise(e.value)),t}if(e&&e.map){if(0===e.length)return;return e.map(this.serialise,this)}return e}deserialiseContent(e){if(e){if(e.element)return this.deserialise(e);if(e.key){const t=new this.namespace.KeyValuePair(this.deserialise(e.key));return e.value&&(t.value=this.deserialise(e.value)),t}if(e.map)return e.map(this.deserialise,this)}return e}serialiseObject(e){const t={};if(e.forEach(((e,n)=>{e&&(t[n.toValue()]=this.serialise(e))})),0!==Object.keys(t).length)return t}deserialiseObject(e,t){Object.keys(e).forEach((n=>{t.set(n,this.deserialise(e[n]))}))}}},27418:e=>{"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,r=Object.prototype.propertyIsEnumerable;e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var r={};return"abcdefghijklmnopqrst".split("").forEach((function(e){r[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},r)).join("")}catch(e){return!1}}()?Object.assign:function(e,o){for(var s,i,a=function(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}(e),l=1;l{var r="function"==typeof Map&&Map.prototype,o=Object.getOwnPropertyDescriptor&&r?Object.getOwnPropertyDescriptor(Map.prototype,"size"):null,s=r&&o&&"function"==typeof o.get?o.get:null,i=r&&Map.prototype.forEach,a="function"==typeof Set&&Set.prototype,l=Object.getOwnPropertyDescriptor&&a?Object.getOwnPropertyDescriptor(Set.prototype,"size"):null,c=a&&l&&"function"==typeof l.get?l.get:null,u=a&&Set.prototype.forEach,p="function"==typeof WeakMap&&WeakMap.prototype?WeakMap.prototype.has:null,h="function"==typeof WeakSet&&WeakSet.prototype?WeakSet.prototype.has:null,f="function"==typeof WeakRef&&WeakRef.prototype?WeakRef.prototype.deref:null,d=Boolean.prototype.valueOf,m=Object.prototype.toString,g=Function.prototype.toString,y=String.prototype.match,v=String.prototype.slice,b=String.prototype.replace,w=String.prototype.toUpperCase,E=String.prototype.toLowerCase,x=RegExp.prototype.test,S=Array.prototype.concat,_=Array.prototype.join,j=Array.prototype.slice,O=Math.floor,k="function"==typeof BigInt?BigInt.prototype.valueOf:null,A=Object.getOwnPropertySymbols,C="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?Symbol.prototype.toString:null,P="function"==typeof Symbol&&"object"==typeof Symbol.iterator,N="function"==typeof Symbol&&Symbol.toStringTag&&(typeof Symbol.toStringTag===P||"symbol")?Symbol.toStringTag:null,I=Object.prototype.propertyIsEnumerable,T=("function"==typeof Reflect?Reflect.getPrototypeOf:Object.getPrototypeOf)||([].__proto__===Array.prototype?function(e){return e.__proto__}:null);function R(e,t){if(e===1/0||e===-1/0||e!=e||e&&e>-1e3&&e<1e3||x.call(/e/,t))return t;var n=/[0-9](?=(?:[0-9]{3})+(?![0-9]))/g;if("number"==typeof e){var r=e<0?-O(-e):O(e);if(r!==e){var o=String(r),s=v.call(t,o.length+1);return b.call(o,n,"$&_")+"."+b.call(b.call(s,/([0-9]{3})/g,"$&_"),/_$/,"")}}return b.call(t,n,"$&_")}var M=n(24654),D=M.custom,F=U(D)?D:null;function L(e,t,n){var r="double"===(n.quoteStyle||t)?'"':"'";return r+e+r}function B(e){return b.call(String(e),/"/g,""")}function $(e){return!("[object Array]"!==W(e)||N&&"object"==typeof e&&N in e)}function q(e){return!("[object RegExp]"!==W(e)||N&&"object"==typeof e&&N in e)}function U(e){if(P)return e&&"object"==typeof e&&e instanceof Symbol;if("symbol"==typeof e)return!0;if(!e||"object"!=typeof e||!C)return!1;try{return C.call(e),!0}catch(e){}return!1}e.exports=function e(t,n,r,o){var a=n||{};if(V(a,"quoteStyle")&&"single"!==a.quoteStyle&&"double"!==a.quoteStyle)throw new TypeError('option "quoteStyle" must be "single" or "double"');if(V(a,"maxStringLength")&&("number"==typeof a.maxStringLength?a.maxStringLength<0&&a.maxStringLength!==1/0:null!==a.maxStringLength))throw new TypeError('option "maxStringLength", if provided, must be a positive integer, Infinity, or `null`');var l=!V(a,"customInspect")||a.customInspect;if("boolean"!=typeof l&&"symbol"!==l)throw new TypeError("option \"customInspect\", if provided, must be `true`, `false`, or `'symbol'`");if(V(a,"indent")&&null!==a.indent&&"\t"!==a.indent&&!(parseInt(a.indent,10)===a.indent&&a.indent>0))throw new TypeError('option "indent" must be "\\t", an integer > 0, or `null`');if(V(a,"numericSeparator")&&"boolean"!=typeof a.numericSeparator)throw new TypeError('option "numericSeparator", if provided, must be `true` or `false`');var m=a.numericSeparator;if(void 0===t)return"undefined";if(null===t)return"null";if("boolean"==typeof t)return t?"true":"false";if("string"==typeof t)return K(t,a);if("number"==typeof t){if(0===t)return 1/0/t>0?"0":"-0";var w=String(t);return m?R(t,w):w}if("bigint"==typeof t){var x=String(t)+"n";return m?R(t,x):x}var O=void 0===a.depth?5:a.depth;if(void 0===r&&(r=0),r>=O&&O>0&&"object"==typeof t)return $(t)?"[Array]":"[Object]";var A=function(e,t){var n;if("\t"===e.indent)n="\t";else{if(!("number"==typeof e.indent&&e.indent>0))return null;n=_.call(Array(e.indent+1)," ")}return{base:n,prev:_.call(Array(t+1),n)}}(a,r);if(void 0===o)o=[];else if(J(o,t)>=0)return"[Circular]";function D(t,n,s){if(n&&(o=j.call(o)).push(n),s){var i={depth:a.depth};return V(a,"quoteStyle")&&(i.quoteStyle=a.quoteStyle),e(t,i,r+1,o)}return e(t,a,r+1,o)}if("function"==typeof t&&!q(t)){var z=function(e){if(e.name)return e.name;var t=y.call(g.call(e),/^function\s*([\w$]+)/);if(t)return t[1];return null}(t),H=Q(t,D);return"[Function"+(z?": "+z:" (anonymous)")+"]"+(H.length>0?" { "+_.call(H,", ")+" }":"")}if(U(t)){var ee=P?b.call(String(t),/^(Symbol\(.*\))_[^)]*$/,"$1"):C.call(t);return"object"!=typeof t||P?ee:G(ee)}if(function(e){if(!e||"object"!=typeof e)return!1;if("undefined"!=typeof HTMLElement&&e instanceof HTMLElement)return!0;return"string"==typeof e.nodeName&&"function"==typeof e.getAttribute}(t)){for(var te="<"+E.call(String(t.nodeName)),ne=t.attributes||[],re=0;re"}if($(t)){if(0===t.length)return"[]";var oe=Q(t,D);return A&&!function(e){for(var t=0;t=0)return!1;return!0}(oe)?"["+X(oe,A)+"]":"[ "+_.call(oe,", ")+" ]"}if(function(e){return!("[object Error]"!==W(e)||N&&"object"==typeof e&&N in e)}(t)){var se=Q(t,D);return"cause"in Error.prototype||!("cause"in t)||I.call(t,"cause")?0===se.length?"["+String(t)+"]":"{ ["+String(t)+"] "+_.call(se,", ")+" }":"{ ["+String(t)+"] "+_.call(S.call("[cause]: "+D(t.cause),se),", ")+" }"}if("object"==typeof t&&l){if(F&&"function"==typeof t[F]&&M)return M(t,{depth:O-r});if("symbol"!==l&&"function"==typeof t.inspect)return t.inspect()}if(function(e){if(!s||!e||"object"!=typeof e)return!1;try{s.call(e);try{c.call(e)}catch(e){return!0}return e instanceof Map}catch(e){}return!1}(t)){var ie=[];return i&&i.call(t,(function(e,n){ie.push(D(n,t,!0)+" => "+D(e,t))})),Y("Map",s.call(t),ie,A)}if(function(e){if(!c||!e||"object"!=typeof e)return!1;try{c.call(e);try{s.call(e)}catch(e){return!0}return e instanceof Set}catch(e){}return!1}(t)){var ae=[];return u&&u.call(t,(function(e){ae.push(D(e,t))})),Y("Set",c.call(t),ae,A)}if(function(e){if(!p||!e||"object"!=typeof e)return!1;try{p.call(e,p);try{h.call(e,h)}catch(e){return!0}return e instanceof WeakMap}catch(e){}return!1}(t))return Z("WeakMap");if(function(e){if(!h||!e||"object"!=typeof e)return!1;try{h.call(e,h);try{p.call(e,p)}catch(e){return!0}return e instanceof WeakSet}catch(e){}return!1}(t))return Z("WeakSet");if(function(e){if(!f||!e||"object"!=typeof e)return!1;try{return f.call(e),!0}catch(e){}return!1}(t))return Z("WeakRef");if(function(e){return!("[object Number]"!==W(e)||N&&"object"==typeof e&&N in e)}(t))return G(D(Number(t)));if(function(e){if(!e||"object"!=typeof e||!k)return!1;try{return k.call(e),!0}catch(e){}return!1}(t))return G(D(k.call(t)));if(function(e){return!("[object Boolean]"!==W(e)||N&&"object"==typeof e&&N in e)}(t))return G(d.call(t));if(function(e){return!("[object String]"!==W(e)||N&&"object"==typeof e&&N in e)}(t))return G(D(String(t)));if(!function(e){return!("[object Date]"!==W(e)||N&&"object"==typeof e&&N in e)}(t)&&!q(t)){var le=Q(t,D),ce=T?T(t)===Object.prototype:t instanceof Object||t.constructor===Object,ue=t instanceof Object?"":"null prototype",pe=!ce&&N&&Object(t)===t&&N in t?v.call(W(t),8,-1):ue?"Object":"",he=(ce||"function"!=typeof t.constructor?"":t.constructor.name?t.constructor.name+" ":"")+(pe||ue?"["+_.call(S.call([],pe||[],ue||[]),": ")+"] ":"");return 0===le.length?he+"{}":A?he+"{"+X(le,A)+"}":he+"{ "+_.call(le,", ")+" }"}return String(t)};var z=Object.prototype.hasOwnProperty||function(e){return e in this};function V(e,t){return z.call(e,t)}function W(e){return m.call(e)}function J(e,t){if(e.indexOf)return e.indexOf(t);for(var n=0,r=e.length;nt.maxStringLength){var n=e.length-t.maxStringLength,r="... "+n+" more character"+(n>1?"s":"");return K(v.call(e,0,t.maxStringLength),t)+r}return L(b.call(b.call(e,/(['\\])/g,"\\$1"),/[\x00-\x1f]/g,H),"single",t)}function H(e){var t=e.charCodeAt(0),n={8:"b",9:"t",10:"n",12:"f",13:"r"}[t];return n?"\\"+n:"\\x"+(t<16?"0":"")+w.call(t.toString(16))}function G(e){return"Object("+e+")"}function Z(e){return e+" { ? }"}function Y(e,t,n,r){return e+" ("+t+") {"+(r?X(n,r):_.call(n,", "))+"}"}function X(e,t){if(0===e.length)return"";var n="\n"+t.prev+t.base;return n+_.call(e,","+n)+"\n"+t.prev}function Q(e,t){var n=$(e),r=[];if(n){r.length=e.length;for(var o=0;o{var t,n,r=e.exports={};function o(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function i(e){if(t===setTimeout)return setTimeout(e,0);if((t===o||!t)&&setTimeout)return t=setTimeout,setTimeout(e,0);try{return t(e,0)}catch(n){try{return t.call(null,e,0)}catch(n){return t.call(this,e,0)}}}!function(){try{t="function"==typeof setTimeout?setTimeout:o}catch(e){t=o}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(e){n=s}}();var a,l=[],c=!1,u=-1;function p(){c&&a&&(c=!1,a.length?l=a.concat(l):u=-1,l.length&&h())}function h(){if(!c){var e=i(p);c=!0;for(var t=l.length;t;){for(a=l,l=[];++u1)for(var n=1;n{"use strict";var r=n(50414);function o(){}function s(){}s.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,s,i){if(i!==r){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:s,resetWarningCache:o};return n.PropTypes=n,n}},45697:(e,t,n)=>{e.exports=n(92703)()},50414:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},55798:e=>{"use strict";var t=String.prototype.replace,n=/%20/g,r="RFC1738",o="RFC3986";e.exports={default:o,formatters:{RFC1738:function(e){return t.call(e,n,"+")},RFC3986:function(e){return String(e)}},RFC1738:r,RFC3986:o}},80129:(e,t,n)=>{"use strict";var r=n(58261),o=n(55235),s=n(55798);e.exports={formats:s,parse:o,stringify:r}},55235:(e,t,n)=>{"use strict";var r=n(12769),o=Object.prototype.hasOwnProperty,s=Array.isArray,i={allowDots:!1,allowPrototypes:!1,allowSparse:!1,arrayLimit:20,charset:"utf-8",charsetSentinel:!1,comma:!1,decoder:r.decode,delimiter:"&",depth:5,ignoreQueryPrefix:!1,interpretNumericEntities:!1,parameterLimit:1e3,parseArrays:!0,plainObjects:!1,strictNullHandling:!1},a=function(e){return e.replace(/&#(\d+);/g,(function(e,t){return String.fromCharCode(parseInt(t,10))}))},l=function(e,t){return e&&"string"==typeof e&&t.comma&&e.indexOf(",")>-1?e.split(","):e},c=function(e,t,n,r){if(e){var s=n.allowDots?e.replace(/\.([^.[]+)/g,"[$1]"):e,i=/(\[[^[\]]*])/g,a=n.depth>0&&/(\[[^[\]]*])/.exec(s),c=a?s.slice(0,a.index):s,u=[];if(c){if(!n.plainObjects&&o.call(Object.prototype,c)&&!n.allowPrototypes)return;u.push(c)}for(var p=0;n.depth>0&&null!==(a=i.exec(s))&&p=0;--s){var i,a=e[s];if("[]"===a&&n.parseArrays)i=[].concat(o);else{i=n.plainObjects?Object.create(null):{};var c="["===a.charAt(0)&&"]"===a.charAt(a.length-1)?a.slice(1,-1):a,u=parseInt(c,10);n.parseArrays||""!==c?!isNaN(u)&&a!==c&&String(u)===c&&u>=0&&n.parseArrays&&u<=n.arrayLimit?(i=[])[u]=o:"__proto__"!==c&&(i[c]=o):i={0:o}}o=i}return o}(u,t,n,r)}};e.exports=function(e,t){var n=function(e){if(!e)return i;if(null!==e.decoder&&void 0!==e.decoder&&"function"!=typeof e.decoder)throw new TypeError("Decoder has to be a function.");if(void 0!==e.charset&&"utf-8"!==e.charset&&"iso-8859-1"!==e.charset)throw new TypeError("The charset option must be either utf-8, iso-8859-1, or undefined");var t=void 0===e.charset?i.charset:e.charset;return{allowDots:void 0===e.allowDots?i.allowDots:!!e.allowDots,allowPrototypes:"boolean"==typeof e.allowPrototypes?e.allowPrototypes:i.allowPrototypes,allowSparse:"boolean"==typeof e.allowSparse?e.allowSparse:i.allowSparse,arrayLimit:"number"==typeof e.arrayLimit?e.arrayLimit:i.arrayLimit,charset:t,charsetSentinel:"boolean"==typeof e.charsetSentinel?e.charsetSentinel:i.charsetSentinel,comma:"boolean"==typeof e.comma?e.comma:i.comma,decoder:"function"==typeof e.decoder?e.decoder:i.decoder,delimiter:"string"==typeof e.delimiter||r.isRegExp(e.delimiter)?e.delimiter:i.delimiter,depth:"number"==typeof e.depth||!1===e.depth?+e.depth:i.depth,ignoreQueryPrefix:!0===e.ignoreQueryPrefix,interpretNumericEntities:"boolean"==typeof e.interpretNumericEntities?e.interpretNumericEntities:i.interpretNumericEntities,parameterLimit:"number"==typeof e.parameterLimit?e.parameterLimit:i.parameterLimit,parseArrays:!1!==e.parseArrays,plainObjects:"boolean"==typeof e.plainObjects?e.plainObjects:i.plainObjects,strictNullHandling:"boolean"==typeof e.strictNullHandling?e.strictNullHandling:i.strictNullHandling}}(t);if(""===e||null==e)return n.plainObjects?Object.create(null):{};for(var u="string"==typeof e?function(e,t){var n,c={},u=t.ignoreQueryPrefix?e.replace(/^\?/,""):e,p=t.parameterLimit===1/0?void 0:t.parameterLimit,h=u.split(t.delimiter,p),f=-1,d=t.charset;if(t.charsetSentinel)for(n=0;n-1&&(g=s(g)?[g]:g),o.call(c,m)?c[m]=r.combine(c[m],g):c[m]=g}return c}(e,n):e,p=n.plainObjects?Object.create(null):{},h=Object.keys(u),f=0;f{"use strict";var r=n(37478),o=n(12769),s=n(55798),i=Object.prototype.hasOwnProperty,a={brackets:function(e){return e+"[]"},comma:"comma",indices:function(e,t){return e+"["+t+"]"},repeat:function(e){return e}},l=Array.isArray,c=String.prototype.split,u=Array.prototype.push,p=function(e,t){u.apply(e,l(t)?t:[t])},h=Date.prototype.toISOString,f=s.default,d={addQueryPrefix:!1,allowDots:!1,charset:"utf-8",charsetSentinel:!1,delimiter:"&",encode:!0,encoder:o.encode,encodeValuesOnly:!1,format:f,formatter:s.formatters[f],indices:!1,serializeDate:function(e){return h.call(e)},skipNulls:!1,strictNullHandling:!1},m={},g=function e(t,n,s,i,a,u,h,f,g,y,v,b,w,E,x,S){for(var _,j=t,O=S,k=0,A=!1;void 0!==(O=O.get(m))&&!A;){var C=O.get(t);if(k+=1,void 0!==C){if(C===k)throw new RangeError("Cyclic object value");A=!0}void 0===O.get(m)&&(k=0)}if("function"==typeof f?j=f(n,j):j instanceof Date?j=v(j):"comma"===s&&l(j)&&(j=o.maybeMap(j,(function(e){return e instanceof Date?v(e):e}))),null===j){if(a)return h&&!E?h(n,d.encoder,x,"key",b):n;j=""}if("string"==typeof(_=j)||"number"==typeof _||"boolean"==typeof _||"symbol"==typeof _||"bigint"==typeof _||o.isBuffer(j)){if(h){var P=E?n:h(n,d.encoder,x,"key",b);if("comma"===s&&E){for(var N=c.call(String(j),","),I="",T=0;T0?j.join(",")||null:void 0}];else if(l(f))R=f;else{var D=Object.keys(j);R=g?D.sort(g):D}for(var F=i&&l(j)&&1===j.length?n+"[]":n,L=0;L0?E+w:""}},12769:(e,t,n)=>{"use strict";var r=n(55798),o=Object.prototype.hasOwnProperty,s=Array.isArray,i=function(){for(var e=[],t=0;t<256;++t)e.push("%"+((t<16?"0":"")+t.toString(16)).toUpperCase());return e}(),a=function(e,t){for(var n=t&&t.plainObjects?Object.create(null):{},r=0;r1;){var t=e.pop(),n=t.obj[t.prop];if(s(n)){for(var r=[],o=0;o=48&&u<=57||u>=65&&u<=90||u>=97&&u<=122||s===r.RFC1738&&(40===u||41===u)?l+=a.charAt(c):u<128?l+=i[u]:u<2048?l+=i[192|u>>6]+i[128|63&u]:u<55296||u>=57344?l+=i[224|u>>12]+i[128|u>>6&63]+i[128|63&u]:(c+=1,u=65536+((1023&u)<<10|1023&a.charCodeAt(c)),l+=i[240|u>>18]+i[128|u>>12&63]+i[128|u>>6&63]+i[128|63&u])}return l},isBuffer:function(e){return!(!e||"object"!=typeof e)&&!!(e.constructor&&e.constructor.isBuffer&&e.constructor.isBuffer(e))},isRegExp:function(e){return"[object RegExp]"===Object.prototype.toString.call(e)},maybeMap:function(e,t){if(s(e)){for(var n=[],r=0;r{"use strict";var n=Object.prototype.hasOwnProperty;function r(e){try{return decodeURIComponent(e.replace(/\+/g," "))}catch(e){return null}}function o(e){try{return encodeURIComponent(e)}catch(e){return null}}t.stringify=function(e,t){t=t||"";var r,s,i=[];for(s in"string"!=typeof t&&(t="?"),e)if(n.call(e,s)){if((r=e[s])||null!=r&&!isNaN(r)||(r=""),s=o(s),r=o(r),null===s||null===r)continue;i.push(s+"="+r)}return i.length?t+i.join("&"):""},t.parse=function(e){for(var t,n=/([^=?#&]+)=?([^&]*)/g,o={};t=n.exec(e);){var s=r(t[1]),i=r(t[2]);null===s||null===i||s in o||(o[s]=i)}return o}},14419:(e,t,n)=>{const r=n(60697),o=n(69450),s=r.types;e.exports=class e{constructor(e,t){if(this._setDefaults(e),e instanceof RegExp)this.ignoreCase=e.ignoreCase,this.multiline=e.multiline,e=e.source;else{if("string"!=typeof e)throw new Error("Expected a regexp or string");this.ignoreCase=t&&-1!==t.indexOf("i"),this.multiline=t&&-1!==t.indexOf("m")}this.tokens=r(e)}_setDefaults(t){this.max=null!=t.max?t.max:null!=e.prototype.max?e.prototype.max:100,this.defaultRange=t.defaultRange?t.defaultRange:this.defaultRange.clone(),t.randInt&&(this.randInt=t.randInt)}gen(){return this._gen(this.tokens,[])}_gen(e,t){var n,r,o,i,a;switch(e.type){case s.ROOT:case s.GROUP:if(e.followedBy||e.notFollowedBy)return"";for(e.remember&&void 0===e.groupNumber&&(e.groupNumber=t.push(null)-1),r="",i=0,a=(n=e.options?this._randSelect(e.options):e.stack).length;i{"use strict";var r=n(34155),o=65536,s=4294967295;var i=n(89509).Buffer,a=n.g.crypto||n.g.msCrypto;a&&a.getRandomValues?e.exports=function(e,t){if(e>s)throw new RangeError("requested too many random bytes");var n=i.allocUnsafe(e);if(e>0)if(e>o)for(var l=0;l{"use strict";function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.CopyToClipboard=void 0;var o=a(n(67294)),s=a(n(20640)),i=["text","onCopy","options","children"];function a(e){return e&&e.__esModule?e:{default:e}}function l(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function c(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function p(e,t){for(var n=0;n{"use strict";var r=n(74300).CopyToClipboard;r.CopyToClipboard=r,e.exports=r},53441:(e,t,n)=>{"use strict";function r(e){return r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},r(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.DebounceInput=void 0;var o=a(n(67294)),s=a(n(91296)),i=["element","onChange","value","minLength","debounceTimeout","forceNotifyByEnter","forceNotifyOnBlur","onKeyDown","onBlur","inputRef"];function a(e){return e&&e.__esModule?e:{default:e}}function l(e,t){if(null==e)return{};var n,r,o=function(e,t){if(null==e)return{};var n,r,o={},s=Object.keys(e);for(r=0;r=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var s=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function c(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function u(e){for(var t=1;t=r?t.notify(e):n.length>o.length&&t.notify(u(u({},e),{},{target:u(u({},e.target),{},{value:""})}))}))})),g(d(t),"onKeyDown",(function(e){"Enter"===e.key&&t.forceNotify(e);var n=t.props.onKeyDown;n&&(e.persist(),n(e))})),g(d(t),"onBlur",(function(e){t.forceNotify(e);var n=t.props.onBlur;n&&(e.persist(),n(e))})),g(d(t),"createNotifier",(function(e){if(e<0)t.notify=function(){return null};else if(0===e)t.notify=t.doNotify;else{var n=(0,s.default)((function(e){t.isDebouncing=!1,t.doNotify(e)}),e);t.notify=function(e){t.isDebouncing=!0,n(e)},t.flush=function(){return n.flush()},t.cancel=function(){t.isDebouncing=!1,n.cancel()}}})),g(d(t),"doNotify",(function(){t.props.onChange.apply(void 0,arguments)})),g(d(t),"forceNotify",(function(e){var n=t.props.debounceTimeout;if(t.isDebouncing||!(n>0)){t.cancel&&t.cancel();var r=t.state.value,o=t.props.minLength;r.length>=o?t.doNotify(e):t.doNotify(u(u({},e),{},{target:u(u({},e.target),{},{value:r})}))}})),t.isDebouncing=!1,t.state={value:void 0===e.value||null===e.value?"":e.value};var n=t.props.debounceTimeout;return t.createNotifier(n),t}return t=c,(n=[{key:"componentDidUpdate",value:function(e){if(!this.isDebouncing){var t=this.props,n=t.value,r=t.debounceTimeout,o=e.debounceTimeout,s=e.value,i=this.state.value;void 0!==n&&s!==n&&i!==n&&this.setState({value:n}),r!==o&&this.createNotifier(r)}}},{key:"componentWillUnmount",value:function(){this.flush&&this.flush()}},{key:"render",value:function(){var e,t,n=this.props,r=n.element,s=(n.onChange,n.value,n.minLength,n.debounceTimeout,n.forceNotifyByEnter),a=n.forceNotifyOnBlur,c=n.onKeyDown,p=n.onBlur,h=n.inputRef,f=l(n,i),d=this.state.value;e=s?{onKeyDown:this.onKeyDown}:c?{onKeyDown:c}:{},t=a?{onBlur:this.onBlur}:p?{onBlur:p}:{};var m=h?{ref:h}:{};return o.default.createElement(r,u(u(u(u({},f),{},{onChange:this.onChange,value:d},e),t),m))}}])&&p(t.prototype,n),r&&p(t,r),Object.defineProperty(t,"prototype",{writable:!1}),c}(o.default.PureComponent);t.DebounceInput=y,g(y,"defaultProps",{element:"input",type:"text",onKeyDown:void 0,onBlur:void 0,value:void 0,minLength:0,debounceTimeout:100,forceNotifyByEnter:!0,forceNotifyOnBlur:!0,inputRef:void 0})},775:(e,t,n)=>{"use strict";var r=n(53441).DebounceInput;r.DebounceInput=r,e.exports=r},64448:(e,t,n)=>{"use strict";var r=n(67294),o=n(27418),s=n(63840);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n