IAMInitOktaUser
Generates a password and sets the password for an Okta user. Enables the account. Sends an email to the user with the account information. This script is running the `send-mail` command, make sure there is a mail sender integration configured.
python · Okta
Source
import uuid import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 from pyzipper import WZ_AES, ZIP_DEFLATED, AESZipFile DEFAULT_PWD_GENERATION_SCRIPT = "GeneratePassword" TEXT_FILE_NAME = "Okta_Password" # File name for the text file (within the zip file) to use EMAIL_ZIP_NAME = "Okta_Password" # File name to use for the zip file when attaching it to the email def find_file_entry_id(file_name: str) -> str: """ Find the entry ID of a file in the context by its name. Args: file_name (str): The name of the file to find. Returns: str: The entry ID of the file. """ file_entries = demisto.context().get("File", []) if isinstance(file_entries, dict): # In case of a single entry file_entries = [file_entries] for item in file_entries: if item["Name"] == file_name: return item["EntryID"] raise DemistoException(f"Could not find file '{file_name}' in the context.") def generate_password( password_generation_script: str, min_lcase: str, max_lcase: str, min_ucase: str, max_ucase: str, min_digits: str, max_digits: str, min_symbols: str, max_symbols: str, ) -> str: """ Generate a random password using a script. Args: password_generation_script (str): The name of the script to use for generating the password. min_lcase (str): The minimum number of lowercase characters to use in the password. max_lcase (str): The maximum number of lowercase characters to use in the password. min_ucase (str): The minimum number of uppercase characters to use in the password. max_ucase (str): The maximum number of uppercase characters to use in the password. min_digits (str): The minimum number of digits to use in the password. max_digits (str): The maximum number of digits to use in the password. min_symbols (str): The minimum number of symbols to use in the password. max_symbols (str): The maximum number of symbols to use in the password. Returns: str: The generated password. """ # Generate a random password if password_generation_script == DEFAULT_PWD_GENERATION_SCRIPT: script_params = { "min_lcase": min_lcase, "max_lcase": max_lcase, "min_ucase": min_ucase, "max_ucase": max_ucase, "min_digits": min_digits, "max_digits": max_digits, "min_symbols": min_symbols, "max_symbols": max_symbols, } pwd_generation_script_output = demisto.executeCommand(password_generation_script, script_params) else: pwd_generation_script_output = demisto.executeCommand(password_generation_script, {}) if is_error(pwd_generation_script_output): raise Exception( f"An error occurred while trying to generate a new password for the user. " f"Error is:\n{get_error(pwd_generation_script_output)}" ) else: password_output = pwd_generation_script_output[0]["Contents"] if isinstance(password_output, dict): return password_output["NEW_PASSWORD"] elif isinstance(password_output, str): return password_output else: raise Exception( f"Could not parse the generated password from {password_generation_script} outputs. " f"Please make sure the output of the script is a string, or a dictionary containing " f"a key named NEW_PASSWORD." ) def okta_update_user(username: str, password: str, temporary_password: str, password_generation_script: str) -> None: """ Update user's password in Okta, and enable the user account in case it was disabled. Args: username (str): The username of the user to update. password (str): The new password. temporary_password (str): Whether the password is temporary or not ("true" or "false"). password_generation_script (str): The name of the script that generates the password (used for error handling). Raises: DemistoException: In case of a known potential error. """ # Update user password set_password_outputs = demisto.executeCommand( "okta-set-password", { "username": username, "password": password, "temporary_password": temporary_password, }, ) if is_error(set_password_outputs): error_message = get_error(set_password_outputs) if "400" in error_message: raise DemistoException( f"An error occurred while trying to set a new password for the user. " f"Please make sure that the '{password_generation_script}' script " f"complies with your domain's password complexity policy." ) raise DemistoException(f"An error occurred while trying to set a new password for the user:\n{error_message}") # Enable user (in case it was disabled) enable_outputs = demisto.executeCommand("okta-activate-user", {"username": username}) if is_error(enable_outputs): error_message = get_error(enable_outputs) if "the user is already active" not in error_message: raise DemistoException(f"An error occurred while trying to enable the user account. Error:\n{error_message}") def send_email( username: str, email_recipient: str, email_subject: str, email_body: str, display_name: str | None, error_message: str | None = None, password: str | None = None, zip_file_entry_id: str | None = None, ) -> list | dict: """ Email the user with the password (plain text or encrypted). One of 'error_message', 'password' or 'zip_file_entry_id' must be provided. Args: username (str): The username of the user. email_recipient (str): The email address of the recipient. email_subject (str): The subject of the email. email_body (str): The body of the email. display_name (str | None, optional): The display name of the user. error_message (str | None, optional): An error message to include in the email. password (str | None, optional): The password to send in the email (plain text). zip_file_entry_id (str | None, optional): The entry ID of the zip file to send in the email (encrypted). Returns: list | dict: The outputs of the send-mail command. """ if not any((error_message, password, zip_file_entry_id)): raise ValueError("Either 'password' or 'zip_file_entry_id' must be provided.") if error_message: email_subject = f"'User Activation In Okta' failed for user '{display_name}'" email_body = ( "Hello,\n\n" "This message was sent to inform you that an error occurred while trying " f"'to activate the user account of '{username}' in Okta.\n\n'" f"'The error is: '{error_message}'\n\nRegards,\nIAM Team'" ) else: if not email_subject: email_subject = f"User '{display_name or username}' was successfully activated in Okta" email_password = "Available in the attached zip file" if zip_file_entry_id else password if not email_body: email_body = "Hello,\n\nThe following account has been activated in Okta:\n\n" if display_name: email_body += f"Name: {display_name}\n" email_body += f"Username: {username}\nPassword: {email_password}\n\nRegards,\nIAM Team" else: email_body += f"\nUsername: {username}\nPassword: {email_password}" send_email_args = {"to": email_recipient, "subject": email_subject, "body": email_body} if zip_file_entry_id: send_email_args |= {"attachIDs": zip_file_entry_id, "attachNames": f"{EMAIL_ZIP_NAME}.zip"} return demisto.executeCommand("send-mail", send_email_args) @polling_function( name="IAMInitOktaUser", interval=10, requires_polling_arg=False, ) def create_zip_with_password(args: dict, generated_password: str, zip_password: str) -> PollResult: """ Create a zip file with a password. The function returns a zip file to the war room, and calls this script recursively using polling. Args: args (dict): The arguments passed to the script. generated_password (str): The password to encrypt. zip_password (str): The password to use for encrypting the zip file. Returns: PollResult: The polling result. """ text_file_name = f"{TEXT_FILE_NAME}.txt" zip_file_name = f"{EMAIL_ZIP_NAME}_{uuid.uuid4()}.zip" try: with open(text_file_name, "w") as text_file: text_file.write(generated_password) demisto.debug(f"zipping {text_file_name=}") with AESZipFile(zip_file_name, mode="w", compression=ZIP_DEFLATED, encryption=WZ_AES) as zf: zf.pwd = bytes(zip_password, "utf-8") zf.write(text_file_name) with open(zip_file_name, "rb") as zip_file: zip_content = zip_file.read() except Exception as e: raise DemistoException(f"Could not generate zip file. Error:\n{e!s}") finally: for file_name in (text_file_name, zip_file_name): if os.path.exists(file_name): os.remove(file_name) return_results(fileResult(zip_file_name, zip_content)) return PollResult( response=None, continue_to_poll=True, partial_result=CommandResults(readable_output=f"Encrypted zip file generated. File name: '{zip_file_name}'."), args_for_next_run={**args, "zip_file_name": zip_file_name}, ) def main(): args = demisto.args() password_generation_script = args.get("pwdGenerationScript", DEFAULT_PWD_GENERATION_SCRIPT) username = args.get("username") display_name = args.get("displayname") email_recipient = args.get("to_email") email_subject = args.get("email_subject") email_body = args.get("email_body") zip_password = args.get("ZipProtectWithPassword") temporary_password = args.get("temporary_password", "false") zip_file_name = args.get("zip_file_name") generated_password: str | None = None file_entry_id: str | None = None context_outputs: dict[str, str] = {"success": "true"} error_message: str | None = None if not zip_password: return_warning( "It is highly recommended to run the script using the 'ZipProtectWithPassword' argument," "as sending a plain text password in an email is an insecure practice." ) # If zip file is already generated and this is the second iteration, we skip this section if not (zip_password and zip_file_name): try: generated_password = generate_password( password_generation_script=password_generation_script, min_lcase=args.get("min_lcase", "0"), max_lcase=args.get("max_lcase", "10"), min_ucase=args.get("min_ucase", "0"), max_ucase=args.get("max_ucase", "10"), min_digits=args.get("min_digits", "0"), max_digits=args.get("max_digits", "10"), min_symbols=args.get("min_symbols", "0"), max_symbols=args.get("max_symbols", "10"), ) okta_update_user( username=username, password=generated_password, temporary_password=temporary_password, password_generation_script=password_generation_script, ) if zip_password: # Rerun the script using polling (with a 'zip_file_name' value) return_results( create_zip_with_password(args=args, generated_password=generated_password, zip_password=zip_password) ) return except Exception as e: context_outputs["success"] = "false" error_message = str(e) context_outputs["errorDetails"] = error_message demisto.error(traceback.format_exc()) else: file_entry_id = find_file_entry_id(zip_file_name) try: send_mail_outputs = send_email( display_name=display_name, username=username, error_message=error_message, email_recipient=email_recipient, email_subject=email_subject, email_body=email_body, password=generated_password, zip_file_entry_id=file_entry_id, ) if is_error(send_mail_outputs): raise DemistoException(f"An error occurred while trying to send mail:\n{get_error(send_mail_outputs)}") context_outputs["sentMail"] = "true" except Exception as e: demisto.error(traceback.format_exc()) context_outputs["sentMail"] = "false" context_outputs["sendMailError"] = str(e) if context_outputs["success"] and context_outputs["sentMail"]: readable_output = f"Successfully activated user {username}. An email with the user details was sent to {email_recipient}." else: readable_output = "" if error_message: readable_output += f"{error_message}\n" if context_outputs.get("sendMailError"): readable_output += context_outputs["sendMailError"] return_results(CommandResults(outputs_prefix="IAM.InitOktaUser", outputs=context_outputs, readable_output=readable_output)) if __name__ in ("__main__", "builtin", "builtins"): main()
README
Generates a password and sets the password for an Okta user.
Enables the account.
Sends an email to the user with the account information.
This script is running the send-mail command, make sure there is a mail sender integration configured.
Script Data
| Name | Description |
|---|---|
| Script Type | python3 |
| Tags | IAM, Utility, Okta |
| Cortex XSOAR Version | 6.5.0 |
Inputs
| Argument Name | Description |
|---|---|
| pwdGenerationScript | The password generator script. |
| username | The Okta username of the user. |
| displayname | The display name of the employee. |
| temporary_password | When true, you’ll need to change the password in the next login. |
| to_email | The email address that the password will be sent to. |
| inc_id | The incident ID. |
| email_subject | The subject of the email sent to IT. |
| email_body | The body of the email sent to IT. |
| ZipProtectWithPassword | The password to protect the zip file that contains the generated password. if not provided, the generated password will be send in the body of the email. |
| min_lcase | Minimum number of lower case characters to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| max_lcase | Maximum number of lower case characters to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| min_ucase | Minimum number of upper case characters to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| max_ucase | Maximum number of upper case characters to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| min_digits | Minimum number of digits to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| max_digits | Maximum number of digits to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| min_symbols | Minimum number of symbols to include in password. Used with the GeneratePassword script (leave empty if using another script). |
| max_symbols | Maximum number of symbols to include in password. Used with the GeneratePassword script (leave empty if using another script). |
Outputs
| Path | Description | Type |
|---|---|---|
| IAM.InitOktaUser.success | True if the Okta user was successfully activated, false otherwise. | Boolean |
| IAM.InitOktaUser.sentMail | True if the mail containing the information about the user activation and its auto-generated password was successfully sent to IT, false otherwise. | Boolean |
| IAM.InitOktaUser.errorDetails | The error details, if exists. | String |
| IAM.InitOktaUser.sendMailError | The error received from send-mail command, if exists. | String |