"""User Creation.""" import argparse import json from lambdacommon.common_config import logger from botocore.exceptions import ClientError import pymysql import sentry_sdk import config from src.connectors.boto_clients import secrets_manager_client from src.logic import database from src.utils import password # Sentry handler sentry_sdk.init(config.SENTRY_DSN) def create_mysql_user(user, env, rds_credentials, user_password=None): """Create MySQL user. Args: user (str): username env (str): environment rds_credentials (dict): master user credentials user_password (str): user-supplied password Returns: dict: user credentials """ if not user_password: user_password = password.generate_random_password() connection = pymysql.connect( host=rds_credentials['host'], user=rds_credentials['username'], password=rds_credentials['password'], ) with connection: with connection.cursor() as cursor: try: cursor.execute( "CREATE USER %s@'%%' IDENTIFIED BY %s", ( user, user_password)) if env == 'dev' or config.MYSQL_ACCESS_TYPE == 'readwrite': # RDS does not support the FILE privilege cursor.execute( 'GRANT ALTER, ALTER ROUTINE, CREATE, ' 'CREATE ROUTINE, CREATE TEMPORARY TABLES, ' 'CREATE VIEW, DELETE, DROP, EXECUTE, INDEX, ' 'INSERT, LOCK TABLES, PROCESS, REFERENCES, ' 'SELECT, SHOW DATABASES, SHOW VIEW, TRIGGER, ' "UPDATE ON *.* TO %s@'%%'", user) else: cursor.execute( "GRANT SELECT, SHOW VIEW ON *.* TO %s@'%%'", user) user_credentials = { 'host': rds_credentials['host'], 'user': user, 'password': user_password } return user_credentials except pymysql.err.OperationalError as error: raise SystemExit(f'Access or privilege error: {error}') def main(): """Create a MySQL user.""" parser = argparse.ArgumentParser() parser.add_argument( '-u', '--user', required=True, help='Name of user to create.') parser.add_argument( '-d', '--db-name', required=True, help='Name of RDS host in the format ' 'env-service-name, e.g. qa-ows-assets') parser.add_argument( '-p', '--password', required=False, help='Provide a user-supplied password for ' 'the user to be created. Minimum 24 characters. ' 'If not specified, a password will be auto-generated') args = parser.parse_args() db_instance, db_type = database.find_database_identifier(args.db_name) if args.password: if len(args.password) >= 24: logger.info('Using user-supplied password.') credentials = database.reset_rds_master_credentials( args.db_name, db_type, wait=True) user_credentials = create_mysql_user( args.user, args.db_name.split('-')[0], credentials, user_password=args.password) else: raise SystemExit('Password must be 24 characters.') else: credentials = database.reset_rds_master_credentials( args.db_name, db_type, wait=True) user_credentials = create_mysql_user( args.user, args.db_name.split('-')[0], credentials) logger.info(f' Credentials are {user_credentials}') # try: # # example secret id: users/myusername/qa/ows-video/rds_credentials # prefix = config.USER_CREDENTIALS_SECRET_PREFIX # user = args.user # environment = args.db_name.split('-')[0] # service = '-'.join(args.db_name.split('-')[1:]) # secret_id = f'{prefix}/{user}/{environment}/{service}/rds_credentials' # try: # response = secrets_manager_client.create_secret( # Name=secret_id, # Description=secret_id, # SecretString=json.dumps(user_credentials) # ) # except ClientError as error: # # Check if the secret already exists. If so, update it # if error.response['Error']['Code'] == 'ResourceExistsException': # logger.info('Secret already exists. Creating a new version...') # response = secrets_manager_client.update_secret( # SecretId=secret_id, # SecretString=json.dumps(user_credentials) # ) # logger.info( # f"Secret created: ARN: {response['ARN']}; " # f"Name: {response['Name']};") # except ClientError as error: # raise SystemError(f'Error creating secret: {error}') if __name__ == '__main__': main()