Return-Path: X-Original-To: archive-asf-public-internal@cust-asf2.ponee.io Delivered-To: archive-asf-public-internal@cust-asf2.ponee.io Received: from cust-asf.ponee.io (cust-asf.ponee.io [163.172.22.183]) by cust-asf2.ponee.io (Postfix) with ESMTP id D980B2004C8 for ; Mon, 9 May 2016 19:09:41 +0200 (CEST) Received: by cust-asf.ponee.io (Postfix) id D81531609A8; Mon, 9 May 2016 17:09:41 +0000 (UTC) Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by cust-asf.ponee.io (Postfix) with SMTP id 93F8D16099C for ; Mon, 9 May 2016 19:09:39 +0200 (CEST) Received: (qmail 80043 invoked by uid 500); 9 May 2016 17:09:38 -0000 Mailing-List: contact commits-help@atlas.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@atlas.incubator.apache.org Delivered-To: mailing list commits@atlas.incubator.apache.org Received: (qmail 80034 invoked by uid 99); 9 May 2016 17:09:38 -0000 Received: from pnap-us-west-generic-nat.apache.org (HELO spamd1-us-west.apache.org) (209.188.14.142) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 09 May 2016 17:09:38 +0000 Received: from localhost (localhost [127.0.0.1]) by spamd1-us-west.apache.org (ASF Mail Server at spamd1-us-west.apache.org) with ESMTP id 6F951C718F for ; Mon, 9 May 2016 17:09:37 +0000 (UTC) X-Virus-Scanned: Debian amavisd-new at spamd1-us-west.apache.org X-Spam-Flag: NO X-Spam-Score: -5.299 X-Spam-Level: X-Spam-Status: No, score=-5.299 tagged_above=-999 required=6.31 tests=[KAM_ASCII_DIVIDERS=0.8, KAM_LAZY_DOMAIN_SECURITY=1, RCVD_IN_DNSWL_HI=-5, RCVD_IN_MSPIKE_H3=-0.01, RCVD_IN_MSPIKE_WL=-0.01, RP_MATCHES_RCVD=-2.079] autolearn=disabled Received: from mx2-lw-us.apache.org ([10.40.0.8]) by localhost (spamd1-us-west.apache.org [10.40.0.7]) (amavisd-new, port 10024) with ESMTP id ZXarfhf6pJ23 for ; Mon, 9 May 2016 17:09:33 +0000 (UTC) Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx2-lw-us.apache.org (ASF Mail Server at mx2-lw-us.apache.org) with SMTP id 2FDAE5F1E5 for ; Mon, 9 May 2016 17:09:33 +0000 (UTC) Received: (qmail 79964 invoked by uid 99); 9 May 2016 17:09:32 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 09 May 2016 17:09:32 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 40F4BDFA0C; Mon, 9 May 2016 17:09:32 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: yhemanth@apache.org To: commits@atlas.incubator.apache.org Date: Mon, 09 May 2016 17:09:33 -0000 Message-Id: <4ec1b43eb1794781a56f9df97f47c2ef@git.apache.org> In-Reply-To: <72c393badb8f458bb474d3e40b47c6fb@git.apache.org> References: <72c393badb8f458bb474d3e40b47c6fb@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [2/2] incubator-atlas git commit: ATLAS-497 Simple Authorization (saqeeb.s via yhemanth) archived-at: Mon, 09 May 2016 17:09:42 -0000 ATLAS-497 Simple Authorization (saqeeb.s via yhemanth) Project: http://git-wip-us.apache.org/repos/asf/incubator-atlas/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-atlas/commit/a963e980 Tree: http://git-wip-us.apache.org/repos/asf/incubator-atlas/tree/a963e980 Diff: http://git-wip-us.apache.org/repos/asf/incubator-atlas/diff/a963e980 Branch: refs/heads/master Commit: a963e9806c7dd85b50d6957ec915233865c6216b Parents: 34f51a2 Author: Hemanth Yamijala Authored: Mon May 9 22:39:11 2016 +0530 Committer: Hemanth Yamijala Committed: Mon May 9 22:39:11 2016 +0530 ---------------------------------------------------------------------- distro/src/conf/atlas-application.properties | 3 + distro/src/conf/policy-store.txt | 9 + pom.xml | 1 + release-log.txt | 1 + .../atlas/authorize/AtlasAccessRequest.java | 117 ++++++ .../atlas/authorize/AtlasAccessorTypes.java | 22 ++ .../atlas/authorize/AtlasActionTypes.java | 22 ++ .../authorize/AtlasAuthorizationException.java | 40 ++ .../authorize/AtlasAuthorizationUtils.java | 132 +++++++ .../apache/atlas/authorize/AtlasAuthorizer.java | 43 +++ .../atlas/authorize/AtlasResourceTypes.java | 23 ++ .../org/apache/atlas/authorize/PolicyDef.java | 67 ++++ .../apache/atlas/authorize/PolicyParser.java | 238 ++++++++++++ .../org/apache/atlas/authorize/PolicyUtil.java | 164 ++++++++ .../atlas/authorize/SimpleAtlasAuthorizer.java | 380 +++++++++++++++++++ .../org/apache/atlas/util/FileReaderUtil.java | 56 +++ .../web/filters/AtlasAuthorizationFilter.java | 166 ++++++++ webapp/src/main/resources/spring-security.xml | 3 + .../atlas/authorize/PolicyParserTest.java | 167 ++++++++ .../apache/atlas/authorize/PolicyUtilTest.java | 95 +++++ .../authorize/SimpleAtlasAuthorizerTest.java | 185 +++++++++ .../web/security/FileAuthenticationTest.java | 17 +- 22 files changed, 1947 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/distro/src/conf/atlas-application.properties ---------------------------------------------------------------------- diff --git a/distro/src/conf/atlas-application.properties b/distro/src/conf/atlas-application.properties index 290105f..119865d 100755 --- a/distro/src/conf/atlas-application.properties +++ b/distro/src/conf/atlas-application.properties @@ -124,3 +124,6 @@ atlas.login.method=FILE ### File path of users-credentials atlas.login.credentials.file=${sys:atlas.home}/conf/users-credentials.properties +#########POLICY FILE PATH ######### +atlas.auth.policy.file=${sys:atlas.home}/conf/policy-store.txt + http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/distro/src/conf/policy-store.txt ---------------------------------------------------------------------- diff --git a/distro/src/conf/policy-store.txt b/distro/src/conf/policy-store.txt new file mode 100644 index 0000000..b072b49 --- /dev/null +++ b/distro/src/conf/policy-store.txt @@ -0,0 +1,9 @@ +##Policy Format +##r-READ, w-WRITE, u-UPDATE, d-DELETE +##Policy_Name;;User_Name1:Operations_Allowed,User_Name2:Operations_Allowed;;Group_Name1:Operations_Allowed,Group_Name2:Operations_Allowed;;Resource_Type1:Resource_Name,Resource_Type2:Resource_Name +## +adminPolicy;;admin:rwud;;ROLE_ADMIN:rwud;;type:*,entity:*,operation:* +typeReadPolicy;;nixon:rw;;;;type:*,entity:* +classReadPolicy;;saqeeb:r;;;;type:*,entity:* +dataScientistPolicy;;;;DATA_SCIENTIST:r;;type:*,entity:* +dataStewardPolicy;;;;DATA_STEWARD:rwu;;type:*,entity:* http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/pom.xml ---------------------------------------------------------------------- diff --git a/pom.xml b/pom.xml index 7b872c3..0606d80 100755 --- a/pom.xml +++ b/pom.xml @@ -1668,6 +1668,7 @@ **/atlas.data/** **/${sys:atlas.data}/** + **/policy-store.txt http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/release-log.txt ---------------------------------------------------------------------- diff --git a/release-log.txt b/release-log.txt index 4bacdfb..1afc1ed 100644 --- a/release-log.txt +++ b/release-log.txt @@ -3,6 +3,7 @@ Apache Atlas Release Notes --trunk - unreleased INCOMPATIBLE CHANGES: +ATLAS-497 Simple Authorization (saqeeb.s via yhemanth) ATLAS-661 REST API Authentication (nixonrodrigues via yhemanth) ATLAS-672 UI: Make dashboard v2 the default UI implementation (bergenholtz via yhemanth) ATLAS-532 Change Data types of all timestamps in Hive model(currently long)(sumasai via yhemanth) http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessRequest.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessRequest.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessRequest.java new file mode 100644 index 0000000..5db9646 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessRequest.java @@ -0,0 +1,117 @@ +/** + * 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 java.util.Date; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AtlasAccessRequest { + + private static Logger LOG = LoggerFactory.getLogger(AtlasAccessRequest.class); + private static boolean isDebugEnabled = LOG.isDebugEnabled(); + private List resourceType = null; + private String resource = null; + private AtlasActionTypes action = null; + private String user = null; + private List userGroups = null; + private Date accessTime = null; + private String clientIPAddress = null; + + public AtlasAccessRequest(List resourceType, String resource, AtlasActionTypes action, + String user, List userGroups) { + if (isDebugEnabled) { + LOG.debug("<== AtlasAccessRequestImpl-- Initializing AtlasAccessRequest"); + } + setResource(resource); + setAction(action); + setUser(user); + setUserGroups(userGroups); + setResourceType(resourceType); + + // set remaining fields to default value + setAccessTime(null); + setClientIPAddress(null); + } + + public List getResourceTypes() { + return resourceType; + } + + public void setResourceType(List resourceType) { + this.resourceType = resourceType; + } + + public String getResource() { + return resource; + } + + public void setResource(String resource) { + this.resource = resource; + } + + public AtlasActionTypes getAction() { + return action; + } + + public void setAction(AtlasActionTypes action) { + this.action = action; + } + + public String getUser() { + return user; + } + + public void setUser(String user) { + this.user = user; + } + + public void setUserGroups(List userGroups) { + this.userGroups = userGroups; + } + + public List getUserGroups() { + return userGroups; + } + + public Date getAccessTime() { + return accessTime; + } + + public void setAccessTime(Date accessTime) { + this.accessTime = accessTime; + } + + public String getClientIPAddress() { + return clientIPAddress; + } + + public void setClientIPAddress(String clientIPAddress) { + this.clientIPAddress = clientIPAddress; + } + + @Override + public String toString() { + return "AtlasAccessRequest [resourceType=" + resourceType + ", resource=" + resource + ", action=" + action + + ", user=" + user + ", userGroups=" + userGroups + ", accessTime=" + accessTime + ", clientIPAddress=" + + clientIPAddress + "]"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessorTypes.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessorTypes.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessorTypes.java new file mode 100644 index 0000000..5f3827a --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAccessorTypes.java @@ -0,0 +1,22 @@ +/** + * 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; + +public enum AtlasAccessorTypes { + USER, GROUP; +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasActionTypes.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasActionTypes.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasActionTypes.java new file mode 100644 index 0000000..13c8b53 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasActionTypes.java @@ -0,0 +1,22 @@ +/** + * 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; + +public enum AtlasActionTypes { + READ, WRITE, UPDATE, DELETE; +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationException.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationException.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationException.java new file mode 100644 index 0000000..676c9f9 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationException.java @@ -0,0 +1,40 @@ +/** + * 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; + +public class AtlasAuthorizationException extends Exception { + private static final long serialVersionUID = 1L; + + public AtlasAuthorizationException() { + + } + + public AtlasAuthorizationException(String message) { + super(message); + } + + public AtlasAuthorizationException(String message, Throwable exception) { + super(message, exception); + } + + public AtlasAuthorizationException(String message, Throwable exception, boolean enableSuppression, + boolean writableStackTrace) { + super(message, exception, enableSuppression, writableStackTrace); + } +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java new file mode 100644 index 0000000..211ee7f --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizationUtils.java @@ -0,0 +1,132 @@ +/** + * 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 java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; + +import org.apache.atlas.AtlasClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.base.Strings; + +public class AtlasAuthorizationUtils { + private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthorizationUtils.class); + private static boolean isDebugEnabled = LOG.isDebugEnabled(); + private static final String BASE_URL = "/" + AtlasClient.BASE_URI; + + public static String parse(String fullPath, String subPath) { + String api = null; + if (!Strings.isNullOrEmpty(fullPath)) { + api = fullPath.substring(subPath.length(), fullPath.length()); + + } + if (isDebugEnabled) { + LOG.debug("Extracted " + api + " from path : " + fullPath); + } + return api; + } + + public static String getApi(String u) { + if (isDebugEnabled) { + LOG.debug("getApi <=== from " + u); + } + if (u.startsWith(BASE_URL)) { + u = parse(u, BASE_URL); + } + String[] split = u.split("/"); + return split[0]; + } + + public static AtlasActionTypes getAtlasAction(String method) { + AtlasActionTypes action = null; + + switch (method.toUpperCase()) { + case "POST": + action = AtlasActionTypes.WRITE; + break; + case "GET": + action = AtlasActionTypes.READ; + break; + case "PUT": + action = AtlasActionTypes.UPDATE; + break; + case "DELETE": + action = AtlasActionTypes.DELETE; + break; + default: + if (isDebugEnabled) { + LOG.debug("Invalid HTTP method in request : " + method + " this is serious!!!"); + } + break; + } + + if (isDebugEnabled) { + LOG.debug("==> AtlasAuthorizationFilter getAtlasAction HTTP Method " + method + " mapped to AtlasAction : " + + action); + } + return action; + } + + public static List getAtlasResourceType(String contextPath) throws ServletException { + List resourceTypes = new ArrayList(); + if (isDebugEnabled) { + LOG.debug("getAtlasResourceType <=== for " + contextPath); + } + String api = getApi(contextPath); + + if (api.startsWith("types")) { + resourceTypes.add(AtlasResourceTypes.TYPE); + } else if ((api.startsWith("discovery") && api.contains("gremlin")) || api.startsWith("admin") + || api.startsWith("graph")) { + resourceTypes.add(AtlasResourceTypes.OPERATION); + } else if ((api.startsWith("entities") && contextPath.contains("traits")) || api.startsWith("discovery")) { + resourceTypes.add(AtlasResourceTypes.ENTITY); + resourceTypes.add(AtlasResourceTypes.TYPE); + } else if (api.startsWith("entities") || api.startsWith("lineage")) { + resourceTypes.add(AtlasResourceTypes.ENTITY); + } else { + LOG.error("Unable to find Atlas Resource corresponding to : " + api); + throw new ServletException("Unable to find Atlas Resource corresponding to : " + api); + } + + if (isDebugEnabled) { + LOG.debug("Returning AtlasResources " + resourceTypes + " for api " + api); + } + return resourceTypes; + } + + /* + * This implementation will be changed for Resource level Authorization. + */ + public static String getAtlasResource(HttpServletRequest requeset, AtlasActionTypes action) { + if (isDebugEnabled) { + LOG.debug("getAtlasResource <=== " + + "This implementation will be changed for Resource level Authorization."); + } + return "*"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java new file mode 100644 index 0000000..7c93c7a --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasAuthorizer.java @@ -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. + */ + +package org.apache.atlas.authorize; + + +public interface AtlasAuthorizer { + /** + * This method will load the policy file and would initialize the required data-structures. + */ + public void init(); + + /** + * This method is responsible to perform the actual authorization for every REST API call. It will check the if the + * user:u can perform action:a on resource:r. + * + * @param request + * @return + */ + public boolean isAccessAllowed(AtlasAccessRequest request) throws AtlasAuthorizationException; + + /** + * This method is responsible to perform the cleanup and release activities. It must be called when you are done + * with the Authorization activity and once it's called a restart would be required. Try to invoke this while + * destroying the context. + */ + public void cleanUp(); +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java b/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java new file mode 100644 index 0000000..8ce3f4c --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/AtlasResourceTypes.java @@ -0,0 +1,23 @@ +/** + * 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; + +public enum AtlasResourceTypes { + ENTITY, TYPE, OPERATION; +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/PolicyDef.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/PolicyDef.java b/webapp/src/main/java/org/apache/atlas/authorize/PolicyDef.java new file mode 100644 index 0000000..0ee39df --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/PolicyDef.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.atlas.authorize; + +import java.util.List; +import java.util.Map; + +public class PolicyDef { + + private String policyName; + private Map> users; + private Map> groups; + private Map> resources; + + public String getPolicyName() { + return policyName; + } + + public void setPolicyName(String policyName) { + this.policyName = policyName; + } + + 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> getResources() { + return resources; + } + + public void setResources(Map> resources) { + this.resources = resources; + } + + @Override + public String toString() { + return "PolicyDef [policyName=" + policyName + ", users=" + users + ", groups=" + groups + ", resources=" + + resources + "]"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/PolicyParser.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/PolicyParser.java b/webapp/src/main/java/org/apache/atlas/authorize/PolicyParser.java new file mode 100644 index 0000000..51a6dc2 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/PolicyParser.java @@ -0,0 +1,238 @@ +/** + * 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PolicyParser { + + private static Logger LOG = LoggerFactory.getLogger(PolicyParser.class); + private static boolean isDebugEnabled = LOG.isDebugEnabled(); + public static final int POLICYNAME = 0; + + public static final int USER_INDEX = 1; + public static final int USERNAME = 0; + public static final int USER_AUTHORITIES = 1; + + public static final int GROUP_INDEX = 2; + public static final int GROUPNAME = 0; + public static final int GROUP_AUTHORITIES = 1; + + public static final int RESOURCE_INDEX = 3; + public static final int RESOURCE_TYPE = 0; + public static final int RESOURCE_NAME = 1; + + private List getListOfAutorities(String auth) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser getListOfAutorities"); + } + List authorities = new ArrayList(); + + for (int i = 0; i < auth.length(); i++) { + char access = auth.toLowerCase().charAt(i); + switch (access) { + case 'r': + authorities.add(AtlasActionTypes.READ); + break; + case 'w': + authorities.add(AtlasActionTypes.WRITE); + break; + case 'u': + authorities.add(AtlasActionTypes.UPDATE); + break; + case 'd': + authorities.add(AtlasActionTypes.DELETE); + break; + + default: + if (LOG.isErrorEnabled()) { + LOG.error("Invalid Action"); + } + break; + } + } + if (isDebugEnabled) { + LOG.debug("==> PolicyParser getListOfAutorities"); + } + return authorities; + } + + public List parsePolicies(List policies) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser parsePolicies"); + } + List policyDefs = new ArrayList(); + for (String policy : policies) { + PolicyDef policyDef = parsePolicy(policy); + policyDefs.add(policyDef); + } + if (isDebugEnabled) { + LOG.debug("==> PolicyParser parsePolicies"); + LOG.debug(policyDefs.toString()); + } + return policyDefs; + } + + private PolicyDef parsePolicy(String data) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser parsePolicy"); + } + PolicyDef def = new PolicyDef(); + String[] props = data.split(";;"); + def.setPolicyName(props[POLICYNAME]); + parseUsers(props[USER_INDEX], def); + parseGroups(props[GROUP_INDEX], def); + parseResources(props[RESOURCE_INDEX], def); + if (isDebugEnabled) { + LOG.debug("policy successfully parsed!!!"); + LOG.debug("==> PolicyParser parsePolicy"); + } + return def; + } + + private boolean validateEntity(String entity) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser validateEntity"); + } + boolean isValidEntity = Pattern.matches("(.+:.+)+", entity); + boolean isEmpty = entity.isEmpty(); + if (isValidEntity == false || isEmpty == true) { + if (isDebugEnabled) { + LOG.debug("group/user/resource not properly define in Policy"); + LOG.debug("==> PolicyParser validateEntity"); + } + return false; + } else { + if (isDebugEnabled) { + LOG.debug("==> PolicyParser validateEntity"); + } + return true; + } + + } + + private void parseUsers(String usersDef, PolicyDef def) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser parseUsers"); + } + String[] users = usersDef.split(","); + String[] userAndRole = null; + Map> usersMap = new HashMap>(); + if (validateEntity(usersDef)) { + for (String user : users) { + if (!Pattern.matches("(.+:.+)+", user)) { + continue; + } + userAndRole = user.split(":"); + if (def.getUsers() != null) { + usersMap = def.getUsers(); + } + List userAutorities = usersMap.get(userAndRole[USERNAME]); + if (userAutorities == null) { + + userAutorities = new ArrayList(); + } + userAutorities = getListOfAutorities(userAndRole[USER_AUTHORITIES]); + usersMap.put(userAndRole[USERNAME], userAutorities); + def.setUsers(usersMap); + } + + } else { + def.setUsers(usersMap); + } + if (isDebugEnabled) { + LOG.debug("==> PolicyParser parseUsers"); + } + } + + private void parseGroups(String groupsDef, PolicyDef def) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser parseGroups"); + } + String[] groups = groupsDef.split("\\,"); + String[] groupAndRole = null; + Map> groupsMap = new HashMap>(); + if (validateEntity(groupsDef.trim())) { + for (String group : groups) { + if (!Pattern.matches("(.+:.+)+", group)) { + continue; + } + groupAndRole = group.split("[:]"); + if (def.getGroups() != null) { + groupsMap = def.getGroups(); + } + List groupAutorities = groupsMap.get(groupAndRole[GROUPNAME]); + if (groupAutorities == null) { + groupAutorities = new ArrayList(); + } + groupAutorities = getListOfAutorities(groupAndRole[GROUP_AUTHORITIES]); + groupsMap.put(groupAndRole[GROUPNAME], groupAutorities); + def.setGroups(groupsMap); + } + + } else { + def.setGroups(groupsMap); + } + if (isDebugEnabled) { + LOG.debug("==> PolicyParser parseGroups"); + } + + } + + private void parseResources(String resourceDef, PolicyDef def) { + if (isDebugEnabled) { + LOG.debug("<== PolicyParser parseResources"); + } + String[] resources = resourceDef.split(","); + String[] resourceTypeAndName = null; + Map> resourcesMap = new HashMap>(); + if (validateEntity(resourceDef)) { + for (String resource : resources) { + if (!Pattern.matches("(.+:.+)+", resource)) { + continue; + } + resourceTypeAndName = resource.split("[:]"); + if (def.getResources() != null) { + resourcesMap = def.getResources(); + } + AtlasResourceTypes resourceType = + AtlasResourceTypes.valueOf(resourceTypeAndName[RESOURCE_TYPE].toUpperCase()); + List resourceList = resourcesMap.get(resourceType); + if (resourceList == null) { + resourceList = new ArrayList(); + } + resourceList.add(resourceTypeAndName[RESOURCE_NAME]); + resourcesMap.put(resourceType, resourceList); + def.setResources(resourcesMap); + } + } else { + def.setResources(resourcesMap); + } + if (isDebugEnabled) { + LOG.debug("==> PolicyParser parseResources"); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/PolicyUtil.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/PolicyUtil.java b/webapp/src/main/java/org/apache/atlas/authorize/PolicyUtil.java new file mode 100644 index 0000000..a565f96 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/PolicyUtil.java @@ -0,0 +1,164 @@ +/** 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PolicyUtil { + + private static Logger LOG = LoggerFactory.getLogger(PolicyUtil.class); + private static boolean isDebugEnabled = LOG.isDebugEnabled(); + private Map>> userReadMap; + private Map>> userWriteMap; + private Map>> userUpdateMap; + private Map>> userDeleteMap; + + private Map>> groupReadMap; + private Map>> groupWriteMap; + private Map>> groupUpdateMap; + private Map>> groupDeleteMap; + + /** + * @return the userReadMap + */ + public Map>> getUserReadMap() { + return userReadMap; + } + + /** + * @return the userWriteMap + */ + public Map>> getUserWriteMap() { + return userWriteMap; + } + + /** + * @return the userUpdateMap + */ + public Map>> getUserUpdateMap() { + return userUpdateMap; + } + + /** + * @return the userDeleteMap + */ + public Map>> getUserDeleteMap() { + return userDeleteMap; + } + + /** + * @return the groupReadMap + */ + public Map>> getGroupReadMap() { + return groupReadMap; + } + + /** + * @return the groupWriteMap + */ + public Map>> getGroupWriteMap() { + return groupWriteMap; + } + + /** + * @return the groupUpdateMap + */ + public Map>> getGroupUpdateMap() { + return groupUpdateMap; + } + + /** + * @return the groupDeleteMap + */ + public Map>> getGroupDeleteMap() { + return groupDeleteMap; + } + + public Map>> createPermissionMap(List policyDefList, + AtlasActionTypes permissionType, AtlasAccessorTypes principalType) { + if (isDebugEnabled) { + LOG.debug("<== PolicyUtil createPermissionMap"); + LOG.debug("Creating Permission Map for :: " + permissionType + " & " + principalType); + } + Map>> userReadMap = + new HashMap>>(); + + // Iterate over the list of policies to create map + for (PolicyDef policyDef : policyDefList) { + LOG.info("Processing policy def : " + policyDef); + Map> principalMap = + principalType.equals(AtlasAccessorTypes.USER) ? policyDef.getUsers() : policyDef.getGroups(); + // For every policy extract the resource list and populate the user map + for (Entry> e : principalMap.entrySet()) { + // Check if the user has passed permission type like READ + if (!e.getValue().contains(permissionType)) { + continue; + } + // See if the current user is already added to map + String username = e.getKey(); + Map> userResourceList = userReadMap.get(username); + + // If its not added then create a new resource list + if (userResourceList == null) { + if (isDebugEnabled) { + LOG.debug("Resource list not found for " + username + ", creating it"); + } + userResourceList = new HashMap>(); + } + /* + * Iterate over resources from the current policy def and update the resource list for the current user + */ + for (Entry> resourceTypeMap : policyDef.getResources().entrySet()) { + // For the current resourceType in the policyDef, get the + // current list of resources already added + AtlasResourceTypes type = resourceTypeMap.getKey(); + List resourceList = userResourceList.get(type); + + if (resourceList == null) { + // if the resource list was not added for this type then + // create and add all the resources in this policy + resourceList = new ArrayList(); + resourceList.addAll(resourceTypeMap.getValue()); + } else { + // if the resource list is present then merge both the + // list + resourceList.removeAll(resourceTypeMap.getValue()); + resourceList.addAll(resourceTypeMap.getValue()); + } + + userResourceList.put(type, resourceList); + } + userReadMap.put(username, userResourceList); + LOG.info("userReadMap=====>>>>>> " + userReadMap); + } + } + if (isDebugEnabled) { + LOG.debug("Returning Map for " + principalType + " :: " + userReadMap); + LOG.debug("==> PolicyUtil createPermissionMap"); + } + return userReadMap; + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/authorize/SimpleAtlasAuthorizer.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/authorize/SimpleAtlasAuthorizer.java b/webapp/src/main/java/org/apache/atlas/authorize/SimpleAtlasAuthorizer.java new file mode 100644 index 0000000..2a32e4e --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/authorize/SimpleAtlasAuthorizer.java @@ -0,0 +1,380 @@ +/** + * 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 java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasException; +import org.apache.atlas.util.FileReaderUtil; +import org.apache.atlas.util.PropertiesUtil; +import org.apache.commons.configuration.Configuration; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.io.IOCase; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.annotations.VisibleForTesting; + +public final class SimpleAtlasAuthorizer implements AtlasAuthorizer { + private static final Logger LOG = LoggerFactory.getLogger(SimpleAtlasAuthorizer.class); + private boolean isDebugEnabled = LOG.isDebugEnabled(); + + private final static String WILDCARD_ASTERISK = "*"; + private final static String WILDCARDS = "*?"; + private boolean optIgnoreCase = false; + + private Map>> userReadMap = null; + private Map>> userWriteMap = null; + private Map>> userUpdateMap = null; + private Map>> userDeleteMap = null; + private Map>> groupReadMap = null; + private Map>> groupWriteMap = null; + private Map>> groupUpdateMap = null; + private Map>> groupDeleteMap = null; + private static AtlasAuthorizer defaultAuthorizer = new SimpleAtlasAuthorizer(); + + private SimpleAtlasAuthorizer() { + } + + public static AtlasAuthorizer getInstance() { + return defaultAuthorizer; + } + + @Override + public void init() { + if (isDebugEnabled) { + LOG.debug("<== SimpleAtlasAuthorizer init"); + } + try { + + PolicyUtil util = new PolicyUtil(); + PolicyParser parser = new PolicyParser(); + optIgnoreCase = Boolean.valueOf(PropertiesUtil.getProperty("optIgnoreCase", "false")); + + if (isDebugEnabled) { + LOG.debug("Read from PropertiesUtil --> optIgnoreCase :: " + optIgnoreCase); + } + + Configuration configuration = ApplicationProperties.get(); + String policyStorePath = configuration.getString("atlas.auth.policy.file"); + + if (isDebugEnabled) { + LOG.debug("Loading Apache Atlas policies from : " + policyStorePath); + } + + List policies = FileReaderUtil.readFile(policyStorePath); + List policyDef = parser.parsePolicies(policies); + + userReadMap = util.createPermissionMap(policyDef, AtlasActionTypes.READ, AtlasAccessorTypes.USER); + userWriteMap = util.createPermissionMap(policyDef, AtlasActionTypes.WRITE, AtlasAccessorTypes.USER); + userUpdateMap = util.createPermissionMap(policyDef, AtlasActionTypes.UPDATE, AtlasAccessorTypes.USER); + userDeleteMap = util.createPermissionMap(policyDef, AtlasActionTypes.DELETE, AtlasAccessorTypes.USER); + + groupReadMap = util.createPermissionMap(policyDef, AtlasActionTypes.READ, AtlasAccessorTypes.GROUP); + groupWriteMap = util.createPermissionMap(policyDef, AtlasActionTypes.WRITE, AtlasAccessorTypes.GROUP); + groupUpdateMap = util.createPermissionMap(policyDef, AtlasActionTypes.UPDATE, AtlasAccessorTypes.GROUP); + groupDeleteMap = util.createPermissionMap(policyDef, AtlasActionTypes.DELETE, AtlasAccessorTypes.GROUP); + + if (isDebugEnabled) { + LOG.debug("\n\nUserReadMap :: " + userReadMap + "\nGroupReadMap :: " + groupReadMap); + LOG.debug("\n\nUserWriteMap :: " + userWriteMap + "\nGroupWriteMap :: " + groupWriteMap); + LOG.debug("\n\nUserUpdateMap :: " + userUpdateMap + "\nGroupUpdateMap :: " + groupUpdateMap); + LOG.debug("\n\nUserDeleteMap :: " + userDeleteMap + "\nGroupDeleteMap :: " + groupDeleteMap); + } + + } catch (IOException | AtlasException e) { + if (LOG.isErrorEnabled()) { + LOG.error("SimpleAtlasAuthorizer could not be initialized properly due to : ", e); + } + } + } + + @Override + public boolean isAccessAllowed(AtlasAccessRequest request) throws AtlasAuthorizationException { + if (isDebugEnabled) { + LOG.debug("<== SimpleAtlasAuthorizer isAccessAllowed"); + LOG.debug("isAccessAllowd(" + request + ")"); + } + String user = request.getUser(); + List groups = request.getUserGroups(); + AtlasActionTypes action = request.getAction(); + String resource = request.getResource(); + List resourceTypes = request.getResourceTypes(); + if (isDebugEnabled) + LOG.debug("Checking for :: \nUser :: " + user + "\nGroups :: " + groups + "\nAction :: " + action + + "\nResource :: " + resource); + + boolean isAccessAllowed = false; + boolean isUser = user == null ? false : true; + boolean isGroup = groups == null ? false : true; + + if ((!isUser && !isGroup) || action == null || resource == null) { + if (isDebugEnabled) { + LOG.debug("Please check the formation AtlasAccessRequest."); + } + return isAccessAllowed; + } else { + if (isDebugEnabled) { + LOG.debug("checkAccess for Operation :: " + action + " on Resource " + resourceTypes + ":" + resource); + } + switch (action) { + case READ: + isAccessAllowed = checkAccess(user, resourceTypes, resource, userReadMap); + isAccessAllowed = + isAccessAllowed == false ? checkAccessForGroups(groups, resourceTypes, resource, groupReadMap) + : isAccessAllowed; + break; + case WRITE: + isAccessAllowed = checkAccess(user, resourceTypes, resource, userWriteMap); + isAccessAllowed = + isAccessAllowed == false ? checkAccessForGroups(groups, resourceTypes, resource, groupWriteMap) + : isAccessAllowed; + break; + case UPDATE: + isAccessAllowed = checkAccess(user, resourceTypes, resource, userUpdateMap); + isAccessAllowed = + isAccessAllowed == false + ? checkAccessForGroups(groups, resourceTypes, resource, groupUpdateMap) : isAccessAllowed; + break; + case DELETE: + isAccessAllowed = checkAccess(user, resourceTypes, resource, userDeleteMap); + isAccessAllowed = + isAccessAllowed == false + ? checkAccessForGroups(groups, resourceTypes, resource, groupDeleteMap) : isAccessAllowed; + break; + default: + if (isDebugEnabled) { + LOG.debug("Invalid Action " + action); + LOG.debug("Raising an exception!!!"); + } + throw new AtlasAuthorizationException("Invalid Exception :: " + action); + } + } + + if (isDebugEnabled) { + LOG.debug("==> +SimpleAtlasAuthorizer isAccessAllowed = " + isAccessAllowed); + } + + return isAccessAllowed; + } + + private boolean checkAccess(String accessor, List resourceTypes, String resource, + Map>> map) { + if (isDebugEnabled) { + LOG.debug("<== SimpleAtlasAuthorizer checkAccess"); + LOG.debug("Now checking access for accessor : " + accessor + "\nResource Types : " + resourceTypes + + "\nResource : " + resource + "\nMap : " + map); + } + boolean result = true; + Map> rescMap = map.get(accessor); + if (rescMap != null) { + for (AtlasResourceTypes resourceType : resourceTypes) { + List accessList = rescMap.get(resourceType); + if (isDebugEnabled) { + LOG.debug("\nChecking for resource : " + resource + " in list : " + accessList + "\n"); + } + if (accessList != null) { + result = result && isMatch(resource, accessList); + } else { + result = false; + } + } + } else { + result = false; + if (isDebugEnabled) + LOG.debug("Key " + accessor + " missing. Returning with result : " + result); + } + + if (isDebugEnabled) { + LOG.debug("Check for " + accessor + " :: " + result); + LOG.debug("==> SimpleAtlasAuthorizer checkAccess"); + } + return result; + } + + private boolean checkAccessForGroups(List groups, List resourceType, String resource, + Map>> map) { + boolean isAccessAllowed = false; + if (isDebugEnabled) { + LOG.debug("<== SimpleAtlasAuthorizer checkAccessForGroups"); + } + + for (String group : groups) { + isAccessAllowed = checkAccess(group, resourceType, resource, map); + if (isAccessAllowed) { + break; + } + } + + if (isDebugEnabled) { + LOG.debug("==> SimpleAtlasAuthorizer checkAccessForGroups"); + } + return isAccessAllowed; + } + + private boolean resourceMatchHelper(List policyResource) { + boolean isMatchAny = false; + if (isDebugEnabled) { + LOG.debug("<== SimpleAtlasAuthorizer resourceMatchHelper"); + } + + boolean optWildCard = true; + + List policyValues = new ArrayList(); + + if (policyResource != null) { + boolean isWildCardPresent = !optWildCard; + for (String policyValue : policyResource) { + if (StringUtils.isEmpty(policyValue)) { + continue; + } + if (StringUtils.containsOnly(policyValue, WILDCARD_ASTERISK)) { + isMatchAny = true; + } else if (!isWildCardPresent && StringUtils.containsAny(policyValue, WILDCARDS)) { + isWildCardPresent = true; + } + policyValues.add(policyValue); + } + optWildCard = optWildCard && isWildCardPresent; + } else { + isMatchAny = false; + } + + if (isDebugEnabled) { + LOG.debug("==> SimpleAtlasAuthorizer resourceMatchHelper"); + } + return isMatchAny; + } + + private boolean isMatch(String resource, List policyValues) { + if (isDebugEnabled) { + LOG.debug("<== SimpleAtlasAuthorizer isMatch"); + } + boolean isMatchAny = resourceMatchHelper(policyValues); + boolean isMatch = false; + boolean allValuesRequested = isAllValuesRequested(resource); + + if (allValuesRequested || isMatchAny) { + isMatch = isMatchAny; + } else { + for (String policyValue : policyValues) { + if (policyValue.contains("*")) { + isMatch = + optIgnoreCase ? FilenameUtils.wildcardMatch(resource, policyValue, IOCase.INSENSITIVE) + : FilenameUtils.wildcardMatch(resource, policyValue, IOCase.SENSITIVE); + } else { + isMatch = + optIgnoreCase ? StringUtils.equalsIgnoreCase(resource, policyValue) : StringUtils.equals( + resource, policyValue); + } + if (isMatch) { + break; + } + } + } + + if (isMatch == false) { + + if (isDebugEnabled) { + StringBuilder sb = new StringBuilder(); + sb.append("["); + for (String policyValue : policyValues) { + sb.append(policyValue); + sb.append(" "); + } + sb.append("]"); + + LOG.debug("AtlasDefaultResourceMatcher.isMatch returns FALSE, (resource=" + resource + + ", policyValues=" + sb.toString() + ")"); + } + + } + + if (isDebugEnabled) { + LOG.debug("==> SimpleAtlasAuthorizer isMatch(" + resource + "): " + isMatch); + } + + return isMatch; + } + + private boolean isAllValuesRequested(String resource) { + boolean result = StringUtils.isEmpty(resource) || WILDCARD_ASTERISK.equals(resource); + return result; + } + + @Override + public void cleanUp() { + if (isDebugEnabled) { + LOG.debug("<== +SimpleAtlasAuthorizer cleanUp"); + } + userReadMap = null; + userWriteMap = null; + userUpdateMap = null; + userDeleteMap = null; + groupReadMap = null; + groupWriteMap = null; + groupUpdateMap = null; + groupDeleteMap = null; + if (isDebugEnabled) { + LOG.debug("==> +SimpleAtlasAuthorizer cleanUp"); + } + } + + /* + * NOTE :: This method is added for setting the maps for testing purpose. + */ + @VisibleForTesting + public void setResourcesForTesting(Map>> userMap, + Map>> groupMap, AtlasActionTypes actionTypes) { + + switch (actionTypes) { + case READ: + this.userReadMap = userMap; + this.groupReadMap = groupMap; + break; + + case WRITE: + + this.userWriteMap = userMap; + this.groupWriteMap = groupMap; + break; + case UPDATE: + + this.userUpdateMap = userMap; + this.groupUpdateMap = groupMap; + break; + case DELETE: + + this.userDeleteMap = userMap; + this.groupDeleteMap = groupMap; + break; + + default: + if (isDebugEnabled) { + LOG.debug("No such action available"); + } + break; + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/util/FileReaderUtil.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/util/FileReaderUtil.java b/webapp/src/main/java/org/apache/atlas/util/FileReaderUtil.java new file mode 100644 index 0000000..22eaff9 --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/util/FileReaderUtil.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.atlas.util; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import org.apache.log4j.Logger; + +public class FileReaderUtil { + private static Logger LOG = Logger.getLogger(FileReaderUtil.class); + private static boolean isDebugEnabled = LOG.isDebugEnabled(); + + public static List readFile(String path) throws IOException { + if (isDebugEnabled) { + LOG.debug("<== FileReaderUtil readFile"); + } + LOG.info("reading the file" + path); + BufferedReader br = new BufferedReader(new FileReader(path)); + List list = new ArrayList(); + String line = null; + while ((line = br.readLine()) != null) { + if ((!line.startsWith("##")) && Pattern.matches(".+;;.*;;.*;;.+", line)) + list.add(line); + } + + if (isDebugEnabled) { + LOG.debug("==> FileReaderUtil readFile"); + LOG.debug("Policies read :: " + list); + } + if (br != null) { + br.close(); + } + return list; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthorizationFilter.java ---------------------------------------------------------------------- diff --git a/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthorizationFilter.java b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthorizationFilter.java new file mode 100644 index 0000000..13fc7da --- /dev/null +++ b/webapp/src/main/java/org/apache/atlas/web/filters/AtlasAuthorizationFilter.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.atlas.web.filters; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.apache.atlas.AtlasClient; +import org.apache.atlas.AtlasException; +import org.apache.atlas.authorize.AtlasAccessRequest; +import org.apache.atlas.authorize.AtlasActionTypes; +import org.apache.atlas.authorize.AtlasAuthorizationException; +import org.apache.atlas.authorize.AtlasAuthorizer; +import org.apache.atlas.authorize.AtlasResourceTypes; +import org.apache.atlas.authorize.SimpleAtlasAuthorizer; +import org.json.simple.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.filter.GenericFilterBean; +import static org.apache.atlas.authorize.AtlasAuthorizationUtils.*; + +import com.google.common.base.Strings; + +public class AtlasAuthorizationFilter extends GenericFilterBean { + + private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthorizationFilter.class); + private static boolean isDebugEnabled = LOG.isDebugEnabled(); + private AtlasAuthorizer authorizer = SimpleAtlasAuthorizer.getInstance(); + + private final String BASE_URL = "/" + AtlasClient.BASE_URI; + + public AtlasAuthorizationFilter() { + if (isDebugEnabled) { + LOG.debug("<== AtlasAuthorizationFilter() -- " + "Now initializing the Apache Atlas Authorizer!!!"); + } + authorizer.init(); + } + + @Override + public void destroy() { + if (isDebugEnabled) { + LOG.debug("<== AtlasAuthorizationFilter destroy"); + } + authorizer.cleanUp(); + super.destroy(); + } + + @SuppressWarnings("unchecked") + @Override + public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, + ServletException { + if (isDebugEnabled) { + LOG.debug("==> AuthorizationFilter.doFilter"); + } + + HttpServletRequest request = (HttpServletRequest) req; + String pathInfo = request.getServletPath(); + if (pathInfo.startsWith(BASE_URL)) { + if (isDebugEnabled) { + LOG.debug(pathInfo + " is a valid REST API request!!!"); + } + + AtlasActionTypes action = getAtlasAction(request.getMethod()); + String userName = null; + List groups = new ArrayList(); + StringBuilder sb = new StringBuilder(); + + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + + if (auth != null) { + userName = String.valueOf(auth.getPrincipal()); + Collection authorities = auth.getAuthorities(); + for (GrantedAuthority c : authorities) { + groups.add(c.getAuthority()); + } + sb.append("============================\n"); + sb.append("UserName ==>> " + userName + "\nGroups ==>> " + groups); + } else { + if (LOG.isErrorEnabled()) { + LOG.error("Cannot obtain Security Context : " + auth); + } + throw new ServletException("Cannot obtain Security Context : " + auth); + } + + sb.append("\n" + "URL :: " + request.getRequestURL() + " Action :: " + action); + sb.append("\nrequest.getServletPath() :: " + pathInfo); + sb.append("\n============================\n"); + + if (isDebugEnabled) { + LOG.debug(sb.toString()); + } + sb = null; + List atlasResourceType = getAtlasResourceType(pathInfo); + String resource = getAtlasResource(request, action); + AtlasAccessRequest atlasRequest = + new AtlasAccessRequest(atlasResourceType, resource, action, userName, groups); + boolean accessAllowed = false; + try { + accessAllowed = authorizer.isAccessAllowed(atlasRequest); + } catch (AtlasAuthorizationException e) { + if (LOG.isErrorEnabled()) { + LOG.error("Access Restricted. Could not process the request due to : " + e); + } + } + if (isDebugEnabled) { + LOG.debug("Authorizer result :: " + accessAllowed); + } + if (accessAllowed) { + if (isDebugEnabled) { + LOG.debug("Access is allowed so forwarding the request!!!"); + } + chain.doFilter(req, res); + } else { + JSONObject json = new JSONObject(); + json.put("AuthorizationError", "Sorry you are not authorized for " + action.name() + " on " + + atlasResourceType + " : " + resource); + HttpServletResponse response = (HttpServletResponse) res; + response.setContentType("application/json"); + response.setStatus(HttpServletResponse.SC_FORBIDDEN); + + response.sendError(HttpServletResponse.SC_FORBIDDEN, json.toString()); + if (isDebugEnabled) { + LOG.debug("Sorry you are not authorized for " + action.name() + " on " + atlasResourceType + " : " + + resource); + LOG.debug("Returning 403 since the access is blocked update!!!!"); + } + return; + } + + } else { + if (isDebugEnabled) { + LOG.debug("Ignoring request " + pathInfo); + } + chain.doFilter(req, res); + } + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/main/resources/spring-security.xml ---------------------------------------------------------------------- diff --git a/webapp/src/main/resources/spring-security.xml b/webapp/src/main/resources/spring-security.xml index d90b9d6..4590eef 100644 --- a/webapp/src/main/resources/spring-security.xml +++ b/webapp/src/main/resources/spring-security.xml @@ -49,6 +49,7 @@ + + + http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/test/java/org/apache/atlas/authorize/PolicyParserTest.java ---------------------------------------------------------------------- diff --git a/webapp/src/test/java/org/apache/atlas/authorize/PolicyParserTest.java b/webapp/src/test/java/org/apache/atlas/authorize/PolicyParserTest.java new file mode 100644 index 0000000..507d4c6 --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/authorize/PolicyParserTest.java @@ -0,0 +1,167 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.testng.annotations.Test; + +public class PolicyParserTest { + + @Test + public void testParsePoliciesWithAllProperties() { + List policies = new ArrayList(); + policies.add("hivePolicy;;usr1:r,usr2:rw;;grp1:rwu,grp2:u;;entity:*abc,operation:*xyz,type:PII"); + /* Creating group data */ + Map> groupMap = new HashMap>(); + List accessList1 = new ArrayList(); + accessList1.add(AtlasActionTypes.READ); + accessList1.add(AtlasActionTypes.WRITE); + accessList1.add(AtlasActionTypes.UPDATE); + + groupMap.put("grp1", accessList1); + List accessList2 = new ArrayList(); + accessList2.add(AtlasActionTypes.UPDATE); + groupMap.put("grp2", accessList2); + + /* Creating user data */ + Map> usersMap = new HashMap>(); + List usr1AccessList = new ArrayList(); + usr1AccessList.add(AtlasActionTypes.READ); + usersMap.put("usr1", usr1AccessList); + + List usr2AccessList = new ArrayList(); + usr2AccessList.add(AtlasActionTypes.READ); + usr2AccessList.add(AtlasActionTypes.WRITE); + usersMap.put("usr2", usr2AccessList); + + /* Creating resources data */ + Map> resourceMap = new HashMap>(); + List resource1List = new ArrayList(); + resource1List.add("*abc"); + resourceMap.put(AtlasResourceTypes.ENTITY, resource1List); + + List resource2List = new ArrayList(); + resource2List.add("*xyz"); + resourceMap.put(AtlasResourceTypes.OPERATION, resource2List); + + List resource3List = new ArrayList(); + resource3List.add("PII"); + resourceMap.put(AtlasResourceTypes.TYPE, resource3List); + + List policyDefs = new PolicyParser().parsePolicies(policies); + for (PolicyDef def : policyDefs) { + + assertEquals(def.getPolicyName(), "hivePolicy"); + assertEquals(def.getGroups(), groupMap); + assertEquals(def.getUsers(), usersMap); + assertEquals(def.getResources(), resourceMap); + + } + + } + + @Test + public void testParsePoliciesWithOutUserProperties() { + List policies = new ArrayList(); + policies.add("hivePolicy;;;;grp1:rwu,grp2:u;;entity:*abc,operation:*xyz,type:PII"); + // Creating group data + Map> groupMap = new HashMap>(); + List accessList1 = new ArrayList(); + accessList1.add(AtlasActionTypes.READ); + accessList1.add(AtlasActionTypes.WRITE); + accessList1.add(AtlasActionTypes.UPDATE); + + groupMap.put("grp1", accessList1); + List accessList2 = new ArrayList(); + accessList2.add(AtlasActionTypes.UPDATE); + groupMap.put("grp2", accessList2); + + // Creating user data + Map> usersMap = new HashMap>(); + + // Creating resources data + Map> resourceMap = new HashMap>(); + List resource1List = new ArrayList(); + resource1List.add("*abc"); + resourceMap.put(AtlasResourceTypes.ENTITY, resource1List); + + List resource2List = new ArrayList(); + resource2List.add("*xyz"); + resourceMap.put(AtlasResourceTypes.OPERATION, resource2List); + + List resource3List = new ArrayList(); + resource3List.add("PII"); + resourceMap.put(AtlasResourceTypes.TYPE, resource3List); + + List policyDefs = new PolicyParser().parsePolicies(policies); + for (PolicyDef def : policyDefs) { + + assertEquals(def.getPolicyName(), "hivePolicy"); + assertEquals(def.getGroups(), groupMap); + assertEquals(def.getUsers(), usersMap); + assertEquals(def.getResources(), resourceMap); + + } + + } + + @Test + public void testParsePoliciesWithOutGroupProperties() { + List policies = new ArrayList(); + policies.add("hivePolicy;;usr1:r,usr2:rw;;;;entity:*abc,operation:*xyz,type:PII"); + // Creating group data + Map> groupMap = new HashMap>(); + + // Creating user data + Map> usersMap = new HashMap>(); + List usr1AccessList = new ArrayList(); + usr1AccessList.add(AtlasActionTypes.READ); + usersMap.put("usr1", usr1AccessList); + + List usr2AccessList = new ArrayList(); + usr2AccessList.add(AtlasActionTypes.READ); + usr2AccessList.add(AtlasActionTypes.WRITE); + usersMap.put("usr2", usr2AccessList); + + // Creating resources data + Map> resourceMap = new HashMap>(); + List resource1List = new ArrayList(); + resource1List.add("*abc"); + resourceMap.put(AtlasResourceTypes.ENTITY, resource1List); + + List resource2List = new ArrayList(); + resource2List.add("*xyz"); + resourceMap.put(AtlasResourceTypes.OPERATION, resource2List); + + List resource3List = new ArrayList(); + resource3List.add("PII"); + resourceMap.put(AtlasResourceTypes.TYPE, resource3List); + + List policyDefs = new PolicyParser().parsePolicies(policies); + for (PolicyDef def : policyDefs) { + assertEquals(def.getPolicyName(), "hivePolicy"); + assertEquals(def.getGroups(), groupMap); + assertEquals(def.getUsers(), usersMap); + assertEquals(def.getResources(), resourceMap); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-atlas/blob/a963e980/webapp/src/test/java/org/apache/atlas/authorize/PolicyUtilTest.java ---------------------------------------------------------------------- diff --git a/webapp/src/test/java/org/apache/atlas/authorize/PolicyUtilTest.java b/webapp/src/test/java/org/apache/atlas/authorize/PolicyUtilTest.java new file mode 100644 index 0000000..59e88c9 --- /dev/null +++ b/webapp/src/test/java/org/apache/atlas/authorize/PolicyUtilTest.java @@ -0,0 +1,95 @@ +/* + * 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 static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.testng.annotations.Test; + +public class PolicyUtilTest { + + @Test + public void testCreatePermissionMap() { + + HashMap> resourceMap = new HashMap>(); + List resource1List = new ArrayList(); + resource1List.add("*abc"); + resourceMap.put(AtlasResourceTypes.ENTITY, resource1List); + + List resource2List = new ArrayList(); + resource2List.add("*xyz"); + resourceMap.put(AtlasResourceTypes.OPERATION, resource2List); + + List resource3List = new ArrayList(); + resource3List.add("PII"); + resourceMap.put(AtlasResourceTypes.TYPE, resource3List); + + Map>> permissionMap = + new HashMap>>(); + permissionMap.put("grp1", resourceMap); + + List policies = new ArrayList(); + policies.add("hivePolicy;;usr1:r,usr2:rw;;grp1:rwu,grp2:u;;entity:*abc,operation:*xyz,type:PII"); + List policyDefList = new PolicyParser().parsePolicies(policies); + + Map>> createdPermissionMap = + new PolicyUtil().createPermissionMap(policyDefList, AtlasActionTypes.READ, AtlasAccessorTypes.GROUP); + + assertEquals(permissionMap, createdPermissionMap); + + } + + @Test + public void testMergeCreatePermissionMap() { + + HashMap> resourceMap = new HashMap>(); + List resource1List = new ArrayList(); + resource1List.add("*abc"); + resourceMap.put(AtlasResourceTypes.ENTITY, resource1List); + + List resource2List = new ArrayList(); + resource2List.add("*x"); + resource2List.add("*xyz"); + resourceMap.put(AtlasResourceTypes.OPERATION, resource2List); + + List resource3List = new ArrayList(); + resource3List.add("PII"); + resourceMap.put(AtlasResourceTypes.TYPE, resource3List); + + Map>> permissionMap = + new HashMap>>(); + permissionMap.put("grp1", resourceMap); + + List policies = new ArrayList(); + policies.add("hivePolicys;;;;grp1:rwu;;entity:*abc,operation:*xyz,operation:*x"); + policies.add("hivePolicy;;;;grp1:rwu;;entity:*abc,operation:*xyz"); + policies.add("hivePolicy;;usr1:r,usr2:rw;;grp1:rwu;;entity:*abc,operation:*xyz"); + policies.add("hivePolicy;;usr1:r,usr2:rw;;grp1:rwu,grp2:u;;entity:*abc,operation:*xyz,type:PII"); + List policyDefList = new PolicyParser().parsePolicies(policies); + + Map>> createdPermissionMap = + new PolicyUtil().createPermissionMap(policyDefList, AtlasActionTypes.READ, AtlasAccessorTypes.GROUP); + + assertEquals(permissionMap, createdPermissionMap); + + } +}