"""Auth0 related functions.""" import random import re import string from datetime import datetime from auth0.v3 import Auth0Error from auth0.v3.authentication import Database, GetToken from auth0.v3.management import Auth0 from owsresponse import response from permissions import config from permissions.connectors.sentry import sentry_client from permissions.constants import error def get_auth0_management_handle(): """Get api_token to make management api calls.""" get_token = GetToken(config.AUTH0_DOMAIN) token = get_token.client_credentials( config.AUTH0_MACHINE_CLIENT_ID, config.AUTH0_MACHINE_CLIENT_SECRET, config.AUTH0_URL ) return Auth0(config.AUTH0_DOMAIN, token['access_token']) def gen_password(): """Generate a random password that will pass Auth0. Returns: A random string """ password_characters = string.ascii_letters + string.digits + string.punctuation length = 5 prefix = ''.join(random.choice(password_characters) for i in range(length)) valid = 'j5)pQsna7WX?' password_chars = list(f'{prefix}{valid}') random.shuffle(password_chars) password = ''.join(password_chars) if re.search(r'(\w)\1+', password): password = re.sub(r'(\w)\1+', r'\1', password) return password def create_user( email, name, set_email_verified, first_name=None, last_name=None, user_types=None, user_metadata={}, app_metadata={}, ): """Create a new user in auth0.""" userData = { 'password': gen_password(), 'connection': config.AUTH0_CONNECTION, 'name': name, 'email': email, 'verify_email': False, 'email_verified': set_email_verified, 'user_metadata': user_metadata, 'app_metadata': app_metadata, } if first_name: userData['user_metadata']['first_name'] = first_name if last_name: userData['user_metadata']['last_name'] = last_name if user_types: userData['user_metadata']['user_types'] = user_types auth0 = get_auth0_management_handle() try: user = auth0.users.create(userData) if not user.get('user_metadata') or not user.get('user_metadata').get('orchardIdentityId'): return response.create_error_response( error.CREATE_AUTH0_USER, 'New User is missing orchardIdentityId' ) return response.Response( { 'name': user.get('name'), 'email': user.get('email'), 'auth0_user_id': user.get('user_id').replace('auth0|', ''), 'id': user.get('user_metadata').get('orchardIdentityId'), 'first_name': first_name, 'last_name': last_name, 'user_types': user_types, 'default_brand': user.get('user_metadata').get('defaultBrand'), } ) except Auth0Error as err: sentry_client.capture_exception() return response.create_error_response(error.CREATE_AUTH0_USER, f'For {email}: {str(err)}') def bulk_send_password_reset(emails, is_artist, user_metadata): """Trigger multiple send_password_reset emails.""" db = Database(config.AUTH0_DOMAIN) result = [] if user_metadata.get('should_send_collaborator_access_email', False): client_id = config.AUTH0_MONEYHUB_APP_CLIENT_ID elif is_artist: client_id = config.AUTH0_INSIGHT_APP_CLIENT_ID else: client_id = config.AUTH0_SETTINGS_APP_CLIENT_ID for email in emails: # send password reset using settings client id so in email template we can know that # this request is from settings app and we can change the template accordingly. result.append(db.change_password(client_id, email, config.AUTH0_CONNECTION)) return response.Response(result) def update_user_metadata(new_label_profile): """Update user_metadata.""" auth0 = get_auth0_management_handle() for auth0_id, metadata in new_label_profile.items(): auth0_id = auth0_id if 'auth0|' in auth0_id else f'auth0|{auth0_id}' try: auth0.users.update(auth0_id, {'user_metadata': metadata}) except Auth0Error: sentry_client.capture_exception() def activate_deactivate_user(auth0_id, active=True): """Update user as blocked or not.""" auth0 = get_auth0_management_handle() userData = { 'connection': config.AUTH0_CONNECTION, 'blocked': not active, 'user_metadata': {'blocked_at': datetime.now().isoformat() if not active else None}, } auth0_id = auth0_id if 'auth0|' in auth0_id else f'auth0|{auth0_id}' try: user = auth0.users.update(auth0_id, userData) return response.Response(user, status=200) except Auth0Error as err: sentry_client.capture_exception() if err.status_code and err.status_code == 404: return response.Response('User not found in auth0 but can continue.') return response.create_error_response( error.CODE_FAILED_UPDATE_AUTH0_USER, f'For {auth0_id}: {str(err)}' )