This is an automated email from the ASF dual-hosted git repository.

pinal-shah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git


The following commit(s) were added to refs/heads/master by this push:
     new f9e482f697 ATLAS-5388: Add topic-scoped authorization for REST 
notification POST. (#753)
f9e482f697 is described below

commit f9e482f697d0fafd702f44cb5e63e861e9156b74
Author: UmeshPatil-1 <[email protected]>
AuthorDate: Tue Oct 27 16:33:45 2026 +0530

    ATLAS-5388: Add topic-scoped authorization for REST notification POST. 
(#753)
---
 .../atlas/authorize/AtlasAuthorizationUtils.java   | 36 ++++++++++++
 .../atlas/authorize/AtlasAuthorizeConstants.java   | 25 ++++++++
 .../apache/atlas/authorize/AtlasAuthorizer.java    |  4 ++
 .../atlas/authorize/AtlasNoneAuthorizer.java       |  5 ++
 .../atlas/authorize/AtlasNotificationRequest.java  | 44 +++++++++++++++
 .../org/apache/atlas/authorize/AtlasPrivilege.java |  2 +-
 .../authorize/simple/AtlasSimpleAuthorizer.java    | 53 +++++++++++++++++
 .../authorize/simple/AtlasSimpleAuthzPolicy.java   | 66 +++++++++++++++++++---
 .../main/resources/atlas-simple-authz-policy.json  | 18 +++++-
 .../AtlasAuthorizationUtilsNotificationTest.java   | 58 +++++++++++++++++++
 .../simple/AtlasSimpleAuthorizerTest.java          | 60 ++++++++++++++++++++
 .../test/resources/atlas-simple-authz-policy.json  | 16 ++++++
 distro/src/conf/atlas-simple-authz-policy.json     | 18 +++++-
 .../rest/web/rest/NotificationREST.java            |  4 +-
 14 files changed, 396 insertions(+), 13 deletions(-)

diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
index a1c68b3a39..b40d03b307 100644
--- 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java
@@ -80,6 +80,14 @@ public class AtlasAuthorizationUtils {
         }
     }
 
+    public static void verifyAccess(AtlasNotificationRequest request, 
Object... errorMsgParams) throws AtlasBaseException {
+        if (!isAccessAllowed(request)) {
+            String message = (errorMsgParams != null && errorMsgParams.length 
> 0) ? StringUtils.join(errorMsgParams) : "";
+
+            throw new AtlasBaseException(AtlasErrorCode.UNAUTHORIZED_ACCESS, 
request.getUser(), message);
+        }
+    }
+
     public static void scrubSearchResults(AtlasSearchResultScrubRequest 
request) {
         String userName = getCurrentUserName();
 
@@ -231,6 +239,34 @@ public class AtlasAuthorizationUtils {
         return ret;
     }
 
+    public static boolean isAccessAllowed(AtlasNotificationRequest request) {
+        MetricRecorder metric = 
RequestContext.get().startMetricRecord("isAccessAllowed");
+
+        boolean ret      = false;
+        String  userName = getCurrentUserName();
+
+        if (request == null || StringUtils.isBlank(request.getTopicName())) {
+            ret = false;
+        } else if (StringUtils.isNotEmpty(userName)) {
+            try {
+                AtlasAuthorizer authorizer = 
AtlasAuthorizerFactory.getAtlasAuthorizer();
+
+                request.setUser(userName, getCurrentUserGroups());
+                
request.setClientIPAddress(RequestContext.get().getClientIPAddress());
+                
request.setForwardedAddresses(RequestContext.get().getForwardedAddresses());
+                
request.setRemoteIPAddress(RequestContext.get().getClientIPAddress());
+
+                ret = authorizer.isAccessAllowed(request);
+            } catch (AtlasAuthorizationException e) {
+                LOG.error("Unable to obtain AtlasAuthorizer", e);
+            }
+        }
+
+        RequestContext.get().endMetricRecord(metric);
+
+        return ret;
+    }
+
     public static void filterTypesDef(AtlasTypesDefFilterRequest request) {
         MetricRecorder metric   = 
RequestContext.get().startMetricRecord("filterTypesDef");
         String         userName = getCurrentUserName();
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizeConstants.java
 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizeConstants.java
new file mode 100644
index 0000000000..8dc4f8d1df
--- /dev/null
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizeConstants.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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.atlas.authorize;
+
+public final class AtlasAuthorizeConstants {
+    public static final String NOTIFICATION_TOPIC_RESOURCE_TYPE = 
"notification-topic";
+
+    private AtlasAuthorizeConstants() {
+    }
+}
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java
index 845b05f9a6..1af12af87b 100644
--- 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java
@@ -65,6 +65,10 @@ public interface AtlasAuthorizer {
         return true;
     }
 
+    default boolean isAccessAllowed(AtlasNotificationRequest request) throws 
AtlasAuthorizationException {
+        return false;
+    }
+
     /**
      * scrub search-results to handle entities for which the user doesn't have 
access
      * @param request
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java
 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java
index 86909d9249..971bd4e148 100644
--- 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasNoneAuthorizer.java
@@ -49,6 +49,11 @@ public class AtlasNoneAuthorizer implements AtlasAuthorizer {
         return true;
     }
 
+    @Override
+    public boolean isAccessAllowed(AtlasNotificationRequest request) {
+        return true;
+    }
+
     @Override
     public void scrubSearchResults(AtlasSearchResultScrubRequest request) {
     }
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasNotificationRequest.java
 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasNotificationRequest.java
new file mode 100644
index 0000000000..f9c0b53a75
--- /dev/null
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasNotificationRequest.java
@@ -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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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.atlas.authorize;
+
+public class AtlasNotificationRequest extends AtlasAccessRequest {
+    private final String topicName;
+
+    public AtlasNotificationRequest(AtlasPrivilege action, String topicName) {
+        super(action);
+
+        this.topicName = topicName;
+    }
+
+    public String getTopicName() {
+        return topicName;
+    }
+
+    public String getResourceType() {
+        return AtlasAuthorizeConstants.NOTIFICATION_TOPIC_RESOURCE_TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return "AtlasNotificationRequest[action=" + getAction() + ", 
topicName=" + topicName + ", resourceType=" + getResourceType() +
+                ", accessTime=" + getAccessTime() + ", user=" + getUser() + ", 
userGroups=" + getUserGroups() +
+                ", clientIPAddress=" + getClientIPAddress() + ", 
forwardedAddresses=" + getForwardedAddresses() +
+                ", remoteIPAddress=" + getRemoteIPAddress() + "]";
+    }
+}
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/AtlasPrivilege.java 
b/authorization/src/main/java/org/apache/atlas/authorize/AtlasPrivilege.java
index 68402fd507..e02d89b708 100644
--- a/authorization/src/main/java/org/apache/atlas/authorize/AtlasPrivilege.java
+++ b/authorization/src/main/java/org/apache/atlas/authorize/AtlasPrivilege.java
@@ -48,7 +48,7 @@ public enum AtlasPrivilege {
 
     ADMIN_AUDITS("admin-audits"),
 
-    SERVICE_NOTIFICATION_POST("service-notification-post");
+    POST_NOTIFICATION("post-notification");
 
     private final String type;
 
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java
 
b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java
index 2de553b047..98c545a65b 100755
--- 
a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizer.java
@@ -24,6 +24,7 @@ import org.apache.atlas.authorize.AtlasAccessRequest;
 import org.apache.atlas.authorize.AtlasAdminAccessRequest;
 import org.apache.atlas.authorize.AtlasAuthorizer;
 import org.apache.atlas.authorize.AtlasEntityAccessRequest;
+import org.apache.atlas.authorize.AtlasNotificationRequest;
 import org.apache.atlas.authorize.AtlasPrivilege;
 import org.apache.atlas.authorize.AtlasRelationshipAccessRequest;
 import org.apache.atlas.authorize.AtlasSearchResultScrubRequest;
@@ -32,6 +33,7 @@ import org.apache.atlas.authorize.AtlasTypesDefFilterRequest;
 import 
org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.AtlasAdminPermission;
 import org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.AtlasAuthzRole;
 import 
org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.AtlasEntityPermission;
+import 
org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.AtlasNotificationPermission;
 import 
org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.AtlasRelationshipPermission;
 import 
org.apache.atlas.authorize.simple.AtlasSimpleAuthzPolicy.AtlasTypePermission;
 import org.apache.atlas.model.discovery.AtlasSearchResult;
@@ -253,6 +255,45 @@ public final class AtlasSimpleAuthorizer implements 
AtlasAuthorizer {
         return hasEnd1EntityAccess && hasEnd2EntityAccess;
     }
 
+    @Override
+    public boolean isAccessAllowed(AtlasNotificationRequest request) {
+        LOG.debug("==> SimpleAtlasAuthorizer.isAccessAllowed({})", request);
+
+        if (request == null || StringUtils.isBlank(request.getTopicName())) {
+            LOG.debug("<== SimpleAtlasAuthorizer.isAccessAllowed({}): false 
(missing topic)", request);
+
+            return false;
+        }
+
+        boolean     ret   = false;
+        Set<String> roles = getRoles(request.getUser(), 
request.getUserGroups());
+
+        for (String role : roles) {
+            List<AtlasNotificationPermission> permissions = 
getNotificationPermissionsForRole(role);
+
+            if (permissions != null) {
+                final String action    = request.getAction() != null ? 
request.getAction().getType() : null;
+                final String topicName = request.getTopicName();
+
+                for (AtlasNotificationPermission permission : permissions) {
+                    if (isMatch(action, permission.getPrivileges()) && 
isMatch(topicName, permission.getTopicNames())) {
+                        ret = true;
+
+                        break;
+                    }
+                }
+            }
+
+            if (ret) {
+                break;
+            }
+        }
+
+        LOG.debug("<== SimpleAtlasAuthorizer.isAccessAllowed({}): {}", 
request, ret);
+
+        return ret;
+    }
+
     @Override
     public void scrubSearchResults(AtlasSearchResultScrubRequest request) {
         LOG.debug("==> SimpleAtlasAuthorizer.scrubSearchResults({})", request);
@@ -370,6 +411,18 @@ public final class AtlasSimpleAuthorizer implements 
AtlasAuthorizer {
         return ret;
     }
 
+    private List<AtlasNotificationPermission> 
getNotificationPermissionsForRole(String roleName) {
+        List<AtlasNotificationPermission> ret = null;
+
+        if (authzPolicy != null && roleName != null) {
+            AtlasAuthzRole role = authzPolicy.getRoles().get(roleName);
+
+            ret = role != null ? role.getNotificationPermissions() : null;
+        }
+
+        return ret;
+    }
+
     private boolean isMatch(String value, List<String> patterns) {
         boolean ret = value == null;
 
diff --git 
a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthzPolicy.java
 
b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthzPolicy.java
index b4899a8bdf..88dc5f330c 100644
--- 
a/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthzPolicy.java
+++ 
b/authorization/src/main/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthzPolicy.java
@@ -76,19 +76,25 @@ public class AtlasSimpleAuthzPolicy implements Serializable 
{
     public static class AtlasAuthzRole implements Serializable {
         private static final long serialVersionUID = 1L;
 
-        private List<AtlasAdminPermission>        adminPermissions;
-        private List<AtlasTypePermission>         typePermissions;
-        private List<AtlasEntityPermission>       entityPermissions;
-        private List<AtlasRelationshipPermission> relationshipPermissions;
+        private List<AtlasAdminPermission>         adminPermissions;
+        private List<AtlasTypePermission>          typePermissions;
+        private List<AtlasEntityPermission>        entityPermissions;
+        private List<AtlasRelationshipPermission>  relationshipPermissions;
+        private List<AtlasNotificationPermission>  notificationPermissions;
 
         public AtlasAuthzRole() {
         }
 
         public AtlasAuthzRole(List<AtlasAdminPermission> adminPermissions, 
List<AtlasTypePermission> typePermissions, List<AtlasEntityPermission> 
entityPermissions, List<AtlasRelationshipPermission> relationshipPermissions) {
-            this.adminPermissions        = adminPermissions;
-            this.typePermissions         = typePermissions;
-            this.entityPermissions       = entityPermissions;
-            this.relationshipPermissions = relationshipPermissions;
+            this(adminPermissions, typePermissions, entityPermissions, 
relationshipPermissions, null);
+        }
+
+        public AtlasAuthzRole(List<AtlasAdminPermission> adminPermissions, 
List<AtlasTypePermission> typePermissions, List<AtlasEntityPermission> 
entityPermissions, List<AtlasRelationshipPermission> relationshipPermissions, 
List<AtlasNotificationPermission> notificationPermissions) {
+            this.adminPermissions         = adminPermissions;
+            this.typePermissions          = typePermissions;
+            this.entityPermissions        = entityPermissions;
+            this.relationshipPermissions  = relationshipPermissions;
+            this.notificationPermissions  = notificationPermissions;
         }
 
         public List<AtlasAdminPermission> getAdminPermissions() {
@@ -122,6 +128,14 @@ public class AtlasSimpleAuthzPolicy implements 
Serializable {
         public void 
setRelationshipPermissions(List<AtlasRelationshipPermission> 
relationshipPermissions) {
             this.relationshipPermissions = relationshipPermissions;
         }
+
+        public List<AtlasNotificationPermission> getNotificationPermissions() {
+            return notificationPermissions;
+        }
+
+        public void 
setNotificationPermissions(List<AtlasNotificationPermission> 
notificationPermissions) {
+            this.notificationPermissions = notificationPermissions;
+        }
     }
 
     @JsonAutoDetect(getterVisibility = PUBLIC_ONLY, setterVisibility = 
PUBLIC_ONLY, fieldVisibility = NONE)
@@ -391,4 +405,40 @@ public class AtlasSimpleAuthzPolicy implements 
Serializable {
             this.end2EntityClassification = end2EntityClassification;
         }
     }
+
+    @JsonAutoDetect(getterVisibility = PUBLIC_ONLY, setterVisibility = 
PUBLIC_ONLY, fieldVisibility = NONE)
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    @JsonIgnoreProperties(ignoreUnknown = true)
+    @XmlRootElement
+    @XmlAccessorType(XmlAccessType.PROPERTY)
+    public static class AtlasNotificationPermission implements Serializable {
+        private static final long serialVersionUID = 1L;
+
+        private List<String> privileges;
+        private List<String> topicNames;
+
+        public AtlasNotificationPermission() {
+        }
+
+        public AtlasNotificationPermission(List<String> privileges, 
List<String> topicNames) {
+            this.privileges  = privileges;
+            this.topicNames  = topicNames;
+        }
+
+        public List<String> getPrivileges() {
+            return privileges;
+        }
+
+        public void setPrivileges(List<String> privileges) {
+            this.privileges = privileges;
+        }
+
+        public List<String> getTopicNames() {
+            return topicNames;
+        }
+
+        public void setTopicNames(List<String> topicNames) {
+            this.topicNames = topicNames;
+        }
+    }
 }
diff --git a/authorization/src/main/resources/atlas-simple-authz-policy.json 
b/authorization/src/main/resources/atlas-simple-authz-policy.json
index bd9d5ed3cc..38b1ddf562 100644
--- a/authorization/src/main/resources/atlas-simple-authz-policy.json
+++ b/authorization/src/main/resources/atlas-simple-authz-policy.json
@@ -36,6 +36,21 @@
           "end2EntityId":             [ ".*" ],
           "end2EntityClassification": [ ".*" ]
         }
+      ],
+      "notificationPermissions": [
+        {
+          "privileges":  [ ".*" ],
+          "topicNames":  [ ".*" ]
+        }
+      ]
+    },
+
+    "HIVE_HOOK_SERVICE": {
+      "notificationPermissions": [
+        {
+          "privileges":  [ "post-notification" ],
+          "topicNames":  [ "ATLAS_HOOK" ]
+        }
       ]
     },
 
@@ -84,7 +99,8 @@
 
   "userRoles": {
     "admin": [ "ROLE_ADMIN" ],
-    "rangertagsync": [ "DATA_SCIENTIST" ]
+    "rangertagsync": [ "DATA_SCIENTIST" ],
+    "hivehook": [ "HIVE_HOOK_SERVICE" ]
   },
 
   "groupRoles": {
diff --git 
a/authorization/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsNotificationTest.java
 
b/authorization/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsNotificationTest.java
new file mode 100644
index 0000000000..db1cab829f
--- /dev/null
+++ 
b/authorization/src/test/java/org/apache/atlas/authorize/AtlasAuthorizationUtilsNotificationTest.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.apache.atlas.authorize;
+
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.testng.AssertJUnit;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+public class AtlasAuthorizationUtilsNotificationTest {
+    private static final String TOPIC_ATLAS_HOOK = "ATLAS_HOOK";
+
+    @AfterMethod
+    public void clearSecurityContext() {
+        SecurityContextHolder.clearContext();
+    }
+
+    @Test
+    public void testNotificationAccessDeniedWhenTopicNull() {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, null);
+
+        AssertJUnit.assertFalse("null topic must be denied at authorization 
utils layer",
+                AtlasAuthorizationUtils.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testNotificationAccessDeniedWhenTopicBlank() {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, "  ");
+
+        AssertJUnit.assertFalse("blank topic must be denied at authorization 
utils layer",
+                AtlasAuthorizationUtils.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testNotificationAccessDeniedWhenNoAuthenticatedUser() {
+        SecurityContextHolder.clearContext();
+
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, TOPIC_ATLAS_HOOK);
+
+        AssertJUnit.assertFalse("empty username must fail closed for 
notification ingress",
+                AtlasAuthorizationUtils.isAccessAllowed(request));
+    }
+}
diff --git 
a/authorization/src/test/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizerTest.java
 
b/authorization/src/test/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizerTest.java
index d61ae7afca..7f00e4df1c 100644
--- 
a/authorization/src/test/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizerTest.java
+++ 
b/authorization/src/test/java/org/apache/atlas/authorize/simple/AtlasSimpleAuthorizerTest.java
@@ -21,6 +21,7 @@ import org.apache.atlas.authorize.AtlasAuthorizationException;
 import org.apache.atlas.authorize.AtlasAuthorizer;
 import org.apache.atlas.authorize.AtlasAuthorizerFactory;
 import org.apache.atlas.authorize.AtlasEntityAccessRequest;
+import org.apache.atlas.authorize.AtlasNotificationRequest;
 import org.apache.atlas.authorize.AtlasPrivilege;
 import org.apache.atlas.model.instance.AtlasClassification;
 import org.apache.atlas.model.instance.AtlasEntityHeader;
@@ -50,6 +51,10 @@ public class AtlasSimpleAuthorizerTest {
     private static final String USER_FINANCE_PII      = "financePII";
     private static final String USER_IN_ADMIN_GROUP   = "admin-group-user";
     private static final String USER_IN_UNKNOWN_GROUP = "unknown-group-user";
+    private static final String USER_HIVE_HOOK        = "hivehook";
+
+    private static final String TOPIC_ATLAS_HOOK      = "ATLAS_HOOK";
+    private static final String TOPIC_ATLAS_ENTITIES  = "ATLAS_ENTITIES";
 
     private static final Map<String, Set<String>> USER_GROUPS       = new 
HashMap<>();
     private static final List<AtlasPrivilege>     ENTITY_PRIVILEGES = new 
ArrayList<>();
@@ -309,6 +314,60 @@ public class AtlasSimpleAuthorizerTest {
         }
     }
 
+    @Test
+    public void testPostNotificationAllowedForAdminUser() throws 
AtlasAuthorizationException {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, TOPIC_ATLAS_HOOK);
+
+        setUser(request, USER_ADMIN);
+
+        AssertJUnit.assertTrue("admin should be allowed to post notification", 
authorizer.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testPostNotificationDeniedForDataScientistUser() throws 
AtlasAuthorizationException {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, TOPIC_ATLAS_HOOK);
+
+        setUser(request, USER_DATA_SCIENTIST);
+
+        AssertJUnit.assertFalse("data scientist should be denied to post 
notification", authorizer.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testPostNotificationAllowedForTopicSpecificRole() throws 
AtlasAuthorizationException {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, TOPIC_ATLAS_HOOK);
+
+        setUser(request, USER_HIVE_HOOK);
+
+        AssertJUnit.assertTrue("hivehook should be allowed to post to 
ATLAS_HOOK", authorizer.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testPostNotificationDeniedForTopicSpecificRoleOnOtherTopic() 
throws AtlasAuthorizationException {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, 
TOPIC_ATLAS_ENTITIES);
+
+        setUser(request, USER_HIVE_HOOK);
+
+        AssertJUnit.assertFalse("hivehook should be denied to post to 
ATLAS_ENTITIES", authorizer.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testPostNotificationDeniedForNullTopic() throws 
AtlasAuthorizationException {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, null);
+
+        setUser(request, USER_ADMIN);
+
+        AssertJUnit.assertFalse("null topic must not bypass topic scoping", 
authorizer.isAccessAllowed(request));
+    }
+
+    @Test
+    public void testPostNotificationDeniedForBlankTopic() throws 
AtlasAuthorizationException {
+        AtlasNotificationRequest request = new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, "   ");
+
+        setUser(request, USER_ADMIN);
+
+        AssertJUnit.assertFalse("blank topic must not bypass topic scoping", 
authorizer.isAccessAllowed(request));
+    }
+
     @Test
     public void testBusinessMetadata() {
         try {
@@ -351,6 +410,7 @@ public class AtlasSimpleAuthorizerTest {
         USER_GROUPS.put(USER_FINANCE_PII, 
Collections.singleton("FINANCE_PII"));
         USER_GROUPS.put(USER_IN_ADMIN_GROUP, 
Collections.singleton("ROLE_ADMIN"));
         USER_GROUPS.put(USER_IN_UNKNOWN_GROUP, 
Collections.singleton("UNKNOWN_GROUP"));
+        USER_GROUPS.put(USER_HIVE_HOOK, Collections.emptySet());
 
         ENTITY_PRIVILEGES.add(AtlasPrivilege.ENTITY_CREATE);
         ENTITY_PRIVILEGES.add(AtlasPrivilege.ENTITY_UPDATE);
diff --git a/authorization/src/test/resources/atlas-simple-authz-policy.json 
b/authorization/src/test/resources/atlas-simple-authz-policy.json
index cada904470..376cea1380 100644
--- a/authorization/src/test/resources/atlas-simple-authz-policy.json
+++ b/authorization/src/test/resources/atlas-simple-authz-policy.json
@@ -35,6 +35,21 @@
           "endTwoEntityId":             [ ".*" ],
           "endTwoEntityClassification": [ ".*" ]
         }
+      ],
+      "notificationPermissions": [
+        {
+          "privileges":  [ ".*" ],
+          "topicNames":  [ ".*" ]
+        }
+      ]
+    },
+
+    "HIVE_HOOK_SERVICE": {
+      "notificationPermissions": [
+        {
+          "privileges":  [ "post-notification" ],
+          "topicNames":  [ "ATLAS_HOOK" ]
+        }
       ]
     },
 
@@ -133,6 +148,7 @@
   "userRoles": {
     "admin":          [ "ROLE_ADMIN" ],
     "rangertagsync":  [ "DATA_SCIENTIST" ],
+    "hivehook":       [ "HIVE_HOOK_SERVICE" ],
     "dataScientist1": [ "DATA_SCIENTIST"],
     "dataSteward1":   [ "DATA_STEWARD"]
   },
diff --git a/distro/src/conf/atlas-simple-authz-policy.json 
b/distro/src/conf/atlas-simple-authz-policy.json
index a5761cc0e9..1d8e9ef1df 100644
--- a/distro/src/conf/atlas-simple-authz-policy.json
+++ b/distro/src/conf/atlas-simple-authz-policy.json
@@ -36,6 +36,21 @@
           "end2EntityId":             [ ".*" ],
           "end2EntityClassification": [ ".*" ]
         }
+      ],
+      "notificationPermissions": [
+        {
+          "privileges":  [ ".*" ],
+          "topicNames":  [ ".*" ]
+        }
+      ]
+    },
+
+    "HIVE_HOOK_SERVICE": {
+      "notificationPermissions": [
+        {
+          "privileges":  [ "post-notification" ],
+          "topicNames":  [ "ATLAS_HOOK" ]
+        }
       ]
     },
 
@@ -83,7 +98,8 @@
 
   "userRoles": {
     "admin": [ "ROLE_ADMIN" ],
-    "rangertagsync": [ "DATA_SCIENTIST" ]
+    "rangertagsync": [ "DATA_SCIENTIST" ],
+    "hivehook": [ "HIVE_HOOK_SERVICE" ]
   },
 
   "groupRoles": {
diff --git 
a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java
 
b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java
index bbecba3a63..ca9618ad09 100644
--- 
a/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java
+++ 
b/rest-notification-webapp/src/main/java/org/apache/atlas/notification/rest/web/rest/NotificationREST.java
@@ -21,8 +21,8 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.node.ArrayNode;
 import org.apache.atlas.AtlasConfiguration;
 import org.apache.atlas.AtlasErrorCode;
-import org.apache.atlas.authorize.AtlasAdminAccessRequest;
 import org.apache.atlas.authorize.AtlasAuthorizationUtils;
+import org.apache.atlas.authorize.AtlasNotificationRequest;
 import org.apache.atlas.authorize.AtlasPrivilege;
 import org.apache.atlas.exception.AtlasBaseException;
 import org.apache.atlas.hook.AtlasHook;
@@ -91,7 +91,7 @@ public class NotificationREST {
     public void handleNotifications(@PathParam("topicName") String topicName, 
@Context HttpServletRequest request) throws AtlasBaseException, IOException {
         LOG.debug("Handling notifications for topic {}", topicName);
 
-        AtlasAuthorizationUtils.verifyAccess(new 
AtlasAdminAccessRequest(AtlasPrivilege.SERVICE_NOTIFICATION_POST), "post on 
rest notification service");
+        AtlasAuthorizationUtils.verifyAccess(new 
AtlasNotificationRequest(AtlasPrivilege.POST_NOTIFICATION, topicName), "post on 
notification topic ", topicName);
 
         if (!TOPICS.contains(topicName)) {
             throw new AtlasBaseException(AtlasErrorCode.INVALID_TOPIC_NAME, 
topicName);

Reply via email to