Skip to content

Commit 3142ee4

Browse files
authored
Add support for View Permissions (#526)
Follow the permissions patterns :)
1 parent 5812dc9 commit 3142ee4

File tree

6 files changed

+123
-1
lines changed

6 files changed

+123
-1
lines changed

tableauserverclient/models/permissions_item.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class Resource:
3838
Flow = 'flow'
3939
Table = 'table'
4040
Database = 'database'
41+
View = 'view'
4142

4243

4344
class PermissionsRule(object):

tableauserverclient/models/view_item.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def __init__(self):
2121
self._sheet_type = None
2222
self._updated_at = None
2323
self._workbook_id = None
24+
self._permissions = None
2425
self.tags = set()
2526

2627
def _set_preview_image(self, preview_image):
@@ -106,6 +107,16 @@ def updated_at(self):
106107
def workbook_id(self):
107108
return self._workbook_id
108109

110+
@property
111+
def permissions(self):
112+
if self._permissions is None:
113+
error = "View item must be populated with permissions first."
114+
raise UnpopulatedPropertyError(error)
115+
return self._permissions()
116+
117+
def _set_permissions(self, permissions):
118+
self._permissions = permissions
119+
109120
@classmethod
110121
def from_response(cls, resp, ns, workbook_id=''):
111122
return cls.from_xml_element(ET.fromstring(resp), ns, workbook_id)

tableauserverclient/server/endpoint/views_endpoint.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from .endpoint import Endpoint, api
22
from .exceptions import MissingRequiredFieldError
33
from .resource_tagger import _ResourceTagger
4+
from .permissions_endpoint import _PermissionsEndpoint
45
from .. import RequestFactory, ViewItem, PaginationItem
56
from ...models.tag_item import TagItem
67
import logging
@@ -13,6 +14,7 @@ class Views(Endpoint):
1314
def __init__(self, parent_srv):
1415
super(Views, self).__init__(parent_srv)
1516
self._resource_tagger = _ResourceTagger(parent_srv)
17+
self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
1618

1719
# Used because populate_preview_image functionaliy requires workbook endpoint
1820
@property
@@ -109,6 +111,18 @@ def _get_view_csv(self, view_item, req_options):
109111
csv = server_response.iter_content(1024)
110112
return csv
111113

114+
@api(version='3.2')
115+
def populate_permissions(self, item):
116+
self._permissions.populate(item)
117+
118+
@api(version='3.2')
119+
def update_permissions(self, resource, rules):
120+
return self._permissions.update(resource, rules)
121+
122+
@api(version='3.2')
123+
def delete_permission(self, item, capability_item):
124+
return self._permissions.delete(item, capability_item)
125+
112126
# Update view. Currently only tags can be updated
113127
def update(self, view_item):
114128
if not view_item.id:
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<tsResponse xmlns="http://tableau.com/api"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-3.7.xsd">
4+
<permissions>
5+
<view id="e490bec4-2652-4fda-8c4e-f087db6fa328" name="Overview">
6+
<owner id="26183d16-82f7-4fcf-b163-0e607bf292bc"/>
7+
</view>
8+
<granteeCapabilities>
9+
<group id="c8f2773a-c83a-11e8-8c8f-33e6d787b506"/>
10+
<capabilities>
11+
<capability name="ExportData" mode="Allow"/>
12+
<capability name="ViewComments" mode="Allow"/>
13+
<capability name="AddComment" mode="Allow"/>
14+
<capability name="Read" mode="Allow"/>
15+
<capability name="ExportImage" mode="Allow"/>
16+
</capabilities>
17+
</granteeCapabilities>
18+
</permissions>
19+
</tsResponse>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?xml version='1.0' encoding='UTF-8'?>
2+
<tsResponse xmlns="http://tableau.com/api" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://tableau.com/api http://tableau.com/api/ts-api-2.3.xsd">
3+
<permissions>
4+
<view id="21778de4-b7b9-44bc-a599-1506a2639ace" name="ComplianceDashboard">
5+
<owner id="91d5931a-2cd6-4dbc-8b7c-211e85cf5fa3" />
6+
</view>
7+
<granteeCapabilities>
8+
<group id="5e5e1978-71fa-11e4-87dd-7382f5c437af" />
9+
<capabilities>
10+
<capability name="Read" mode="Deny" />
11+
</capabilities>
12+
</granteeCapabilities>
13+
<granteeCapabilities>
14+
<user id="7c37ee24-c4b1-42b6-a154-eaeab7ee330a" />
15+
<capabilities>
16+
<capability name="Write" mode="Allow" />
17+
</capabilities>
18+
</granteeCapabilities>
19+
</permissions>
20+
</tsResponse>
21+

test/test_view.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import tableauserverclient as TSC
55

66
from tableauserverclient.datetime_helpers import format_datetime
7+
from tableauserverclient import UserItem, GroupItem, PermissionsRule
78

89
TEST_ASSET_DIR = os.path.join(os.path.dirname(__file__), 'assets')
910

@@ -13,13 +14,15 @@
1314
POPULATE_PREVIEW_IMAGE = os.path.join(TEST_ASSET_DIR, 'Sample View Image.png')
1415
POPULATE_PDF = os.path.join(TEST_ASSET_DIR, 'populate_pdf.pdf')
1516
POPULATE_CSV = os.path.join(TEST_ASSET_DIR, 'populate_csv.csv')
17+
POPULATE_PERMISSIONS_XML = os.path.join(TEST_ASSET_DIR, 'view_populate_permissions.xml')
18+
UPDATE_PERMISSIONS = os.path.join(TEST_ASSET_DIR, 'view_update_permissions.xml')
1619
UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'workbook_update.xml')
1720

1821

1922
class ViewTests(unittest.TestCase):
2023
def setUp(self):
2124
self.server = TSC.Server('http://test')
22-
self.server.version = '2.7'
25+
self.server.version = '3.2'
2326

2427
# Fake sign in
2528
self.server._site_id = 'dad65087-b08b-4603-af4e-2887b8aafc67'
@@ -170,6 +173,59 @@ def test_populate_image_missing_id(self):
170173
single_view._id = None
171174
self.assertRaises(TSC.MissingRequiredFieldError, self.server.views.populate_image, single_view)
172175

176+
def test_populate_permissions(self):
177+
with open(POPULATE_PERMISSIONS_XML, 'rb') as f:
178+
response_xml = f.read().decode('utf-8')
179+
with requests_mock.mock() as m:
180+
m.get(self.baseurl + "/e490bec4-2652-4fda-8c4e-f087db6fa328/permissions", text=response_xml)
181+
single_view = TSC.ViewItem()
182+
single_view._id = "e490bec4-2652-4fda-8c4e-f087db6fa328"
183+
184+
self.server.views.populate_permissions(single_view)
185+
permissions = single_view.permissions
186+
187+
self.assertEqual(permissions[0].grantee.tag_name, 'group')
188+
self.assertEqual(permissions[0].grantee.id, 'c8f2773a-c83a-11e8-8c8f-33e6d787b506')
189+
self.assertDictEqual(permissions[0].capabilities, {
190+
TSC.Permission.Capability.ViewComments: TSC.Permission.Mode.Allow,
191+
TSC.Permission.Capability.Read: TSC.Permission.Mode.Allow,
192+
TSC.Permission.Capability.AddComment: TSC.Permission.Mode.Allow,
193+
TSC.Permission.Capability.ExportData: TSC.Permission.Mode.Allow,
194+
TSC.Permission.Capability.ExportImage: TSC.Permission.Mode.Allow,
195+
196+
})
197+
198+
def test_add_permissions(self):
199+
with open(UPDATE_PERMISSIONS, 'rb') as f:
200+
response_xml = f.read().decode('utf-8')
201+
202+
single_view = TSC.ViewItem()
203+
single_view._id = '21778de4-b7b9-44bc-a599-1506a2639ace'
204+
205+
bob = UserItem.as_reference("7c37ee24-c4b1-42b6-a154-eaeab7ee330a")
206+
group_of_people = GroupItem.as_reference("5e5e1978-71fa-11e4-87dd-7382f5c437af")
207+
208+
new_permissions = [
209+
PermissionsRule(bob, {'Write': 'Allow'}),
210+
PermissionsRule(group_of_people, {'Read': 'Deny'})
211+
]
212+
213+
with requests_mock.mock() as m:
214+
m.put(self.baseurl + "/21778de4-b7b9-44bc-a599-1506a2639ace/permissions", text=response_xml)
215+
permissions = self.server.views.update_permissions(single_view, new_permissions)
216+
217+
self.assertEqual(permissions[0].grantee.tag_name, 'group')
218+
self.assertEqual(permissions[0].grantee.id, '5e5e1978-71fa-11e4-87dd-7382f5c437af')
219+
self.assertDictEqual(permissions[0].capabilities, {
220+
TSC.Permission.Capability.Read: TSC.Permission.Mode.Deny
221+
})
222+
223+
self.assertEqual(permissions[1].grantee.tag_name, 'user')
224+
self.assertEqual(permissions[1].grantee.id, '7c37ee24-c4b1-42b6-a154-eaeab7ee330a')
225+
self.assertDictEqual(permissions[1].capabilities, {
226+
TSC.Permission.Capability.Write: TSC.Permission.Mode.Allow
227+
})
228+
173229
def test_update_tags(self):
174230
with open(ADD_TAGS_XML, 'rb') as f:
175231
add_tags_xml = f.read().decode('utf-8')

0 commit comments

Comments
 (0)