diff --git a/tableauserverclient/__init__.py b/tableauserverclient/__init__.py
index 85972d48b..9bce2acdb 100644
--- a/tableauserverclient/__init__.py
+++ b/tableauserverclient/__init__.py
@@ -3,7 +3,8 @@
GroupItem, JobItem, BackgroundJobItem, PaginationItem, ProjectItem, ScheduleItem, \
SiteItem, TableauAuth, UserItem, ViewItem, WorkbookItem, UnpopulatedPropertyError, \
HourlyInterval, DailyInterval, WeeklyInterval, MonthlyInterval, IntervalItem, TaskItem, \
- SubscriptionItem, Target
+ SubscriptionItem, Target, PermissionsCollection, Permission, PermissionsRule, PermissionsGrantee
+
from .server import RequestOptions, CSVRequestOptions, ImageRequestOptions, PDFRequestOptions, Filter, Sort, \
Server, ServerResponseError, MissingRequiredFieldError, NotSignedInError, Pager
from ._version import get_versions
diff --git a/tableauserverclient/models/__init__.py b/tableauserverclient/models/__init__.py
index 63a861cbb..63e4bef14 100644
--- a/tableauserverclient/models/__init__.py
+++ b/tableauserverclient/models/__init__.py
@@ -17,3 +17,4 @@
from .view_item import ViewItem
from .workbook_item import WorkbookItem
from .subscription_item import SubscriptionItem
+from .permissions_item import Permission, PermissionsCollection, PermissionsRule, PermissionsGrantee
diff --git a/tableauserverclient/models/exceptions.py b/tableauserverclient/models/exceptions.py
index 28d738e73..86c28ac33 100644
--- a/tableauserverclient/models/exceptions.py
+++ b/tableauserverclient/models/exceptions.py
@@ -1,2 +1,6 @@
class UnpopulatedPropertyError(Exception):
pass
+
+
+class UnknownGranteeTypeError(Exception):
+ pass
diff --git a/tableauserverclient/models/permissions_item.py b/tableauserverclient/models/permissions_item.py
new file mode 100644
index 000000000..e67812e3b
--- /dev/null
+++ b/tableauserverclient/models/permissions_item.py
@@ -0,0 +1,147 @@
+import xml.etree.ElementTree as ET
+import logging
+
+from .exceptions import UnknownGranteeTypeError
+
+
+logger = logging.getLogger("tableau.models.permissions_item")
+
+
+class Permission:
+ class GranteeType:
+ User = "user"
+ Group = "group"
+
+ class CapabilityMode:
+ Allow = "Allow"
+ Deny = "Deny"
+
+ class DatasourceCapabilityType:
+ ChangePermissions = "ChangePermissions"
+ Connect = "Connect"
+ Delete = "Delete"
+ ExportXml = "ExportXml"
+ Read = "Read"
+ Write = "Write"
+
+ class WorkbookCapabilityType:
+ AddComment = "AddComment"
+ ChangeHierarchy = "ChangeHierarchy"
+ ChangePermissions = "ChangePermissions"
+ Delete = "Delete"
+ ExportData = "ExportData"
+ ExportImage = "ExportImage"
+ ExportXml = "ExportXml"
+ Filter = "Filter"
+ Read = "Read"
+ ShareView = "ShareView"
+ ViewComments = "ViewComments"
+ ViewUnderlyingData = "ViewUnderlyingData"
+ WebAuthoring = "WebAuthoring"
+ Write = "Write"
+
+ class ProjectCapabilityType:
+ ProjectLeader = "ProjectLeader"
+ Read = "Read"
+ Write = "Write"
+
+
+class PermissionsGrantee(object):
+ def __init__(self, grantee_type, grantee_id):
+
+ if grantee_type not in [
+ Permission.GranteeType.User,
+ Permission.GranteeType.Group,
+ ]:
+ raise UnknownGranteeTypeError(grantee_type)
+
+ self._grantee_type = grantee_type
+ self._grantee_id = grantee_id
+
+ @classmethod
+ def from_xml_element(cls, xml_element):
+ tag_without_namespace = xml_element.tag.split("}")[-1]
+ return cls(tag_without_namespace, xml_element.get("id"))
+
+ def to_xml_element(self):
+ xml_element = ET.Element(self.grantee_type)
+ xml_element.set("id", self.grantee_id)
+ return xml_element
+
+ @property
+ def grantee_type(self):
+ return self._grantee_type
+
+ @property
+ def grantee_id(self):
+ return self._grantee_id
+
+
+class PermissionsRule(object):
+ def __init__(self, grantee, permissions_map=None):
+ self._grantee = grantee
+ self.permissions_map = permissions_map or {}
+
+ @property
+ def grantee(self):
+ return self._grantee
+
+ def to_xml_element(self):
+ xml_element = ET.Element("granteeCapabilities")
+ xml_element.append(self.grantee.to_xml_element())
+ capabilities_element = ET.SubElement(xml_element, "capabilities")
+ for permission, mode in self.permissions_map.items():
+ ET.SubElement(
+ capabilities_element, "capability", {"name": permission, "mode": mode}
+ )
+ return xml_element
+
+
+class PermissionsCollection(object):
+ def __init__(self, rules):
+ self._rules = rules
+
+ @property
+ def rules(self):
+ return self._rules
+
+ @classmethod
+ def from_response(cls, resp, ns=None):
+ parsed_response = ET.fromstring(resp)
+
+ capabilities = []
+ all_xml = parsed_response.findall(".//t:granteeCapabilities", namespaces=ns)
+
+ for grantee_capability_xml in all_xml:
+ user_grantee_element = grantee_capability_xml.find(
+ "./t:user", namespaces=ns
+ )
+ group_grantee_element = grantee_capability_xml.find(
+ "./t:group", namespaces=ns
+ )
+ grantee_element = (
+ user_grantee_element
+ if user_grantee_element is not None
+ else group_grantee_element
+ )
+
+ grantee = PermissionsGrantee.from_xml_element(grantee_element)
+
+ capability_elements = grantee_capability_xml.findall(
+ ".//t:capabilities/t:capability", namespaces=ns
+ )
+ capability_map = {
+ el.get("name"): el.get("mode") for el in capability_elements
+ }
+
+ capability_item = PermissionsRule(grantee, capability_map)
+
+ capabilities.append(capability_item)
+
+ return cls(capabilities)
+
+ def to_xml_element(self):
+ xml_element = ET.Element("permissions")
+ for permission_rule in self.rules:
+ xml_element.append(permission_rule.to_xml_element())
+ return xml_element
diff --git a/tableauserverclient/models/project_item.py b/tableauserverclient/models/project_item.py
index 92e0282ae..a390ecb2d 100644
--- a/tableauserverclient/models/project_item.py
+++ b/tableauserverclient/models/project_item.py
@@ -1,5 +1,6 @@
import xml.etree.ElementTree as ET
from .property_decorators import property_is_enum, property_not_empty
+from .exceptions import UnpopulatedPropertyError
class ProjectItem(object):
diff --git a/tableauserverclient/models/workbook_item.py b/tableauserverclient/models/workbook_item.py
index 8df036516..ca7c3b68e 100644
--- a/tableauserverclient/models/workbook_item.py
+++ b/tableauserverclient/models/workbook_item.py
@@ -27,6 +27,7 @@ def __init__(self, project_id, name=None, show_tabs=False):
self.tags = set()
self.materialized_views_config = {'materialized_views_enabled': None,
'run_materialization_now': None}
+ self._permissions = None
@property
def connections(self):
@@ -35,6 +36,13 @@ def connections(self):
raise UnpopulatedPropertyError(error)
return self._connections()
+ @property
+ def permissions(self):
+ if self._permissions is None:
+ error = "Workbook item must be populated with permissions first."
+ raise UnpopulatedPropertyError(error)
+ return self._permissions
+
@property
def content_url(self):
return self._content_url
@@ -120,6 +128,9 @@ def materialized_views_config(self, value):
def _set_connections(self, connections):
self._connections = connections
+ def _set_permissions(self, permissions):
+ self._permissions = permissions
+
def _set_views(self, views):
self._views = views
diff --git a/tableauserverclient/server/__init__.py b/tableauserverclient/server/__init__.py
index 8c5cb314c..19b9f9d17 100644
--- a/tableauserverclient/server/__init__.py
+++ b/tableauserverclient/server/__init__.py
@@ -4,7 +4,8 @@
from .sort import Sort
from .. import ConnectionItem, DatasourceItem, JobItem, BackgroundJobItem, \
GroupItem, PaginationItem, ProjectItem, ScheduleItem, SiteItem, TableauAuth,\
- UserItem, ViewItem, WorkbookItem, TaskItem, SubscriptionItem
+ UserItem, ViewItem, WorkbookItem, TaskItem, SubscriptionItem, PermissionsCollection, \
+ Permission, PermissionsRule, PermissionsGrantee
from .endpoint import Auth, Datasources, Endpoint, Groups, Projects, Schedules, \
Sites, Users, Views, Workbooks, Subscriptions, ServerResponseError, \
MissingRequiredFieldError
diff --git a/tableauserverclient/server/endpoint/permissions_endpoint.py b/tableauserverclient/server/endpoint/permissions_endpoint.py
new file mode 100644
index 000000000..4de18c267
--- /dev/null
+++ b/tableauserverclient/server/endpoint/permissions_endpoint.py
@@ -0,0 +1,76 @@
+import logging
+
+from .. import RequestFactory, PermissionsCollection
+
+from .endpoint import Endpoint, api
+from .exceptions import MissingRequiredFieldError
+
+
+logger = logging.getLogger(__name__)
+
+
+class _PermissionsEndpoint(Endpoint):
+ """ Adds permission model to another endpoint
+
+ Tableau permissions model is identical between objects but they are nested under
+ the parent object endpoint (i.e. permissions for workbooks are under
+ /workbooks/:id/permission). This class is meant to be instantiated inside a
+ parent endpoint which has these supported endpoints
+ """
+
+ def __init__(self, parent_srv, owner_baseurl):
+ super(_PermissionsEndpoint, self).__init__(parent_srv)
+
+ # owner_baseurl is the baseurl of the parent. The MUST be a lambda
+ # since we don't know the full site URL until we sign in. If
+ # populated without, we will get a sign-in error
+ self.owner_baseurl = owner_baseurl
+
+ def update(self, item, permission_collection):
+ url = "{0}/{1}/permissions".format(self.owner_baseurl(), item.id)
+ update_req = RequestFactory.Permission.add_req(permission_collection)
+ response = self.put_request(url, update_req)
+ permissions = PermissionsCollection.from_response(
+ response.content, self.parent_srv.namespace
+ )
+
+ logger.info("Updated permissions for item {0}".format(item.id))
+
+ return permissions
+
+ def delete(self, item, permission_collection):
+ for rule in permission_collection.rules:
+ for capability_type, capability_mode in rule.permissions_map.items():
+ url = "{0}/{1}/permissions/{2}s/{3}/{4}/{5}".format(
+ self.owner_baseurl(),
+ item.id,
+ rule.grantee.grantee_type,
+ rule.grantee.grantee_id,
+ capability_type,
+ capability_mode,
+ )
+ self.delete_request(url)
+ logger.info(
+ "Deleted permission for {0} {1} item {2}".format(
+ rule.grantee.grantee_type, rule.grantee.grantee_id, item.id
+ )
+ )
+
+ def populate(self, item):
+ if not item.id:
+ error = (
+ "Server item is missing ID. Item must be retrieved from server first."
+ )
+ raise MissingRequiredFieldError(error)
+
+ permissions = self._get_permissions(item)
+ item._set_permissions(permissions)
+ logger.info("Populated permissions for item (ID: {0})".format(item.id))
+
+ def _get_permissions(self, item, req_options=None):
+ url = "{0}/{1}/permissions".format(self.owner_baseurl(), item.id)
+ server_response = self.get_request(url, req_options)
+ permissions = PermissionsCollection.from_response(
+ server_response.content, self.parent_srv.namespace
+ )
+ return permissions
diff --git a/tableauserverclient/server/endpoint/workbooks_endpoint.py b/tableauserverclient/server/endpoint/workbooks_endpoint.py
index 772ed79b9..38ef8c3cf 100644
--- a/tableauserverclient/server/endpoint/workbooks_endpoint.py
+++ b/tableauserverclient/server/endpoint/workbooks_endpoint.py
@@ -1,4 +1,5 @@
-from .endpoint import Endpoint, api, parameter_added_in
+from .endpoint import api, parameter_added_in, Endpoint
+from .permissions_endpoint import _PermissionsEndpoint
from .exceptions import InternalServerError, MissingRequiredFieldError
from .fileuploads_endpoint import Fileuploads
from .resource_tagger import _ResourceTagger
@@ -25,6 +26,7 @@ class Workbooks(Endpoint):
def __init__(self, parent_srv):
super(Workbooks, self).__init__(parent_srv)
self._resource_tagger = _ResourceTagger(parent_srv)
+ self._permissions = _PermissionsEndpoint(parent_srv, lambda: self.baseurl)
@property
def baseurl(self):
@@ -216,6 +218,19 @@ def _get_wb_preview_image(self, workbook_item):
preview_image = server_response.content
return preview_image
+ @api(version='2.0')
+ def populate_permissions(self, item):
+ self._permissions.populate(item)
+
+ @api(version='2.0')
+ def update_permission(self, item, permission_item):
+ permissions = self._permissions.update(item, permission_item)
+ item._set_permissions(permissions)
+
+ @api(version='2.0')
+ def delete_permission(self, item, capability_item):
+ self._permissions.delete(item, capability_item)
+
# Publishes workbook. Chunking method if file over 64MB
@api(version="2.0")
@parameter_added_in(as_job='3.0')
diff --git a/tableauserverclient/server/request_factory.py b/tableauserverclient/server/request_factory.py
index fdc799af1..72dbb3ef5 100644
--- a/tableauserverclient/server/request_factory.py
+++ b/tableauserverclient/server/request_factory.py
@@ -142,30 +142,9 @@ def update_req(self, group_item, default_site_role):
class PermissionRequest(object):
- def _add_capability(self, parent_element, capability_set, mode):
- for capability_item in capability_set:
- capability_element = ET.SubElement(parent_element, 'capability')
- capability_element.attrib['name'] = capability_item
- capability_element.attrib['mode'] = mode
-
- def add_req(self, permission_item):
+ def add_req(self, permission_collection):
xml_request = ET.Element('tsRequest')
- permissions_element = ET.SubElement(xml_request, 'permissions')
-
- for user_capability in permission_item.user_capabilities:
- grantee_element = ET.SubElement(permissions_element, 'granteeCapabilities')
- grantee_capabilities_element = ET.SubElement(grantee_element, user_capability.User)
- grantee_capabilities_element.attrib['id'] = user_capability.grantee_id
- capabilities_element = ET.SubElement(grantee_element, 'capabilities')
- self._add_capability(capabilities_element, user_capability.allowed, user_capability.Allow)
- self._add_capability(capabilities_element, user_capability.denied, user_capability.Deny)
-
- for group_capability in permission_item.group_capabilities:
- grantee_element = ET.SubElement(permissions_element, 'granteeCapabilities')
- ET.SubElement(grantee_element, group_capability, id=group_capability.grantee_id)
- capabilities_element = ET.SubElement(grantee_element, 'capabilities')
- self._add_capability(capabilities_element, group_capability.allowed, group_capability.Allow)
- self._add_capability(capabilities_element, group_capability.denied, group_capability.Deny)
+ xml_request.append(permission_collection.to_xml_element())
return ET.tostring(xml_request)
diff --git a/test/assets/workbook_populate_permissions.xml b/test/assets/workbook_populate_permissions.xml
new file mode 100644
index 000000000..57517d719
--- /dev/null
+++ b/test/assets/workbook_populate_permissions.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/assets/workbook_update_permissions.xml b/test/assets/workbook_update_permissions.xml
new file mode 100644
index 000000000..d220bb660
--- /dev/null
+++ b/test/assets/workbook_update_permissions.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/test_workbook.py b/test/test_workbook.py
index ae814c0b2..65f2e687a 100644
--- a/test/test_workbook.py
+++ b/test/test_workbook.py
@@ -16,6 +16,7 @@
GET_EMPTY_XML = os.path.join(TEST_ASSET_DIR, 'workbook_get_empty.xml')
GET_XML = os.path.join(TEST_ASSET_DIR, 'workbook_get.xml')
POPULATE_CONNECTIONS_XML = os.path.join(TEST_ASSET_DIR, 'workbook_populate_connections.xml')
+POPULATE_PERMISSIONS_XML = os.path.join(TEST_ASSET_DIR, 'workbook_populate_permissions.xml')
POPULATE_PDF = os.path.join(TEST_ASSET_DIR, 'populate_pdf.pdf')
POPULATE_PREVIEW_IMAGE = os.path.join(TEST_ASSET_DIR, 'RESTAPISample Image.png')
POPULATE_VIEWS_XML = os.path.join(TEST_ASSET_DIR, 'workbook_populate_views.xml')
@@ -23,6 +24,7 @@
PUBLISH_XML = os.path.join(TEST_ASSET_DIR, 'workbook_publish.xml')
PUBLISH_ASYNC_XML = os.path.join(TEST_ASSET_DIR, 'workbook_publish_async.xml')
UPDATE_XML = os.path.join(TEST_ASSET_DIR, 'workbook_update.xml')
+UPDATE_PERMISSIONS_XML = os.path.join(TEST_ASSET_DIR, 'workbook_update_permissions.xml')
class WorkbookTests(unittest.TestCase):
@@ -270,6 +272,161 @@ def test_populate_connections(self):
self.assertEqual('4506225a-0d32-4ab1-82d3-c24e85f7afba', single_workbook.connections[0].datasource_id)
self.assertEqual('World Indicators', single_workbook.connections[0].datasource_name)
+ def test_populate_permissions(self):
+ with open(POPULATE_PERMISSIONS_XML, 'rb') as f:
+ response_xml = f.read().decode('utf-8')
+ with requests_mock.mock() as m:
+ m.get(self.baseurl + '/21778de4-b7b9-44bc-a599-1506a2639ace/permissions', text=response_xml)
+ single_workbook = TSC.WorkbookItem('test')
+ single_workbook._id = '21778de4-b7b9-44bc-a599-1506a2639ace'
+
+ self.server.workbooks.populate_permissions(single_workbook)
+ permissions = single_workbook.permissions
+
+ self.assertEqual(permissions.rules[0].grantee.grantee_type, TSC.Permission.GranteeType.Group)
+ self.assertEqual(permissions.rules[0].grantee.grantee_id, '5e5e1978-71fa-11e4-87dd-7382f5c437af')
+ self.assertDictEqual(permissions.rules[0].permissions_map, {
+ TSC.Permission.WorkbookCapabilityType.WebAuthoring: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.Read: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.Filter: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.AddComment: TSC.Permission.CapabilityMode.Allow
+ })
+
+ self.assertEqual(permissions.rules[1].grantee.grantee_type, TSC.Permission.GranteeType.User)
+ self.assertEqual(permissions.rules[1].grantee.grantee_id, '7c37ee24-c4b1-42b6-a154-eaeab7ee330a')
+ self.assertDictEqual(permissions.rules[1].permissions_map, {
+ TSC.Permission.WorkbookCapabilityType.ExportImage: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.ShareView: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.ExportData: TSC.Permission.CapabilityMode.Deny,
+ TSC.Permission.WorkbookCapabilityType.ViewComments: TSC.Permission.CapabilityMode.Deny
+ })
+
+ def test_update_permissions(self):
+ with open(UPDATE_PERMISSIONS_XML, 'rb') as f:
+ response_xml = f.read().decode('utf-8')
+
+ with requests_mock.mock() as m:
+ adapter = m.put(self.baseurl + '/21778de4-b7b9-44bc-a599-1506a2639ace/permissions', text=response_xml)
+ single_workbook = TSC.WorkbookItem('test')
+ single_workbook._id = '21778de4-b7b9-44bc-a599-1506a2639ace'
+
+ permisssion_collection = TSC.PermissionsCollection([
+ TSC.PermissionsRule(
+ TSC.PermissionsGrantee(TSC.Permission.GranteeType.Group, '5e5e1978-71fa-11e4-87dd-7382f5c437af'),
+ {
+ TSC.Permission.WorkbookCapabilityType.WebAuthoring: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.Read: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.Filter: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.AddComment: TSC.Permission.CapabilityMode.Allow,
+ }
+ ),
+ TSC.PermissionsRule(
+ TSC.PermissionsGrantee(TSC.Permission.GranteeType.User, '7c37ee24-c4b1-42b6-a154-eaeab7ee330a'),
+ {
+ TSC.Permission.WorkbookCapabilityType.ExportImage: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.ShareView: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.ExportData: TSC.Permission.CapabilityMode.Deny,
+ TSC.Permission.WorkbookCapabilityType.ViewComments: TSC.Permission.CapabilityMode.Deny,
+ }
+ ),
+ ])
+
+ self.server.workbooks.update_permission(single_workbook, permisssion_collection)
+
+ # Check request
+ request_el = ET.fromstring(adapter.last_request.text)
+ permissions_el = request_el.find('./permissions')
+ grantee_capabilities = permissions_el.findall('./granteeCapabilities')
+
+ permission_map = []
+
+ for grantee_capability in grantee_capabilities:
+ user = grantee_capability.find('./user')
+ group = grantee_capability.find('./group')
+ grantee = user if user is not None else group
+ permission_map.append({
+ 'grantee_id': grantee.get('id'),
+ 'grantee_type': grantee.tag,
+ 'capabilities': {},
+ })
+ for capability in grantee_capability.find('./capabilities'):
+ permission_map[-1]['capabilities'][capability.get('name')] = capability.get('mode')
+
+ self.assertEqual(
+ permission_map,
+ [
+ {
+ 'grantee_id': '5e5e1978-71fa-11e4-87dd-7382f5c437af',
+ 'grantee_type': 'group',
+ 'capabilities': {
+ 'WebAuthoring': 'Allow',
+ 'Read': 'Allow',
+ 'Filter': 'Allow',
+ 'AddComment': 'Allow'
+ }
+ },
+ {
+ 'grantee_id': '7c37ee24-c4b1-42b6-a154-eaeab7ee330a',
+ 'grantee_type': 'user',
+ 'capabilities': {
+ 'ExportImage': 'Allow',
+ 'ShareView': 'Allow',
+ 'ExportData': 'Deny',
+ 'ViewComments': 'Deny'
+ }
+ }
+ ]
+
+ )
+
+ def test_delete_permissions(self):
+ with requests_mock.mock() as m:
+ adapter = m.delete(requests_mock.ANY)
+ single_workbook = TSC.WorkbookItem('test')
+ single_workbook._id = '21778de4-b7b9-44bc-a599-1506a2639ace'
+
+ permisssion_collection = TSC.PermissionsCollection([
+ TSC.PermissionsRule(
+ TSC.PermissionsGrantee(TSC.Permission.GranteeType.Group, '5e5e1978-71fa-11e4-87dd-7382f5c437af'),
+ {
+ TSC.Permission.WorkbookCapabilityType.WebAuthoring: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.Read: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.Filter: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.AddComment: TSC.Permission.CapabilityMode.Allow,
+ }
+ ),
+ TSC.PermissionsRule(
+ TSC.PermissionsGrantee(TSC.Permission.GranteeType.User, '7c37ee24-c4b1-42b6-a154-eaeab7ee330a'),
+ {
+ TSC.Permission.WorkbookCapabilityType.ExportImage: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.ShareView: TSC.Permission.CapabilityMode.Allow,
+ TSC.Permission.WorkbookCapabilityType.ExportData: TSC.Permission.CapabilityMode.Deny,
+ TSC.Permission.WorkbookCapabilityType.ViewComments: TSC.Permission.CapabilityMode.Deny,
+ }
+ ),
+ ])
+
+ self.server.workbooks.delete_permission(single_workbook, permisssion_collection)
+
+ # Check request
+ url_requests_made = [r.url for r in adapter.request_history]
+
+ base_url = '{}/{}'.format(self.server.workbooks.baseurl, single_workbook._id)
+ group_base_url = '{}/permissions/groups/{}'.format(base_url, '5e5e1978-71fa-11e4-87dd-7382f5c437af')
+ user_base_url = '{}/permissions/users/{}'.format(base_url, '7c37ee24-c4b1-42b6-a154-eaeab7ee330a')
+
+ for url in [
+ '{}/WebAuthoring/Allow'.format(group_base_url),
+ '{}/Read/Allow'.format(group_base_url),
+ '{}/Filter/Allow'.format(group_base_url),
+ '{}/AddComment/Allow'.format(group_base_url),
+ '{}/ExportImage/Allow'.format(user_base_url),
+ '{}/ShareView/Allow'.format(user_base_url),
+ '{}/ExportData/Deny'.format(user_base_url),
+ '{}/ViewComments/Deny'.format(user_base_url),
+ ]:
+ self.assertIn(url, url_requests_made)
+
def test_populate_connections_missing_id(self):
single_workbook = TSC.WorkbookItem('test')
self.assertRaises(TSC.MissingRequiredFieldError,