Skip to content

Commit 43464c7

Browse files
Merge pull request #16 from SendSafely/v1.0.7
V1.0.7.2
2 parents a229b98 + baa09ff commit 43464c7

File tree

5 files changed

+265
-9
lines changed

5 files changed

+265
-9
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import json
2+
3+
from sendsafely import SendSafely, Package
4+
5+
# Edit these variables
6+
api_key = ""
7+
api_secret = ""
8+
base_url = "https://companyabc.sendsafely.com"
9+
# Make sure all directories exist on your file system
10+
upload_file = "fileToUpload.txt"
11+
download_filename = "fileDownloaded.txt"
12+
download_dir = "."
13+
recipient = "user@foobar.com"
14+
directory_id = ""
15+
16+
def main():
17+
sendsafely = SendSafely(base_url, api_key, api_secret)
18+
workspace_package = Package(sendsafely, workspace=True)
19+
print("Successfully created new workspace " + workspace_package.package_id)
20+
21+
workspace_package.update_workspace_name("MyWorkspace")
22+
print("Successfully updated Workspace name")
23+
24+
secure_link = base_url + "/receive/?thread=" + workspace_package.package_id + "&packageCode=" + workspace_package.package_code + "#keyCode=" + workspace_package.client_secret
25+
print("Secure link is " + secure_link)
26+
27+
directory_id = workspace_package.create_directory("MyDirectory")["directoryId"]
28+
print("Successfully created new directory " + directory_id)
29+
30+
with open(upload_file, 'wb') as file:
31+
content = bytes("SendSafely lets you easily exchange encrypted files and information with anyone on any device.", "utf-8")
32+
file.write(content)
33+
34+
f = workspace_package.encrypt_and_upload_file(upload_file, directory_id=directory_id)
35+
file_id = f["fileId"]
36+
print("Successfully encrypted and uploaded file id " + str(file_id) + " to " + directory_id)
37+
38+
workspace_package.add_recipient(recipient)
39+
print("Successfully added recipient " + recipient)
40+
41+
new_directory_id = workspace_package.create_directory("MyNewDirectory")["directoryId"]
42+
print("Successfully created new directory " + new_directory_id)
43+
44+
workspace_package.move_file(file_id, new_directory_id)
45+
print("Moved file " + file_id + " to " + new_directory_id + "")
46+
47+
workspace_package.move_directory(directory_id, new_directory_id)
48+
print("Moved directory " + directory_id + " to " + new_directory_id + "")
49+
50+
workspace_package.download_and_decrypt_file(file_id, directory_id=new_directory_id, download_directory=download_dir, file_name=download_filename)
51+
print("Successfully downloaded and decrypted file " + download_filename)
52+
53+
print("Package Info: ")
54+
print(json.dumps(workspace_package.get_info(), indent=4, sort_keys=True))
55+
56+
if __name__ == '__main__':
57+
main()

sendsafely/Package.py

Lines changed: 123 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import json
22
import math
33
import os
4+
import re
5+
46
import requests
57
import cryptography
68
major, minor, patch = [int(x, 10) for x in cryptography.__version__.split('.')]
@@ -16,7 +18,8 @@
1618
UploadFileException, DeletePackageException, KeycodeRequiredException, GetPackageInformationFailedException, \
1719
UploadKeycodeException, AddRecipientFailedException, UpdateRecipientFailedException, UploadMessageException, \
1820
GetPublicKeysFailedException, GetFileInformationException, DeleteFileException, GetPackageMessageException, \
19-
AddFileFailedException
21+
AddFileFailedException, MoveFileException, GetDirectoryException, DeleteDirectoryException, \
22+
RenameDirectoryException, UpdatePackageException, MoveDirectoryException, CreateDirectoryException
2023

2124
from sendsafely.utilities import _generate_keycode, make_headers, _encrypt_message, _encrypt_file_part, _upload_file_part_to_s3, \
2225
_calculate_package_checksum, _decrypt_message, _get_upload_urls, _get_download_urls, _update_file_completion_status, _encrypt_keycode, \
@@ -28,7 +31,7 @@ class Package:
2831
To be used in conjunction with it's handler the SendSafely object. Should not be instantiated directly.
2932
"""
3033

31-
def __init__(self, sendsafely_instance, package_variables=None):
34+
def __init__(self, sendsafely_instance, package_variables=None, workspace=False):
3235
"""
3336
:param sendsafely_instance: The authenticated SendSafely object.
3437
:param package_variables:
@@ -39,7 +42,10 @@ def __init__(self, sendsafely_instance, package_variables=None):
3942
if package_variables is None:
4043
self.client_secret = _generate_keycode()
4144
self.sendsafely = sendsafely_instance
42-
data = {"vdr": "false"}
45+
if workspace:
46+
data = {"vdr": "true"}
47+
else:
48+
data = {"vdr": "false"}
4349
endpoint = "/package"
4450
url = self.sendsafely.BASE_URL + endpoint
4551
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint,
@@ -181,7 +187,7 @@ def encrypt_and_upload_file(self, filepath, directory_id=None, progress_instance
181187
progress = progress + 1
182188
part = part + 25
183189
file.close()
184-
response = _update_file_completion_status(self, file_id=file_id, complete=True)
190+
response = _update_file_completion_status(self, file_id=file_id, directory_id=directory_id, complete=True)
185191
response["fileId"] = file_id
186192
return response
187193
except Exception as e:
@@ -276,17 +282,20 @@ def _add_file(self, filename, filesize, parts=1, directory_id=None):
276282
raise AddFileFailedException(details=response["message"])
277283
return response
278284

279-
def delete_file_from_package(self, file_id):
285+
def delete_file_from_package(self, file_id, directory_id=None):
280286
"""
281287
Deletes the file with the specified id from the package with the specified ID
282288
"""
283-
endpoint = "/package/" + self.package_id + "/file/" + file_id
289+
if directory_id:
290+
endpoint = "/package/" + self.package_id + "/directory/" + directory_id + "/file/" + file_id
291+
else:
292+
endpoint = "/package/" + self.package_id + "/file/" + file_id
284293
url = self.sendsafely.BASE_URL + endpoint
285294
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint)
286295
try:
287296
response = requests.delete(url=url, headers=headers).json()
288297
except Exception as e:
289-
raise DeleteFileException(details=e)
298+
raise DeleteFileException(details=str(e))
290299
if response["response"] != "SUCCESS":
291300
raise DeleteFileException(details=response["message"])
292301
return response
@@ -315,6 +324,7 @@ def download_and_decrypt_file(self, file_id, directory_id=None, download_directo
315324
if not file_name:
316325
file_name = file_info["fileName"]
317326
total = file_info["fileParts"]
327+
file_name = re.sub(r'[<>:\"/\\|?*]', '_', file_name)
318328
file_path = download_directory + "/" + file_name
319329
passphrase = self.server_secret + self.client_secret
320330
progress = 1
@@ -338,6 +348,111 @@ def download_and_decrypt_file(self, file_id, directory_id=None, download_directo
338348
except Exception as e:
339349
raise DownloadFileException(details=str(e))
340350

351+
def update_workspace_name(self, workspace_name):
352+
"""
353+
Rename a Workspace (packageLabel)
354+
"""
355+
endpoint = "/package/" + self.package_id
356+
url = self.sendsafely.BASE_URL + endpoint
357+
body = {"label": workspace_name}
358+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint, request_body=json.dumps(body))
359+
try:
360+
response = requests.post(url=url, headers=headers, json=body).json()
361+
except Exception as e:
362+
raise UpdatePackageException(details=str(e))
363+
364+
if response["response"] != "SUCCESS":
365+
raise UpdatePackageException(details=response["message"])
366+
return response
367+
368+
def move_file(self, file_id, destination_directory_id):
369+
"""
370+
Moves a Workspace file with the specified id to the directory with the specified ID
371+
"""
372+
endpoint = "/package/" + self.package_id + "/directory/" + destination_directory_id + "/file/" + file_id
373+
url = self.sendsafely.BASE_URL + endpoint
374+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint)
375+
try:
376+
response = requests.post(url=url, headers=headers).json()
377+
except Exception as e:
378+
raise MoveFileException(details=str(e))
379+
380+
if response["response"] != "SUCCESS":
381+
raise MoveFileException(details=response["message"])
382+
return response
383+
384+
def create_directory(self, directory_name, source_directory_id=None):
385+
if not source_directory_id:
386+
source_directory_id = self.sendsafely.get_package_information(self.package_id)["rootDirectoryId"]
387+
endpoint = "/package/" + self.package_id + "/directory/" + source_directory_id + "/subdirectory/"
388+
url = self.sendsafely.BASE_URL + endpoint
389+
body = {"directoryName": directory_name}
390+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint, request_body=json.dumps(body))
391+
try:
392+
response = requests.put(url=url, headers=headers, json=body).json()
393+
except Exception as e:
394+
raise CreateDirectoryException(details=str(e))
395+
396+
if response["response"] != "SUCCESS":
397+
raise CreateDirectoryException(details=response["message"])
398+
return response
399+
400+
def move_directory(self, source_directory_id, target_directory_id):
401+
"""
402+
Moves a Workspace directory with the specified id to the directory with the specified ID
403+
"""
404+
endpoint = "/package/" + self.package_id + "/move/" + source_directory_id + "/" + target_directory_id
405+
url = self.sendsafely.BASE_URL + endpoint
406+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint)
407+
try:
408+
response = requests.post(url=url, headers=headers).json()
409+
except Exception as e:
410+
raise MoveDirectoryException(details=str(e))
411+
412+
if response["response"] != "SUCCESS":
413+
raise MoveDirectoryException(details=response["message"])
414+
return response
415+
416+
def get_directory_information(self, directory_id):
417+
endpoint = "/package/" + self.package_id + "/directory/" + directory_id
418+
url = self.sendsafely.BASE_URL + endpoint
419+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint)
420+
try:
421+
response = requests.get(url=url, headers=headers).json()
422+
except Exception as e:
423+
raise GetDirectoryException(details=str(e))
424+
425+
if response["response"] != "SUCCESS":
426+
raise GetDirectoryException(details=response["message"])
427+
return response
428+
429+
def delete_directory(self, directory_id):
430+
endpoint = "/package/" + self.package_id + "/directory/" + directory_id
431+
url = self.sendsafely.BASE_URL + endpoint
432+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint)
433+
try:
434+
response = requests.delete(url=url, headers=headers).json()
435+
except Exception as e:
436+
raise DeleteDirectoryException(details=str(e))
437+
438+
if response["response"] != "SUCCESS":
439+
raise DeleteDirectoryException(details=response["message"])
440+
return response
441+
442+
def rename_directory(self, directory_id, directory_name):
443+
endpoint = "/package/" + self.package_id + "/directory/" + directory_id
444+
url = self.sendsafely.BASE_URL + endpoint
445+
body = {"directoryName": directory_name}
446+
headers = make_headers(self.sendsafely.API_SECRET, self.sendsafely.API_KEY, endpoint, request_body=json.dumps(body))
447+
try:
448+
response = requests.post(url, headers=headers, json=body).json()
449+
except Exception as e:
450+
raise RenameDirectoryException(details=str(e))
451+
452+
if response["response"] != "SUCCESS":
453+
raise RenameDirectoryException(details=response["message"])
454+
return response
455+
341456
def _block_operation_without_keycode(self):
342457
if not self.initialized_via_keycode:
343458
raise KeycodeRequiredException()
@@ -350,3 +465,4 @@ def calculate_progress(self, file_id, current, total, progress_instance):
350465
progress_instance.update_progress(file_id, percent)
351466

352467

468+

sendsafely/exceptions.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ def __init__(self, details=None):
2121
super().__init__(display)
2222

2323

24+
class UpdatePackageException(Exception):
25+
"""Exception raised during package update.
26+
"""
27+
def __init__(self, details=None):
28+
self.message = "An error occurred during package update."
29+
self.details = details
30+
display = self.details
31+
if display is None:
32+
display = self.message
33+
super().__init__(display)
34+
35+
2436
class DeletePackageException(Exception):
2537
"""Exception raised during package deletion.
2638
"""
@@ -45,6 +57,30 @@ def __init__(self, details=None):
4557
super().__init__(display)
4658

4759

60+
class DeleteDirectoryException(Exception):
61+
"""Exception raised during Workspace directory deletion.
62+
"""
63+
def __init__(self, details=None):
64+
self.message = "An error occurred during Workspace directory deletion."
65+
self.details = details
66+
display = self.details
67+
if display is None:
68+
display = self.message
69+
super().__init__(display)
70+
71+
72+
class RenameDirectoryException(Exception):
73+
"""Exception raised while trying to rename a Workspace directory.
74+
"""
75+
def __init__(self, details=None):
76+
self.message = "An error occurred while trying to rename a Workspace directory."
77+
self.details = details
78+
display = self.details
79+
if display is None:
80+
display = self.message
81+
super().__init__(display)
82+
83+
4884
class FinalizePackageFailedException(Exception):
4985
"""Exception raised while finalizing package.
5086
"""
@@ -260,3 +296,48 @@ def __init__(self, details=None):
260296
display = self.message
261297
super().__init__(display)
262298

299+
300+
class MoveFileException(Exception):
301+
"""Exception that occurs while attempting to move a Workspace file
302+
"""
303+
def __init__(self, details=None):
304+
self.message = "An error occurred while attempting to move a Workspace file."
305+
self.details = details
306+
display = self.details
307+
if display is None:
308+
display = self.message
309+
super().__init__(display)
310+
311+
312+
class MoveDirectoryException(Exception):
313+
"""Exception that occurs while attempting to move a Workspace directory
314+
"""
315+
def __init__(self, details=None):
316+
self.message = "An error occurred while attempting to move a Workspace directory."
317+
self.details = details
318+
display = self.details
319+
if display is None:
320+
display = self.message
321+
super().__init__(display)
322+
323+
class CreateDirectoryException(Exception):
324+
"""Exception that occurs while creating a Workspace subdirectory
325+
"""
326+
def __init__(self, details=None):
327+
self.message = "An error occurred while attempting to create a Workspace directory."
328+
self.details = details
329+
display = self.details
330+
if display is None:
331+
display = self.message
332+
super().__init__(display)
333+
334+
class GetDirectoryException(Exception):
335+
"""Exception that occurs while attempting to get Directory information
336+
"""
337+
def __init__(self, details=None):
338+
self.message = "An error occurred while getting Directory information."
339+
self.details = details
340+
display = self.details
341+
if display is None:
342+
display = self.message
343+
super().__init__(display)

sendsafely/utilities.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ def _get_upload_urls(package, file_id, part=1):
230230
return upload_urls
231231

232232

233-
def _update_file_completion_status(package, file_id, complete=False):
233+
def _update_file_completion_status(package, file_id, directory_id=None, complete=False):
234234
"""
235235
Sets the file upload status as complete, the server will verify if all segments have been uploaded
236236
:param file_id: The ID (string) of the file we're updating (must be associated with the package_id from previously)
@@ -240,6 +240,8 @@ def _update_file_completion_status(package, file_id, complete=False):
240240
endpoint = '/package/' + package.package_id + '/file/' + file_id + '/upload-complete'
241241
url = package.sendsafely.BASE_URL + endpoint
242242
body = {'complete': complete}
243+
if directory_id is not None:
244+
body['directoryId'] = directory_id
243245
headers = make_headers(package.sendsafely.API_SECRET, package.sendsafely.API_KEY, endpoint, request_body=json.dumps(body))
244246
return requests.post(url=url, json=body, headers=headers).json()
245247

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name='sendsafely',
5-
version='1.0.6',
5+
version='1.0.7.2',
66
packages=['sendsafely'],
77
description='The SendSafely Client API allows programmatic access to SendSafely and provides a layer of abstraction from our REST API, which requires developers to perform several complex tasks in a correct manner.',
88
long_description_content_type="text/markdown",

0 commit comments

Comments
 (0)