This is an automated email from the ASF dual-hosted git repository. madhan pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ranger.git
commit f3e8e0a648e33fdcd947136a1d7308db9fbe5109 Author: Madhan Neethiraj <mad...@apache.org> AuthorDate: Tue Sep 5 16:45:12 2023 -0700 RANGER-4398: security-zone API enhancements to support incremental updates and resource pagination --- .../ranger/plugin/model/RangerPrincipal.java | 97 ++++ .../ranger/plugin/model/RangerSecurityZone.java | 46 +- .../ranger/plugin/model/RangerSecurityZoneV2.java | 507 +++++++++++++++++++++ .../plugin/util/RangerSecurityZoneHelper.java | 399 ++++++++++++++++ .../python/apache_ranger/client/ranger_client.py | 72 ++- .../main/python/apache_ranger/model/ranger_base.py | 22 + .../python/apache_ranger/model/ranger_principal.py | 59 +++ .../apache_ranger/model/ranger_security_zone.py | 105 ++++- intg/src/main/python/setup.py | 2 +- .../src/main/python/security_zone_v2.py | 149 ++++++ .../java/org/apache/ranger/rest/PublicAPIsv2.java | 63 +++ .../org/apache/ranger/rest/SecurityZoneREST.java | 140 +++++- 12 files changed, 1640 insertions(+), 21 deletions(-) diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPrincipal.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPrincipal.java new file mode 100644 index 000000000..226dc4d7c --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerPrincipal.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import org.codehaus.jackson.annotate.JsonAutoDetect; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.Objects; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class RangerPrincipal implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + public enum PrincipalType { USER, GROUP, ROLE } + + private PrincipalType type; + private String name; + + public RangerPrincipal() { + this(null, null); + } + + public RangerPrincipal(PrincipalType type, String name) { + setType(type); + setName(name); + } + + public PrincipalType getType() { + return type; + } + + public void setType(PrincipalType type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + return Objects.hash(type, name); + } + + @Override + public boolean equals(Object obj) { + final boolean ret; + + if (this == obj) { + ret = true; + } else if (obj == null) { + ret = false; + } else if (getClass() != obj.getClass()) { + ret = false; + } else { + RangerPrincipal other = (RangerPrincipal) obj; + + ret = Objects.equals(type, other.type) && Objects.equals(name, other.name); + } + + return ret; + } + + @Override + public String toString() { + return "{type=" + type + ", name=" + name + "}"; + } +} diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java index 71d64ca83..47f8041b7 100644 --- a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZone.java @@ -23,6 +23,8 @@ import org.codehaus.jackson.annotate.JsonAutoDetect; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import org.codehaus.jackson.map.annotate.JsonSerialize; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResourceBase; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -147,14 +149,20 @@ public class RangerSecurityZone extends RangerBaseModelObject implements java.io @JsonIgnoreProperties(ignoreUnknown=true) public static class RangerSecurityZoneService implements java.io.Serializable { private static final long serialVersionUID = 1L; - private List<HashMap<String, List<String>>> resources; + private List<HashMap<String, List<String>>> resources; + private List<RangerSecurityZoneResourceBase> resourcesBaseInfo; public RangerSecurityZoneService() { - this(null); + this(null, null); } public RangerSecurityZoneService(List<HashMap<String, List<String>>> resources) { + this(resources, null); + } + + public RangerSecurityZoneService(List<HashMap<String, List<String>>> resources, List<RangerSecurityZoneResourceBase> resourcesBaseInfo) { setResources(resources); + setResourcesBaseInfo(resourcesBaseInfo); } public List<HashMap<String, List<String>>> getResources() { return resources; } @@ -163,22 +171,40 @@ public class RangerSecurityZone extends RangerBaseModelObject implements java.io this.resources = resources == null ? new ArrayList<>() : resources; } + public List<RangerSecurityZoneResourceBase> getResourcesBaseInfo() { return resourcesBaseInfo; } + + public void setResourcesBaseInfo(List<RangerSecurityZoneResourceBase> resourcesBaseInfo) { + this.resourcesBaseInfo = resourcesBaseInfo == null ? new ArrayList<>() : resourcesBaseInfo; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("{resources={"); - for (Map<String, List<String>> resource : resources) { - sb.append("[ "); - for (Map.Entry<String, List<String>> entry : resource.entrySet()) { - sb.append("{resource-def-name=").append(entry.getKey()).append(", values=").append(entry.getValue()).append("},"); + sb.append("{resources=["); + if (resources != null) { + for (int i = 0; i < resources.size(); i++) { + HashMap<String, List<String>> resource = resources.get(i); + RangerSecurityZoneResourceBase baseInfo = (resourcesBaseInfo != null && resourcesBaseInfo.size() > i) ? resourcesBaseInfo.get(i) : null; + + sb.append("{resource="); + if (resource != null) { + for (Map.Entry<String, List<String>> entry : resource.entrySet()) { + sb.append("{resource-def-name=").append(entry.getKey()).append(", values=").append(entry.getValue()).append("} "); + } + } + sb.append("} "); + + sb.append("{baseInfo="); + if (baseInfo != null) { + baseInfo.toString(sb); + } + sb.append("} "); } - sb.append(" ],"); } - sb.append("}}"); + sb.append("]}"); return sb.toString(); } - } } diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneV2.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneV2.java new file mode 100644 index 000000000..329780c15 --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerSecurityZoneV2.java @@ -0,0 +1,507 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.model; + +import org.codehaus.jackson.annotate.JsonAutoDetect; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.codehaus.jackson.map.annotate.JsonSerialize; + +import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) +@JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY) +@JsonIgnoreProperties(ignoreUnknown=true) +public class RangerSecurityZoneV2 extends RangerBaseModelObject implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private Map<String, RangerSecurityZoneServiceV2> services; + private List<String> tagServices; + private List<RangerPrincipal> admins; + private List<RangerPrincipal> auditors; + + public RangerSecurityZoneV2() { + this(null, null, null, null, null, null); + } + + public RangerSecurityZoneV2(String name, String description, Map<String, RangerSecurityZoneServiceV2> services, List<String> tagServices, List<RangerPrincipal> admins, List<RangerPrincipal> auditors) { + setName(name); + setDescription(description); + setServices(services); + setTagServices(tagServices); + setAdmins(admins); + setAuditors(auditors); + } + + public RangerSecurityZoneV2(RangerSecurityZone other) { + setId(other.getId()); + setGuid(other.getGuid()); + setIsEnabled(other.getIsEnabled()); + setCreatedBy(other.getCreatedBy()); + setUpdatedBy(other.getUpdatedBy()); + setCreateTime(other.getCreateTime()); + setUpdateTime(other.getUpdateTime()); + setVersion(other.getVersion()); + setName(other.getName()); + setDescription(other.getDescription()); + setTagServices((other.getTagServices() != null) ? new ArrayList<>(other.getTagServices()) : new ArrayList<>()); + setAdmins(toPrincipals(other.getAdminUsers(), other.getAdminUserGroups(), other.getAdminRoles())); + setAuditors(toPrincipals(other.getAuditUsers(), other.getAuditUserGroups(), other.getAuditRoles())); + + services = new HashMap<>(); + + if (other.getServices() != null) { + for (Map.Entry<String, RangerSecurityZoneService> entry : other.getServices().entrySet()) { + services.put(entry.getKey(), new RangerSecurityZoneServiceV2(entry.getValue())); + } + } + } + + public String getName() { return name; } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { return description; } + + public void setDescription(String description) { + this.description = description; + } + + public Map<String, RangerSecurityZoneServiceV2> getServices() { return services; } + + public void setServices(Map<String, RangerSecurityZoneServiceV2> services) { + this.services = services == null ? new HashMap<>() : services; + } + + public List<String> getTagServices() { + return tagServices; + } + + public void setTagServices(List<String> tagServices) { + this.tagServices = (tagServices != null) ? tagServices : new ArrayList<>(); + } + + public List<RangerPrincipal> getAdmins() { return admins; } + + public void setAdmins(List<RangerPrincipal> admins) { + this.admins = admins == null ? new ArrayList<>() : admins; + } + + public List<RangerPrincipal> getAuditors() { return auditors; } + + public void setAuditors(List<RangerPrincipal> auditors) { + this.auditors = auditors == null ? new ArrayList<>() : auditors; + } + + public RangerSecurityZone toV1() { + RangerSecurityZone ret = new RangerSecurityZone(); + + ret.setId(getId()); + ret.setGuid(getGuid()); + ret.setIsEnabled(getIsEnabled()); + ret.setCreatedBy(getCreatedBy()); + ret.setUpdatedBy(getUpdatedBy()); + ret.setCreateTime(getCreateTime()); + ret.setUpdateTime(getUpdateTime()); + ret.setVersion(getVersion()); + ret.setName(name); + ret.setDescription(description); + + if (services != null) { + for (Map.Entry<String, RangerSecurityZoneServiceV2> entry : services.entrySet()) { + ret.getServices().put(entry.getKey(), entry.getValue().toV1()); + } + } + + if (tagServices != null) { + ret.getTagServices().addAll(tagServices); + } + + fromPrincipals(admins, ret.getAdminUsers(), ret.getAdminUserGroups(), ret.getAdminRoles()); + fromPrincipals(auditors, ret.getAuditUsers(), ret.getAuditUserGroups(), ret.getAuditRoles()); + + return ret; + } + + @Override + public String toString() { + return "{name=" + name + + ", description="+ description + + ", services=" + services + + ", tagServices=" + tagServices + + ", admins=" + admins + + ", auditors=" + auditors + +"}"; + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneServiceV2 implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private List<RangerSecurityZoneResource> resources; + + public RangerSecurityZoneServiceV2() { + this((List<RangerSecurityZoneResource>) null); + } + + public RangerSecurityZoneServiceV2(List<RangerSecurityZoneResource> resources) { + setResources(resources); + } + + public RangerSecurityZoneServiceV2(RangerSecurityZoneService other) { + resources = new ArrayList<>(); + + if (other != null && other.getResources() != null) { + for (int i = 0; i < other.getResources().size(); i++) { + RangerSecurityZoneResource resource = getResourceAt(other, i); + + if (resource != null) { + resources.add(resource); + } + } + } + } + + public List<RangerSecurityZoneResource> getResources() { return resources; } + + public void setResources(List<RangerSecurityZoneResource> resources) { + this.resources = resources == null ? new ArrayList<>() : resources; + } + + public RangerSecurityZoneService toV1() { + RangerSecurityZoneService ret = new RangerSecurityZoneService(); + + if (resources != null) { + for (RangerSecurityZoneResource resource : resources) { + ret.getResources().add((HashMap<String, List<String>> ) resource.getResource()); + ret.getResourcesBaseInfo().add(new RangerSecurityZoneResourceBase(resource)); + } + } + return ret; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{resources=["); + if (resources != null) { + for (RangerSecurityZoneResource resource : resources) { + if (resource != null) { + resource.toString(sb).append(" "); + } + } + } + sb.append("]}"); + + return sb.toString(); + } + + private RangerSecurityZoneResource getResourceAt(RangerSecurityZoneService zoneService, int idx) { + Map<String, List<String>> resource = zoneService.getResources() != null && zoneService.getResources().size() > idx ? zoneService.getResources().get(idx) : null; + RangerSecurityZoneResourceBase baseInfo = zoneService.getResourcesBaseInfo() != null && zoneService.getResourcesBaseInfo().size() > idx ? zoneService.getResourcesBaseInfo().get(idx) : null; + + return resource != null ? new RangerSecurityZoneResource(resource, baseInfo) : null; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneResourceBase implements java.io.Serializable { + private Long id; + private String createdBy; + private String updatedBy; + private Date createTime; + private Date updateTime; + + public RangerSecurityZoneResourceBase() { } + + public RangerSecurityZoneResourceBase(RangerSecurityZoneResourceBase other) { + if (other != null) { + setId(other.getId()); + setCreatedBy(other.getCreatedBy()); + setCreateTime(other.getCreateTime()); + setUpdatedBy(other.getUpdatedBy()); + setUpdateTime(other.getUpdateTime()); + } + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public String getUpdatedBy() { + return updatedBy; + } + + public void setUpdatedBy(String updatedBy) { + this.updatedBy = updatedBy; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + public StringBuilder toString(StringBuilder sb) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append("{id=").append(id) + .append(", createdBy=").append(createdBy) + .append(", createTime=").append(createTime) + .append(", updatedBy=").append(updatedBy) + .append(", updateTime=").append(updateTime) + .append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneResource extends RangerSecurityZoneResourceBase implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private Map<String, List<String>> resource; + + public RangerSecurityZoneResource() { this(null, null); } + + public RangerSecurityZoneResource(Map<String, List<String>> resource) { + this(resource, null); + } + + public RangerSecurityZoneResource(Map<String, List<String>> resource, RangerSecurityZoneResourceBase baseObj) { + super(baseObj); + + setResource(resource); + } + + public Map<String, List<String>> getResource() { return resource; } + + public void setResource(Map<String, List<String>> resource) { this.resource = resource == null ? new HashMap<>() : resource; } + + @Override + public String toString() { + return toString(new StringBuilder()).toString(); + } + + @Override + public StringBuilder toString(StringBuilder sb) { + if (sb == null) { + sb = new StringBuilder(); + } + + sb.append("{resource="); + super.toString(sb); + if (resource != null) { + for (Map.Entry<String, List<String>> entry : resource.entrySet()) { + sb.append("{resource-def-name=").append(entry.getKey()) + .append(", values=").append(entry.getValue()).append("} "); + } + } + sb.append("}"); + + return sb; + } + } + + @JsonAutoDetect(fieldVisibility=JsonAutoDetect.Visibility.ANY) + @JsonSerialize(include=JsonSerialize.Inclusion.NON_EMPTY) + @JsonIgnoreProperties(ignoreUnknown=true) + public static class RangerSecurityZoneChangeRequest implements java.io.Serializable { + private static final long serialVersionUID = 1L; + + private String name; + private String description; + private Map<String, RangerSecurityZoneServiceV2> resourcesToUpdate; + private Map<String, RangerSecurityZoneServiceV2> resourcesToRemove; + private List<String> tagServicesToAdd; + private List<String> tagServicesToRemove; + private List<RangerPrincipal> adminsToAdd; + private List<RangerPrincipal> adminsToRemove; + private List<RangerPrincipal> auditorsToAdd; + private List<RangerPrincipal> auditorsToRemove; + + public RangerSecurityZoneChangeRequest() { } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Map<String, RangerSecurityZoneServiceV2> getResourcesToUpdate() { + return resourcesToUpdate; + } + + public void setResourcesToUpdate(Map<String, RangerSecurityZoneServiceV2> resourcesToUpdate) { + this.resourcesToUpdate = resourcesToUpdate; + } + + public Map<String, RangerSecurityZoneServiceV2> getResourcesToRemove() { + return resourcesToRemove; + } + + public void setResourcesToRemove(Map<String, RangerSecurityZoneServiceV2> resourcesToRemove) { + this.resourcesToRemove = resourcesToRemove; + } + + public List<String> getTagServicesToAdd() { + return tagServicesToAdd; + } + + public void setTagServicesToAdd(List<String> tagServicesToAdd) { + this.tagServicesToAdd = tagServicesToAdd; + } + + public List<String> getTagServicesToRemove() { + return tagServicesToRemove; + } + + public void setTagServicesToRemove(List<String> tagServicesToRemove) { + this.tagServicesToRemove = tagServicesToRemove; + } + + public List<RangerPrincipal> getAdminsToAdd() { + return adminsToAdd; + } + + public void setAdminsToAdd(List<RangerPrincipal> adminsToAdd) { + this.adminsToAdd = adminsToAdd; + } + + public List<RangerPrincipal> getAdminsToRemove() { + return adminsToRemove; + } + + public void setAdminsToRemove(List<RangerPrincipal> adminsToRemove) { + this.adminsToRemove = adminsToRemove; + } + + public List<RangerPrincipal> getAuditorsToAdd() { + return auditorsToAdd; + } + + public void setAuditorsToAdd(List<RangerPrincipal> auditorsToAdd) { + this.auditorsToAdd = auditorsToAdd; + } + + public List<RangerPrincipal> getAuditorsToRemove() { + return auditorsToRemove; + } + + public void setAuditorsToRemove(List<RangerPrincipal> auditorsToRemove) { + this.auditorsToRemove = auditorsToRemove; + } + + + } + + private void fromPrincipals(List<RangerPrincipal> principals, List<String> users, List<String> groups, List<String> roles) { + if (principals != null) { + for (RangerPrincipal principal : principals) { + if (principal.getType() == RangerPrincipal.PrincipalType.USER) { + users.add(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.GROUP) { + groups.add(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.ROLE) { + roles.add(principal.getName()); + } + } + } + } + + private List<RangerPrincipal> toPrincipals(List<String> users, List<String> groups, List<String> roles) { + List<RangerPrincipal> ret = new ArrayList<>(); + + if (users != null) { + for (String name : users) { + ret.add(new RangerPrincipal(RangerPrincipal.PrincipalType.USER, name)); + } + } + + if (groups != null) { + for (String name : groups) { + ret.add(new RangerPrincipal(RangerPrincipal.PrincipalType.GROUP, name)); + } + } + + if (roles != null) { + for (String name : roles) { + ret.add(new RangerPrincipal(RangerPrincipal.PrincipalType.ROLE, name)); + } + } + + return ret; + } +} + diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSecurityZoneHelper.java b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSecurityZoneHelper.java new file mode 100644 index 000000000..460b55e1e --- /dev/null +++ b/agents-common/src/main/java/org/apache/ranger/plugin/util/RangerSecurityZoneHelper.java @@ -0,0 +1,399 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.ranger.plugin.util; + +import org.apache.ranger.plugin.model.RangerPrincipal; +import org.apache.ranger.plugin.model.RangerSecurityZone; +import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneChangeRequest; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResource; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResourceBase; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneServiceV2; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +public class RangerSecurityZoneHelper { + private final RangerSecurityZone zone; + private final String currentUser; + private final Map<String, RangerSecurityZoneServiceHelper> services; + + + public RangerSecurityZoneHelper(RangerSecurityZone zone, String currentUser) { + this.zone = zone; + this.currentUser = currentUser; + this.services = new HashMap<>(); + + for (Map.Entry<String, RangerSecurityZoneService> entry : zone.getServices().entrySet()) { + this.services.put(entry.getKey(), new RangerSecurityZoneServiceHelper(entry.getValue(), currentUser)); + } + } + + public RangerSecurityZone getZone() { return zone; } + + public RangerSecurityZoneServiceHelper getZoneService(String serviceName) { + return services.get(serviceName); + } + + public RangerSecurityZoneServiceHelper addZoneService(String serviceName) { + RangerSecurityZoneServiceHelper ret = services.get(serviceName); + + if (ret == null) { + RangerSecurityZoneService zoneService = zone.getServices().get(serviceName); + + if (zoneService == null) { + zoneService = new RangerSecurityZoneService(); + + zone.getServices().put(serviceName, zoneService); + } + + ret = new RangerSecurityZoneServiceHelper(zoneService, currentUser); + + services.put(serviceName, ret); + } + + return ret; + } + + public void removeService(String serviceName) { + services.remove(serviceName); + zone.getServices().remove(serviceName); + } + + public RangerSecurityZone updateZone(RangerSecurityZoneChangeRequest changeData) { + if (changeData.getName() != null) { + zone.setName(changeData.getName()); + } + + if (changeData.getDescription() != null) { + zone.setDescription(changeData.getDescription()); + } + + if (changeData.getResourcesToUpdate() != null) { + for (Map.Entry<String, RangerSecurityZoneServiceV2> entry : changeData.getResourcesToUpdate().entrySet()) { + String serviceName = entry.getKey(); + RangerSecurityZoneServiceV2 zoneService = entry.getValue(); + RangerSecurityZoneServiceHelper zoneServiceHelper = addZoneService(serviceName); + + if (zoneService != null && zoneService.getResources() != null) { + for (RangerSecurityZoneResource resource : zoneService.getResources()) { + if (resource != null) { + zoneServiceHelper.updateResource(resource); + } + } + } + } + } + + if (changeData.getResourcesToRemove() != null) { + for (Map.Entry<String, RangerSecurityZoneServiceV2> entry : changeData.getResourcesToRemove().entrySet()) { + String serviceName = entry.getKey(); + RangerSecurityZoneServiceV2 zoneService = entry.getValue(); + RangerSecurityZoneServiceHelper zoneServiceHelper = getZoneService(serviceName); + + if (zoneServiceHelper != null && zoneService != null && zoneService.getResources() != null) { + for (RangerSecurityZoneResource resource : zoneService.getResources()) { + if (resource != null) { + if (resource.getId() != null) { + zoneServiceHelper.removeResource(resource.getId()); + } else if (resource.getResource() != null) { + zoneServiceHelper.removeResource(resource.getResource()); + } + } + } + + if (zoneServiceHelper.getResourceCount() == 0) { + removeService(serviceName); + } + } + } + } + + if (changeData.getTagServicesToAdd() != null) { + zone.getTagServices().addAll(changeData.getTagServicesToAdd()); + } + + if (changeData.getTagServicesToRemove() != null) { + zone.getTagServices().removeAll(changeData.getTagServicesToRemove()); + } + + if (changeData.getAdminsToAdd() != null) { + addPrincipals(changeData.getAdminsToAdd(), zone.getAdminUsers(), zone.getAdminUserGroups(), zone.getAdminRoles()); + } + + if (changeData.getAdminsToRemove() != null) { + removePrincipals(changeData.getAdminsToRemove(), zone.getAdminUsers(), zone.getAdminUserGroups(), zone.getAdminRoles()); + } + + if (changeData.getAuditorsToAdd() != null) { + addPrincipals(changeData.getAuditorsToAdd(), zone.getAuditUsers(), zone.getAuditUserGroups(), zone.getAuditRoles()); + } + + if (changeData.getAuditorsToRemove() != null) { + removePrincipals(changeData.getAuditorsToRemove(), zone.getAuditUsers(), zone.getAuditUserGroups(), zone.getAuditRoles()); + } + + return zone; + } + + private void addPrincipals(List<RangerPrincipal> principals, List<String> users, List<String> groups, List<String> roles) { + for (RangerPrincipal principal : principals) { + if (principal.getType() == RangerPrincipal.PrincipalType.USER) { + users.add(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.GROUP) { + groups.add(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.ROLE) { + roles.add(principal.getName()); + } + } + } + + private void removePrincipals(List<RangerPrincipal> principals, List<String> users, List<String> groups, List<String> roles) { + for (RangerPrincipal principal : principals) { + if (principal.getType() == RangerPrincipal.PrincipalType.USER) { + users.remove(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.GROUP) { + groups.remove(principal.getName()); + } else if (principal.getType() == RangerPrincipal.PrincipalType.ROLE) { + roles.remove(principal.getName()); + } + } + } + + public static class RangerSecurityZoneServiceHelper { + private final RangerSecurityZoneService zoneService; + private final String currentUser; + private final List<HashMap<String, List<String>>> resources; + private final List<RangerSecurityZoneResourceBase> resourcesBaseInfo; + private long nextResourceId = 0; + + public RangerSecurityZoneServiceHelper(RangerSecurityZoneService zoneService, String currentUser) { + this.zoneService = zoneService; + this.currentUser = currentUser; + + if (zoneService.getResources() != null) { + this.resources = zoneService.getResources(); + } else { + this.resources = new ArrayList<>(); + + zoneService.setResources(this.resources); + } + + if (zoneService.getResourcesBaseInfo() != null) { + this.resourcesBaseInfo = zoneService.getResourcesBaseInfo(); + } else { + this.resourcesBaseInfo = new ArrayList<>(); + + zoneService.setResourcesBaseInfo(this.resourcesBaseInfo); + } + + // make sure resourcesBaseInfo has as many entries as resources + for (int i = resourcesBaseInfo.size(); i < resources.size(); i++) { + RangerSecurityZoneResourceBase baseInfo = new RangerSecurityZoneResourceBase(); + + setCreated(baseInfo); + + resourcesBaseInfo.add(baseInfo); + } + + // remove any additional resourcesBaseInfo entries + for (int i = resources.size(); i < resourcesBaseInfo.size(); ) { + resourcesBaseInfo.remove(i); + } + + // compute nextResourceId + for (RangerSecurityZoneResourceBase baseInfo : resourcesBaseInfo) { + if (baseInfo.getId() != null && nextResourceId <= baseInfo.getId()) { + nextResourceId = baseInfo.getId() + 1; + } + } + + // set missing IDs + for (RangerSecurityZoneResourceBase baseInfo : resourcesBaseInfo) { + if (baseInfo.getId() == null) { + baseInfo.setId(nextResourceId++); + } + } + } + + public RangerSecurityZoneService getZoneService() { return zoneService; } + + public int getResourceCount() { + return resources != null ? resources.size() : 0; + } + + public List<RangerSecurityZoneResource> getResources() { + List<RangerSecurityZoneResource> ret = new ArrayList<>(); + + if (resources != null) { + for (int i = 0; i < resources.size(); i++) { + ret.add(getResourceAt(i)); + } + } + + return Collections.unmodifiableList(ret); + } + + public List<RangerSecurityZoneResource> getResources(int startIdx, int count) { + List<RangerSecurityZoneResource> ret = new ArrayList<>(); + + if (resources != null) { + for (int i = 0; i < count; i++) { + RangerSecurityZoneResource resource = getResourceAt(startIdx + i); + + if (resource == null) { + break; + } + + ret.add(resource); + } + } + + return Collections.unmodifiableList(ret); + } + + public RangerSecurityZoneResource getResource(long id) { + int idx = getResourceIdx(id); + + return idx != -1 ? getResourceAt(idx) : null; + } + + public RangerSecurityZoneResource getResource(Map<String, List<String>> resource) { + int idx = getResourceIdx(resource); + + return idx != -1 ? getResourceAt(idx) : null; + } + + public RangerSecurityZoneResource addResource(RangerSecurityZoneResource resource) { + setCreated(resource); + + resources.add((HashMap<String, List<String>>) resource.getResource()); + resourcesBaseInfo.add(new RangerSecurityZoneResourceBase(resource)); + + return resource; + } + + public RangerSecurityZoneResource updateResource(RangerSecurityZoneResource resource) { + Long resourceId = resource.getId(); + int resourceIdx = resourceId != null ? getResourceIdx(resourceId) : -1; + + if (resourceIdx == -1) { + addResource(resource); + } else { + setUpdated(resource); + + resources.set(resourceIdx, (HashMap<String, List<String>>) resource.getResource()); + resourcesBaseInfo.set(resourceIdx, new RangerSecurityZoneResourceBase(resource)); + } + + return resource; + } + + public RangerSecurityZoneResource removeResource(long id) { + int idx = getResourceIdx(id); + + return idx != -1 ? removeResourceAt(idx) : null; + } + + public RangerSecurityZoneResource removeResource(Map<String, List<String>> resource) { + int idx = getResourceIdx(resource); + + return idx != -1 ? removeResourceAt(idx) : null; + } + + private RangerSecurityZoneResource getResourceAt(int idx) { + RangerSecurityZoneResource ret = null; + HashMap<String, List<String>> resource = (resources != null && resources.size() > idx) ? resources.get(idx) : null; + RangerSecurityZoneResourceBase resourceBaseInfo = (resourcesBaseInfo != null && resourcesBaseInfo.size() > idx) ? resourcesBaseInfo.get(idx) : null; + + if (resource != null) { + ret = new RangerSecurityZoneResource(resource, resourceBaseInfo); + } + + return ret; + } + + private RangerSecurityZoneResource removeResourceAt(int idx) { + RangerSecurityZoneResource ret = null; + HashMap<String, List<String>> resource = (resources != null && resources.size() > idx) ? resources.remove(idx) : null; + RangerSecurityZoneResourceBase resourceBaseInfo = (resourcesBaseInfo != null && resourcesBaseInfo.size() > idx) ? resourcesBaseInfo.remove(idx) : null; + + if (resource != null) { + ret = new RangerSecurityZoneResource(resource, resourceBaseInfo); + } + + return ret; + } + + private int getResourceIdx(long id) { + int ret = -1; + + if (resourcesBaseInfo != null) { + for (int i = 0; i < resourcesBaseInfo.size(); i++) { + RangerSecurityZoneResourceBase baseInfo = resourcesBaseInfo.get(i); + + if (baseInfo != null && baseInfo.getId() != null && baseInfo.getId().equals(id)) { + ret = i; + + break; + } + } + } + + return ret; + } + + private int getResourceIdx(Map<String, List<String>> resource) { + int ret = -1; + + if (resources != null) { + for (int i = 0; i < resources.size(); i++) { + HashMap<String, List<String>> res = resources.get(i); + + if (Objects.equals(resource, res)) { + ret = i; + + break; + } + } + } + + return ret; + } + + private void setCreated(RangerSecurityZoneResourceBase baseInfo) { + baseInfo.setId(nextResourceId++); + baseInfo.setCreatedBy(currentUser); + baseInfo.setCreateTime(new Date()); + baseInfo.setUpdatedBy(currentUser); + baseInfo.setUpdateTime(new Date()); + } + + private void setUpdated(RangerSecurityZoneResourceBase baseInfo) { + baseInfo.setUpdatedBy(currentUser); + baseInfo.setUpdateTime(new Date()); + } + } +} diff --git a/intg/src/main/python/apache_ranger/client/ranger_client.py b/intg/src/main/python/apache_ranger/client/ranger_client.py index 484a42128..f6e865107 100644 --- a/intg/src/main/python/apache_ranger/client/ranger_client.py +++ b/intg/src/main/python/apache_ranger/client/ranger_client.py @@ -20,10 +20,10 @@ import json import logging from apache_ranger.exceptions import RangerServiceException -from apache_ranger.model.ranger_base import RangerBase +from apache_ranger.model.ranger_base import RangerBase, PList from apache_ranger.model.ranger_policy import RangerPolicy from apache_ranger.model.ranger_role import RangerRole -from apache_ranger.model.ranger_security_zone import RangerSecurityZone, RangerSecurityZoneHeaderInfo +from apache_ranger.model.ranger_security_zone import RangerSecurityZone, RangerSecurityZoneV2, RangerSecurityZoneHeaderInfo, RangerSecurityZoneResource from apache_ranger.model.ranger_service import RangerService, RangerServiceHeaderInfo from apache_ranger.model.ranger_service_def import RangerServiceDef from apache_ranger.model.ranger_service_tags import RangerServiceTags @@ -229,6 +229,59 @@ class RangerClient: return type_coerce_list(resp, RangerSecurityZone) + def create_security_zone_v2(self, securityZone): + resp = self.client_http.call_api(RangerClient.CREATE_ZONE_V2, request_data=securityZone) + + return type_coerce(resp, RangerSecurityZoneV2) + + def update_security_zone_v2(self, zoneId, securityZone): + resp = self.client_http.call_api(RangerClient.UPDATE_ZONE_V2_BY_ID.format_path({ 'id': zoneId }), request_data=securityZone) + + return type_coerce(resp, RangerSecurityZoneV2) + + def partial_update_security_zone_v2(self, zoneId, changeData): + resp = self.client_http.call_api(RangerClient.PARTIAL_UPDATE_ZONE_V2_BY_ID.format_path({ 'id': zoneId }), request_data=changeData) + + return type_coerce(resp, RangerSecurityZoneV2) + + def get_security_zone_v2(self, zoneName): + resp = self.client_http.call_api(RangerClient.GET_ZONE_V2_BY_NAME.format_path({ 'name': zoneName })) + + return type_coerce(resp, RangerSecurityZoneV2) + + def get_security_zone_v2_by_id(self, zoneId): + resp = self.client_http.call_api(RangerClient.GET_ZONE_V2_BY_ID.format_path({ 'id': zoneId })) + + return type_coerce(resp, RangerSecurityZoneV2) + + def zone_v2_get_resources(self, zoneName, serviceName, filter=None): + resp = self.client_http.call_api(RangerClient.ZONE_V2_GET_RESOURCES.format_path({'name': zoneName, 'serviceName': serviceName}), filter) + ret = type_coerce(resp, PList) + + if ret is not None: + ret.type_coerce_list(RangerSecurityZoneResource) + + return ret + + def zone_v2_by_id_get_resources(self, zoneId, serviceName, filter=None): + resp = self.client_http.call_api(RangerClient.ZONE_V2_BY_ID_GET_RESOURCES.format_path({'id': zoneId, 'serviceName': serviceName}), filter) + ret = type_coerce(resp, PList) + + if ret is not None: + ret.type_coerce_list(RangerSecurityZoneResource) + + return ret + + def find_security_zones_v2(self, filter=None): + resp = self.client_http.call_api(RangerClient.FIND_ZONES_V2, filter) + ret = type_coerce(resp, PList) + + if ret is not None: + ret.type_coerce_list(RangerSecurityZoneV2) + + return ret + + # Role APIs def create_role(self, serviceName, role, params=None): if params is None: @@ -337,6 +390,12 @@ class RangerClient: URI_ZONE_HEADERS = URI_BASE + "/zone-headers" URI_ZONE_SERVICE_HEADERS = URI_ZONE + "/{id}/service-headers" URI_ZONE_NAMES_FOR_RESOURCE = URI_BASE + "/zone-names/{serviceName}/resource" + URI_ZONE_V2 = URI_BASE + "/zones-v2" + URI_ZONE_V2_BY_ID = URI_ZONE_V2 + "/{id}" + URI_ZONE_V2_BY_NAME = URI_ZONE_V2 + "/name/{name}" + URL_ZONE_V2_BY_ID_RESOURCES = URI_ZONE_V2_BY_ID + "/resources/{serviceName}" + URL_ZONE_V2_BY_NAME_RESOURCES = URI_ZONE_V2_BY_NAME+ "/resources/{serviceName}" + URI_ZONE_V2_PARTIAL_BY_ID = URI_ZONE_V2_BY_ID + "/partial" URI_SERVICE_TAGS = URI_SERVICE + "/{serviceName}/tags" URI_PLUGIN_INFO = URI_BASE + "/plugins/info" @@ -375,7 +434,6 @@ class RangerClient: CREATE_ZONE = API(URI_ZONE, HttpMethod.POST, HTTPStatus.OK) UPDATE_ZONE_BY_ID = API(URI_ZONE_BY_ID, HttpMethod.PUT, HTTPStatus.OK) - UPDATE_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.PUT, HTTPStatus.OK) DELETE_ZONE_BY_ID = API(URI_ZONE_BY_ID, HttpMethod.DELETE, HTTPStatus.NO_CONTENT) DELETE_ZONE_BY_NAME = API(URI_ZONE_BY_NAME, HttpMethod.DELETE, HTTPStatus.NO_CONTENT) GET_ZONE_BY_ID = API(URI_ZONE_BY_ID, HttpMethod.GET, HTTPStatus.OK) @@ -384,6 +442,14 @@ class RangerClient: GET_ZONE_HEADERS = API(URI_ZONE_HEADERS, HttpMethod.GET, HTTPStatus.OK) GET_ZONE_SERVICE_HEADERS = API(URI_ZONE_SERVICE_HEADERS, HttpMethod.GET, HTTPStatus.OK) GET_ZONE_NAMES_FOR_RESOURCE = API(URI_ZONE_NAMES_FOR_RESOURCE, HttpMethod.GET, HTTPStatus.OK) + CREATE_ZONE_V2 = API(URI_ZONE_V2, HttpMethod.POST, HTTPStatus.OK) + UPDATE_ZONE_V2_BY_ID = API(URI_ZONE_V2_BY_ID, HttpMethod.PUT, HTTPStatus.OK) + PARTIAL_UPDATE_ZONE_V2_BY_ID = API(URI_ZONE_V2_PARTIAL_BY_ID, HttpMethod.PUT, HTTPStatus.OK) + GET_ZONE_V2_BY_NAME = API(URI_ZONE_V2_BY_NAME, HttpMethod.GET, HTTPStatus.OK) + GET_ZONE_V2_BY_ID = API(URI_ZONE_V2_BY_ID, HttpMethod.GET, HTTPStatus.OK) + ZONE_V2_GET_RESOURCES = API(URL_ZONE_V2_BY_NAME_RESOURCES, HttpMethod.GET, HTTPStatus.OK) + ZONE_V2_BY_ID_GET_RESOURCES = API(URL_ZONE_V2_BY_ID_RESOURCES, HttpMethod.GET, HTTPStatus.OK) + FIND_ZONES_V2 = API(URI_ZONE_V2, HttpMethod.GET, HTTPStatus.OK) CREATE_ROLE = API(URI_ROLE, HttpMethod.POST, HTTPStatus.OK) UPDATE_ROLE_BY_ID = API(URI_ROLE_BY_ID, HttpMethod.PUT, HTTPStatus.OK) diff --git a/intg/src/main/python/apache_ranger/model/ranger_base.py b/intg/src/main/python/apache_ranger/model/ranger_base.py index 2111534d0..2cb06b8bd 100644 --- a/intg/src/main/python/apache_ranger/model/ranger_base.py +++ b/intg/src/main/python/apache_ranger/model/ranger_base.py @@ -71,3 +71,25 @@ class RangerBaseModelObject(RangerBase): self.createTime = attrs.get('createTime') self.updateTime = attrs.get('updateTime') self.version = attrs.get('version') + +class PList(RangerBase): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerBase.__init__(self, attrs) + + self.startIndex = attrs.get('startIndex') + self.pageSize = attrs.get('pageSize') + self.totalCount = attrs.get('totalCount') + self.resultSize = attrs.get('resultSize') + self.sortType = attrs.get('sortType') + self.sortBy = attrs.get('sortBy') + self.queryTimeMS = attrs.get('queryTimeMS') + self.list = attrs.get('list') + + def type_coerce_attrs(self): + super(PList, self).type_coerce_attrs() + + def type_coerce_list(self, elemType): + self.list = type_coerce_list(self.list, elemType) diff --git a/intg/src/main/python/apache_ranger/model/ranger_principal.py b/intg/src/main/python/apache_ranger/model/ranger_principal.py new file mode 100644 index 000000000..e592cf130 --- /dev/null +++ b/intg/src/main/python/apache_ranger/model/ranger_principal.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from apache_ranger.model.ranger_base import RangerBase +from apache_ranger.utils import * +from strenum import StrEnum + +class PrincipalType(StrEnum): + USER = 'USER' + GROUP = 'GROUP' + ROLE = 'ROLE' + + @classmethod + def value_of(cls, val): + if isinstance(val, PrincipalType): + return val + else: + for key, member in cls.__members__.items(): + if val == member.name or val == member.value: + return member + else: + raise ValueError(f"'{cls.__name__}' enum not found for '{val}'") + + +class RangerPrincipal(RangerBase): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerBase.__init__(self, attrs) + + self.type = attrs.get('type') + self.name = attrs.get('name') + + def __hash__(self): + return hash((self.type, self.name)) + + def __eq__(self, other): + return (self.type, self.name) == (other.type, other.name) + + def type_coerce_attrs(self): + super(RangerPrincipal, self).type_coerce_attrs() + + self.type = PrincipalType.value_of(self.type) diff --git a/intg/src/main/python/apache_ranger/model/ranger_security_zone.py b/intg/src/main/python/apache_ranger/model/ranger_security_zone.py index 6faa15744..044848d34 100644 --- a/intg/src/main/python/apache_ranger/model/ranger_security_zone.py +++ b/intg/src/main/python/apache_ranger/model/ranger_security_zone.py @@ -16,8 +16,23 @@ # See the License for the specific language governing permissions and # limitations under the License. -from apache_ranger.model.ranger_base import RangerBase, RangerBaseModelObject -from apache_ranger.utils import * +from apache_ranger.model.ranger_base import RangerBase, RangerBaseModelObject +from apache_ranger.utils import * +from apache_ranger.model.ranger_principal import RangerPrincipal + + +class RangerSecurityZoneResourceBase(RangerBase): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerBase.__init__(self, attrs) + + self.id = attrs.get('id') + self.createdBy = attrs.get('createdBy') + self.updatedBy = attrs.get('updatedBy') + self.createTime = attrs.get('createTime') + self.updateTime = attrs.get('updateTime') class RangerSecurityZoneService(RangerBase): @@ -27,12 +42,14 @@ class RangerSecurityZoneService(RangerBase): RangerBase.__init__(self, attrs) - self.resources = attrs.get('resources') + self.resources = attrs.get('resources') + self.resourcesBaseInfo = attrs.get('resourcesBaseInfo') def type_coerce_attrs(self): super(RangerSecurityZoneService, self).type_coerce_attrs() - self.resources = type_coerce_list_dict(self.resources, list) + self.resources = type_coerce_list(self.resources, dict) + self.resourcesBaseInfo = type_coerce_list(self.resourcesBaseInfo, RangerSecurityZoneResourceBase) class RangerSecurityZone(RangerBaseModelObject): @@ -58,6 +75,86 @@ class RangerSecurityZone(RangerBaseModelObject): self.services = type_coerce_dict(self.services, RangerSecurityZoneService) + +class RangerSecurityZoneResource(RangerSecurityZoneResourceBase): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerSecurityZoneResourceBase.__init__(self, attrs) + + self.resource = attrs.get('resource') + + def type_coerce_attrs(self): + super(RangerSecurityZoneResource, self).type_coerce_attrs() + + self.resource = type_coerce_dict(self.resource, list) + + +class RangerSecurityZoneServiceV2(RangerBase): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerBase.__init__(self, attrs) + + self.resources = attrs.get('resources') + + def type_coerce_attrs(self): + super(RangerSecurityZoneServiceV2, self).type_coerce_attrs() + + self.resources = type_coerce_list(self.resources, RangerSecurityZoneResource) + + +class RangerSecurityZoneV2(RangerBaseModelObject): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerBaseModelObject.__init__(self, attrs) + + self.name = attrs.get('name') + self.description = attrs.get('description') + self.services = attrs.get('services') + self.tagServices = attrs.get('tagServices') + self.admins = attrs.get('admins') + self.auditors = attrs.get('auditors') + + def type_coerce_attrs(self): + super(RangerSecurityZoneV2, self).type_coerce_attrs() + + self.services = type_coerce_dict(self.services, RangerSecurityZoneServiceV2) + self.admins = type_coerce_list(self.admins, RangerPrincipal) + self.auditors = type_coerce_list(self.auditors, RangerPrincipal) + +class RangerSecurityZoneChangeRequest(RangerBase): + def __init__(self, attrs=None): + if attrs is None: + attrs = {} + + RangerBaseModelObject.__init__(self, attrs) + + self.name = attrs.get('name') + self.description = attrs.get('description') + self.resourcesToUpdate = attrs.get('resourcesToUpdate') + self.resourcesToRemove = attrs.get('resourcesToRemove') + self.tagServicesToAdd = attrs.get('tagServicesToAdd') + self.tagServicesToRemove = attrs.get('tagServicesToRemove') + self.adminsToAdd = attrs.get('adminsToAdd') + self.adminsToRemove = attrs.get('adminsToRemove') + self.auditorsToAdd = attrs.get('auditorsToAdd') + self.auditorsToRemove = attrs.get('auditorsToRemove') + + def type_coerce_attrs(self): + super(RangerSecurityZoneChangeRequest, self).type_coerce_attrs() + + self.resourcesToUpdate = type_coerce_dict(self.resourcesToUpdate, RangerSecurityZoneServiceV2) + self.resourcesToRemove = type_coerce_dict(self.resourcesToRemove, RangerSecurityZoneServiceV2) + self.adminsToAdd = type_coerce_list(self.adminsToAdd, RangerPrincipal) + self.adminsToRemove = type_coerce_list(self.adminsToRemove, RangerPrincipal) + self.auditorsToAdd = type_coerce_list(self.auditorsToAdd, RangerPrincipal) + self.auditorsToRemove = type_coerce_list(self.auditorsToRemove, RangerPrincipal) + class RangerSecurityZoneHeaderInfo(RangerBaseModelObject): def __init__(self, attrs=None): if attrs is None: diff --git a/intg/src/main/python/setup.py b/intg/src/main/python/setup.py index 0a4b1c66e..c7dfaa0b4 100644 --- a/intg/src/main/python/setup.py +++ b/intg/src/main/python/setup.py @@ -19,7 +19,7 @@ from setuptools import setup, find_packages # External dependencies -requirements = ['requests>=2.24'] +requirements = ['requests>=2.24','strenum>=0.4.15'] long_description = '' with open("README.md", "r") as fh: diff --git a/ranger-examples/sample-client/src/main/python/security_zone_v2.py b/ranger-examples/sample-client/src/main/python/security_zone_v2.py new file mode 100644 index 000000000..b7e15969a --- /dev/null +++ b/ranger-examples/sample-client/src/main/python/security_zone_v2.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python + +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +from apache_ranger.client.ranger_client import * +from apache_ranger.model.ranger_security_zone import * +from apache_ranger.model.ranger_principal import * +from datetime import datetime + + + +## create a client to connect to Apache Ranger admin server +ranger_url = 'http://localhost:6080' +ranger_auth = ('admin', 'rangerR0cks!') + +# For Kerberos authentication +# +# from requests_kerberos import HTTPKerberosAuth +# +# ranger_auth = HTTPKerberosAuth() + + +print(f'\nUsing Ranger at {ranger_url}'); + +ranger = RangerClient(ranger_url, ranger_auth) + +# to disable SSL certificate validation (not recommended for production use!) +# +# ranger.session.verify = False + + +print('\nListing security-zones..') +zones = ranger.find_security_zones_v2() + +print(f' {len(zones.list)} security-zones found') +for zone in zones.list: + print(f' id: {zone.id}, name: {zone.name}') + +now = datetime.now() + +zone_name = 'zone1-' + now.strftime('%Y%m%d-%H%M%S-%f') + +zone = RangerSecurityZoneV2() +zone.name = zone_name +zone.description = 'zone created by example script' +zone.admins = [ RangerPrincipal({ 'type': PrincipalType.USER, 'name': 'admin' }) ] +zone.auditors = [ RangerPrincipal({ 'type': PrincipalType.USER, 'name': 'admin' }) ] + +print(f'\nCreating security-zone: name={zone_name}') + +created_zone = ranger.create_security_zone_v2(zone) + +print(f' created zone: {created_zone}') + +zone_id = created_zone.id + + +print(f'\nRetrieving zone by ID: id={zone_id}') + +retrieved_zone = ranger.get_security_zone_v2_by_id(zone_id) + +print(f' retrieved zone: id: {retrieved_zone.id}, name: {retrieved_zone.name}') + + +print(f'\nRetrieving zone by name: name={zone_name}') + +retrieved_zone = ranger.get_security_zone_v2(zone_name) + +print(f' retrieved zone: id: {retrieved_zone.id}, name: {retrieved_zone.name}') + + +print('\nListing security-zones..') +zones = ranger.find_security_zones_v2() + +print(f' {len(zones.list)} security-zones found') +for zone in zones.list: + print(f' id: {zone.id}, name: {zone.name}') + + +change_req = RangerSecurityZoneChangeRequest() +change_req.resourcesToUpdate = { 'dev_hive': RangerSecurityZoneServiceV2({ 'resources': [ { 'resource': { 'database': [ 'db1' ] } } ] }), 'dev_hdfs': RangerSecurityZoneServiceV2({ 'resources': [ { 'resource': { 'path': [ '/path1' ] } } ] }) } +change_req.tagServicesToAdd = [ 'dev_tag' ] +change_req.adminsToAdd = [ RangerPrincipal({ 'type': 'GROUP', 'name': 'public' }) ] +change_req.auditorsToAdd = [ RangerPrincipal({ 'type': 'GROUP', 'name': 'public' }) ] + +print(f'\nUpdating zone: add resources, add tag-services, add admins, add auditors..') +print(f' change-request: {change_req}') + +ranger.partial_update_security_zone_v2(created_zone.id, change_req) +retrieved_zone = ranger.get_security_zone_v2(zone_name) + +print(f' updated_zone: {retrieved_zone}') + + +change_req = RangerSecurityZoneChangeRequest() +change_req.resourcesToRemove = { 'dev_hive': RangerSecurityZoneServiceV2({ 'resources': [ { 'id': 0 } ] }) } # remove resource by ID +change_req.tagServicesToRemove = [ 'dev_tag' ] +change_req.adminsToRemove = [ RangerPrincipal({ 'type': 'USER', 'name': 'admin' }) ] +change_req.auditorsToRemove = [ RangerPrincipal({ 'type': 'USER', 'name': 'admin' }) ] + +print(f'\nUpdating zone: remove resource-by-id, remove tag-services, remove admins, remove auditors..') +print(f' change-request: {change_req}') + +ranger.partial_update_security_zone_v2(created_zone.id, change_req) +retrieved_zone = ranger.get_security_zone_v2(zone_name) + +print(f' updated_zone: {retrieved_zone}') + + +change_req = RangerSecurityZoneChangeRequest() +change_req.resourcesToUpdate = { 'dev_hdfs': RangerSecurityZoneServiceV2({ 'resources': [ { 'resource': { 'path': [ zone.name ] } } ] }) } +change_req.resourcesToRemove = { 'dev_hdfs': RangerSecurityZoneServiceV2({ 'resources': [ { 'resource': { 'path': [ '/path1' ] } } ] }) } # remove resource by value + +print(f'\nUpdating zone: remove resource-by-value, add resource..') +print(f' change-request: {change_req}') + +ranger.partial_update_security_zone_v2(created_zone.id, change_req) +retrieved_zone = ranger.get_security_zone_v2(zone_name) + +print(f' updated_zone: {retrieved_zone}') + +print(f'\nDeleting zone id={zone_id}') + +ranger.delete_security_zone_by_id(zone_id) + +print(f' deleted zone: id: {zone_id}, name: {zone.name}') + + +print('\nListing security-zones..') +zones = ranger.find_security_zones_v2() + +print(f' {len(zones.list)} security-zones found') +for zone in zones.list: + print(f' id: {zone.id}, name: {zone.name}') diff --git a/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java b/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java index cd906ed22..92ade823b 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/PublicAPIsv2.java @@ -34,6 +34,10 @@ import org.apache.ranger.plugin.model.RangerServiceDef; import org.apache.ranger.plugin.model.RangerServiceHeaderInfo; import org.apache.ranger.plugin.model.RangerServiceResource; import org.apache.ranger.plugin.model.RangerServiceTags; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneChangeRequest; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResource; +import org.apache.ranger.plugin.store.PList; import org.apache.ranger.plugin.util.GrantRevokeRoleRequest; import org.apache.ranger.plugin.util.RangerPurgeResult; import org.apache.ranger.plugin.util.ServiceTags; @@ -216,6 +220,65 @@ public class PublicAPIsv2 { return securityZoneRest.getZoneNamesForResource(serviceName, request); } + @POST + @Path("/api/zones-v2") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + public RangerSecurityZoneV2 createSecurityZone(RangerSecurityZoneV2 securityZone) { + return securityZoneRest.createSecurityZone(securityZone); + } + + @PUT + @Path("/api/zones-v2/{id}") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + public RangerSecurityZoneV2 updateSecurityZone(@PathParam("id") Long zoneId, RangerSecurityZoneV2 securityZone) { + return securityZoneRest.updateSecurityZone(zoneId, securityZone); + } + + @PUT + @Path("/api/zones-v2/{id}/partial") + @Consumes({ "application/json" }) + @Produces({ "application/json" }) + public Boolean updateSecurityZone(@PathParam("id") Long zoneId, RangerSecurityZoneChangeRequest changeRequest) { + return securityZoneRest.updateSecurityZone(zoneId, changeRequest); + } + + @GET + @Path("/api/zones-v2/name/{name}") + @Produces({ "application/json" }) + public RangerSecurityZoneV2 getSecurityZoneV2(@PathParam("name") String zoneName) { + return securityZoneRest.getSecurityZoneV2(zoneName); + } + + @GET + @Path("/api/zones-v2/{id}") + @Produces({ "application/json" }) + public RangerSecurityZoneV2 getSecurityZoneV2(@PathParam("id") Long zoneId) { + return securityZoneRest.getSecurityZoneV2(zoneId); + } + + @GET + @Path("/api/zones-v2/{id}/resources/{serviceName}") + @Produces({ "application/json" }) + public PList<RangerSecurityZoneResource> getResources(@PathParam("id") Long zoneId, @PathParam("serviceName") String serviceName, @Context HttpServletRequest request) { + return securityZoneRest.getResources(zoneId, serviceName, request); + } + + @GET + @Path("/api/zones-v2/name/{name}/resources/{serviceName}") + @Produces({ "application/json" }) + public PList<RangerSecurityZoneResource> getResources(@PathParam("name") String zoneName, @PathParam("serviceName") String serviceName, @Context HttpServletRequest request) { + return securityZoneRest.getResources(zoneName, serviceName, request); + } + + @GET + @Path("/api/zones-v2") + @Produces({ "application/json"}) + public PList<RangerSecurityZoneV2> getAllZonesV2(@Context HttpServletRequest request){ + return securityZoneRest.getAllZonesV2(request); + } + /* * ServiceDef Manipulation APIs */ diff --git a/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java b/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java index 55d6aaac5..7c1e01053 100644 --- a/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java +++ b/security-admin/src/main/java/org/apache/ranger/rest/SecurityZoneREST.java @@ -58,11 +58,17 @@ import org.apache.ranger.db.RangerDaoManager; import org.apache.ranger.entity.XXService; import org.apache.ranger.entity.XXServiceDef; import org.apache.ranger.plugin.model.RangerSecurityZone; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2; import org.apache.ranger.plugin.model.validation.RangerSecurityZoneValidator; import org.apache.ranger.plugin.model.validation.RangerValidator; +import org.apache.ranger.plugin.store.PList; +import org.apache.ranger.plugin.util.RangerSecurityZoneHelper; +import org.apache.ranger.plugin.util.RangerSecurityZoneHelper.RangerSecurityZoneServiceHelper; import org.apache.ranger.plugin.util.SearchFilter; import org.apache.ranger.service.RangerSecurityZoneServiceService; import org.apache.ranger.plugin.model.RangerSecurityZone.RangerSecurityZoneService; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneChangeRequest; +import org.apache.ranger.plugin.model.RangerSecurityZoneV2.RangerSecurityZoneResource; import org.apache.ranger.view.RangerSecurityZoneList; import org.apache.ranger.plugin.store.EmbeddedServiceDefsUtil; import org.slf4j.Logger; @@ -114,7 +120,6 @@ public class SecurityZoneREST { @Autowired ServiceMgr serviceMgr; - @POST @Path("/zones") @Consumes({ "application/json" }) @@ -126,6 +131,10 @@ public class SecurityZoneREST { RangerSecurityZone ret; try { + RangerSecurityZoneHelper zoneHelper = new RangerSecurityZoneHelper(securityZone, bizUtil.getCurrentUserLoginId()); // this populates resourcesBaseInfo + + securityZone = zoneHelper.getZone(); + ensureAdminAccess(securityZone); removeEmptyEntries(securityZone); RangerSecurityZoneValidator validator = validatorFactory.getSecurityZoneValidator(svcStore, securityZoneStore); @@ -162,6 +171,10 @@ public class SecurityZoneREST { throw restErrorUtil.createRESTException("Cannot update unzoned zone"); } + RangerSecurityZoneHelper zoneHelper = new RangerSecurityZoneHelper(securityZone, bizUtil.getCurrentUserLoginId()); // this populates resourcesBaseInfo + + securityZone = zoneHelper.getZone(); + ensureUserAllowOperationOnServiceForZone(securityZone); removeEmptyEntries(securityZone); if (securityZone.getId() != null && !zoneId.equals(securityZone.getId())) { @@ -380,7 +393,113 @@ public class SecurityZoneREST { return ret; } - private void ensureAdminAccess(){ + public RangerSecurityZoneV2 createSecurityZone(RangerSecurityZoneV2 securityZone) { + LOG.debug("==> createSecurityZone({})", securityZone); + + RangerSecurityZone retV1 = createSecurityZone(securityZone.toV1()); + RangerSecurityZoneV2 ret = retV1 != null ? new RangerSecurityZoneV2(retV1) : null; + + LOG.debug("<== createSecurityZone({}): ret={}", securityZone, ret); + + return ret; + } + + public RangerSecurityZoneV2 updateSecurityZone(Long zoneId, RangerSecurityZoneV2 securityZone) { + LOG.debug("==> updateSecurityZone({}, {})", zoneId, securityZone); + + RangerSecurityZone retV1 = updateSecurityZone(zoneId, securityZone.toV1()); + RangerSecurityZoneV2 ret = retV1 != null ? new RangerSecurityZoneV2(retV1) : null; + + LOG.debug("<== updateSecurityZone({}, {}): ret={}", zoneId, securityZone, ret); + + return ret; + } + + public Boolean updateSecurityZone(Long zoneId, RangerSecurityZoneChangeRequest changeData) { + LOG.debug("==> updateSecurityZone({}, {})", zoneId, changeData); + + RangerSecurityZone zone = getSecurityZone(zoneId); + RangerSecurityZoneHelper zoneHelper = new RangerSecurityZoneHelper(zone, bizUtil.getCurrentUserLoginId()); + RangerSecurityZone updatedZone = zoneHelper.updateZone(changeData); + + RangerSecurityZone retV1 = updateSecurityZone(zoneId, updatedZone); + Boolean ret = retV1 != null; + + LOG.debug("<== updateSecurityZone({}, {}): ret={}", zoneId, changeData, ret); + + return ret; + } + + public RangerSecurityZoneV2 getSecurityZoneV2(String zoneName) { + LOG.debug("==> getSecurityZoneV2({})", zoneName); + + RangerSecurityZone retV1 = getSecurityZone(zoneName); + RangerSecurityZoneV2 ret = retV1 != null ? new RangerSecurityZoneV2(retV1) : null; + + LOG.debug("<== getSecurityZoneV2({}): ret={}", zoneName, ret); + + return ret; + } + + public RangerSecurityZoneV2 getSecurityZoneV2(Long zoneId) { + LOG.debug("==> getSecurityZoneV2({})", zoneId); + + RangerSecurityZone retV1 = getSecurityZone(zoneId); + RangerSecurityZoneV2 ret = retV1 != null ? new RangerSecurityZoneV2(retV1) : null; + + LOG.debug("<== getSecurityZoneV2({}): ret={}", zoneId, ret); + + return ret; + } + + public PList<RangerSecurityZoneResource> getResources(Long zoneId, String serviceName, HttpServletRequest request) { + LOG.debug("==> getResources(zoneId={}, serviceName={})", zoneId, serviceName); + + PList<RangerSecurityZoneResource> ret = getResources(getSecurityZone(zoneId), serviceName, request); + + LOG.debug("<== getResources(zoneId={}, serviceName={}): ret={}", zoneId, serviceName, ret); + + return ret; + } + + public PList<RangerSecurityZoneResource> getResources(String zoneName, String serviceName, HttpServletRequest request) { + LOG.debug("==> getResources(zoneName={}, serviceName={})", zoneName, serviceName); + + PList<RangerSecurityZoneResource> ret = getResources(getSecurityZone(zoneName), serviceName, request); + + LOG.debug("<== getResources(zoneName={}, serviceName={}): ret={}", zoneName, serviceName, ret); + + return ret; + } + + public PList<RangerSecurityZoneV2> getAllZonesV2(HttpServletRequest request) { + LOG.debug("==> getAllZonesV2()"); + + PList<RangerSecurityZoneV2> ret = new PList<>(); + RangerSecurityZoneList retList = getAllZones(request); + + if (retList != null) { + ret.setList(new ArrayList<>(retList.getListSize())); + ret.setPageSize(retList.getPageSize()); + ret.setStartIndex(retList.getStartIndex()); + ret.setResultSize(retList.getResultSize()); + ret.setTotalCount(retList.getTotalCount()); + ret.setSortBy(retList.getSortBy()); + ret.setSortType(retList.getSortType()); + + if (retList.getSecurityZones() != null) { + for (RangerSecurityZone zone : retList.getSecurityZones()) { + ret.getList().add(new RangerSecurityZoneV2(zone)); + } + } + } + + LOG.debug("<== getAllZonesV2(): ret={}", ret); + + return ret; + } + + private void ensureAdminAccess(){ if(!bizUtil.isAdmin()){ String userName = bizUtil.getCurrentUserLoginId(); throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, "Ranger Security Zone is not accessible for user '" + userName + "'.", true); @@ -529,7 +648,7 @@ public class SecurityZoneREST { } } - + private void throwRestError(String message){ throw restErrorUtil.createRESTException(HttpServletResponse.SC_FORBIDDEN, message, true); } @@ -595,4 +714,19 @@ public class SecurityZoneREST { } } } + + private PList<RangerSecurityZoneResource> getResources(RangerSecurityZone zone, String serviceName, @Context HttpServletRequest request) { + RangerSecurityZoneHelper zoneHelper = new RangerSecurityZoneHelper(zone, bizUtil.getCurrentUserLoginId()); + RangerSecurityZoneServiceHelper zoneServiceHelper = zoneHelper.getZoneService(serviceName); + PList<RangerSecurityZoneResource> ret = null; + + if (zoneServiceHelper != null) { + SearchFilter filter = searchUtil.getSearchFilter(request, Collections.emptyList()); + List<RangerSecurityZoneResource> result = zoneServiceHelper.getResources(filter.getStartIndex(), filter.getMaxRows()); + + ret = new PList<>(result, filter.getStartIndex(), filter.getMaxRows(), zoneServiceHelper.getResourceCount(), result.size(), null, null); + } + + return ret; + } }