From 720aa4db21307c67e7848454f3eceff157ba80bf Mon Sep 17 00:00:00 2001 From: lthievenaz-keeper Date: Wed, 7 Jan 2026 08:54:06 +0000 Subject: [PATCH 1/2] Rename user_create.py to user_onboarding__create_and_push.py --- examples/{user_create.py => user_onboarding__create_and_push.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/{user_create.py => user_onboarding__create_and_push.py} (100%) diff --git a/examples/user_create.py b/examples/user_onboarding__create_and_push.py similarity index 100% rename from examples/user_create.py rename to examples/user_onboarding__create_and_push.py From ffa535e20ea2dae1ca8dc86bc650aa2e398af058 Mon Sep 17 00:00:00 2001 From: lthievenaz-keeper Date: Wed, 7 Jan 2026 08:58:35 +0000 Subject: [PATCH 2/2] Create user_onboarding__create_and_login.py Added script that is similar to the create_and_push one, however in this one you can *login as* the created users in commander and run any commands from the SDK in their vault directly - yielding more capabilities than the enterprise-push command. --- examples/user_onboarding__create_and_login.py | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 examples/user_onboarding__create_and_login.py diff --git a/examples/user_onboarding__create_and_login.py b/examples/user_onboarding__create_and_login.py new file mode 100644 index 000000000..9b3802038 --- /dev/null +++ b/examples/user_onboarding__create_and_login.py @@ -0,0 +1,171 @@ +''' _ __ + | |/ /___ ___ _ __ ___ _ _ ® + | ' list,list + api.query_enterprise(params) + active_usernames = [user['username'] for user in params.enterprise['users'] if user['status']!='invited'] + invited_users = [user for user in params.enterprise['users'] if user['status']=='invited'] + + return active_usernames, invited_users + + +def generate_password(params,length=20): # (KeeperParams, int) => str + from keepercommander.generator import generate + import re + password_rules, min_iterations = login_v3_flow.get_default_password_rules(params) + while True: + password = generate(length) + + failed_rules = [] + for rule in password_rules: + pattern = re.compile(rule.pattern) + if not re.match(pattern, password): + failed_rules.append(rule.description) + if len(failed_rules) == 0: + return password + + +def get_user_vault(admin_params, user, folder=None, password_length=20, replace_invited=True): # (KeeperParams, dict, str, int, bool) => KeeperParams + ''' + user_dict_format = { + 'username': 'user@email.com' + 'node_id': 1067368092533492, # Optional, also supports name + 'full_name': 'Example Name', # Optional + 'job_title': 'Example Job Title' # Optional + } + Folder must already exist in admin vault for folder flag + ''' + + from keepercommander.commands.enterprise_create_user import CreateEnterpriseUserCommand + + if not user['username']: + print('get_user_vault function needs at least a username') + return + email = user['username'] + + # Get all users by status + active_usernames, invited_users = compile_users(admin_params) + + # Delete invited (if allowed) + for invited_user in invited_users: + if invited_user['username'] == email: + print(f'Invited user for {email} found',end='') + if not replace_invited: + print(' - Not allowed to replace, could not create user.') + return + print(' - replacing...') + eu.execute(admin_params,email=[email],delete=True,force=True) + # replace empty user fields with that of found user + for key in ['node_id','full_name','job_title']: + if user.get(key,None) is None and invited_user.get(key,None) is not None: + user[key] = invited_user[key] + + # Create user + user_record = None + if email not in active_usernames: + print(f'Creating user vault for {email}...') + record_uid = CreateEnterpriseUserCommand().execute(admin_params,email=email,node=user.get('node_id',None),name=user.get('full_name',None),folder=folder) + user_record = api.get_record(admin_params,record_uid) + eu.execute(admin_params,email=[email],jobtitle=user.get('job_title',None)) + else: + print(f'Active user found for {email}. Could not create user, but will attempt to sign in using vault records.') + record_search = api.search_records(admin_params,f'Keeper Account: {email}') + if len(record_search)!=1: + print(f'Error looking up record with title "Keeper Account: {email}". Could not sign in as user.') + return + user_record = record_search[0] + + if user_record is None: + print(f'Error looking up record with UID {record_uid}') + return + + # Sign in as user + print(f'Signing in as user {email}...') + user_params = KeeperParams() + user_params.user = email + user_params.password = user_record.password + + if email not in active_usernames: + # Reset tmp pwd + new_password = generate_password(admin_params) + login_v3_flow.login(user_params, new_password_if_reset_required=new_password) + + # Update record password + user_params.password = new_password + from keepercommander.commands.record_edit import RecordUpdateCommand + RecordUpdateCommand().execute(admin_params, record=record_uid, fields=[f'password={new_password}']) + + api.login(user_params) + api.sync_down(user_params) + print('Sign in Successful') + return user_params + + +# RUNTIME + +# Login as admin +print('Signing in as admin...') +admin_params = KeeperParams() +admin_params.user = input('Admin email: ') +api.login(admin_params) +api.sync_down(admin_params) + +# Create/get vault for User A (minimal example) +user_a_params = get_user_vault(admin_params,{'username':USER_A}) +# Create/get vault for User B (extended example) +user_b_params = get_user_vault( + admin_params, + { + 'username':USER_B, + 'full_name': 'Jane Doe', + 'job_title': 'DevOps Engineer' + }, + folder='DevOps users' +) + +# Run ad-hoc commands for User A +cli.do_command(user_a_params,'mkdir "Sample user folder" -uf') +cli.do_command(user_a_params,'record-add -rt login -t "Sample record" --folder "Sample user folder"') + +from keepercommander.importer.imp_exp import _import as run_import +# Run CSV import for User A +run_import(user_a_params, 'csv', 'csv_file.csv') + +# Run JSON import for User B +run_import(user_b_params, 'json', 'json_file.json') + +# Re-expire Master Passwords +eu.execute(admin_params, email=[USER_A,USER_B], expire=True, force=True)