20
20
21
21
from keeper_secrets_manager_core .utils import bytes_to_base64 , url_safe_str_to_bytes
22
22
23
- from typing import Any , Sequence
23
+ from typing import Sequence , List
24
24
25
25
from .base import Command , dump_report_data , user_choice , as_boolean
26
26
from . import record
54
54
--purge : Remove the application and purge it from the trash
55
55
--force : Do not prompt for confirmation
56
56
57
+ { bcolors .BOLD } Grant User Access to Application (Share Application):{ bcolors .ENDC }
58
+ { bcolors .OKGREEN } secrets-manager app share { bcolors .OKBLUE } [APP NAME OR UID]{ bcolors .OKGREEN } --email { bcolors .OKBLUE } [USERNAME] { bcolors .ENDC }
59
+
60
+ { bcolors .BOLD } Revoke User Access to Application (Unshare Application):{ bcolors .ENDC }
61
+ { bcolors .OKGREEN } secrets-manager app unshare { bcolors .OKBLUE } [APP NAME OR UID]{ bcolors .OKGREEN } --email { bcolors .OKBLUE } [USERNAME]{ bcolors .ENDC }
62
+
57
63
{ bcolors .BOLD } Add Client Device:{ bcolors .ENDC }
58
64
{ bcolors .OKGREEN } secrets-manager client add --app { bcolors .OKBLUE } [APP NAME OR UID] { bcolors .OKGREEN } --unlock-ip{ bcolors .ENDC }
59
65
Options:
91
97
ksm_parser = argparse .ArgumentParser (prog = 'secrets-manager' , description = 'Keeper Secrets Management (KSM) Commands' ,
92
98
add_help = False )
93
99
ksm_parser .add_argument ('command' , type = str , action = 'store' , nargs = "*" ,
94
- help = 'One of: "app list", "app get", "app create", "app remove", "client add ", ' +
95
- '"client remove", "share add" or "share remove"' )
100
+ help = 'One of: "app list", "app get", "app create", "app remove", "app share", "app unshare ", ' +
101
+ '"client add", "client remove", "share add" or "share remove"' )
96
102
ksm_parser .add_argument ('--secret' , '-s' , type = str , action = 'append' , required = False ,
97
103
help = 'Record UID' )
98
104
ksm_parser .add_argument ('--app' , '-a' , type = str , action = 'store' , required = False ,
121
127
ksm_parser .add_argument ('-f' , '--force' , dest = 'force' , action = 'store_true' , help = 'do not prompt' )
122
128
ksm_parser .add_argument ('--config-init' , type = str , dest = 'config_init' , action = 'store' ,
123
129
help = 'Initialize client config' ) # json, b64, file
130
+ # Application sharing options
131
+ ksm_parser .add_argument ('--email' , action = 'store' , type = str , dest = 'email' , help = 'Email of user to grant / remove application access to / from' )
132
+ ksm_parser .add_argument ('--admin' , action = 'store_true' , help = 'Allow share recipient to manage application' )
133
+
124
134
125
135
126
136
def ms_to_str (ms , frmt = '%Y-%m-%d %H:%M:%S' ):
@@ -139,7 +149,7 @@ def get_parser(self):
139
149
140
150
def execute (self , params , ** kwargs ):
141
151
142
- ksm_command = kwargs .get ('command' )
152
+ ksm_command = kwargs .get ('command' ) # type: List[str]
143
153
ksm_helpflag = kwargs .get ('helpflag' )
144
154
145
155
if len (ksm_command ) == 0 or ksm_helpflag :
@@ -208,6 +218,27 @@ def execute(self, params, **kwargs):
208
218
209
219
return
210
220
221
+ if ksm_obj in ['app' , 'apps' ] and ksm_action in ['share' , 'unshare' ]:
222
+ app_name_or_uid = kwargs .get ('app' ) or ksm_command [2 ] if len (ksm_command ) == 3 else None
223
+ if not app_name_or_uid :
224
+ print (
225
+ f'''{ bcolors .WARNING } Application name is missing.{ bcolors .ENDC } \n '''
226
+ f'''\t Ex: { bcolors .OKGREEN } secrets-manager app { ksm_action } { bcolors .OKBLUE } --app=MyApp{ bcolors .OKGREEN } [email protected] { bcolors .ENDC } '''
227
+ )
228
+ return
229
+ email = kwargs .get ('email' )
230
+ unshare = 'un' in ksm_action
231
+ is_admin = kwargs .get ('admin' , False )
232
+ if not email :
233
+ print (
234
+ f'''{ bcolors .WARNING } Email is missing.{ bcolors .ENDC } \n '''
235
+ f'''\t Ex: { bcolors .OKGREEN } secrets-manager app { ksm_action } --app=MyApp { bcolors .OKBLUE } [email protected] { bcolors .ENDC } '''
236
+ )
237
+ return
238
+
239
+ KSMCommand .share_app (params , app_name_or_uid , email , is_admin = is_admin , unshare = unshare )
240
+ return
241
+
211
242
if ksm_obj in ['share' , 'secret' ] and ksm_action is None :
212
243
print (" Add Secret to the App\n \n "
213
244
+ bcolors .OKGREEN + " secrets-manager share add --app " + bcolors .OKBLUE + "[APP NAME or APP UID]"
@@ -293,6 +324,43 @@ def execute(self, params, **kwargs):
293
324
print (f"{ bcolors .WARNING } Unknown combination of KSM commands. " +
294
325
f"Type 'secrets-manager' for more details'{ bcolors .ENDC } " )
295
326
327
+ @staticmethod
328
+ def share_app (params , app_name_or_uid , email , is_admin = False , unshare = False ):
329
+ app_rec = KSMCommand .get_app_record (params , app_name_or_uid )
330
+ if app_rec is None :
331
+ logging .warning ('Application "%s" not found.' % app_name_or_uid )
332
+ return
333
+ app_uid = app_rec .get ('record_uid' , '' )
334
+ app_info = KSMCommand .get_app_info (params , app_uid )
335
+ share_action = unshare and 'remove' or 'grant'
336
+ sf_perm_keys = ('manage_users' , 'manage_records' , 'can_edit' , 'can_share' )
337
+ sf_perms = {k : is_admin and not unshare and 'on' or 'off' for k in sf_perm_keys }
338
+ rec_perms = dict (can_edit = is_admin and not unshare , can_share = is_admin and not unshare )
339
+ users = [email ]
340
+ share_folder_args = dict (** sf_perms , action = share_action , user = users )
341
+ share_rec_args = dict (** rec_perms , action = share_action , email = users )
342
+
343
+ from keepercommander .commands .register import ShareRecordCommand
344
+ from keepercommander .commands .register import ShareFolderCommand
345
+
346
+ # (Un)Share application record
347
+ share_rec_cmd = ShareRecordCommand ()
348
+ share_rec_cmd .execute (params , record = app_uid , ** share_rec_args )
349
+
350
+ get_share_uid = lambda share : utils .base64_url_encode (share .secretUid )
351
+
352
+ # (Un)Share shared-folders associated w/ application
353
+ is_sf = lambda share : APIRequest_pb2 .ApplicationShareType .Name (share .shareType ) == 'SHARE_TYPE_FOLDER'
354
+ shared_folders = [get_share_uid (s ) for ai in app_info for s in ai .shares or [] if is_sf (s )]
355
+ share_folder_cmd = ShareFolderCommand ()
356
+ share_folder_cmd .execute (params , folder = shared_folders , ** share_folder_args )
357
+
358
+ # (Un)Share records associated w/ the application
359
+ is_rec = lambda share : APIRequest_pb2 .ApplicationShareType .Name (share .shareType ) == 'SHARE_TYPE_RECORD'
360
+ shared_recs = [get_share_uid (s ) for ai in app_info for s in ai .shares or [] if is_rec (s )]
361
+ for rec in shared_recs :
362
+ share_rec_cmd .execute (params , ** share_rec_args , record = rec )
363
+
296
364
@staticmethod
297
365
def add_app_share (params , secret_uids , app_name_or_uid , is_editable ):
298
366
0 commit comments