From 529f678ebc0581a684b942e9bdbf9fd8c66e8ddb Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Mon, 7 Aug 2023 23:25:49 +0200 Subject: [PATCH 1/9] add uppdated 1pass .rdfx dynamic folder script with 1pass biometric support, solves #60, improves upon PR #69 --- .../1Password/1Password v8 (Python).rdfx | 554 ++++++++++++++++++ 1 file changed, 554 insertions(+) create mode 100644 Dynamic Folder/1Password/1Password v8 (Python).rdfx diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx new file mode 100644 index 0000000..34aa16e --- /dev/null +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -0,0 +1,554 @@ + + Dynamic Folder Export + + + DynamicFolder + 1Password v8 (Python) + This Dynamic Folder sample allows you to import dynamic credentials from 1Password v8+ + + + +

1Password v8 Dynamic Folder sample

+ +

This Dynamic Folder sample allows you to import credentials from 1Password. Requires 1Password version 8 or above. The 1Password CLI tool is required and the path where it is installed must be configured in the "Custom Properties" section.

+ +

Items are imported as Dynamic Credentials. This means that the username and password fields will remain empty after reloading the dynamic folder and only be requested when a connection is established that uses one of the credentials of this dynamic folder.

+ +

By default, the last signed in account in 1Password is used (this is defined by the 1Password CLI tool). If you require fetching from a specific account, you can specify this in the "Account" custom property.

+ +

By default, items of all vaults are imported. If you only want to retrieve items of a specific vault (or a list of specific vaults, comma-separated), you can configure the "Vaults" custom property.

+ +

Requirements

+ +
    +
  • 1Password CLI tool (Version 2+)
  • +
  • Python 3 (Python 2 is not supported)
  • +
  • Python Module: sys
  • +
  • Python Module: json
  • +
  • Python Module: subprocess
  • +
+ +

 

+ +

Setup

+ +
    +
  • Specify the full, absolute path to the 1Password command-line tool in the "OP Path" variable in the "Custom Properties" section.
  • +
  • Optionally specify the 1Password account ID (found via op account list) in the "Account" variable in the "Custom Properties" section.
  • +
  • Optionally specify the vault ID or ID's (via op vault list) you want to filter on in the "Vaults" variable in the "Custom Properties" section.
  • +
+]]>
+ + + 1Password CLI path + Header + + + + OP Path (Windows) + Text + C:\Program Files\1Password CLI\op.exe + + + OP Path (macOS) + Text + /usr/local/bin/op + + + Filters + Header + + + + Account + Text + + + + Vaults + Text + + + + python + + python + +
+
+
\ No newline at end of file From ba6559ecbaec425d490c9500c8304cff7bd583d7 Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Mon, 7 Aug 2023 23:49:47 +0200 Subject: [PATCH 2/9] remove old, unsecure 1pass dynamic folder script --- .../1Password/1Password (Python).rdfe | 77 ------------------- 1 file changed, 77 deletions(-) delete mode 100644 Dynamic Folder/1Password/1Password (Python).rdfe diff --git a/Dynamic Folder/1Password/1Password (Python).rdfe b/Dynamic Folder/1Password/1Password (Python).rdfe deleted file mode 100644 index d5da20b..0000000 --- a/Dynamic Folder/1Password/1Password (Python).rdfe +++ /dev/null @@ -1,77 +0,0 @@ -{ - "Name": "Dynamic Folder Export", - "Objects": [ - { - "Type": "DynamicFolder", - "Name": "1Password (Python)", - "Description": "This Dynamic Folder sample allows you to import dynamic credentials from 1Password.", - "Notes": "\n\n\n

1Password Dynamic Folder sample

\n\n

Version: 2.0.5
\nAuthor: Royal Apps

\n\n

This Dynamic Folder sample allows you to import credentials from 1Password. The 1Password command-line tool is required and the path where it is installed must be configured in the "Custom Properties" section. Also, your 1Password login details must be provided in the "Login" section of the custom properties.

\n\n

Items are imported as Dynamic Credentials. This means that the username and password fields will remain empty after reloading the dynamic folder and only be requested when a connection is established that uses one of the credentials of this dynamic folder. While you can instruct the script to import regular credentials (by setting the "Dynamic Credentials" setting in the "Custom Properties" section to "No", it's not(!) recommended to do so as it has many downsides. For instance, it will take much longer to retrieve all items because a web request to 1Password's web service will need to be made for each individual item. 1Password also enforces API rate limits, so if you have a lot of items, you might not be able to retrieve all of them before hitting the rate limit.

\n\n

By default, items of all vaults are imported. If you only want to retrieve items of a specific vault, you can configure the vault's name in the "Filter" section of the "Custom Properties".

\n\n

Requirements

\n\n\n\n

 

\n\n

Setup

\n\n\n", - "CustomProperties": [ - { - "Name": "Command Line Tool Configuration", - "Type": "Header", - "Value": "" - }, - { - "Name": "OP Path (Windows)", - "Type": "Text", - "Value": "C:\\Program Files\\op.exe" - }, - { - "Name": "OP Path (macOS)", - "Type": "Text", - "Value": "/usr/local/bin/op" - }, - { - "Name": "Login", - "Type": "Header", - "Value": "" - }, - { - "Name": "Sign In Address", - "Type": "URL", - "Value": "my.1password.com" - }, - { - "Name": "Email Address", - "Type": "Email", - "Value": "TODO" - }, - { - "Name": "Secret Key", - "Type": "Protected", - "Value": "TODO" - }, - { - "Name": "Master Password", - "Type": "Protected", - "Value": "TODO" - }, - { - "Name": "Filter", - "Type": "Header", - "Value": "" - }, - { - "Name": "Vault", - "Type": "Text", - "Value": "" - }, - { - "Name": "Configuration", - "Type": "Header", - "Value": "" - }, - { - "Name": "Dynamic Credentials", - "Type": "YesNo", - "Value": "True" - } - ], - "Script": "from __future__ import print_function\r\nfrom functools import partial\r\nfrom sys import platform as _platform\r\nfrom subprocess import Popen, PIPE\r\n\r\nimport tempfile\r\nimport sys\r\nimport json\r\nimport subprocess\r\nimport os\r\nimport base64\r\n\r\nop_path_windows = r\"$CustomProperty.OPPathWindows$\"\r\nop_path_macOS = r\"$CustomProperty.OPPathmacOS$\"\r\nsign_in_address = r\"$CustomProperty.SignInAddress$\"\r\nemail_address = r\"$CustomProperty.EmailAddress$\"\r\nsecret_key = r\"$CustomProperty.SecretKey$\"\r\nmaster_password = r\"$CustomProperty.MasterPassword$\"\r\nfilter_vault = r\"$CustomProperty.Vault$\"\r\ncreate_dynamic_credentials = r\"$CustomProperty.DynamicCredentials$\".lower() != \"no\"\r\n# item_id = r\"$DynamicCredential.EffectiveID$\"\r\n\r\nclass RoyalUtils:\r\n\t@staticmethod\r\n\tdef is_macOS():\r\n\t\tplat = _platform.lower()\r\n\r\n\t\treturn plat.startswith(\"darwin\")\r\n\r\n\t@staticmethod\r\n\tdef get_last_line(the_string: str):\r\n\t\tstripped_str = the_string.strip()\r\n\r\n\t\tif \"\\n\" in stripped_str:\r\n\t\t\tlines = stripped_str.splitlines()\r\n\t\t\tstripped_str = lines[len(lines) - 1]\r\n\r\n\t\treturn stripped_str\r\n\r\n\t@staticmethod\r\n\tdef random_uuid():\r\n\t\tuuid = base64.b32encode(os.urandom(16)).decode().lower().rstrip(\"=\")\r\n\r\n\t\treturn uuid\r\n\r\n\t@staticmethod\r\n\tdef exit_with_error(message, exception=None):\r\n\t\tprintError = partial(print, file=sys.stderr) # python2 compatibility\r\n\r\n\t\texception_message = str(exception) if exception else \"N/A\"\r\n\r\n\t\tfull_message = message + exception_message\r\n\r\n\t\tprintError(full_message)\r\n\t\tsys.exit(1)\r\n\r\n\t@staticmethod\r\n\tdef to_json(obj, pretty=False):\r\n\t\treturn json.dumps(obj, indent=4) if pretty else json.dumps(obj)\r\n\r\n\t@staticmethod\r\n\tdef decode_to_utf8_string(potential_bytes):\r\n\t\tif isinstance(potential_bytes, str):\r\n\t\t\treturn potential_bytes\r\n\t\telse:\r\n\t\t\treturn potential_bytes.decode(\"utf-8\")\r\n\r\nif RoyalUtils.is_macOS():\r\n\timport pexpect\r\nelse:\r\n\timport wexpect\r\n\r\nclass RoyalInputPrompt:\r\n\t@staticmethod\r\n\tdef show(title: str, message: str, defaultValue: str):\r\n\t\tif RoyalUtils.is_macOS(): # macOS\r\n\t\t\treturn RoyalInputPrompt._show_macOS(title, message, defaultValue)\r\n\t\telse: # Windows\r\n\t\t\treturn RoyalInputPrompt._show_windows(title, message, defaultValue)\r\n\r\n\t@staticmethod\r\n\tdef _escape_string_quotes_js(target_string: str):\r\n\t\tescaped_string = target_string.replace('\"', '\\\\\"')\r\n\r\n\t\treturn escaped_string\r\n\r\n\t@staticmethod\r\n\tdef _escape_string_quotes_vbs(target_string: str):\r\n\t\tescaped_string = target_string.replace('\"', '\\\"\\\"')\r\n\r\n\t\treturn escaped_string\r\n\r\n\t@staticmethod\r\n\tdef _show_macOS(title: str, message: str, defaultValue: str):\r\n\t\tscript = f\"\"\"\r\n\t\tfunction showInputPrompt(title, message, defaultValue) {{\r\n\t\t\tlet app = Application.currentApplication();\r\n\t\t\tapp.includeStandardAdditions = true;\r\n\t\t\t\r\n\t\t\tvar value = \"\";\r\n\t\t\t\r\n\t\t\ttry {{\r\n\t\t\t\tlet response = app.displayDialog(message, {{\r\n\t\t\t\t\twithTitle: title,\r\n\t\t\t\t\tdefaultAnswer: defaultValue,\r\n\t\t\t\t\twithIcon: \"note\",\r\n\t\t\t\t\tbuttons: [ \"Cancel\", \"OK\" ],\r\n\t\t\t\t\tdefaultButton: \"OK\",\r\n\t\t\t\t\tcancelButton: \"Cancel\"\r\n\t\t\t\t}});\r\n\t\t\t\t\r\n\t\t\t\tif (response.buttonReturned == \"OK\") {{\r\n\t\t\t\t\tvalue = response.textReturned;\r\n\t\t\t\t}}\r\n\t\t\t}} catch {{ }}\r\n\t\t\t\r\n\t\t\treturn value;\r\n\t\t}}\r\n\r\n\t\tfunction run(argv) {{\r\n\t\t\tlet value = showInputPrompt(\"{RoyalInputPrompt._escape_string_quotes_js(title)}\", \"{RoyalInputPrompt._escape_string_quotes_js(message)}\", \"{RoyalInputPrompt._escape_string_quotes_js(defaultValue)}\");\r\n\t\t\t\r\n\t\t\treturn value;\r\n\t\t}}\"\"\"\r\n\r\n\t\tencoding = \"utf-8\"\r\n\r\n\t\tcmd = [ \"osascript\", \"-l\", \"JavaScript\", \"-\" ]\r\n\r\n\t\tproc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)\r\n\t\tstdout, _ = proc.communicate(script.encode(encoding))\r\n\r\n\t\treturn_value = RoyalUtils.decode_to_utf8_string(stdout)\r\n\r\n\t\treturn return_value\r\n\r\n\t@staticmethod\r\n\tdef _show_windows(title: str, message: str, defaultValue: str):\r\n\t\treturn_value = \"\"\r\n\r\n\t\tscript = f\"\"\"\r\n\t\tWScript.Echo InputBox(\"{RoyalInputPrompt._escape_string_quotes_vbs(message)}\", \"{RoyalInputPrompt._escape_string_quotes_vbs(title)}\", \"{RoyalInputPrompt._escape_string_quotes_vbs(defaultValue)}\")\r\n\t\t\"\"\"\r\n\r\n\t\tencoding = \"utf-8\"\r\n\r\n\t\ttemp_file = tempfile.NamedTemporaryFile(suffix=\".vbs\", mode=\"w\", encoding=encoding, delete=False)\r\n\t\ttemp_file_path = temp_file.name\r\n\r\n\t\ttemp_file.write(script)\r\n\t\ttemp_file.flush()\r\n\r\n\t\ttemp_file.close()\r\n\r\n\t\ttry:\r\n\t\t\tcmd = [ \"cscript.exe\", \"/Nologo\", temp_file_path ]\r\n\r\n\t\t\tproc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)\r\n\t\t\tstdout, _ = proc.communicate()\r\n\r\n\t\t\treturn_value = RoyalUtils.decode_to_utf8_string(stdout)\r\n\t\texcept:\r\n\t\t\tpass\r\n\t\tfinally:\r\n\t\t\tif os.path.exists(temp_file_path):\r\n\t\t\t\tos.remove(temp_file_path)\r\n\r\n\t\treturn return_value\r\n\r\nclass OnePassword:\r\n\tconfig_path = \"\"\r\n\tconfig_file_path = \"\"\r\n\r\n\top_path = \"\"\r\n\tsession_token = \"\"\r\n\taccount_shorthand = \"\"\r\n\r\n\top_cli_version = \"\"\r\n\tis_op_cli_v2 = False\r\n\r\n\tunknown_error_string = \"An unknown error occurred.\"\r\n\r\n\tdef __init__(self, op_path):\r\n\t\tself.config_path = os.path.expanduser(\"~/.config/op\")\r\n\t\tself.config_file_path = os.path.join(self.config_path, \"config\")\r\n\r\n\t\tself.op_path = op_path\r\n\r\n\t\top_cli_version = \"1\"\r\n\r\n\t\ttry:\r\n\t\t\top_cli_version = self.get_cli_version()\r\n\t\texcept:\r\n\t\t\tpass\r\n\r\n\t\tif not op_cli_version:\r\n\t\t\top_cli_version = \"1\"\r\n\r\n\t\tself.op_cli_version = op_cli_version\r\n\t\tself.is_op_cli_v2 = op_cli_version.startswith(\"2\")\r\n\t\r\n\tdef get_cli_version(self):\r\n\t\tcmd_get_version = [ self.op_path, \"--version\" ]\r\n\t\top = subprocess.Popen(cmd_get_version, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\top_cli_version = \"\"\r\n\r\n\t\tif success:\r\n\t\t\top_cli_version = RoyalUtils.decode_to_utf8_string(output)\r\n\t\t\top_cli_version = RoyalUtils.get_last_line(op_cli_version)\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\r\n\t\t\traise Exception(err)\r\n\t\t\r\n\t\treturn op_cli_version\r\n\t\r\n\tdef get_device_id(self):\r\n\t\tdevice_id = os.environ.get(\"OP_DEVICE\")\r\n\r\n\t\treturn device_id\r\n\t\r\n\tdef set_device_id(self, device_id):\r\n\t\tos.environ[\"OP_DEVICE\"] = device_id\r\n\r\n\t\tif not os.path.exists(self.config_file_path):\r\n\t\t\tconfig_json = RoyalUtils.to_json({ \"device\": device_id }, True)\r\n\r\n\t\t\ttry:\r\n\t\t\t\tfile = open(self.config_file_path, \"w\")\r\n\r\n\t\t\t\tfile.write(config_json)\r\n\t\t\t\tfile.close()\r\n\r\n\t\t\t\tif RoyalUtils.is_macOS:\r\n\t\t\t\t\tos.chmod(self.config_file_path, 0o600)\r\n\t\t\t\t\r\n\t\t\t\treturn True\r\n\t\t\texcept:\r\n\t\t\t\treturn False\r\n\t\t\r\n\t\treturn True\r\n\t\r\n\tdef get_account_shorthand(self, sign_in_address, email_address):\r\n\t\tshorthand = sign_in_address.replace(\".\", \"_\") + \"__\" + email_address.replace(\".\", \"_\").replace(\"@\", \"_\").replace(\"+\", \"_\")\r\n\r\n\t\treturn shorthand\r\n\r\n\tdef sign_out(self):\r\n\t\tcmd_signout = [ ]\r\n\r\n\t\tif self.account_shorthand:\r\n\t\t\tcmd_signout = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"signout\",\r\n\t\t\t\t\"--account\", self.account_shorthand\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_signout = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"signout\"\r\n\t\t\t]\r\n\r\n\t\top = subprocess.Popen(cmd_signout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\t\t\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\tself.session_token = \"\"\r\n\t\t\tself.account_shorthand = \"\"\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\r\n\t\t\traise Exception(err)\r\n\t\r\n\tdef sign_in(self, sign_in_address, email_address, secret_key, master_password, second_try=False, add_account=False):\r\n\t\tshorthand = self.get_account_shorthand(sign_in_address, email_address)\r\n\r\n\t\ttimeout = 10\r\n\r\n\t\tcmd = self.op_path\r\n\r\n\t\targs = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tif add_account:\r\n\t\t\t\targs = [\r\n\t\t\t\t\t\"account\", \"add\",\r\n\t\t\t\t\t\"--signin\",\r\n\t\t\t\t\t\"--address\", sign_in_address,\r\n\t\t\t\t\t\"--email\", email_address,\r\n\t\t\t\t\t\"--secret-key\", secret_key,\r\n\t\t\t\t\t\"--shorthand\", shorthand,\r\n\t\t\t\t\t\"--raw\"\r\n\t\t\t\t]\r\n\t\t\telse:\r\n\t\t\t\targs = [\r\n\t\t\t\t\t\"signin\",\r\n\t\t\t\t\t\"--account\", shorthand,\r\n\t\t\t\t\t\"--raw\"\r\n\t\t\t\t]\r\n\t\telse:\r\n\t\t\tadd_account = True\r\n\t\t\targs = [\r\n\t\t\t\t\"signin\",\r\n\t\t\t\tsign_in_address,\r\n\t\t\t\temail_address,\r\n\t\t\t\tsecret_key,\r\n\t\t\t\t\"--shorthand\", shorthand,\r\n\t\t\t\t\"--raw\"\r\n\t\t\t]\r\n\r\n\t\teof = None\r\n\t\tproc = None\r\n\r\n\t\tif RoyalUtils.is_macOS():\r\n\t\t\teof = pexpect.EOF\r\n\t\t\tproc = pexpect.spawn(cmd, args)\r\n\t\telse:\r\n\t\t\teof = wexpect.EOF\r\n\t\t\tproc = wexpect.spawn(cmd, args)\r\n\r\n\t\texp_str_password = f\"Enter the password for.*:\"\r\n\t\texp_str_error = \"\\[ERROR\\].*\"\r\n\t\texp_str_biometric_unlock_enabled = \"Biometric unlock integration with the 1Password app is enabled*\"\r\n\t\texp_str_add_account = \"Do you want to add an account manually now?\"\r\n\r\n\t\terr = None\r\n\r\n\t\ttry:\r\n\t\t\texp_pw_prompt = proc.expect([ exp_str_password, exp_str_error, exp_str_biometric_unlock_enabled, exp_str_add_account ], timeout=timeout)\r\n\r\n\t\t\tif exp_pw_prompt == 0: # Password Prompt\r\n\t\t\t\tproc.sendline(master_password)\r\n\r\n\t\t\t\texp_str_mfa = \"Enter your.*authentication code.*:\"\r\n\r\n\t\t\t\texp_mfa = proc.expect([ exp_str_mfa, exp_str_error, eof ], timeout=timeout)\r\n\r\n\t\t\t\tif exp_mfa == 0: # MFA Prompt\r\n\t\t\t\t\tmfa_code = RoyalInputPrompt.show(\"1Password Multi-Factor-Authentication\", f\"Enter multi-factor authentication code for {email_address}:\", \"\")\r\n\t\t\t\t\tmfa_code = mfa_code.replace(\"\\n\", \"\").replace(\"\\r\", \"\")\r\n\r\n\t\t\t\t\tif not mfa_code:\r\n\t\t\t\t\t\traise Exception(\"No multi-factor code provided\")\r\n\t\t\t\t\t\r\n\t\t\t\t\tproc.sendline(mfa_code)\r\n\t\t\t\t\texp_mfa_sent = proc.expect([ exp_str_error, eof ], timeout=timeout)\r\n\r\n\t\t\t\t\tif exp_mfa_sent == 0: # Error\r\n\t\t\t\t\t\terr = RoyalUtils.get_last_line(RoyalUtils.decode_to_utf8_string(proc.match.string))\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\traise Exception(err)\r\n\t\t\t\telif exp_mfa == 1: # Error\r\n\t\t\t\t\terr = RoyalUtils.get_last_line(RoyalUtils.decode_to_utf8_string(proc.match.string))\r\n\t\t\t\t\t\r\n\t\t\t\t\traise Exception(err)\r\n\r\n\t\t\t\toutput = proc.before\r\n\r\n\t\t\t\tif output:\r\n\t\t\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\t\t\t\t\r\n\t\t\t\tproc.close()\r\n\t\t\t\texit_code = proc.wait()\r\n\t\t\t\t\r\n\t\t\t\tsuccess = exit_code == 0\r\n\r\n\t\t\t\tif success:\r\n\t\t\t\t\tself.session_token = RoyalUtils.get_last_line(output)\r\n\t\t\t\t\tself.account_shorthand = shorthand\r\n\t\t\t\telse:\r\n\t\t\t\t\tif not err:\r\n\t\t\t\t\t\terr = self.unknown_error_string\r\n\r\n\t\t\t\t\traise Exception(err)\r\n\t\t\telif exp_pw_prompt == 1: # Error\r\n\t\t\t\terr = RoyalUtils.get_last_line(RoyalUtils.decode_to_utf8_string(proc.match.string))\r\n\r\n\t\t\t\tif not second_try:\r\n\t\t\t\t\tif \"No saved device ID\" in err:\r\n\t\t\t\t\t\texport_start_str = \"export OP_DEVICE=\"\r\n\t\t\t\t\t\tdevice_id = \"\"\r\n\r\n\t\t\t\t\t\tif export_start_str in err:\r\n\t\t\t\t\t\t\tstart_index = err.index(export_start_str) + len(export_start_str)\r\n\t\t\t\t\t\t\tdevice_id = err[start_index:]\r\n\t\t\t\t\t\t\tend_index = device_id.index(\"`\")\r\n\t\t\t\t\t\t\tdevice_id = device_id[:end_index]\r\n\r\n\t\t\t\t\t\tif not device_id or len(device_id) != 26:\r\n\t\t\t\t\t\t\tdevice_id = RoyalUtils.random_uuid()\r\n\r\n\t\t\t\t\t\tself.set_device_id(device_id)\r\n\r\n\t\t\t\t\t\tself.sign_in(sign_in_address, email_address, secret_key, master_password, second_try=True, add_account=add_account)\r\n\r\n\t\t\t\t\t\treturn\r\n\r\n\t\t\t\tif not add_account:\r\n\t\t\t\t\t# Try resolving the error by re-adding the account\r\n\t\t\t\t\treturn self.sign_in(sign_in_address, email_address, secret_key, master_password, second_try, add_account=True)\r\n\r\n\t\t\t\tif not err:\r\n\t\t\t\t\terr = self.unknown_error_string\r\n\r\n\t\t\t\traise Exception(err)\r\n\t\t\telif exp_pw_prompt == 2: # Biometric unlock enabled\r\n\t\t\t\terr = \"Biometric unlock integration with the 1Password app is enabled. This feature is not supported by the 1Password Dynamic Folder Script. Please disable biometric unlock in the 1Password app.\"\r\n\t\t\t\t\r\n\t\t\t\traise Exception(err)\r\n\t\t\telif exp_pw_prompt == 3: # Account doesn't exist yet\r\n\t\t\t\treturn self.sign_in(sign_in_address, email_address, secret_key, master_password, second_try, add_account=True)\r\n\t\texcept Exception as ex:\r\n\t\t\terr = str(ex)\r\n\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\r\n\t\t\traise Exception(err)\r\n\r\n\tdef get_vaults(self):\r\n\t\tcmd_list_vaults = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tcmd_list_vaults = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"vault\", \"list\",\r\n\t\t\t\t\"--format=json\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_list_vaults = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"list\", \"vaults\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\t\r\n\t\top = subprocess.Popen(cmd_list_vaults, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\r\n\t\t\tvaults_str = \"\"\r\n\r\n\t\t\tif self.is_op_cli_v2:\r\n\t\t\t\tvaults_str = output\r\n\t\t\telse:\r\n\t\t\t\tvaults_str = RoyalUtils.get_last_line(output)\r\n\t\t\t\r\n\t\t\tvaults = json.loads(vaults_str)\r\n\r\n\t\t\treturn vaults\r\n\t\t\r\n\t\tif not err:\r\n\t\t\terr = self.unknown_error_string\r\n\t\telse:\r\n\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\t\t\r\n\t\traise Exception(err)\r\n\r\n\tdef get_items(self, vault):\r\n\t\tcmd_list_items = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tcmd_list_items = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"item\", \"list\",\r\n\t\t\t\t\"--format=json\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_list_items = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"list\", \"items\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\t\r\n\t\tif not vault:\r\n\t\t\tvault = \"\"\r\n\t\t\r\n\t\tvault = vault.strip()\r\n\t\t\r\n\t\tif vault:\r\n\t\t\tcmd_list_items.append(\"--vault\")\r\n\t\t\tcmd_list_items.append(vault)\r\n\r\n\t\top = subprocess.Popen(cmd_list_items, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\r\n\t\t\titems_str = \"\"\r\n\r\n\t\t\tif self.is_op_cli_v2:\r\n\t\t\t\titems_str = output\r\n\t\t\telse:\r\n\t\t\t\titems_str = RoyalUtils.get_last_line(output)\r\n\r\n\t\t\titems = json.loads(items_str)\r\n\t\t\r\n\t\t\treturn items\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\t\t\r\n\t\t\traise Exception(err)\r\n\r\n\tdef get_item_details(self, item_id):\r\n\t\tcmd_get_item = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tcmd_get_item = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"item\", \"get\", item_id,\r\n\t\t\t\t# \"--fields\", \"label=password,label=privatekey\",\r\n\t\t\t\t\"--format=JSON\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_get_item = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"get\", \"item\", item_id,\r\n\t\t\t\t\"--fields\", \"username,password\",\r\n\t\t\t\t\"--format\", \"JSON\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\r\n\t\top = subprocess.Popen(cmd_get_item, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\r\n\t\t\titem_str = \"\"\r\n\r\n\t\t\tif self.is_op_cli_v2:\r\n\t\t\t\titem_str = output\r\n\t\t\telse:\r\n\t\t\t\titem_str = RoyalUtils.get_last_line(output)\r\n\t\t\t\r\n\t\t\titem = json.loads(item_str)\r\n\t\t\r\n\t\t\treturn item\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\t\t\t\r\n\t\t\traise Exception(err)\r\n\r\nclass Converter:\r\n\t@staticmethod\r\n\tdef get_vault_name(vaults, vault_id, is_op_cli_v2):\r\n\t\tfor vault in vaults:\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\tif vault.get(\"id\", \"\") == vault_id:\r\n\t\t\t\t\treturn vault.get(\"name\", \"\")\r\n\t\t\telse:\r\n\t\t\t\tif vault.get(\"uuid\", \"\") == vault_id:\r\n\t\t\t\t\treturn vault.get(\"name\", \"\")\r\n\t\t\r\n\t\treturn \"\"\r\n\r\n\t@staticmethod\r\n\tdef convert_items(vaults, items, is_op_cli_v2):\r\n\t\tobjects = []\r\n\r\n\t\tfor item in items:\r\n\t\t\toverview = None\r\n\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\toverview = item\r\n\t\t\telse:\r\n\t\t\t\toverview = item.get(\"overview\", None)\r\n\r\n\t\t\tif overview == None:\r\n\t\t\t\tcontinue\r\n\r\n\t\t\tid = \"\"\r\n\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\tid = item.get(\"id\", \"\")\r\n\t\t\telse:\r\n\t\t\t\tid = item.get(\"uuid\", \"\")\r\n\t\t\t\r\n\t\t\ttitle = overview.get(\"title\", \"N/A\")\r\n\t\t\turl = overview.get(\"url\", \"\")\r\n\r\n\t\t\tvault_id = \"\"\r\n\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\titem_vault = item.get(\"vault\", None)\r\n\r\n\t\t\t\tif item_vault:\r\n\t\t\t\t\tvault_id = item_vault.get(\"id\", \"\")\r\n\t\t\telse:\r\n\t\t\t\tvault_id = item.get(\"vaultUuid\", \"\")\r\n\r\n\t\t\tvault_name = Converter.get_vault_name(vaults, vault_id, is_op_cli_v2)\r\n\r\n\t\t\titem_details = item.get(\"__item_details\", None)\r\n\t\t\tconverted_item_details = None\r\n\r\n\t\t\tif item_details is not None:\r\n\t\t\t\tconverted_item_details = Converter.convert_item(item_details, is_op_cli_v2)\r\n\r\n\t\t\tcred_type = \"DynamicCredential\" if converted_item_details is None else \"Credential\"\r\n\r\n\t\t\tcred = {\r\n\t\t\t\t\"Type\": cred_type,\r\n\t\t\t\t\"ID\": id,\r\n\t\t\t\t\"Name\": title,\r\n\t\t\t\t\"Path\": vault_name\r\n\t\t\t}\r\n\r\n\t\t\tif url != \"\":\r\n\t\t\t\tcred[\"URL\"] = url\r\n\t\t\t\r\n\t\t\tif converted_item_details is not None:\r\n\t\t\t\tfor key in converted_item_details:\r\n\t\t\t\t\tcred[key] = converted_item_details[key]\r\n\r\n\t\t\tobjects.append(cred)\r\n\r\n\t\tobjects = sorted(objects, key = lambda i: (i[\"Path\"], i[\"Name\"]))\r\n\r\n\t\tstore = {\r\n\t\t\t\"Objects\": objects\r\n\t\t}\r\n\r\n\t\treturn store\r\n\t\r\n\t@staticmethod\r\n\tdef convert_item(item_details, is_op_cli_v2):\r\n\t\tusername = None\r\n\t\tpassword = None\r\n\t\tprivate_key = None\r\n\r\n\t\tif is_op_cli_v2:\r\n\t\t\tfields = item_details.get(\"fields\")\r\n\r\n\t\t\tif not fields:\r\n\t\t\t\tfields = item_details\r\n\r\n\t\t\tfor field in fields:\r\n\t\t\t\tfield_id = field.get(\"id\", None)\r\n\t\t\t\tfield_label = field.get(\"label\", None)\r\n\t\t\t\tfield_value = field.get(\"value\", None)\r\n\r\n\t\t\t\tif field_id == \"username\" or field_label == \"username\":\r\n\t\t\t\t\tusername = field_value\r\n\t\t\t\telif field_id == \"password\" or field_label == \"password\":\r\n\t\t\t\t\tpassword = field_value\r\n\t\t\t\telif field_id == \"private_key\" or field_label == \"private key\":\r\n\t\t\t\t\tprivate_key = field_value\r\n\t\telse:\r\n\t\t\tusername = item_details.get(\"username\", None)\r\n\t\t\tpassword = item_details.get(\"password\", None)\r\n\r\n\t\tcred = { }\r\n\r\n\t\tif username is not None:\r\n\t\t\tcred[\"Username\"] = username\r\n\r\n\t\tif password is not None:\r\n\t\t\tcred[\"Password\"] = password\r\n\r\n\t\tif private_key is not None:\r\n\t\t\tcred[\"KeyFileContent\"] = private_key\r\n\r\n\t\treturn cred\r\n\r\nclass Coordinator:\r\n\top_path = \"\"\r\n\tsign_in_address = \"\"\r\n\temail_address = \"\"\r\n\tsecret_key = \"\"\r\n\tmaster_password = \"\"\r\n\tcreate_dynamic_credentials = True\r\n\r\n\terror_message_sign_in = \"Error while signing in: \"\r\n\terror_message_sign_out = \"Error while signing out: \"\r\n\terror_message_get_vaults = \"Error while getting vaults: \"\r\n\terror_message_get_items = \"Error while getting items: \"\r\n\terror_message_get_item_details = \"Error while getting item details: \"\r\n\r\n\tdef __init__(self, op_path_windows, op_path_macOS, sign_in_address, email_address, secret_key, master_password, create_dynamic_credentials):\r\n\t\tself.op_path = op_path_macOS if RoyalUtils.is_macOS() else op_path_windows\r\n\t\tself.sign_in_address = sign_in_address\r\n\t\tself.email_address = email_address\r\n\t\tself.secret_key = secret_key\r\n\t\tself.master_password = master_password\r\n\t\tself.create_dynamic_credentials = create_dynamic_credentials\r\n\t\t\r\n\tdef get_items(self, vault):\r\n\t\top = OnePassword(self.op_path)\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception:\r\n\t\t\tpass\r\n\r\n\t\ttry:\r\n\t\t\top.sign_in(self.sign_in_address, self.email_address, self.secret_key, self.master_password)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_in, e)\r\n\r\n\t\tvaults = None\r\n\r\n\t\ttry:\r\n\t\t\tvaults = op.get_vaults()\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_get_vaults, e)\r\n\t\t\r\n\t\titems = None\r\n\r\n\t\ttry:\r\n\t\t\titems = op.get_items(vault)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_get_items, e)\r\n\t\t\r\n\t\titems_details = [ ]\r\n\r\n\t\tif not self.create_dynamic_credentials:\r\n\t\t\tfor item in items:\r\n\t\t\t\tid = \"\"\r\n\r\n\t\t\t\tif op.is_op_cli_v2:\r\n\t\t\t\t\tid = item.get(\"id\", \"\")\r\n\t\t\t\telse:\r\n\t\t\t\t\tid = item.get(\"uuid\", \"\")\r\n\t\t\t\t\r\n\t\t\t\titem_details = op.get_item_details(id)\r\n\r\n\t\t\t\titem[\"__item_details\"] = item_details\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_out, e)\r\n\r\n\t\tstore = Converter.convert_items(vaults, items, op.is_op_cli_v2)\r\n\t\tstore_json = RoyalUtils.to_json(store, True)\r\n\r\n\t\tprint(store_json)\r\n\t\r\n\tdef get_item_details(self, item_id):\r\n\t\top = OnePassword(self.op_path)\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception:\r\n\t\t\tpass\r\n\r\n\t\ttry:\r\n\t\t\top.sign_in(self.sign_in_address, self.email_address, self.secret_key, self.master_password)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_in, e)\r\n\r\n\t\titem_details = None\r\n\r\n\t\ttry:\r\n\t\t\titem_details = op.get_item_details(item_id)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_get_item_details, e)\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_out, e)\r\n\r\n\t\tstore = Converter.convert_item(item_details, op.is_op_cli_v2)\r\n\t\tstore_json = RoyalUtils.to_json(store, True)\r\n\r\n\t\tprint(store_json)\r\n\r\ncoordinator = Coordinator(op_path_windows, op_path_macOS, sign_in_address, email_address, secret_key, master_password, create_dynamic_credentials)\r\ncoordinator.get_items(filter_vault)\r\n# coordinator.get_item_details(item_id)", - "ScriptInterpreter": "python", - "DynamicCredentialScriptInterpreter": "python", - "DynamicCredentialScript": "from __future__ import print_function\r\nfrom functools import partial\r\nfrom sys import platform as _platform\r\nfrom subprocess import Popen, PIPE\r\n\r\nimport tempfile\r\nimport sys\r\nimport json\r\nimport subprocess\r\nimport os\r\nimport base64\r\n\r\nop_path_windows = r\"$CustomProperty.OPPathWindows$\"\r\nop_path_macOS = r\"$CustomProperty.OPPathmacOS$\"\r\nsign_in_address = r\"$CustomProperty.SignInAddress$\"\r\nemail_address = r\"$CustomProperty.EmailAddress$\"\r\nsecret_key = r\"$CustomProperty.SecretKey$\"\r\nmaster_password = r\"$CustomProperty.MasterPassword$\"\r\nfilter_vault = r\"$CustomProperty.Vault$\"\r\ncreate_dynamic_credentials = r\"$CustomProperty.DynamicCredentials$\".lower() != \"no\"\r\nitem_id = r\"$DynamicCredential.EffectiveID$\"\r\n\r\nclass RoyalUtils:\r\n\t@staticmethod\r\n\tdef is_macOS():\r\n\t\tplat = _platform.lower()\r\n\r\n\t\treturn plat.startswith(\"darwin\")\r\n\r\n\t@staticmethod\r\n\tdef get_last_line(the_string: str):\r\n\t\tstripped_str = the_string.strip()\r\n\r\n\t\tif \"\\n\" in stripped_str:\r\n\t\t\tlines = stripped_str.splitlines()\r\n\t\t\tstripped_str = lines[len(lines) - 1]\r\n\r\n\t\treturn stripped_str\r\n\r\n\t@staticmethod\r\n\tdef random_uuid():\r\n\t\tuuid = base64.b32encode(os.urandom(16)).decode().lower().rstrip(\"=\")\r\n\r\n\t\treturn uuid\r\n\r\n\t@staticmethod\r\n\tdef exit_with_error(message, exception=None):\r\n\t\tprintError = partial(print, file=sys.stderr) # python2 compatibility\r\n\r\n\t\texception_message = str(exception) if exception else \"N/A\"\r\n\r\n\t\tfull_message = message + exception_message\r\n\r\n\t\tprintError(full_message)\r\n\t\tsys.exit(1)\r\n\r\n\t@staticmethod\r\n\tdef to_json(obj, pretty=False):\r\n\t\treturn json.dumps(obj, indent=4) if pretty else json.dumps(obj)\r\n\r\n\t@staticmethod\r\n\tdef decode_to_utf8_string(potential_bytes):\r\n\t\tif isinstance(potential_bytes, str):\r\n\t\t\treturn potential_bytes\r\n\t\telse:\r\n\t\t\treturn potential_bytes.decode(\"utf-8\")\r\n\r\nif RoyalUtils.is_macOS():\r\n\timport pexpect\r\nelse:\r\n\timport wexpect\r\n\r\nclass RoyalInputPrompt:\r\n\t@staticmethod\r\n\tdef show(title: str, message: str, defaultValue: str):\r\n\t\tif RoyalUtils.is_macOS(): # macOS\r\n\t\t\treturn RoyalInputPrompt._show_macOS(title, message, defaultValue)\r\n\t\telse: # Windows\r\n\t\t\treturn RoyalInputPrompt._show_windows(title, message, defaultValue)\r\n\r\n\t@staticmethod\r\n\tdef _escape_string_quotes_js(target_string: str):\r\n\t\tescaped_string = target_string.replace('\"', '\\\\\"')\r\n\r\n\t\treturn escaped_string\r\n\r\n\t@staticmethod\r\n\tdef _escape_string_quotes_vbs(target_string: str):\r\n\t\tescaped_string = target_string.replace('\"', '\\\"\\\"')\r\n\r\n\t\treturn escaped_string\r\n\r\n\t@staticmethod\r\n\tdef _show_macOS(title: str, message: str, defaultValue: str):\r\n\t\tscript = f\"\"\"\r\n\t\tfunction showInputPrompt(title, message, defaultValue) {{\r\n\t\t\tlet app = Application.currentApplication();\r\n\t\t\tapp.includeStandardAdditions = true;\r\n\t\t\t\r\n\t\t\tvar value = \"\";\r\n\t\t\t\r\n\t\t\ttry {{\r\n\t\t\t\tlet response = app.displayDialog(message, {{\r\n\t\t\t\t\twithTitle: title,\r\n\t\t\t\t\tdefaultAnswer: defaultValue,\r\n\t\t\t\t\twithIcon: \"note\",\r\n\t\t\t\t\tbuttons: [ \"Cancel\", \"OK\" ],\r\n\t\t\t\t\tdefaultButton: \"OK\",\r\n\t\t\t\t\tcancelButton: \"Cancel\"\r\n\t\t\t\t}});\r\n\t\t\t\t\r\n\t\t\t\tif (response.buttonReturned == \"OK\") {{\r\n\t\t\t\t\tvalue = response.textReturned;\r\n\t\t\t\t}}\r\n\t\t\t}} catch {{ }}\r\n\t\t\t\r\n\t\t\treturn value;\r\n\t\t}}\r\n\r\n\t\tfunction run(argv) {{\r\n\t\t\tlet value = showInputPrompt(\"{RoyalInputPrompt._escape_string_quotes_js(title)}\", \"{RoyalInputPrompt._escape_string_quotes_js(message)}\", \"{RoyalInputPrompt._escape_string_quotes_js(defaultValue)}\");\r\n\t\t\t\r\n\t\t\treturn value;\r\n\t\t}}\"\"\"\r\n\r\n\t\tencoding = \"utf-8\"\r\n\r\n\t\tcmd = [ \"osascript\", \"-l\", \"JavaScript\", \"-\" ]\r\n\r\n\t\tproc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)\r\n\t\tstdout, _ = proc.communicate(script.encode(encoding))\r\n\r\n\t\treturn_value = RoyalUtils.decode_to_utf8_string(stdout)\r\n\r\n\t\treturn return_value\r\n\r\n\t@staticmethod\r\n\tdef _show_windows(title: str, message: str, defaultValue: str):\r\n\t\treturn_value = \"\"\r\n\r\n\t\tscript = f\"\"\"\r\n\t\tWScript.Echo InputBox(\"{RoyalInputPrompt._escape_string_quotes_vbs(message)}\", \"{RoyalInputPrompt._escape_string_quotes_vbs(title)}\", \"{RoyalInputPrompt._escape_string_quotes_vbs(defaultValue)}\")\r\n\t\t\"\"\"\r\n\r\n\t\tencoding = \"utf-8\"\r\n\r\n\t\ttemp_file = tempfile.NamedTemporaryFile(suffix=\".vbs\", mode=\"w\", encoding=encoding, delete=False)\r\n\t\ttemp_file_path = temp_file.name\r\n\r\n\t\ttemp_file.write(script)\r\n\t\ttemp_file.flush()\r\n\r\n\t\ttemp_file.close()\r\n\r\n\t\ttry:\r\n\t\t\tcmd = [ \"cscript.exe\", \"/Nologo\", temp_file_path ]\r\n\r\n\t\t\tproc = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)\r\n\t\t\tstdout, _ = proc.communicate()\r\n\r\n\t\t\treturn_value = RoyalUtils.decode_to_utf8_string(stdout)\r\n\t\texcept:\r\n\t\t\tpass\r\n\t\tfinally:\r\n\t\t\tif os.path.exists(temp_file_path):\r\n\t\t\t\tos.remove(temp_file_path)\r\n\r\n\t\treturn return_value\r\n\r\nclass OnePassword:\r\n\tconfig_path = \"\"\r\n\tconfig_file_path = \"\"\r\n\r\n\top_path = \"\"\r\n\tsession_token = \"\"\r\n\taccount_shorthand = \"\"\r\n\r\n\top_cli_version = \"\"\r\n\tis_op_cli_v2 = False\r\n\r\n\tunknown_error_string = \"An unknown error occurred.\"\r\n\r\n\tdef __init__(self, op_path):\r\n\t\tself.config_path = os.path.expanduser(\"~/.config/op\")\r\n\t\tself.config_file_path = os.path.join(self.config_path, \"config\")\r\n\r\n\t\tself.op_path = op_path\r\n\r\n\t\top_cli_version = \"1\"\r\n\r\n\t\ttry:\r\n\t\t\top_cli_version = self.get_cli_version()\r\n\t\texcept:\r\n\t\t\tpass\r\n\r\n\t\tif not op_cli_version:\r\n\t\t\top_cli_version = \"1\"\r\n\r\n\t\tself.op_cli_version = op_cli_version\r\n\t\tself.is_op_cli_v2 = op_cli_version.startswith(\"2\")\r\n\t\r\n\tdef get_cli_version(self):\r\n\t\tcmd_get_version = [ self.op_path, \"--version\" ]\r\n\t\top = subprocess.Popen(cmd_get_version, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\top_cli_version = \"\"\r\n\r\n\t\tif success:\r\n\t\t\top_cli_version = RoyalUtils.decode_to_utf8_string(output)\r\n\t\t\top_cli_version = RoyalUtils.get_last_line(op_cli_version)\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\r\n\t\t\traise Exception(err)\r\n\t\t\r\n\t\treturn op_cli_version\r\n\t\r\n\tdef get_device_id(self):\r\n\t\tdevice_id = os.environ.get(\"OP_DEVICE\")\r\n\r\n\t\treturn device_id\r\n\t\r\n\tdef set_device_id(self, device_id):\r\n\t\tos.environ[\"OP_DEVICE\"] = device_id\r\n\r\n\t\tif not os.path.exists(self.config_file_path):\r\n\t\t\tconfig_json = RoyalUtils.to_json({ \"device\": device_id }, True)\r\n\r\n\t\t\ttry:\r\n\t\t\t\tfile = open(self.config_file_path, \"w\")\r\n\r\n\t\t\t\tfile.write(config_json)\r\n\t\t\t\tfile.close()\r\n\r\n\t\t\t\tif RoyalUtils.is_macOS:\r\n\t\t\t\t\tos.chmod(self.config_file_path, 0o600)\r\n\t\t\t\t\r\n\t\t\t\treturn True\r\n\t\t\texcept:\r\n\t\t\t\treturn False\r\n\t\t\r\n\t\treturn True\r\n\t\r\n\tdef get_account_shorthand(self, sign_in_address, email_address):\r\n\t\tshorthand = sign_in_address.replace(\".\", \"_\") + \"__\" + email_address.replace(\".\", \"_\").replace(\"@\", \"_\").replace(\"+\", \"_\")\r\n\r\n\t\treturn shorthand\r\n\r\n\tdef sign_out(self):\r\n\t\tcmd_signout = [ ]\r\n\r\n\t\tif self.account_shorthand:\r\n\t\t\tcmd_signout = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"signout\",\r\n\t\t\t\t\"--account\", self.account_shorthand\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_signout = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"signout\"\r\n\t\t\t]\r\n\r\n\t\top = subprocess.Popen(cmd_signout, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\t\t\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\tself.session_token = \"\"\r\n\t\t\tself.account_shorthand = \"\"\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\r\n\t\t\traise Exception(err)\r\n\t\r\n\tdef sign_in(self, sign_in_address, email_address, secret_key, master_password, second_try=False, add_account=False):\r\n\t\tshorthand = self.get_account_shorthand(sign_in_address, email_address)\r\n\r\n\t\ttimeout = 10\r\n\r\n\t\tcmd = self.op_path\r\n\r\n\t\targs = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tif add_account:\r\n\t\t\t\targs = [\r\n\t\t\t\t\t\"account\", \"add\",\r\n\t\t\t\t\t\"--signin\",\r\n\t\t\t\t\t\"--address\", sign_in_address,\r\n\t\t\t\t\t\"--email\", email_address,\r\n\t\t\t\t\t\"--secret-key\", secret_key,\r\n\t\t\t\t\t\"--shorthand\", shorthand,\r\n\t\t\t\t\t\"--raw\"\r\n\t\t\t\t]\r\n\t\t\telse:\r\n\t\t\t\targs = [\r\n\t\t\t\t\t\"signin\",\r\n\t\t\t\t\t\"--account\", shorthand,\r\n\t\t\t\t\t\"--raw\"\r\n\t\t\t\t]\r\n\t\telse:\r\n\t\t\tadd_account = True\r\n\t\t\targs = [\r\n\t\t\t\t\"signin\",\r\n\t\t\t\tsign_in_address,\r\n\t\t\t\temail_address,\r\n\t\t\t\tsecret_key,\r\n\t\t\t\t\"--shorthand\", shorthand,\r\n\t\t\t\t\"--raw\"\r\n\t\t\t]\r\n\r\n\t\teof = None\r\n\t\tproc = None\r\n\r\n\t\tif RoyalUtils.is_macOS():\r\n\t\t\teof = pexpect.EOF\r\n\t\t\tproc = pexpect.spawn(cmd, args)\r\n\t\telse:\r\n\t\t\teof = wexpect.EOF\r\n\t\t\tproc = wexpect.spawn(cmd, args)\r\n\r\n\t\texp_str_password = f\"Enter the password for.*:\"\r\n\t\texp_str_error = \"\\[ERROR\\].*\"\r\n\t\texp_str_biometric_unlock_enabled = \"Biometric unlock integration with the 1Password app is enabled*\"\r\n\t\texp_str_add_account = \"Do you want to add an account manually now?\"\r\n\r\n\t\terr = None\r\n\r\n\t\ttry:\r\n\t\t\texp_pw_prompt = proc.expect([ exp_str_password, exp_str_error, exp_str_biometric_unlock_enabled, exp_str_add_account ], timeout=timeout)\r\n\r\n\t\t\tif exp_pw_prompt == 0: # Password Prompt\r\n\t\t\t\tproc.sendline(master_password)\r\n\r\n\t\t\t\texp_str_mfa = \"Enter your.*authentication code.*:\"\r\n\r\n\t\t\t\texp_mfa = proc.expect([ exp_str_mfa, exp_str_error, eof ], timeout=timeout)\r\n\r\n\t\t\t\tif exp_mfa == 0: # MFA Prompt\r\n\t\t\t\t\tmfa_code = RoyalInputPrompt.show(\"1Password Multi-Factor-Authentication\", f\"Enter multi-factor authentication code for {email_address}:\", \"\")\r\n\t\t\t\t\tmfa_code = mfa_code.replace(\"\\n\", \"\").replace(\"\\r\", \"\")\r\n\r\n\t\t\t\t\tif not mfa_code:\r\n\t\t\t\t\t\traise Exception(\"No multi-factor code provided\")\r\n\t\t\t\t\t\r\n\t\t\t\t\tproc.sendline(mfa_code)\r\n\t\t\t\t\texp_mfa_sent = proc.expect([ exp_str_error, eof ], timeout=timeout)\r\n\r\n\t\t\t\t\tif exp_mfa_sent == 0: # Error\r\n\t\t\t\t\t\terr = RoyalUtils.get_last_line(RoyalUtils.decode_to_utf8_string(proc.match.string))\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\traise Exception(err)\r\n\t\t\t\telif exp_mfa == 1: # Error\r\n\t\t\t\t\terr = RoyalUtils.get_last_line(RoyalUtils.decode_to_utf8_string(proc.match.string))\r\n\t\t\t\t\t\r\n\t\t\t\t\traise Exception(err)\r\n\r\n\t\t\t\toutput = proc.before\r\n\r\n\t\t\t\tif output:\r\n\t\t\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\t\t\t\t\r\n\t\t\t\tproc.close()\r\n\t\t\t\texit_code = proc.wait()\r\n\t\t\t\t\r\n\t\t\t\tsuccess = exit_code == 0\r\n\r\n\t\t\t\tif success:\r\n\t\t\t\t\tself.session_token = RoyalUtils.get_last_line(output)\r\n\t\t\t\t\tself.account_shorthand = shorthand\r\n\t\t\t\telse:\r\n\t\t\t\t\tif not err:\r\n\t\t\t\t\t\terr = self.unknown_error_string\r\n\r\n\t\t\t\t\traise Exception(err)\r\n\t\t\telif exp_pw_prompt == 1: # Error\r\n\t\t\t\terr = RoyalUtils.get_last_line(RoyalUtils.decode_to_utf8_string(proc.match.string))\r\n\r\n\t\t\t\tif not second_try:\r\n\t\t\t\t\tif \"No saved device ID\" in err:\r\n\t\t\t\t\t\texport_start_str = \"export OP_DEVICE=\"\r\n\t\t\t\t\t\tdevice_id = \"\"\r\n\r\n\t\t\t\t\t\tif export_start_str in err:\r\n\t\t\t\t\t\t\tstart_index = err.index(export_start_str) + len(export_start_str)\r\n\t\t\t\t\t\t\tdevice_id = err[start_index:]\r\n\t\t\t\t\t\t\tend_index = device_id.index(\"`\")\r\n\t\t\t\t\t\t\tdevice_id = device_id[:end_index]\r\n\r\n\t\t\t\t\t\tif not device_id or len(device_id) != 26:\r\n\t\t\t\t\t\t\tdevice_id = RoyalUtils.random_uuid()\r\n\r\n\t\t\t\t\t\tself.set_device_id(device_id)\r\n\r\n\t\t\t\t\t\tself.sign_in(sign_in_address, email_address, secret_key, master_password, second_try=True, add_account=add_account)\r\n\r\n\t\t\t\t\t\treturn\r\n\r\n\t\t\t\tif not add_account:\r\n\t\t\t\t\t# Try resolving the error by re-adding the account\r\n\t\t\t\t\treturn self.sign_in(sign_in_address, email_address, secret_key, master_password, second_try, add_account=True)\r\n\r\n\t\t\t\tif not err:\r\n\t\t\t\t\terr = self.unknown_error_string\r\n\r\n\t\t\t\traise Exception(err)\r\n\t\t\telif exp_pw_prompt == 2: # Biometric unlock enabled\r\n\t\t\t\terr = \"Biometric unlock integration with the 1Password app is enabled. This feature is not supported by the 1Password Dynamic Folder Script. Please disable biometric unlock in the 1Password app.\"\r\n\t\t\t\t\r\n\t\t\t\traise Exception(err)\r\n\t\t\telif exp_pw_prompt == 3: # Account doesn't exist yet\r\n\t\t\t\treturn self.sign_in(sign_in_address, email_address, secret_key, master_password, second_try, add_account=True)\r\n\t\texcept Exception as ex:\r\n\t\t\terr = str(ex)\r\n\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\r\n\t\t\traise Exception(err)\r\n\r\n\tdef get_vaults(self):\r\n\t\tcmd_list_vaults = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tcmd_list_vaults = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"vault\", \"list\",\r\n\t\t\t\t\"--format=json\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_list_vaults = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"list\", \"vaults\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\t\r\n\t\top = subprocess.Popen(cmd_list_vaults, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\r\n\t\t\tvaults_str = \"\"\r\n\r\n\t\t\tif self.is_op_cli_v2:\r\n\t\t\t\tvaults_str = output\r\n\t\t\telse:\r\n\t\t\t\tvaults_str = RoyalUtils.get_last_line(output)\r\n\t\t\t\r\n\t\t\tvaults = json.loads(vaults_str)\r\n\r\n\t\t\treturn vaults\r\n\t\t\r\n\t\tif not err:\r\n\t\t\terr = self.unknown_error_string\r\n\t\telse:\r\n\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\t\t\r\n\t\traise Exception(err)\r\n\r\n\tdef get_items(self, vault):\r\n\t\tcmd_list_items = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tcmd_list_items = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"item\", \"list\",\r\n\t\t\t\t\"--format=json\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_list_items = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"list\", \"items\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\t\r\n\t\tif not vault:\r\n\t\t\tvault = \"\"\r\n\t\t\r\n\t\tvault = vault.strip()\r\n\t\t\r\n\t\tif vault:\r\n\t\t\tcmd_list_items.append(\"--vault\")\r\n\t\t\tcmd_list_items.append(vault)\r\n\r\n\t\top = subprocess.Popen(cmd_list_items, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\r\n\t\t\titems_str = \"\"\r\n\r\n\t\t\tif self.is_op_cli_v2:\r\n\t\t\t\titems_str = output\r\n\t\t\telse:\r\n\t\t\t\titems_str = RoyalUtils.get_last_line(output)\r\n\r\n\t\t\titems = json.loads(items_str)\r\n\t\t\r\n\t\t\treturn items\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\t\t\r\n\t\t\traise Exception(err)\r\n\r\n\tdef get_item_details(self, item_id):\r\n\t\tcmd_get_item = [ ]\r\n\r\n\t\tif self.is_op_cli_v2:\r\n\t\t\tcmd_get_item = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"item\", \"get\", item_id,\r\n\t\t\t\t# \"--fields\", \"label=password,label=privatekey\",\r\n\t\t\t\t\"--format=JSON\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\t\telse:\r\n\t\t\tcmd_get_item = [\r\n\t\t\t\tself.op_path,\r\n\t\t\t\t\"get\", \"item\", item_id,\r\n\t\t\t\t\"--fields\", \"username,password\",\r\n\t\t\t\t\"--format\", \"JSON\",\r\n\t\t\t\t\"--account\", self.account_shorthand,\r\n\t\t\t\t\"--session\", self.session_token\r\n\t\t\t]\r\n\r\n\t\top = subprocess.Popen(cmd_get_item, stdout=subprocess.PIPE, stderr=subprocess.PIPE)\r\n\r\n\t\t(output, err) = op.communicate()\r\n\t\texit_code = op.wait()\r\n\r\n\t\tsuccess = exit_code == 0\r\n\r\n\t\tif success:\r\n\t\t\toutput = RoyalUtils.decode_to_utf8_string(output)\r\n\r\n\t\t\titem_str = \"\"\r\n\r\n\t\t\tif self.is_op_cli_v2:\r\n\t\t\t\titem_str = output\r\n\t\t\telse:\r\n\t\t\t\titem_str = RoyalUtils.get_last_line(output)\r\n\t\t\t\r\n\t\t\titem = json.loads(item_str)\r\n\t\t\r\n\t\t\treturn item\r\n\t\telse:\r\n\t\t\tif not err:\r\n\t\t\t\terr = self.unknown_error_string\r\n\t\t\telse:\r\n\t\t\t\terr = RoyalUtils.decode_to_utf8_string(err)\r\n\t\t\t\r\n\t\t\traise Exception(err)\r\n\r\nclass Converter:\r\n\t@staticmethod\r\n\tdef get_vault_name(vaults, vault_id, is_op_cli_v2):\r\n\t\tfor vault in vaults:\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\tif vault.get(\"id\", \"\") == vault_id:\r\n\t\t\t\t\treturn vault.get(\"name\", \"\")\r\n\t\t\telse:\r\n\t\t\t\tif vault.get(\"uuid\", \"\") == vault_id:\r\n\t\t\t\t\treturn vault.get(\"name\", \"\")\r\n\t\t\r\n\t\treturn \"\"\r\n\r\n\t@staticmethod\r\n\tdef convert_items(vaults, items, is_op_cli_v2):\r\n\t\tobjects = []\r\n\r\n\t\tfor item in items:\r\n\t\t\toverview = None\r\n\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\toverview = item\r\n\t\t\telse:\r\n\t\t\t\toverview = item.get(\"overview\", None)\r\n\r\n\t\t\tif overview == None:\r\n\t\t\t\tcontinue\r\n\r\n\t\t\tid = \"\"\r\n\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\tid = item.get(\"id\", \"\")\r\n\t\t\telse:\r\n\t\t\t\tid = item.get(\"uuid\", \"\")\r\n\t\t\t\r\n\t\t\ttitle = overview.get(\"title\", \"N/A\")\r\n\t\t\turl = overview.get(\"url\", \"\")\r\n\r\n\t\t\tvault_id = \"\"\r\n\r\n\t\t\tif is_op_cli_v2:\r\n\t\t\t\titem_vault = item.get(\"vault\", None)\r\n\r\n\t\t\t\tif item_vault:\r\n\t\t\t\t\tvault_id = item_vault.get(\"id\", \"\")\r\n\t\t\telse:\r\n\t\t\t\tvault_id = item.get(\"vaultUuid\", \"\")\r\n\r\n\t\t\tvault_name = Converter.get_vault_name(vaults, vault_id, is_op_cli_v2)\r\n\r\n\t\t\titem_details = item.get(\"__item_details\", None)\r\n\t\t\tconverted_item_details = None\r\n\r\n\t\t\tif item_details is not None:\r\n\t\t\t\tconverted_item_details = Converter.convert_item(item_details, is_op_cli_v2)\r\n\r\n\t\t\tcred_type = \"DynamicCredential\" if converted_item_details is None else \"Credential\"\r\n\r\n\t\t\tcred = {\r\n\t\t\t\t\"Type\": cred_type,\r\n\t\t\t\t\"ID\": id,\r\n\t\t\t\t\"Name\": title,\r\n\t\t\t\t\"Path\": vault_name\r\n\t\t\t}\r\n\r\n\t\t\tif url != \"\":\r\n\t\t\t\tcred[\"URL\"] = url\r\n\t\t\t\r\n\t\t\tif converted_item_details is not None:\r\n\t\t\t\tfor key in converted_item_details:\r\n\t\t\t\t\tcred[key] = converted_item_details[key]\r\n\r\n\t\t\tobjects.append(cred)\r\n\r\n\t\tobjects = sorted(objects, key = lambda i: (i[\"Path\"], i[\"Name\"]))\r\n\r\n\t\tstore = {\r\n\t\t\t\"Objects\": objects\r\n\t\t}\r\n\r\n\t\treturn store\r\n\t\r\n\t@staticmethod\r\n\tdef convert_item(item_details, is_op_cli_v2):\r\n\t\tusername = None\r\n\t\tpassword = None\r\n\t\tprivate_key = None\r\n\r\n\t\tif is_op_cli_v2:\r\n\t\t\tfields = item_details.get(\"fields\")\r\n\r\n\t\t\tif not fields:\r\n\t\t\t\tfields = item_details\r\n\r\n\t\t\tfor field in fields:\r\n\t\t\t\tfield_id = field.get(\"id\", None)\r\n\t\t\t\tfield_label = field.get(\"label\", None)\r\n\t\t\t\tfield_value = field.get(\"value\", None)\r\n\r\n\t\t\t\tif field_id == \"username\" or field_label == \"username\":\r\n\t\t\t\t\tusername = field_value\r\n\t\t\t\telif field_id == \"password\" or field_label == \"password\":\r\n\t\t\t\t\tpassword = field_value\r\n\t\t\t\telif field_id == \"private_key\" or field_label == \"private key\":\r\n\t\t\t\t\tprivate_key = field_value\r\n\t\telse:\r\n\t\t\tusername = item_details.get(\"username\", None)\r\n\t\t\tpassword = item_details.get(\"password\", None)\r\n\r\n\t\tcred = { }\r\n\r\n\t\tif username is not None:\r\n\t\t\tcred[\"Username\"] = username\r\n\r\n\t\tif password is not None:\r\n\t\t\tcred[\"Password\"] = password\r\n\r\n\t\tif private_key is not None:\r\n\t\t\tcred[\"KeyFileContent\"] = private_key\r\n\r\n\t\treturn cred\r\n\r\nclass Coordinator:\r\n\top_path = \"\"\r\n\tsign_in_address = \"\"\r\n\temail_address = \"\"\r\n\tsecret_key = \"\"\r\n\tmaster_password = \"\"\r\n\tcreate_dynamic_credentials = True\r\n\r\n\terror_message_sign_in = \"Error while signing in: \"\r\n\terror_message_sign_out = \"Error while signing out: \"\r\n\terror_message_get_vaults = \"Error while getting vaults: \"\r\n\terror_message_get_items = \"Error while getting items: \"\r\n\terror_message_get_item_details = \"Error while getting item details: \"\r\n\r\n\tdef __init__(self, op_path_windows, op_path_macOS, sign_in_address, email_address, secret_key, master_password, create_dynamic_credentials):\r\n\t\tself.op_path = op_path_macOS if RoyalUtils.is_macOS() else op_path_windows\r\n\t\tself.sign_in_address = sign_in_address\r\n\t\tself.email_address = email_address\r\n\t\tself.secret_key = secret_key\r\n\t\tself.master_password = master_password\r\n\t\tself.create_dynamic_credentials = create_dynamic_credentials\r\n\t\t\r\n\tdef get_items(self, vault):\r\n\t\top = OnePassword(self.op_path)\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception:\r\n\t\t\tpass\r\n\r\n\t\ttry:\r\n\t\t\top.sign_in(self.sign_in_address, self.email_address, self.secret_key, self.master_password)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_in, e)\r\n\r\n\t\tvaults = None\r\n\r\n\t\ttry:\r\n\t\t\tvaults = op.get_vaults()\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_get_vaults, e)\r\n\t\t\r\n\t\titems = None\r\n\r\n\t\ttry:\r\n\t\t\titems = op.get_items(vault)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_get_items, e)\r\n\t\t\r\n\t\titems_details = [ ]\r\n\r\n\t\tif not self.create_dynamic_credentials:\r\n\t\t\tfor item in items:\r\n\t\t\t\tid = \"\"\r\n\r\n\t\t\t\tif op.is_op_cli_v2:\r\n\t\t\t\t\tid = item.get(\"id\", \"\")\r\n\t\t\t\telse:\r\n\t\t\t\t\tid = item.get(\"uuid\", \"\")\r\n\t\t\t\t\r\n\t\t\t\titem_details = op.get_item_details(id)\r\n\r\n\t\t\t\titem[\"__item_details\"] = item_details\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_out, e)\r\n\r\n\t\tstore = Converter.convert_items(vaults, items, op.is_op_cli_v2)\r\n\t\tstore_json = RoyalUtils.to_json(store, True)\r\n\r\n\t\tprint(store_json)\r\n\t\r\n\tdef get_item_details(self, item_id):\r\n\t\top = OnePassword(self.op_path)\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception:\r\n\t\t\tpass\r\n\r\n\t\ttry:\r\n\t\t\top.sign_in(self.sign_in_address, self.email_address, self.secret_key, self.master_password)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_in, e)\r\n\r\n\t\titem_details = None\r\n\r\n\t\ttry:\r\n\t\t\titem_details = op.get_item_details(item_id)\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_get_item_details, e)\r\n\r\n\t\ttry:\r\n\t\t\top.sign_out()\r\n\t\texcept Exception as e:\r\n\t\t\tRoyalUtils.exit_with_error(self.error_message_sign_out, e)\r\n\r\n\t\tstore = Converter.convert_item(item_details, op.is_op_cli_v2)\r\n\t\tstore_json = RoyalUtils.to_json(store, True)\r\n\r\n\t\tprint(store_json)\r\n\r\ncoordinator = Coordinator(op_path_windows, op_path_macOS, sign_in_address, email_address, secret_key, master_password, create_dynamic_credentials)\r\n# coordinator.get_items(filter_vault)\r\ncoordinator.get_item_details(item_id)" - } - ] -} From 71a32324c5516affb3854ae6de4bdfd3982995a3 Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Mon, 7 Aug 2023 23:51:09 +0200 Subject: [PATCH 3/9] add royalts7+ and royaltsx6+ as requirements in notes --- Dynamic Folder/1Password/1Password v8 (Python).rdfx | 1 + 1 file changed, 1 insertion(+) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index 34aa16e..2bc9efa 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -32,6 +32,7 @@

Requirements

    +
  • Royal TS v7 or higher / Royal TSX v6 or higher
  • 1Password CLI tool (Version 2+)
  • Python 3 (Python 2 is not supported)
  • Python Module: sys
  • From f252d674152e2809f0925db9a29a80fb69fb60e1 Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Thu, 2 Nov 2023 02:18:46 +0000 Subject: [PATCH 4/9] ignore MISSING POOLS alerts --- .../1Password/1Password v8 (Python).rdfx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index 2bc9efa..e6bc3b4 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -150,7 +150,9 @@ class OnePassword: cmd_list_items.append("--vault") cmd_list_items.append(vault) - op = subprocess.Popen(cmd_list_items, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + env = os.environ.copy() + env["OBJC_DEBUG_MISSING_POOLS"] = "NO" + op = subprocess.Popen(cmd_list_items, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) (output, err) = op.communicate() exit_code = op.wait() success = exit_code == 0 @@ -181,8 +183,9 @@ class OnePassword: cmd_get_item.append("--account") cmd_get_item.append(self.account) - op = subprocess.Popen(cmd_get_item, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - + env = os.environ.copy() + env["OBJC_DEBUG_MISSING_POOLS"] = "NO" + op = subprocess.Popen(cmd_get_item, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) (output, err) = op.communicate() exit_code = op.wait() @@ -384,7 +387,9 @@ class OnePassword: cmd_list_items.append("--vault") cmd_list_items.append(vault) - op = subprocess.Popen(cmd_list_items, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + env = os.environ.copy() + env["OBJC_DEBUG_MISSING_POOLS"] = "NO" + op = subprocess.Popen(cmd_list_items, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) (output, err) = op.communicate() exit_code = op.wait() success = exit_code == 0 @@ -415,8 +420,9 @@ class OnePassword: cmd_get_item.append("--account") cmd_get_item.append(self.account) - op = subprocess.Popen(cmd_get_item, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - + env = os.environ.copy() + env["OBJC_DEBUG_MISSING_POOLS"] = "NO" + op = subprocess.Popen(cmd_get_item, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) (output, err) = op.communicate() exit_code = op.wait() From 7960062085d4ed87eac6e26c4074a084c216f738 Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Thu, 2 Nov 2023 02:21:56 +0000 Subject: [PATCH 5/9] add required python module + reorder import --- Dynamic Folder/1Password/1Password v8 (Python).rdfx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index e6bc3b4..3caa545 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -83,9 +83,10 @@ python - python - Date: Thu, 2 Nov 2023 13:13:33 +0000 Subject: [PATCH 6/9] be more explicit in 1Password app/CLI requirements --- Dynamic Folder/1Password/1Password v8 (Python).rdfx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index 3caa545..2b92fd7 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -21,7 +21,7 @@

    1Password v8 Dynamic Folder sample

    -

    This Dynamic Folder sample allows you to import credentials from 1Password. Requires 1Password version 8 or above. The 1Password CLI tool is required and the path where it is installed must be configured in the "Custom Properties" section.

    +

    This Dynamic Folder sample allows you to import credentials from 1Password. It requires both the 1Password app (version 8 or above) and the 1Password CLI tool (version 2.19 or above). The 1Password CLI tool must be installed, and the path where it is installed must be configured in the "Custom Properties" section. You also need to turn on the 1Password desktop integration (Settings -> Developer -> enable "Integrate with 1Password CLI").

    Items are imported as Dynamic Credentials. This means that the username and password fields will remain empty after reloading the dynamic folder and only be requested when a connection is established that uses one of the credentials of this dynamic folder.

    From 03d459adb47f49840739f16bf72b3156c709da44 Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Sun, 5 Nov 2023 01:17:45 +0000 Subject: [PATCH 7/9] clarify that the desktop integration is within 1Password (and not Royal) --- Dynamic Folder/1Password/1Password v8 (Python).rdfx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index 2b92fd7..03d9a83 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -21,7 +21,7 @@

    1Password v8 Dynamic Folder sample

    -

    This Dynamic Folder sample allows you to import credentials from 1Password. It requires both the 1Password app (version 8 or above) and the 1Password CLI tool (version 2.19 or above). The 1Password CLI tool must be installed, and the path where it is installed must be configured in the "Custom Properties" section. You also need to turn on the 1Password desktop integration (Settings -> Developer -> enable "Integrate with 1Password CLI").

    +

    This Dynamic Folder sample allows you to import credentials from 1Password. It requires both the 1Password app (version 8 or above) and the 1Password CLI tool (version 2.19 or above). The 1Password CLI tool must be installed, and the path where it is installed must be configured in the "Custom Properties" section. You also need to turn on the 1Password CLI/desktop integration in the 1Password app (Settings -> Developer -> enable "Integrate with 1Password CLI").

    Items are imported as Dynamic Credentials. This means that the username and password fields will remain empty after reloading the dynamic folder and only be requested when a connection is established that uses one of the credentials of this dynamic folder.

    From 4ebf3868e9d6715feb720183bfcea56d148ba5d7 Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Wed, 22 Nov 2023 03:59:22 +0100 Subject: [PATCH 8/9] re-add support for ssh-keys contained within 1pass --- .../1Password/1Password v8 (Python).rdfx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index 03d9a83..094f388 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -250,6 +250,7 @@ class Converter: def convert_item(item_details): username = None password = None + private_key = None fields = item_details.get("fields") @@ -260,9 +261,10 @@ class Converter: if field_id == "username" or field_label == "username": username = field_value - - if field_id == "password" or field_label == "password": + elif field_id == "password" or field_label == "password": password = field_value + elif field_id == "private_key" or field_label == "private key": + private_key = field_value cred = { } @@ -272,6 +274,9 @@ class Converter: if password is not None: cred["Password"] = password + if private_key is not None: + cred["KeyFileContent"] = private_key + return cred class Coordinator: @@ -488,6 +493,7 @@ class Converter: def convert_item(item_details): username = None password = None + private_key = None fields = item_details.get("fields") @@ -498,9 +504,10 @@ class Converter: if field_id == "username" or field_label == "username": username = field_value - - if field_id == "password" or field_label == "password": + elif field_id == "password" or field_label == "password": password = field_value + elif field_id == "private_key" or field_label == "private key": + private_key = field_value cred = { } @@ -510,6 +517,9 @@ class Converter: if password is not None: cred["Password"] = password + if private_key is not None: + cred["KeyFileContent"] = private_key + return cred class Coordinator: From 148d77bcc9e775bd2ad82e6b9920297f4362368c Mon Sep 17 00:00:00 2001 From: Joachim Tingvold Date: Wed, 22 Nov 2023 16:51:55 +0100 Subject: [PATCH 9/9] semantics; requires 1pass v8+ (not v8 only) --- Dynamic Folder/1Password/1Password v8 (Python).rdfx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dynamic Folder/1Password/1Password v8 (Python).rdfx b/Dynamic Folder/1Password/1Password v8 (Python).rdfx index 094f388..a337fa2 100644 --- a/Dynamic Folder/1Password/1Password v8 (Python).rdfx +++ b/Dynamic Folder/1Password/1Password v8 (Python).rdfx @@ -3,7 +3,7 @@ DynamicFolder - 1Password v8 (Python) + 1Password v8+ (Python) This Dynamic Folder sample allows you to import dynamic credentials from 1Password v8+ @@ -19,7 +19,7 @@ .cs8A075B43{text-align:left;text-indent:-18pt;margin:0pt 0pt 0pt 36pt} .csEA39B13A{color:#000000;background-color:transparent;font-family:Arial;font-size:12pt;font-weight:normal;font-style:normal;} -

    1Password v8 Dynamic Folder sample

    +

    1Password v8+ Dynamic Folder sample

    This Dynamic Folder sample allows you to import credentials from 1Password. It requires both the 1Password app (version 8 or above) and the 1Password CLI tool (version 2.19 or above). The 1Password CLI tool must be installed, and the path where it is installed must be configured in the "Custom Properties" section. You also need to turn on the 1Password CLI/desktop integration in the 1Password app (Settings -> Developer -> enable "Integrate with 1Password CLI").