diff --git a/conf/ranger-ssm-audit.xml b/conf/ranger-ssm-audit.xml
new file mode 100644
index 0000000000..63cda06ac3
--- /dev/null
+++ b/conf/ranger-ssm-audit.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+ xasecure.audit.destination.db
+ false
+
+
+
+ xasecure.audit.destination.db.jdbc.driver
+ com.mysql.jdbc.Driver
+
+
+
+ xasecure.audit.destination.db.password
+ rangerlogger
+
+
+
+ xasecure.audit.destination.db.user
+ rangerlogger
+
+
+
+ xasecure.audit.destination.db.batch.filespool.dir
+ /tmp/audit/db/spool
+
+
+
+
+ xasecure.audit.destination.hdfs
+ false
+
+
+
+ xasecure.audit.destination.hdfs.batch.filespool.dir
+ /tmp/audit/hdfs/spool
+
+
+
+
+ xasecure.audit.destination.log4j
+ true
+
+
+
+ xasecure.audit.destination.log4j.logger
+ ranger_audit_logger
+
+
\ No newline at end of file
diff --git a/conf/ranger-ssm-security.xml b/conf/ranger-ssm-security.xml
new file mode 100644
index 0000000000..da56253287
--- /dev/null
+++ b/conf/ranger-ssm-security.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+ ranger.plugin.ssm.service.name
+ ssm
+
+ Name of the Ranger service containing policies for this ssm instance
+
+
+
+
+ ranger.plugin.ssm.policy.source.impl
+ org.apache.ranger.admin.client.RangerAdminRESTClient
+
+ Class to retrieve policies from the source
+
+
+
+
+ ranger.plugin.ssm.policy.rest.ssl.config.file
+ ranger-policymgr-ssl.xml
+
+ Path to the file containing SSL details to contact Ranger Admin
+
+
+
+
+ ranger.plugin.ssm.policy.pollIntervalMs
+ 30000
+
+ How often to poll for changes in policies?
+
+
+
+
+ ranger.plugin.ssm.policy.cache.dir
+ /tmp
+
+ Directory where Ranger policies are cached after successful retrieval from the source
+
+
+
+
+ ranger.plugin.ssm.policy.rest.client.connection.timeoutMs
+ 120000
+
+ RangerRestClient Connection Timeout in Milli Seconds
+
+
+
+
+ ranger.plugin.ssm.policy.rest.client.read.timeoutMs
+ 30000
+
+ RangerRestClient read Timeout in Milli Seconds
+
+
+
diff --git a/conf/smart-default.xml b/conf/smart-default.xml
index 93bc839671..caed88a021 100644
--- a/conf/smart-default.xml
+++ b/conf/smart-default.xml
@@ -526,6 +526,14 @@
+
+ smart.rest.server.auth.ranger.enabled
+ false
+
+ Whether to enable Apache Ranger authorization for SSM REST server.
+
+
+
smart.action.client.cache.ttl
10m
diff --git a/conf/ssm-ranger.json b/conf/ssm-ranger.json
new file mode 100644
index 0000000000..d7c16eb2d8
--- /dev/null
+++ b/conf/ssm-ranger.json
@@ -0,0 +1,145 @@
+{
+ "name": "ssm",
+ "label": "SSM Service",
+ "description": "SSM Ranger Security Plugin",
+ "guid": "b8290b7f-6f69-44a9-89cc-06b6975ea676",
+ "implClass": "io.arenadata.ranger.service.ssm.SsmRangerService",
+ "version": 1,
+ "isEnabled": 1,
+ "resources": [
+ {
+ "itemId": 1,
+ "name": "cluster",
+ "type": "string",
+ "level": 10,
+ "parent": "",
+ "mandatory": false,
+ "lookupSupported": true,
+ "recursiveSupported": true,
+ "excludesSupported": true,
+ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+ "matcherOptions": {
+ "wildCard": true,
+ "ignoreCase": true
+ },
+ "validationRegEx": "",
+ "validationMessage": "",
+ "uiHint": "",
+ "label": "Cluster",
+ "description": "List of SSM cluster nodes",
+ "accessTypeRestrictions": [
+ "VIEW"
+ ]
+ },
+ {
+ "itemId": 2,
+ "name": "rule",
+ "type": "string",
+ "level": 10,
+ "parent": "",
+ "mandatory": false,
+ "lookupSupported": true,
+ "recursiveSupported": true,
+ "excludesSupported": true,
+ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+ "matcherOptions": {
+ "wildCard": true,
+ "ignoreCase": true
+ },
+ "validationRegEx": "",
+ "validationMessage": "",
+ "uiHint": "",
+ "label": "Rule",
+ "description": "Rule",
+ "accessTypeRestrictions": [
+ "VIEW",
+ "CREATE",
+ "DELETE",
+ "EDIT"
+ ]
+ },
+ {
+ "itemId": 3,
+ "name": "action",
+ "type": "string",
+ "level": 10,
+ "parent": "",
+ "mandatory": false,
+ "lookupSupported": true,
+ "recursiveSupported": true,
+ "excludesSupported": true,
+ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+ "matcherOptions": {
+ "wildCard": true,
+ "ignoreCase": true
+ },
+ "validationRegEx": "",
+ "validationMessage": "",
+ "uiHint": "",
+ "label": "Action",
+ "description": "Action",
+ "accessTypeRestrictions": [
+ "VIEW", "SUBMIT"
+ ]
+ },
+ {
+ "itemId": 4,
+ "name": "audit",
+ "type": "string",
+ "level": 10,
+ "parent": "",
+ "mandatory": false,
+ "lookupSupported": true,
+ "recursiveSupported": true,
+ "excludesSupported": true,
+ "matcher": "org.apache.ranger.plugin.resourcematcher.RangerDefaultResourceMatcher",
+ "matcherOptions": {
+ "wildCard": true,
+ "ignoreCase": true
+ },
+ "validationRegEx": "",
+ "validationMessage": "",
+ "uiHint": "",
+ "label": "Audit",
+ "description": "Audit",
+ "accessTypeRestrictions": [
+ "VIEW"
+ ]
+ }
+ ],
+ "accessTypes": [
+ {
+ "itemId": 1,
+ "name": "VIEW",
+ "label": "View"
+ },
+ {
+ "itemId": 2,
+ "name": "CREATE",
+ "label": "Create"
+ },
+ {
+ "itemId": 3,
+ "name": "DELETE",
+ "label": "Delete"
+ },
+ {
+ "itemId": 4,
+ "name": "EDIT",
+ "label": "Edit"
+ },
+ {
+ "itemId": 5,
+ "name": "SUBMIT",
+ "label": "Submit action"
+ }
+ ],
+ "configs": [
+ ],
+ "enums": [
+ ],
+ "contextEnrichers": [
+ ],
+ "policyConditions": [
+ ]
+}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ba1eea8c2d..f598a35f2d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
smart-dist
smart-integration
smart-web-server
+ smart-ranger-plugin
smart-hadoop-support
@@ -109,9 +110,13 @@
2.6.1.Final
smart-hadoop-3.3
smart-hadoop-client-3
+ 2.5.0
+ 5.8.0
1.5.3
org.apache.hadoop.thirdparty
${hadoop-thirdparty-shaded.prefix}.protobuf
+ 3.8.4
+ 8.11.3
diff --git a/smart-integration/src/test/resources/ranger-ssm-audit.xml b/smart-integration/src/test/resources/ranger-ssm-audit.xml
new file mode 100644
index 0000000000..d21c4fe5a7
--- /dev/null
+++ b/smart-integration/src/test/resources/ranger-ssm-audit.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+ xasecure.audit.destination.db
+ false
+
+
+
+ xasecure.audit.destination.db.jdbc.driver
+ com.mysql.jdbc.Driver
+
+
+
+ xasecure.audit.destination.db.jdbc.url
+ jdbc:mysql://10.92.3.109/ranger_audit
+
+
+
+ xasecure.audit.destination.db.password
+ rangerlogger
+
+
+
+ xasecure.audit.destination.db.user
+ rangerlogger
+
+
+
+ xasecure.audit.destination.db.batch.filespool.dir
+ /tmp/audit/db/spool
+
+
+
+
+
+ xasecure.audit.destination.hdfs
+ false
+
+
+
+ xasecure.audit.destination.hdfs.dir
+ hdfs://10.92.3.109:8020/ranger/audit
+
+
+
+ xasecure.audit.destination.hdfs.batch.filespool.dir
+ /tmp/audit/hdfs/spool
+
+
+
+
+
+ xasecure.audit.destination.log4j
+ true
+
+
+
+ xasecure.audit.destination.log4j.logger
+ ranger_audit_logger
+
+
\ No newline at end of file
diff --git a/smart-integration/src/test/resources/ranger-ssm-security.xml b/smart-integration/src/test/resources/ranger-ssm-security.xml
new file mode 100644
index 0000000000..da56253287
--- /dev/null
+++ b/smart-integration/src/test/resources/ranger-ssm-security.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+ ranger.plugin.ssm.service.name
+ ssm
+
+ Name of the Ranger service containing policies for this ssm instance
+
+
+
+
+ ranger.plugin.ssm.policy.source.impl
+ org.apache.ranger.admin.client.RangerAdminRESTClient
+
+ Class to retrieve policies from the source
+
+
+
+
+ ranger.plugin.ssm.policy.rest.ssl.config.file
+ ranger-policymgr-ssl.xml
+
+ Path to the file containing SSL details to contact Ranger Admin
+
+
+
+
+ ranger.plugin.ssm.policy.pollIntervalMs
+ 30000
+
+ How often to poll for changes in policies?
+
+
+
+
+ ranger.plugin.ssm.policy.cache.dir
+ /tmp
+
+ Directory where Ranger policies are cached after successful retrieval from the source
+
+
+
+
+ ranger.plugin.ssm.policy.rest.client.connection.timeoutMs
+ 120000
+
+ RangerRestClient Connection Timeout in Milli Seconds
+
+
+
+
+ ranger.plugin.ssm.policy.rest.client.read.timeoutMs
+ 30000
+
+ RangerRestClient read Timeout in Milli Seconds
+
+
+
diff --git a/smart-integration/src/test/resources/smart-default.xml b/smart-integration/src/test/resources/smart-default.xml
index f399dde436..1fb2ee59a9 100644
--- a/smart-integration/src/test/resources/smart-default.xml
+++ b/smart-integration/src/test/resources/smart-default.xml
@@ -54,4 +54,29 @@
5
Max number of rules that can be executed in parallel
+
+
+ smart.rest.server.security.enabled
+ false
+
+ Whether to enable SSM REST server security.
+
+
+
+
+ smart.rest.server.auth.predefined.enabled
+ false
+
+ Whether to enable SSM REST server basic authentication with users,
+ predefined in the 'smart.rest.server.auth.predefined.users' option.
+
+
+
+
+ smart.rest.server.auth.ranger.enabled
+ false
+
+ Whether to enable Apache Ranger authorization for SSM REST server.
+
+
diff --git a/smart-ranger-plugin/pom.xml b/smart-ranger-plugin/pom.xml
new file mode 100644
index 0000000000..2f63cc93c4
--- /dev/null
+++ b/smart-ranger-plugin/pom.xml
@@ -0,0 +1,79 @@
+
+
+ 4.0.0
+
+ org.smartdata
+ smartdata-project
+ 2.0.0-SNAPSHOT
+
+
+ smart-ranger-plugin
+ smart-ranger-plugin
+ jar
+
+
+
+ org.apache.ranger
+ ranger-plugins-common
+ ${ranger.version}
+
+
+ org.apache.kafka
+ kafka_2.11
+
+
+ log4j
+ log4j
+
+
+ hadoop-common
+ org.apache.hadoop
+
+
+
+
+ org.projectlombok
+ lombok
+ provided
+
+
+ org.apache.zookeeper
+ zookeeper
+ ${zookeeper.version}
+
+
+ com.google.guava
+ guava
+ ${guava.version}
+
+
+ org.apache.zookeeper
+ zookeeper-jute
+ ${zookeeper.version}
+
+
+ org.apache.solr
+ solr-solrj
+ ${solrj.version}
+
+
+
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/SsmRangerResource.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/SsmRangerResource.java
new file mode 100644
index 0000000000..6696350d59
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/SsmRangerResource.java
@@ -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.
+ */
+package org.smartdata.ranger;
+
+import com.google.common.collect.Sets;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import org.smartdata.ranger.authorizer.request.RangerOperationDto;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+@Getter
+@RequiredArgsConstructor
+public enum SsmRangerResource {
+ CLUSTER(Collections.singleton(SsmResourceAccessType.VIEW)),
+ RULE(Sets.newHashSet(SsmResourceAccessType.CREATE, SsmResourceAccessType.VIEW,
+ SsmResourceAccessType.EDIT, SsmResourceAccessType.DELETE)),
+ ACTION(Sets.newHashSet(SsmResourceAccessType.SUBMIT, SsmResourceAccessType.VIEW)),
+ AUDIT(Collections.singleton(SsmResourceAccessType.VIEW));
+
+ private final Set accessTypes;
+
+ public RangerOperationDto getRangerOperationDto(SsmResourceAccessType accessType,
+ String entityId) {
+ if (!accessTypes.contains(accessType)) {
+ throw new IllegalArgumentException("Unknown action: " + accessType);
+ }
+ Map resources = new HashMap<>();
+ Optional.ofNullable(entityId)
+ .ifPresent(value -> resources.put(this.name().toLowerCase(), value));
+ return new RangerOperationDto(accessType.name(), resources);
+ }
+}
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/SsmResourceAccessType.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/SsmResourceAccessType.java
new file mode 100644
index 0000000000..1ffa0f4b0b
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/SsmResourceAccessType.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.smartdata.ranger;
+
+public enum SsmResourceAccessType {
+ VIEW,
+ CREATE,
+ EDIT,
+ DELETE,
+ SUBMIT
+}
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/RangerSsmAuthorizer.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/RangerSsmAuthorizer.java
new file mode 100644
index 0000000000..7d960c50dc
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/RangerSsmAuthorizer.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.smartdata.ranger.authorizer;
+
+import org.smartdata.ranger.authorizer.request.RangerAuthorizeRequest;
+
+public interface RangerSsmAuthorizer {
+
+ boolean authorize(RangerAuthorizeRequest request);
+}
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/impl/RangerSsmAuthorizerImpl.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/impl/RangerSsmAuthorizerImpl.java
new file mode 100644
index 0000000000..e821bb5cc5
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/impl/RangerSsmAuthorizerImpl.java
@@ -0,0 +1,58 @@
+/**
+ * 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.smartdata.ranger.authorizer.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl;
+import org.apache.ranger.plugin.policyengine.RangerAccessResult;
+import org.smartdata.ranger.authorizer.RangerSsmAuthorizer;
+import org.smartdata.ranger.authorizer.request.RangerAuthorizeRequest;
+import org.smartdata.ranger.plugin.impl.RangerSsmPlugin;
+
+import java.util.Date;
+
+@Slf4j
+public class RangerSsmAuthorizerImpl implements RangerSsmAuthorizer {
+
+ private final RangerSsmPlugin ssmPlugin;
+
+ public RangerSsmAuthorizerImpl() {
+ log.debug("Trying to create RangerSsmAuthorizer");
+ ssmPlugin = RangerSsmPlugin.getInstance();
+ log.debug("RangerSsmAuthorizer created");
+ }
+
+ @Override
+ public boolean authorize(RangerAuthorizeRequest request) {
+ log.debug("Perform authorization checking [user={}],[resources={}],[accessType={}]",
+ request.getUser(), request.getOperationDto().getResources(),
+ request.getOperationDto().getAccessType());
+ RangerAccessRequestImpl rangerRequest = new RangerAccessRequestImpl();
+ rangerRequest.setUser(request.getUser());
+ rangerRequest.setAccessType(request.getOperationDto().getAccessType());
+ rangerRequest.setAccessTime(new Date());
+ RangerAccessResourceImpl resource = new RangerAccessResourceImpl();
+ request.getOperationDto().getResources().forEach(resource::setValue);
+ rangerRequest.setResource(resource);
+ RangerAccessResult result = ssmPlugin.isAccessAllowed(rangerRequest);
+ boolean checkResult = result != null && result.getIsAllowed();
+ log.debug("Authorization check [result={}]", checkResult);
+ return checkResult;
+ }
+}
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/request/RangerAuthorizeRequest.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/request/RangerAuthorizeRequest.java
new file mode 100644
index 0000000000..53e744fc51
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/request/RangerAuthorizeRequest.java
@@ -0,0 +1,30 @@
+/**
+ * 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.smartdata.ranger.authorizer.request;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+@EqualsAndHashCode
+@Getter
+@RequiredArgsConstructor
+public class RangerAuthorizeRequest {
+ private final String user;
+ private final RangerOperationDto operationDto;
+}
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/request/RangerOperationDto.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/request/RangerOperationDto.java
new file mode 100644
index 0000000000..2e01150e80
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/authorizer/request/RangerOperationDto.java
@@ -0,0 +1,32 @@
+/**
+ * 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.smartdata.ranger.authorizer.request;
+
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Map;
+
+@EqualsAndHashCode
+@Getter
+@RequiredArgsConstructor
+public class RangerOperationDto {
+ private final String accessType;
+ private final Map resources;
+}
diff --git a/smart-ranger-plugin/src/main/java/org/smartdata/ranger/plugin/impl/RangerSsmPlugin.java b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/plugin/impl/RangerSsmPlugin.java
new file mode 100644
index 0000000000..83d11d0faa
--- /dev/null
+++ b/smart-ranger-plugin/src/main/java/org/smartdata/ranger/plugin/impl/RangerSsmPlugin.java
@@ -0,0 +1,42 @@
+/**
+ * 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.smartdata.ranger.plugin.impl;
+
+import org.apache.ranger.plugin.audit.RangerDefaultAuditHandler;
+import org.apache.ranger.plugin.service.RangerBasePlugin;
+
+public class RangerSsmPlugin extends RangerBasePlugin {
+
+ private static final RangerSsmPlugin instance = new RangerSsmPlugin();
+
+ private RangerSsmPlugin() {
+ super("ssm", "ssm");
+ init();
+ }
+
+ public static RangerSsmPlugin getInstance() {
+ return instance;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ RangerDefaultAuditHandler auditHandler = new RangerDefaultAuditHandler();
+ super.setResultProcessor(auditHandler);
+ }
+}
\ No newline at end of file
diff --git a/smart-web-server/pom.xml b/smart-web-server/pom.xml
index bae3bbed1d..f16063fec7 100644
--- a/smart-web-server/pom.xml
+++ b/smart-web-server/pom.xml
@@ -57,6 +57,12 @@
2.0.0-SNAPSHOT
+
+ org.smartdata
+ smart-ranger-plugin
+ 2.0.0-SNAPSHOT
+
+
org.springframework.boot
spring-boot-starter-web
@@ -135,6 +141,12 @@
junit
test
+
+
+ org.mockito
+ mockito-core
+ test
+
diff --git a/smart-web-server/src/main/java/org/smartdata/server/SmartRestServer.java b/smart-web-server/src/main/java/org/smartdata/server/SmartRestServer.java
index 4398d8b478..a70f041b6d 100644
--- a/smart-web-server/src/main/java/org/smartdata/server/SmartRestServer.java
+++ b/smart-web-server/src/main/java/org/smartdata/server/SmartRestServer.java
@@ -20,6 +20,7 @@
import org.smartdata.conf.SmartConf;
import org.smartdata.server.config.SsmContextInitializer;
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.actuate.autoconfigure.solr.SolrHealthContributorAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
@@ -69,7 +70,8 @@ private void injectToSpringProperties(SmartConf ssmConfig) {
@SpringBootApplication(exclude = {
// it's needed to prevent auto-registration of spring hazelcast node
// in the SSM hazelcast workers cluster
- HazelcastAutoConfiguration.class
+ HazelcastAutoConfiguration.class,
+ SolrHealthContributorAutoConfiguration.class
})
public static class RestServerApplication {
// empty class just to enable auto configs
diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/AuthorizationConfiguration.java b/smart-web-server/src/main/java/org/smartdata/server/config/AuthorizationConfiguration.java
new file mode 100644
index 0000000000..0f09b4f5ac
--- /dev/null
+++ b/smart-web-server/src/main/java/org/smartdata/server/config/AuthorizationConfiguration.java
@@ -0,0 +1,49 @@
+/**
+ * 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.smartdata.server.config;
+
+import org.smartdata.ranger.authorizer.impl.RangerSsmAuthorizerImpl;
+import org.smartdata.server.security.NoneAuthorizationManager;
+import org.smartdata.server.security.RangerAuthorizationManager;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+
+import static org.smartdata.server.config.ConfigKeys.RANGER_AUTHORIZATION_ENABLED;
+import static org.smartdata.server.config.ConfigKeys.WEB_SECURITY_ENABLED;
+
+@Configuration
+public class AuthorizationConfiguration {
+
+ @ConditionalOnProperty(
+ name = {WEB_SECURITY_ENABLED, RANGER_AUTHORIZATION_ENABLED},
+ havingValue = "true")
+ @Bean
+ public AuthorizationManager rangerAuthorizationManager() {
+ return new RangerAuthorizationManager(new RangerSsmAuthorizerImpl());
+ }
+
+ @ConditionalOnMissingBean(AuthorizationManager.class)
+ @Bean
+ public AuthorizationManager noneAuthorizationManager() {
+ return new NoneAuthorizationManager();
+ }
+}
diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java b/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java
index 90c6e8c840..9779654e35 100644
--- a/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java
+++ b/smart-web-server/src/main/java/org/smartdata/server/config/ConfigKeys.java
@@ -33,6 +33,8 @@ public class ConfigKeys {
public static final String SMART_REST_SERVER_KEYTAB_FILE_KEY =
"smart.rest.server.auth.spnego.keytab";
+ public static final String RANGER_AUTHORIZATION_ENABLED = "smart.rest.server.auth.ranger.enabled";
+
public static final String SSL_ENABLED = "smart.rest.server.ssl.enabled";
public static final boolean SSL_ENABLED_DEFAULT = false;
diff --git a/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java b/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java
index a355bfb9a1..2e460554b0 100644
--- a/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java
+++ b/smart-web-server/src/main/java/org/smartdata/server/config/SecurityConfiguration.java
@@ -25,8 +25,10 @@
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
+import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
@@ -53,11 +55,11 @@ public AuthenticationManager authenticationManager(
public SecurityFilterChain securityFilterChain(
HttpSecurity http,
SmartPrincipalManager principalManager,
- List authHttpConfigurers) throws Exception {
+ List authHttpConfigurers,
+ AuthorizationManager authorizationManager) throws Exception {
baseHttpSecurity(http)
- .authorizeRequests()
- .antMatchers(API_ENDPOINTS_PATTERN).authenticated()
- .and()
+ .authorizeHttpRequests(
+ authorize -> authorize.antMatchers(API_ENDPOINTS_PATTERN).access(authorizationManager))
.anonymous().disable()
.addFilterAfter(
new SmartPrincipalInitializerFilter(principalManager),
diff --git a/smart-web-server/src/main/java/org/smartdata/server/security/NoneAuthorizationManager.java b/smart-web-server/src/main/java/org/smartdata/server/security/NoneAuthorizationManager.java
new file mode 100644
index 0000000000..1f0d41851a
--- /dev/null
+++ b/smart-web-server/src/main/java/org/smartdata/server/security/NoneAuthorizationManager.java
@@ -0,0 +1,34 @@
+/**
+ * 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.smartdata.server.security;
+
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+
+import java.util.function.Supplier;
+
+public class NoneAuthorizationManager implements AuthorizationManager {
+
+ @Override
+ public AuthorizationDecision check(Supplier authentication,
+ RequestAuthorizationContext object) {
+ return new AuthorizationDecision(true);
+ }
+}
diff --git a/smart-web-server/src/main/java/org/smartdata/server/security/RangerAuthorizationManager.java b/smart-web-server/src/main/java/org/smartdata/server/security/RangerAuthorizationManager.java
new file mode 100644
index 0000000000..6284b38ec6
--- /dev/null
+++ b/smart-web-server/src/main/java/org/smartdata/server/security/RangerAuthorizationManager.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.smartdata.server.security;
+
+import com.google.common.collect.ImmutableMap;
+import lombok.RequiredArgsConstructor;
+import org.smartdata.ranger.SsmResourceAccessType;
+import org.smartdata.ranger.authorizer.RangerSsmAuthorizer;
+import org.smartdata.ranger.authorizer.request.RangerAuthorizeRequest;
+import org.smartdata.ranger.authorizer.request.RangerOperationDto;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+
+import javax.ws.rs.HttpMethod;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.util.regex.Pattern;
+
+import static org.smartdata.ranger.SsmRangerResource.ACTION;
+import static org.smartdata.ranger.SsmRangerResource.AUDIT;
+import static org.smartdata.ranger.SsmRangerResource.CLUSTER;
+import static org.smartdata.ranger.SsmRangerResource.RULE;
+
+@RequiredArgsConstructor
+public class RangerAuthorizationManager
+ implements AuthorizationManager {
+
+ private static final String ALL_VALUES_EXPRESSION = "*";
+ private static final
+ Map>>
+ OPERATION_MAP =
+ new HashMap>>() {{
+ //cluster
+ put(Pattern.compile("^/api/v2/cluster/nodes$"), ImmutableMap.of(HttpMethod.GET,
+ request -> CLUSTER.getRangerOperationDto(SsmResourceAccessType.VIEW,
+ ALL_VALUES_EXPRESSION)));
+ //rule
+ put(Pattern.compile("^/api/v2/rules$"), ImmutableMap.of(
+ HttpMethod.POST,
+ request -> RULE.getRangerOperationDto(SsmResourceAccessType.CREATE,
+ ALL_VALUES_EXPRESSION),
+ HttpMethod.GET,
+ request -> RULE.getRangerOperationDto(SsmResourceAccessType.VIEW,
+ ALL_VALUES_EXPRESSION)));
+ put(Pattern.compile("^/api/v2/rules/\\d+$"), ImmutableMap.of(
+ HttpMethod.GET,
+ request -> RULE.getRangerOperationDto(SsmResourceAccessType.VIEW,
+ ALL_VALUES_EXPRESSION),
+ HttpMethod.DELETE,
+ request -> RULE.getRangerOperationDto(SsmResourceAccessType.DELETE,
+ ALL_VALUES_EXPRESSION)));
+ //action
+ put(Pattern.compile("^/api/v2/actions$"), ImmutableMap.of(
+ HttpMethod.POST,
+ request -> ACTION.getRangerOperationDto(SsmResourceAccessType.SUBMIT,
+ ALL_VALUES_EXPRESSION),
+ HttpMethod.GET,
+ request -> ACTION.getRangerOperationDto(SsmResourceAccessType.VIEW,
+ ALL_VALUES_EXPRESSION)));
+ put(Pattern.compile("^/api/v2/actions/\\d+$"), ImmutableMap.of(
+ HttpMethod.GET,
+ request -> ACTION.getRangerOperationDto(SsmResourceAccessType.VIEW,
+ ALL_VALUES_EXPRESSION)
+ ));
+ //audit
+ put(Pattern.compile("^/api/v2/audit/events$"), ImmutableMap.of(HttpMethod.GET,
+ request -> AUDIT.getRangerOperationDto(SsmResourceAccessType.VIEW,
+ ALL_VALUES_EXPRESSION)));
+ }};
+
+ private final RangerSsmAuthorizer rangerSsmAuthorizer;
+
+ @Override
+ public AuthorizationDecision check(Supplier authentication,
+ RequestAuthorizationContext request) {
+ return new AuthorizationDecision(
+ OPERATION_MAP.entrySet().stream()
+ .filter(entry -> entry.getKey().matcher(request.getRequest().getServletPath()).find())
+ .map(entry -> Optional.ofNullable(entry.getValue().get(
+ request.getRequest().getMethod()))
+ .map(func -> rangerSsmAuthorizer.authorize(
+ new RangerAuthorizeRequest(authentication.get().getName(),
+ func.apply(request)))))
+ .filter(Optional::isPresent)
+ .map(Optional::get)
+ .findAny()
+ .orElse(true));
+ }
+}
diff --git a/smart-web-server/src/test/java/org/smartdata/server/security/RangerAuthorizationManagerTest.java b/smart-web-server/src/test/java/org/smartdata/server/security/RangerAuthorizationManagerTest.java
new file mode 100644
index 0000000000..cbc4b65ac0
--- /dev/null
+++ b/smart-web-server/src/test/java/org/smartdata/server/security/RangerAuthorizationManagerTest.java
@@ -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.
+ */
+package org.smartdata.server.security;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.smartdata.ranger.SsmResourceAccessType;
+import org.smartdata.ranger.authorizer.RangerSsmAuthorizer;
+import org.smartdata.ranger.authorizer.request.RangerAuthorizeRequest;
+import org.smartdata.ranger.authorizer.request.RangerOperationDto;
+import org.springframework.security.authorization.AuthorizationDecision;
+import org.springframework.security.authorization.AuthorizationManager;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.HttpMethod;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RangerAuthorizationManagerTest {
+ private final RangerSsmAuthorizer rangerSsmAuthorizer = mock(RangerSsmAuthorizer.class);
+ private AuthorizationManager authorizationManager;
+
+ @Before
+ public void setUp() {
+ authorizationManager = new RangerAuthorizationManager(rangerSsmAuthorizer);
+ }
+
+ @Test
+ public void testOperationEntriesFound() {
+ String user = "user";
+ ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(
+ RangerAuthorizeRequest.class);
+ when(rangerSsmAuthorizer.authorize(requestArgumentCaptor.capture())).thenReturn(true);
+ HttpServletRequest httpRequest1 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest2 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest3 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest4 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest5 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest6 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest7 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest8 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest9 = mock(HttpServletRequest.class);
+ when(httpRequest1.getServletPath()).thenReturn("/api/v2/actions/11");
+ when(httpRequest1.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(httpRequest2.getServletPath()).thenReturn("/api/v2/cluster/nodes");
+ when(httpRequest2.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(httpRequest3.getServletPath()).thenReturn("/api/v2/rules");
+ when(httpRequest3.getMethod()).thenReturn(HttpMethod.POST);
+
+ when(httpRequest4.getServletPath()).thenReturn("/api/v2/rules/9");
+ when(httpRequest4.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(httpRequest5.getServletPath()).thenReturn("/api/v2/rules/2");
+ when(httpRequest5.getMethod()).thenReturn(HttpMethod.DELETE);
+
+ when(httpRequest6.getServletPath()).thenReturn("/api/v2/actions");
+ when(httpRequest6.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(httpRequest7.getServletPath()).thenReturn("/api/v2/actions/33");
+ when(httpRequest7.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(httpRequest8.getServletPath()).thenReturn("/api/v2/audit/events");
+ when(httpRequest8.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(httpRequest9.getServletPath()).thenReturn("/api/v2/actions");
+ when(httpRequest9.getMethod()).thenReturn(HttpMethod.POST);
+
+ Authentication authentication = mock(Authentication.class);
+ when(authentication.getName()).thenReturn(user);
+ Map requests =
+ new HashMap() {{
+ put(new RequestAuthorizationContext(httpRequest1),
+ new RangerOperationDto(SsmResourceAccessType.VIEW.name(),
+ ImmutableMap.of("action", "*")));
+ put(new RequestAuthorizationContext(httpRequest2),
+ new RangerOperationDto(SsmResourceAccessType.VIEW.name(),
+ ImmutableMap.of("cluster", "*")));
+ put(new RequestAuthorizationContext(httpRequest3),
+ new RangerOperationDto(SsmResourceAccessType.CREATE.name(),
+ ImmutableMap.of("rule", "*")));
+ put(new RequestAuthorizationContext(httpRequest4),
+ new RangerOperationDto(SsmResourceAccessType.VIEW.name(),
+ ImmutableMap.of("rule", "*")));
+ put(new RequestAuthorizationContext(httpRequest5),
+ new RangerOperationDto(SsmResourceAccessType.DELETE.name(),
+ ImmutableMap.of("rule", "*")));
+ put(new RequestAuthorizationContext(httpRequest6),
+ new RangerOperationDto(SsmResourceAccessType.VIEW.name(),
+ ImmutableMap.of("action", "*")));
+ put(new RequestAuthorizationContext(httpRequest7),
+ new RangerOperationDto(SsmResourceAccessType.VIEW.name(),
+ ImmutableMap.of("action", "*")));
+ put(new RequestAuthorizationContext(httpRequest8),
+ new RangerOperationDto(SsmResourceAccessType.VIEW.name(),
+ ImmutableMap.of("audit", "*")));
+ put(new RequestAuthorizationContext(httpRequest9),
+ new RangerOperationDto(SsmResourceAccessType.SUBMIT.name(),
+ ImmutableMap.of("action", "*")));
+ }};
+
+ requests.forEach((req, value) -> {
+ AuthorizationDecision result = authorizationManager.check(() -> authentication, req);
+ assertTrue(result.isGranted());
+ RangerAuthorizeRequest rangerRequest = requestArgumentCaptor.getValue();
+ assertEquals(user, rangerRequest.getUser());
+ assertEquals(
+ value,
+ rangerRequest.getOperationDto());
+ });
+ }
+
+ @Test
+ public void testOperationNotFound() {
+ String user = "user";
+ HttpServletRequest httpRequest1 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest2 = mock(HttpServletRequest.class);
+ HttpServletRequest httpRequest3 = mock(HttpServletRequest.class);
+ when(httpRequest1.getServletPath()).thenReturn("/api/v2/actions/11/test");
+ when(httpRequest1.getMethod()).thenReturn(HttpMethod.GET);
+ when(httpRequest2.getServletPath()).thenReturn("/api/v2/rules/22/test");
+ when(httpRequest2.getMethod()).thenReturn(HttpMethod.GET);
+ when(httpRequest3.getServletPath()).thenReturn("/api/v2/actions/test");
+ when(httpRequest3.getMethod()).thenReturn(HttpMethod.GET);
+
+ when(rangerSsmAuthorizer.authorize(any())).thenReturn(false);
+ List requests =
+ Arrays.asList(new RequestAuthorizationContext(httpRequest1),
+ new RequestAuthorizationContext(httpRequest2),
+ new RequestAuthorizationContext(httpRequest3)
+ );
+ Authentication authentication = mock(Authentication.class);
+ when(authentication.getName()).thenReturn(user);
+
+ requests.forEach(req -> {
+ AuthorizationDecision result = authorizationManager.check(() -> authentication, req);
+ assertTrue(result.isGranted());
+ });
+ }
+}