Skip to content

Feature/add presets usage restrictions #60

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [Version 1.1.5](https://github.com/dataiku/dss-plugin-sharepoint-online/releases/tag/v1.1.5) - Feature release - 2024-10-15

- Restrict site path, root directory override and write mode on presets

## [Version 1.1.4](https://github.com/dataiku/dss-plugin-sharepoint-online/releases/tag/v1.1.4) - Feature release - 2024-07-16

- Fix writing when using presets with no root folder defined
Expand Down
21 changes: 21 additions & 0 deletions parameter-sets/app-certificate/parameter-set.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,27 @@
"description": "sites/site_name/subsite...",
"mandatory": true
},
{
"name": "cannot_overwrite_site",
"label": " ",
"type": "BOOLEAN",
"description": "Site path cannot be overwritten",
"mandatory": true
},
{
"name": "sharepoint_root",
"label": "Root directory",
"type": "STRING",
"description": "",
"defaultValue": "Shared Documents"
},
{
"name": "cannot_overwrite_root",
"label": " ",
"type": "BOOLEAN",
"description": "Root directory cannot be overwritten",
"mandatory": true
},
{
"name": "tenant_id",
"label": "Tenant ID",
Expand Down Expand Up @@ -63,6 +77,13 @@
"type": "PASSWORD",
"description": "If required by private key",
"mandatory": false
},
{
"name": "cannot_write",
"label": "Read only",
"type": "BOOLEAN",
"description": "This preset is read only",
"mandatory": true
}
]
}
21 changes: 21 additions & 0 deletions parameter-sets/oauth-login/parameter-set.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,40 @@
"description": "sites/site_name/subsite...",
"mandatory": true
},
{
"name": "cannot_overwrite_site",
"label": " ",
"type": "BOOLEAN",
"description": "Site path cannot be overwritten",
"mandatory": true
},
{
"name": "sharepoint_root",
"label": "Root directory",
"type": "STRING",
"description": "",
"defaultValue": "Shared Documents"
},
{
"name": "cannot_overwrite_root",
"label": " ",
"type": "BOOLEAN",
"description": "Root directory cannot be overwritten",
"mandatory": true
},
{
"name": "authorizationEndpoint",
"label": "Authorization endpoint",
"type": "STRING",
"description": "See documentation",
"mandatory": true
},
{
"name": "cannot_write",
"label": "Read only",
"type": "BOOLEAN",
"description": "This preset is read only",
"mandatory": true
}
]
}
21 changes: 21 additions & 0 deletions parameter-sets/sharepoint-login/parameter-set.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,27 @@
"description": "sites/site_name/subsite...",
"mandatory": true
},
{
"name": "cannot_overwrite_site",
"label": " ",
"type": "BOOLEAN",
"description": "Site path cannot be overwritten",
"mandatory": true
},
{
"name": "sharepoint_root",
"label": "Root directory",
"type": "STRING",
"description": "",
"defaultValue": "Shared Documents"
},
{
"name": "cannot_overwrite_root",
"label": " ",
"type": "BOOLEAN",
"description": "Root directory cannot be overwritten",
"mandatory": true
},
{
"name": "sharepoint_username",
"label": "Username",
Expand All @@ -42,6 +56,13 @@
"type": "PASSWORD",
"description": "",
"mandatory": true
},
{
"name": "cannot_write",
"label": "Read only",
"type": "BOOLEAN",
"description": "This preset is read only",
"mandatory": true
}
]
}
21 changes: 21 additions & 0 deletions parameter-sets/site-app-permissions/parameter-set.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,27 @@
"description": "sites/site_name/subsite...",
"mandatory": true
},
{
"name": "cannot_overwrite_site",
"label": " ",
"type": "BOOLEAN",
"description": "Site path cannot be overwritten",
"mandatory": true
},
{
"name": "sharepoint_root",
"label": "Root directory",
"type": "STRING",
"description": "",
"defaultValue": "Shared Documents"
},
{
"name": "cannot_overwrite_root",
"label": " ",
"type": "BOOLEAN",
"description": "Root directory cannot be overwritten",
"mandatory": true
},
{
"name": "tenant_id",
"label": "Tenant ID",
Expand All @@ -49,6 +63,13 @@
"type": "PASSWORD",
"description": "",
"mandatory": true
},
{
"name": "cannot_write",
"label": "Read only",
"type": "BOOLEAN",
"description": "This preset is read only",
"mandatory": true
}
]
}
2 changes: 1 addition & 1 deletion plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "sharepoint-online",
"version": "1.1.4",
"version": "1.1.5",
"meta": {
"label": "SharePoint Online",
"description": "Read and write data from/to your SharePoint Online account",
Expand Down
2 changes: 1 addition & 1 deletion python-lib/dss_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class DSSConstants(object):
"sharepoint_oauth": "The access token is missing"
}
PATH = 'path'
PLUGIN_VERSION = "1.1.4"
PLUGIN_VERSION = "1.1.5"
SECRET_PARAMETERS_KEYS = ["Authorization", "sharepoint_username", "sharepoint_password", "client_secret", "client_certificate", "passphrase"]
SITE_APP_DETAILS = {
"sharepoint_tenant": "The tenant name is missing",
Expand Down
45 changes: 44 additions & 1 deletion python-lib/sharepoint_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ def apply_paths_overwrite(self, config):
sharepoint_root_overwrite = config.get("sharepoint_root_overwrite", "").strip("/")
sharepoint_site_overwrite = config.get("sharepoint_site_overwrite", "").strip("/")
if advanced_parameters and sharepoint_root_overwrite:
assert_can_overwrite_root(config)
self.sharepoint_root = sharepoint_root_overwrite
if advanced_parameters and sharepoint_site_overwrite:
assert_can_overwrite_site(config)
self.sharepoint_site = sharepoint_site_overwrite

def setup_sharepoint_online_url(self, login_details):
Expand Down Expand Up @@ -221,6 +223,7 @@ def get_file_content(self, full_path):
return response

def write_file_content(self, full_path, data):
self.assert_can_write()
self.file_size = len(data)

# Preventive file check out, in case it already exists on SP's side
Expand Down Expand Up @@ -281,6 +284,7 @@ def write_chunked_file_content(self, full_path, data):
return response

def create_folder(self, full_path):
self.assert_can_write()
if is_empty_path(full_path) and is_empty_path(self.sharepoint_root):
return
response = self.session.post(
Expand All @@ -307,7 +311,8 @@ def create_path(self, file_full_path):
if previous_status == 403 and status_code == 404:
logger.error("Could not create folder for '{}'. Check your write permission for the folder {}.".format(path, previous_path))

def move_file(self, full_from_path, full_to_path):
def move_file(self, full_from_path, full_to_path):
self.assert_can_write()
get_move_url = self.get_move_url(
full_from_path,
full_to_path
Expand All @@ -317,23 +322,27 @@ def move_file(self, full_from_path, full_to_path):
return response.json()

def check_in_file(self, full_path):
self.assert_can_write()
logger.info("Checking in {}.".format(full_path))
file_check_in_url = self.get_file_check_in_url(full_path)
self.session.post(file_check_in_url)
return

def check_out_file(self, full_path):
self.assert_can_write()
logger.info("Checking out {}.".format(full_path))
file_check_out_url = self.get_file_check_out_url(full_path)
self.session.post(file_check_out_url)
return

def recycle_file(self, full_path):
self.assert_can_write()
recycle_file_url = self.get_recycle_file_url(full_path)
response = self.session.post(recycle_file_url)
self.assert_response_ok(response, calling_method="recycle_file")

def recycle_folder(self, full_path):
self.assert_can_write()
recycle_folder_url = self.get_recycle_folder_url(full_path)
response = self.session.post(recycle_folder_url)
self.assert_response_ok(response, calling_method="recycle_folder")
Expand Down Expand Up @@ -380,6 +389,7 @@ def get_list_items(self, list_title, params=None):
return response.json().get("ListData", {})

def create_list(self, list_name):
self.assert_can_write()
headers = DSSConstants.JSON_HEADERS
data = {
'__metadata': {
Expand All @@ -400,6 +410,7 @@ def create_list(self, list_name):
return json.get(SharePointConstants.RESULTS_CONTAINER_V2, {})

def recycle_list(self, list_name):
self.assert_can_write()
headers = DSSConstants.JSON_HEADERS
response = self.session.post(
self.get_lists_by_title_url(list_name)+"/recycle()",
Expand Down Expand Up @@ -428,6 +439,7 @@ def get_web_name(self, created_list):
return get_value_from_path(json_response, [SharePointConstants.RESULTS_CONTAINER_V2, "Name"])

def create_custom_field_via_id(self, list_id, field_title, field_type=None):
self.assert_can_write()
field_type = SharePointConstants.FALLBACK_TYPE if field_type is None else field_type
schema_xml = self.get_schema_xml(field_title, field_type)
body = {
Expand Down Expand Up @@ -458,6 +470,7 @@ def get_list_default_view(self, list_name):
return json_response.get(SharePointConstants.RESULTS_CONTAINER_V2, {"Items": {"results": []}}).get("Items", {"results": []}).get("results", [])

def add_column_to_list_default_view(self, column_name, list_name):
self.assert_can_write()
escaped_column_name = self.escape_path(column_name)
list_default_view_url = os.path.join(
self.get_list_default_view_url(list_name),
Expand Down Expand Up @@ -919,6 +932,7 @@ def escape_path(path):

def get_writer(self, dataset_schema, dataset_partitioning,
partition_id, max_workers, batch_size, write_mode):
self.assert_can_write()
return SharePointListWriter(
self.config,
self,
Expand Down Expand Up @@ -972,6 +986,12 @@ def is_column_displayable(self, column, display_metadata=False, metadata_to_retr
return True
return (not column[SharePointConstants.HIDDEN_COLUMN])

def assert_can_write(self):
auth_details = get_auth_details(self.config)
cannot_write = auth_details.get("cannot_write", False)
if cannot_write:
raise SharePointClientError("This preset is read only")


class SharePointSession():

Expand Down Expand Up @@ -1060,6 +1080,29 @@ def get_contextinfo_url():
return form_digest_value


def assert_can_overwrite_root(config):
auth_details = get_auth_details(config)
cannot_overwrite_root = auth_details.get("cannot_overwrite_root", False)
if cannot_overwrite_root:
raise SharePointClientError("Root path overwrite is not allowed on this preset")


def assert_can_overwrite_site(config):
auth_details = get_auth_details(config)
cannot_overwrite_site = auth_details.get("cannot_overwrite_site", False)
if cannot_overwrite_site:
raise SharePointClientError("Site path overwrite is not allowed on this preset")


def get_auth_details(config):
KEY_TO_AUTH_DETAILS = {"oauth": "sharepoint_oauth", "login": "sharepoint_sharepy", "site-app-permissions": "site_app_permissions", "app-certificate": "app_certificate"}
auth_type = config.get("auth_type", None)
key_to_auth_details = KEY_TO_AUTH_DETAILS.get(auth_type, None)
if not key_to_auth_details:
return {}
return config.get(key_to_auth_details, {})


class SuppressFilter(logging.Filter):
# Avoid poluting logs with redondant warnings
# https://github.com/diyan/pywinrm/issues/269
Expand Down