You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by ma...@apache.org on 2022/04/20 23:01:27 UTC

[ranger] branch master updated: RANGER-3475: added public REST API endpoint to import tags

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


The following commit(s) were added to refs/heads/master by this push:
     new 6e9a8ccfe RANGER-3475: added public REST API endpoint to import tags
6e9a8ccfe is described below

commit 6e9a8ccfec9b88abdca7d4dd2fa861c058da1356
Author: Madhan Neethiraj <ma...@apache.org>
AuthorDate: Tue Apr 12 12:57:29 2022 -0700

    RANGER-3475: added public REST API endpoint to import tags
---
 .../ranger/plugin/model/RangerServiceTags.java     | 215 +++++++++++++++++++++
 .../main/java/org/apache/ranger/RangerClient.java  |  11 ++
 .../python/apache_ranger/client/ranger_client.py   |  12 ++
 .../apache_ranger/model/ranger_service_resource.py |  51 +++++
 .../apache_ranger/model/ranger_service_tags.py     |  52 +++++
 .../main/python/apache_ranger/model/ranger_tag.py  |  40 ++++
 .../python/apache_ranger/model/ranger_tagdef.py    |  47 +++++
 .../ranger/examples/sampleclient/SampleClient.java |  55 ++++++
 .../sample-client/src/main/python/sample_client.py |  59 +++++-
 .../java/org/apache/ranger/rest/PublicAPIsv2.java  |  48 +++++
 10 files changed, 584 insertions(+), 6 deletions(-)

diff --git a/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceTags.java b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceTags.java
new file mode 100644
index 000000000..8f59aa5ee
--- /dev/null
+++ b/agents-common/src/main/java/org/apache/ranger/plugin/model/RangerServiceTags.java
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.ranger.plugin.model;
+
+
+import org.apache.ranger.plugin.util.ServiceTags;
+import org.codehaus.jackson.annotate.JsonAutoDetect;
+import org.codehaus.jackson.annotate.JsonAutoDetect.Visibility;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+import org.codehaus.jackson.map.annotate.JsonSerialize;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@JsonAutoDetect(fieldVisibility=Visibility.ANY)
+@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown=true)
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+public class RangerServiceTags implements java.io.Serializable {
+    private static final long serialVersionUID = 1L;
+
+    public static final String OP_SET     = "set";     // set tags for the given serviceResources
+    public static final String OP_DELETE  = "delete";  // delete tags associated with the given serviceResources
+    public static final String OP_REPLACE = "replace"; // replace all resources and tags in the given serviceName with the given serviceResources and tags
+
+    private String                      op = OP_SET;
+    private String                      serviceName;
+    private Map<Long, RangerTagDef>     tagDefinitions;
+    private Map<Long, RangerTag>        tags;
+    private List<RangerServiceResource> serviceResources;
+    private Map<Long, List<Long>>       resourceToTagIds;
+    private Long                        tagVersion;     // read-only field
+    private Date                        tagUpdateTime;  // read-only field
+
+    public RangerServiceTags() {
+        this(OP_SET, null, null, null, null, null, null, null);
+    }
+
+    public RangerServiceTags(String op, String serviceName, Map<Long, RangerTagDef> tagDefinitions,
+                             Map<Long, RangerTag> tags, List<RangerServiceResource> serviceResources,
+                             Map<Long, List<Long>> resourceToTagIds, Long tagVersion, Date tagUpdateTime) {
+        setOp(op);
+        setServiceName(serviceName);
+        setTagDefinitions(tagDefinitions);
+        setTags(tags);
+        setServiceResources(serviceResources);
+        setResourceToTagIds(resourceToTagIds);
+        setTagVersion(tagVersion);
+        setTagUpdateTime(tagUpdateTime);
+    }
+
+    /**
+     * @return the op
+     */
+    public String getOp() {
+        return op;
+    }
+
+    /**
+     * @return the serviceName
+     */
+    public String getServiceName() {
+        return serviceName;
+    }
+
+    /**
+     * @param op the op to set
+     */
+    public void setOp(String op) {
+        this.op = op;
+    }
+
+    /**
+     * @param serviceName the serviceName to set
+     */
+    public void setServiceName(String serviceName) {
+        this.serviceName = serviceName;
+    }
+
+    public Map<Long, RangerTagDef> getTagDefinitions() {
+        return tagDefinitions;
+    }
+
+    public void setTagDefinitions(Map<Long, RangerTagDef> tagDefinitions) {
+        this.tagDefinitions = tagDefinitions == null ? new HashMap<>() : tagDefinitions;
+    }
+
+    public Map<Long, RangerTag> getTags() {
+        return tags;
+    }
+
+    public void setTags(Map<Long, RangerTag> tags) {
+        this.tags = tags == null ? new HashMap<>() : tags;
+    }
+
+    public List<RangerServiceResource> getServiceResources() {
+        return serviceResources;
+    }
+
+    public void setServiceResources(List<RangerServiceResource> serviceResources) {
+        this.serviceResources = serviceResources == null ? new ArrayList<>() : serviceResources;
+    }
+
+    public Map<Long, List<Long>> getResourceToTagIds() {
+        return resourceToTagIds;
+    }
+
+    public void setResourceToTagIds(Map<Long, List<Long>> resourceToTagIds) {
+        this.resourceToTagIds = resourceToTagIds == null ? new HashMap<>() : resourceToTagIds;
+    }
+
+    public Long getTagVersion() {
+        return tagVersion;
+    }
+
+    public void setTagVersion(Long tagVersion) {
+        this.tagVersion = tagVersion;
+    }
+
+    public Date getTagUpdateTime() {
+        return tagUpdateTime;
+    }
+
+    public void setTagUpdateTime(Date tagUpdateTime) {
+        this.tagUpdateTime = tagUpdateTime;
+    }
+
+
+    @Override
+    public String toString( ) {
+        StringBuilder sb = new StringBuilder();
+
+        toString(sb);
+
+        return sb.toString();
+    }
+
+    public StringBuilder toString(StringBuilder sb) {
+        sb.append("RangerServiceTags={")
+          .append("op=").append(op).append(", ")
+          .append("serviceName=").append(serviceName).append(", ")
+          .append("}");
+
+        return sb;
+    }
+
+    public static ServiceTags toServiceTags(RangerServiceTags tags) {
+        ServiceTags ret = null;
+
+        if (tags != null) {
+            ret = new ServiceTags(toServiceTagsOp(tags.getOp()), tags.getServiceName(),
+                                  tags.tagVersion, tags.getTagUpdateTime(), tags.getTagDefinitions(), tags.getTags(),
+                                  tags.getServiceResources(), tags.getResourceToTagIds(), false,
+                                  ServiceTags.TagsChangeExtent.ALL);
+        }
+
+        return ret;
+    }
+
+    public static RangerServiceTags toRangerServiceTags(ServiceTags tags) {
+        RangerServiceTags ret = null;
+
+        if (tags != null) {
+            ret = new RangerServiceTags(toRangerServiceTagsOp(tags.getOp()), tags.getServiceName(),
+                                        tags.getTagDefinitions(), tags.getTags(), tags.getServiceResources(),
+                                        tags.getResourceToTagIds(), tags.getTagVersion(), tags.getTagUpdateTime());
+        }
+
+        return ret;
+    }
+
+    private static String toServiceTagsOp(String rangerServiceTagsOp) {
+        String ret = rangerServiceTagsOp;
+
+        if (RangerServiceTags.OP_SET.equals(rangerServiceTagsOp)) {
+            ret = ServiceTags.OP_ADD_OR_UPDATE;
+        }
+
+        return ret;
+    }
+
+    private static String toRangerServiceTagsOp(String serviceTagsOp) {
+        String ret = serviceTagsOp;
+
+        if (ServiceTags.OP_ADD_OR_UPDATE.equals(serviceTagsOp)) {
+            ret = RangerServiceTags.OP_SET;
+        }
+
+        return ret;
+    }
+}
diff --git a/intg/src/main/java/org/apache/ranger/RangerClient.java b/intg/src/main/java/org/apache/ranger/RangerClient.java
index add084f1a..f92116d36 100644
--- a/intg/src/main/java/org/apache/ranger/RangerClient.java
+++ b/intg/src/main/java/org/apache/ranger/RangerClient.java
@@ -80,6 +80,7 @@ public class RangerClient {
     private static final String URI_ZONE_BY_ID            = URI_ZONE + "/%d";
     private static final String URI_ZONE_BY_NAME          = URI_ZONE + "/name/%s";
 
+    private static final String URI_SERVICE_TAGS          = URI_SERVICE + "/%s/tags";
     private static final String URI_PLUGIN_INFO           = URI_BASE + "/plugins/info";
     private static final String URI_POLICY_DELTAS         = URI_BASE + "/server/policydeltas";
 
@@ -135,6 +136,8 @@ public class RangerClient {
     public static final API REVOKE_ROLE         = new API(URI_REVOKE_ROLE, HttpMethod.PUT, Response.Status.OK);
     public static final API FIND_ROLES          = new API(URI_ROLE, HttpMethod.GET, Response.Status.OK);
 
+    public static final API IMPORT_SERVICE_TAGS  = new API(URI_SERVICE_TAGS, HttpMethod.PUT, Response.Status.NO_CONTENT);
+    public static final API GET_SERVICE_TAGS     = new API(URI_SERVICE_TAGS, HttpMethod.GET, Response.Status.OK);
     public static final API GET_PLUGIN_INFO      = new API(URI_PLUGIN_INFO, HttpMethod.GET, Response.Status.OK);
     public static final API DELETE_POLICY_DELTAS = new API(URI_POLICY_DELTAS, HttpMethod.DELETE, Response.Status.NO_CONTENT);
 
@@ -396,6 +399,14 @@ public class RangerClient {
     /*
      * Admin APIs
      */
+    public void importServiceTags(String serviceName, RangerServiceTags svcTags) throws RangerServiceException {
+        callAPI(IMPORT_SERVICE_TAGS.applyUrlFormat(serviceName), null, svcTags, (GenericType<Void>) null);
+    }
+
+    public RangerServiceTags getServiceTags(String serviceName) throws RangerServiceException {
+        return callAPI(GET_SERVICE_TAGS.applyUrlFormat(serviceName), null, null, RangerServiceTags.class);
+    }
+
     public List<RangerPluginInfo> getPluginsInfo() throws RangerServiceException {
         return callAPI(GET_PLUGIN_INFO, null, null, new GenericType<List<RangerPluginInfo>>(){});
     }
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 9f3a6e076..85f66a43e 100644
--- a/intg/src/main/python/apache_ranger/client/ranger_client.py
+++ b/intg/src/main/python/apache_ranger/client/ranger_client.py
@@ -27,6 +27,7 @@ from apache_ranger.model.ranger_role          import RangerRole
 from apache_ranger.model.ranger_security_zone import RangerSecurityZone
 from apache_ranger.model.ranger_service       import RangerService
 from apache_ranger.model.ranger_service_def   import RangerServiceDef
+from apache_ranger.model.ranger_service_tags  import RangerServiceTags
 from apache_ranger.utils                      import *
 from requests                                 import Session
 from requests                                 import Response
@@ -262,6 +263,14 @@ class RangerClient:
 
 
     # Admin APIs
+    def import_service_tags(self, serviceName, svcTags):
+        self.client_http.call_api(RangerClient.IMPORT_SERVICE_TAGS.format_path({ 'serviceName': serviceName }), request_data=svcTags)
+
+    def get_service_tags(self, serviceName):
+        resp = self.client_http.call_api(RangerClient.GET_SERVICE_TAGS.format_path({ 'serviceName': serviceName }))
+
+        return type_coerce(resp, RangerServiceTags)
+
     def delete_policy_deltas(self, days, reloadServicePoliciesCache):
         self.client_http.call_api(RangerClient.DELETE_POLICY_DELTAS, { 'days': days, 'reloadServicePoliciesCache': reloadServicePoliciesCache})
 
@@ -298,6 +307,7 @@ class RangerClient:
     URI_ZONE_BY_ID          = URI_ZONE + "/{id}"
     URI_ZONE_BY_NAME        = URI_ZONE + "/name/{name}"
 
+    URI_SERVICE_TAGS        = URI_SERVICE + "/{serviceName}/tags"
     URI_PLUGIN_INFO         = URI_BASE + "/plugins/info"
     URI_POLICY_DELTAS       = URI_BASE + "/server/policydeltas"
 
@@ -352,6 +362,8 @@ class RangerClient:
     REVOKE_ROLE               = API(URI_REVOKE_ROLE, HttpMethod.PUT, HTTPStatus.OK)
     FIND_ROLES                = API(URI_ROLE, HttpMethod.GET, HTTPStatus.OK)
 
+    IMPORT_SERVICE_TAGS       = API(URI_SERVICE_TAGS, HttpMethod.PUT, HTTPStatus.NO_CONTENT)
+    GET_SERVICE_TAGS          = API(URI_SERVICE_TAGS, HttpMethod.GET, HTTPStatus.OK)
     GET_PLUGIN_INFO           = API(URI_PLUGIN_INFO, HttpMethod.GET, HTTPStatus.OK)
     DELETE_POLICY_DELTAS      = API(URI_POLICY_DELTAS, HttpMethod.DELETE, HTTPStatus.NO_CONTENT)
 
diff --git a/intg/src/main/python/apache_ranger/model/ranger_service_resource.py b/intg/src/main/python/apache_ranger/model/ranger_service_resource.py
new file mode 100644
index 000000000..2a6b8ebf4
--- /dev/null
+++ b/intg/src/main/python/apache_ranger/model/ranger_service_resource.py
@@ -0,0 +1,51 @@
+#!/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 *
+from apache_ranger.model.ranger_policy import RangerPolicyResource
+from apache_ranger.utils               import *
+
+
+class RangerServiceResource(RangerBaseModelObject):
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+
+        RangerBaseModelObject.__init__(self, attrs)
+
+        self.serviceName      = attrs.get('serviceName')
+        self.resourceElements = attrs.get('resourceElements')
+        self.ownerUser        = attrs.get('ownerUser')
+        self.additionalInfo   = attrs.get('additionalInfo')
+
+    def type_coerce_attrs(self):
+        super(RangerServiceResource, self).type_coerce_attrs()
+
+        self.resourceElements = type_coerce_dict(self.resourceElements, RangerPolicyResource)
+
+
+class RangerTagAttributeDef(RangerBase):
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.name = attrs.get('name')
+        self.type = attrs.get('type')
diff --git a/intg/src/main/python/apache_ranger/model/ranger_service_tags.py b/intg/src/main/python/apache_ranger/model/ranger_service_tags.py
new file mode 100644
index 000000000..9773a8d14
--- /dev/null
+++ b/intg/src/main/python/apache_ranger/model/ranger_service_tags.py
@@ -0,0 +1,52 @@
+#!/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 *
+from apache_ranger.model.ranger_service_resource import RangerServiceResource
+from apache_ranger.model.ranger_tag              import RangerTag
+from apache_ranger.model.ranger_tagdef           import RangerTagDef
+from apache_ranger.utils                         import *
+
+
+class RangerServiceTags(RangerBase):
+    OP_SET     = 'set'     # set tags for the given serviceResources
+    OP_DELETE  = 'delete'  # delete tags associated with the given serviceResources
+    OP_REPLACE = 'replace' # replace all resources and tags in the given serviceName with the given serviceResources and tags
+
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.op               = non_null(attrs.get('op'), 'set')
+        self.serviceName      = attrs.get('serviceName')
+        self.tagDefinitions   = attrs.get('tagDefinitions')
+        self.tags             = attrs.get('tags')
+        self.serviceResources = attrs.get('serviceResources')
+        self.resourceToTagIds = attrs.get('resourceToTagIds')
+        self.tagVersion       = attrs.get('tagVersion')
+        self.tagUpdateTime    = attrs.get('tagUpdateTime')
+
+    def type_coerce_attrs(self):
+        super(RangerServiceTags, self).type_coerce_attrs()
+
+        self.tagDefinitions   = type_coerce_dict(self.tagDefinitions, RangerTagDef)
+        self.tags             = type_coerce_dict(self.tags, RangerTag)
+        self.serviceResources = type_coerce_list(self.serviceResources, RangerServiceResource)
diff --git a/intg/src/main/python/apache_ranger/model/ranger_tag.py b/intg/src/main/python/apache_ranger/model/ranger_tag.py
new file mode 100644
index 000000000..0e329783a
--- /dev/null
+++ b/intg/src/main/python/apache_ranger/model/ranger_tag.py
@@ -0,0 +1,40 @@
+#!/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 *
+from apache_ranger.model.ranger_policy import RangerValiditySchedule
+from apache_ranger.utils               import *
+
+
+class RangerTag(RangerBaseModelObject):
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+
+        RangerBaseModelObject.__init__(self, attrs)
+
+        self.type            = attrs.get('type')
+        self.attributes      = attrs.get('attributes')
+        self.options         = attrs.get('options')
+        self.validityPeriods = attrs.get('validityPeriods')
+
+    def type_coerce_attrs(self):
+        super(RangerTag, self).type_coerce_attrs()
+
+        self.validityPeriods = type_coerce_list(self.validitySchedules, RangerValiditySchedule)
diff --git a/intg/src/main/python/apache_ranger/model/ranger_tagdef.py b/intg/src/main/python/apache_ranger/model/ranger_tagdef.py
new file mode 100644
index 000000000..335888436
--- /dev/null
+++ b/intg/src/main/python/apache_ranger/model/ranger_tagdef.py
@@ -0,0 +1,47 @@
+#!/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 *
+from apache_ranger.utils             import *
+
+
+class RangerTagDef(RangerBaseModelObject):
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+
+        RangerBaseModelObject.__init__(self, attrs)
+
+        self.name          = attrs.get('name')
+        self.source        = attrs.get('source')
+        self.attributeDefs = attrs.get('attributeDefs')
+
+    def type_coerce_attrs(self):
+        super(RangerTagDef, self).type_coerce_attrs()
+
+
+class RangerTagAttributeDef(RangerBase):
+    def __init__(self, attrs=None):
+        if attrs is None:
+            attrs = {}
+
+        RangerBase.__init__(self, attrs)
+
+        self.name = attrs.get('name')
+        self.type = attrs.get('type')
diff --git a/ranger-examples/sample-client/src/main/java/org/apache/ranger/examples/sampleclient/SampleClient.java b/ranger-examples/sample-client/src/main/java/org/apache/ranger/examples/sampleclient/SampleClient.java
index 7d5795065..d0202e47e 100644
--- a/ranger-examples/sample-client/src/main/java/org/apache/ranger/examples/sampleclient/SampleClient.java
+++ b/ranger-examples/sample-client/src/main/java/org/apache/ranger/examples/sampleclient/SampleClient.java
@@ -24,9 +24,15 @@ import org.apache.commons.cli.*;
 import org.apache.ranger.RangerClient;
 import org.apache.ranger.RangerServiceException;
 import org.apache.ranger.plugin.model.RangerPolicy;
+import org.apache.ranger.plugin.model.RangerPolicy.RangerPolicyResource;
 import org.apache.ranger.plugin.model.RangerRole;
 import org.apache.ranger.plugin.model.RangerService;
 import org.apache.ranger.plugin.model.RangerServiceDef;
+import org.apache.ranger.plugin.model.RangerServiceResource;
+import org.apache.ranger.plugin.model.RangerServiceTags;
+import org.apache.ranger.plugin.model.RangerTag;
+import org.apache.ranger.plugin.model.RangerTagDef;
+import org.apache.ranger.plugin.model.RangerTagDef.RangerTagAttributeDef;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -158,6 +164,55 @@ public class SampleClient {
         rangerClient.deletePolicy(serviceName, policyName);
         LOG.info("Policy {} successfully deleted", policyName);
 
+        /* import tags */
+        RangerTagDef tagDefTest1 = new RangerTagDef("test1");
+        RangerTagDef tagDefTest2 = new RangerTagDef("test2");
+
+        tagDefTest1.setAttributeDefs(Arrays.asList(new RangerTagAttributeDef("attr1", "string")));
+
+        RangerTag tagTest1Val1 = new RangerTag(tagDefTest1.getName(), Collections.singletonMap("attr1", "val1"));
+        RangerTag tagTest1Val2 = new RangerTag(tagDefTest1.getName(), Collections.singletonMap("attr1", "val2"));
+        RangerTag tagTest2     = new RangerTag(tagDefTest2.getName(), Collections.emptyMap());
+
+        RangerServiceResource db1 = new RangerServiceResource(serviceName, Collections.singletonMap("database", new RangerPolicyResource("db1")));
+        RangerServiceResource db2 = new RangerServiceResource(serviceName, Collections.singletonMap("database", new RangerPolicyResource("db2")));
+
+        db1.setId(1L);
+        db2.setId(2L);
+
+        RangerServiceTags serviceTags = new RangerServiceTags();
+
+        serviceTags.setOp(RangerServiceTags.OP_SET);
+        serviceTags.getTagDefinitions().put(0L, tagDefTest1);
+        serviceTags.getTagDefinitions().put(1L, tagDefTest2);
+        serviceTags.getTags().put(0L, tagTest1Val1);
+        serviceTags.getTags().put(1L, tagTest1Val2);
+        serviceTags.getTags().put(2L, tagTest2);
+        serviceTags.getServiceResources().add(db1);
+        serviceTags.getServiceResources().add(db2);
+        serviceTags.getResourceToTagIds().put(db1.getId(), Arrays.asList(0L, 2L));
+        serviceTags.getResourceToTagIds().put(db2.getId(), Arrays.asList(1L, 2L));
+
+        LOG.info("Importing tags: {}", serviceTags);
+
+        rangerClient.importServiceTags(serviceName, serviceTags);
+
+        RangerServiceTags serviceTags2 = rangerClient.getServiceTags(serviceName);
+
+        LOG.info("Imported tags: {}", serviceTags2);
+
+        serviceTags.setOp(RangerServiceTags.OP_DELETE);
+        serviceTags.setTagDefinitions(Collections.emptyMap());
+        serviceTags.setTags(Collections.emptyMap());
+        serviceTags.setResourceToTagIds(Collections.emptyMap());
+
+        LOG.info("Deleting tags: {}" + serviceTags);
+
+        rangerClient.importServiceTags(serviceName, serviceTags);
+
+        serviceTags2 = rangerClient.getServiceTags(serviceName);
+
+        LOG.info("Service tags after delete: {}", serviceTags2);
 
         /*
         Delete a Service
diff --git a/ranger-examples/sample-client/src/main/python/sample_client.py b/ranger-examples/sample-client/src/main/python/sample_client.py
index 902734901..e558b6e1e 100644
--- a/ranger-examples/sample-client/src/main/python/sample_client.py
+++ b/ranger-examples/sample-client/src/main/python/sample_client.py
@@ -16,11 +16,16 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import time
 
-from apache_ranger.model.ranger_service import *
-from apache_ranger.client.ranger_client import *
-from apache_ranger.model.ranger_policy  import *
+from apache_ranger.client.ranger_client          import *
+from apache_ranger.model.ranger_policy           import *
+from apache_ranger.model.ranger_service          import *
+from apache_ranger.model.ranger_service_resource import *
+from apache_ranger.model.ranger_service_tags     import *
+from apache_ranger.model.ranger_tagdef           import *
+from apache_ranger.model.ranger_tag              import *
+from datetime                                    import datetime
+
 
 
 ## create a client to connect to Apache Ranger admin server
@@ -50,8 +55,9 @@ print('    ' + str(len(service_defs)) + ' service-defs found')
 for service_def in service_defs:
     print('        ' + 'id: ' + str(service_def.id) + ', name: ' + service_def.name)
 
+now = datetime.now()
 
-service_name = 'dev_hive-' + str(int(time.time() * 1000))
+service_name = 'dev_hive-' + now.strftime('%Y%m%d-%H%M%S-%f')
 
 print('Creating service: name=' + service_name)
 
@@ -271,6 +277,47 @@ updated_policy2 = ranger.update_policy(service_name, policy_name, updated_policy
 print('    updated policy: id: ' + str(updated_policy2.id) + ', description: ' + saved_value + ', updatedDescription: ' + updated_policy2.description)
 
 
+tagdef_test1 = RangerTagDef({'name': 'test1', 'attributeDefs': [ RangerTagAttributeDef({'name': 'attr1', 'type': 'string'}) ]})
+tagdef_test2 = RangerTagDef({'name' : 'test2'})
+
+tag_test1_val1 = RangerTag({'type': 'test1', 'attributes': {'attr1': 'val1'}})
+tag_test1_val2 = RangerTag({'type': 'test1', 'attributes': {'attr1': 'val2'}})
+tag_test2     = RangerTag({'type': 'test2'})
+
+db1 = RangerServiceResource({'id': 1, 'serviceName': service_name})
+db1.resourceElements = { 'database': RangerPolicyResource({ 'values': [ 'db1' ]})}
+
+db2 = RangerServiceResource({'id': 2, 'serviceName': service_name})
+db2.resourceElements = { 'database': RangerPolicyResource({ 'values': [ 'db2' ]})}
+
+tags = RangerServiceTags({'serviceName': service_name})
+tags.op               = RangerServiceTags.OP_SET
+tags.tagDefinitions   = { 0: tagdef_test1, 1: tagdef_test2 }
+tags.tags             = { 0: tag_test1_val1, 1: tag_test1_val2, 2: tag_test2 }
+tags.serviceResources = [ db1, db2 ]
+tags.resourceToTagIds = { 1: [ 0, 2 ], 2: [ 1, 2 ]}
+
+print('Importing tags: ' + str(tags))
+
+ranger.import_service_tags(service_name, tags)
+
+service_tags = ranger.get_service_tags(service_name)
+
+print('Imported tags: ' + str(service_tags))
+
+tags.op               = RangerServiceTags.OP_DELETE
+tags.tagDefinitions   = None
+tags.tags             = None
+tags.resourceToTagIds = None
+
+print('Deleting tags: ' + str(tags))
+
+ranger.import_service_tags(service_name, tags)
+
+service_tags = ranger.get_service_tags(service_name)
+
+print('Service tags after delete: ' + str(service_tags))
+
 print('Deleting policy: id=' + str(policy_id))
 
 ranger.delete_policy_by_id(policy_id)
@@ -313,7 +360,7 @@ for security_zone in security_zones:
 print('Listing roles..')
 roles = ranger.find_roles()
 
-print('    ' + str(len(roles)) + ' roles zones found')
+print('    ' + str(len(roles)) + ' roles found')
 for role in roles:
     print('        id: ' + str(role.id) + ', name: ' + role.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 2c5c60923..18d52fea3 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
@@ -32,8 +32,10 @@ import org.apache.ranger.plugin.model.RangerSecurityZoneHeaderInfo;
 import org.apache.ranger.plugin.model.RangerService;
 import org.apache.ranger.plugin.model.RangerServiceDef;
 import org.apache.ranger.plugin.model.RangerServiceHeaderInfo;
+import org.apache.ranger.plugin.model.RangerServiceTags;
 import org.apache.ranger.plugin.util.GrantRevokeRoleRequest;
 import org.apache.ranger.plugin.util.SearchFilter;
+import org.apache.ranger.plugin.util.ServiceTags;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -598,6 +600,52 @@ public class PublicAPIsv2 {
 		serviceREST.deletePolicyByGUIDAndServiceNameAndZoneName(guid, serviceName, zoneName);
 	}
 
+	@PUT
+	@Path("/api/service/{serviceName}/tags")
+	@Produces({ "application/json", "application/xml" })
+	@PreAuthorize("hasRole('ROLE_SYS_ADMIN')")
+	public void importServiceTags(@PathParam("serviceName") String serviceName, RangerServiceTags svcTags) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("==> PublicAPIsv2.importServiceTags()");
+		}
+
+		ServiceTags serviceTags = RangerServiceTags.toServiceTags(svcTags);
+
+		// overwrite serviceName with the one given in url
+		serviceTags.setServiceName(serviceName);
+
+		tagREST.importServiceTags(serviceTags);
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("<== PublicAPIsv2.importServiceTags()");
+		}
+	}
+
+	@GET
+	@Path("/api/service/{serviceName}/tags")
+	@Produces({ "application/json", "application/xml" })
+	@PreAuthorize("hasRole('ROLE_SYS_ADMIN')")
+	public RangerServiceTags getServiceTags(@PathParam("serviceName") String serviceName, @Context HttpServletRequest request) {
+		if (logger.isDebugEnabled()) {
+			logger.debug("==> PublicAPIsv2.getServiceTags()");
+		}
+
+		Long              lastKnownVersion   = -1L;
+		Long              lastActivationTime = 0L;
+		String            pluginId           = null;
+		Boolean           supportsTagDeltas  = false;
+		String            pluginCapabilities = "";
+		ServiceTags       tags               = tagREST.getServiceTagsIfUpdated(serviceName, lastKnownVersion, lastActivationTime, pluginId, supportsTagDeltas, pluginCapabilities, request);
+		RangerServiceTags ret                = RangerServiceTags.toRangerServiceTags(tags);
+
+		if (logger.isDebugEnabled()) {
+			logger.debug("<== PublicAPIsv2.getServiceTags()");
+		}
+
+		return ret;
+	}
+
+
 	@GET
 	@Path("/api/plugins/info")
 	public List<RangerPluginInfo> getPluginsInfo(@Context HttpServletRequest request) {