Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Service Account support #54

Merged
merged 2 commits into from
Dec 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,16 @@ The library is split into two parts: installation and client in which we are slo
systems as possible the following table should ensure users understand what this library can and can't do at time of
install.

| | MacOS | Linux |
|-----------------|-------|-------|
| Fully supported | Y | Y |
| CLI install | Y | Y |
| SSO login | Y | Y |
| Login via App | Y | Y |
| Biometrics auth | Y | Y |
| Password auth | Y | Y |
| CLI client | Y | Y |
| | MacOS | Linux |
|------------------|-------|-------|
| Fully supported | Y | Y |
| CLI install | Y | Y |
| SSO login | Y | Y |
| Login via App | Y | Y |
| Biometrics auth | Y | Y |
| Password auth | Y | Y |
| CLI client | Y | Y |
| Service account | Y | Y |

## Installation
```bash
Expand Down Expand Up @@ -106,7 +107,29 @@ op.get_item(uuid="example", fields="username")
op.get_item(uuid="example", fields=["username", "password"])

```
##
### Service Accounts
We also support authentication using Service accounts, however these are not interchangeable with other auth routes and
hence other accounts i.e. this token based authentication will take precedence over any other method.
If you wish to use multiple account types, for now you will need to design this workflow yourself
by clearing out the OP_SERVICE_ACCOUNT_TOKEN using `unset OP_SERVICE_ACCOUNT_TOKEN` before re-authenticating with
your preferred account.

To use a service account make sure to fulfil the requirements here:
https://developer.1password.com/docs/service-accounts/use-with-1password-cli
and note that not all of the CLI commands are supported at this time.

Also note that your service account will only have access to certain vaults. In particular it will not be able to see
the `Shared` or `Private` vaults in any account. In our client this means you must always use the `vault` option.

Once you have fulfilled all the requirements, namely `export OP_SERVICE_ACCOUNT_TOKEN=<your token>`, you can then use
our client with:

```python
from onepassword import OnePassword

op = OnePassword()
op.list_vaults()
```

### Input formats
To be sure what you are using is of the right format
Expand Down
37 changes: 29 additions & 8 deletions onepassword/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
_spawn_signin
from onepassword.exceptions import OnePasswordForgottenPassword

SERVICE_ACCOUNT_TOKEN = "OP_SERVICE_ACCOUNT_TOKEN"


class SignIn:
"""
Expand All @@ -32,7 +34,8 @@ def get_account(self, bash_profile: BashProfile | None = None) -> str:

@staticmethod
def _input_account() -> str:
account = input("Please input your 1Password account name e.g. wandera from wandera.1password.com: ")
account = input("Please input your 1Password personal or business acount name e.g. company from "
"company.1password.com: ")
return account

def _get_account_bash(self, bash_profile: BashProfile) -> str:
Expand Down Expand Up @@ -250,6 +253,22 @@ def signin(self, account: str | None = None) -> None:
self._update_bash_account(account, bash_profile)


class ServiceSignIn(SignIn):
def __init__(self):
self.signin()

def signin(self):
if os.environ['OP_SERVICE_ACCOUNT_TOKEN'] != "":
print("Using service account, for supported commands see: "
"https://developer.1password.com/docs/service-accounts/use-with-1password-cli#supported-commands")
self.account_details = yaml.safe_load(read_bash_return("op user get --me", single=False))
self.account = self.account_details["Name"]
else:
print("No service account found, please setup on the web version of 1Password for more information go here:"
" https://developer.1password.com/docs/service-accounts/use-with-1password-cli")



class OnePassword:
"""
Class for integrating with a OnePassword password manager
Expand All @@ -260,14 +279,16 @@ class OnePassword:
"""
def __init__(self, signin_method: str = "app", account: str | None = None, password: str | None = None) -> None:
# pragma: no cover

if signin_method == "app":
self.signin_strategy = AppSignIn(account)
elif signin_method == "manual":
self.signin_strategy = ManualSignIn(account, password)
if SERVICE_ACCOUNT_TOKEN in os.environ.keys():
self.signin_strategy = ServiceSignIn()
else:
raise ValueError("Unrecognised 'signin_method', options are: 'app' or 'manual'. "
"See: https://developer.1password.com/docs/cli/verify")
if signin_method == "app":
self.signin_strategy = AppSignIn(account)
elif signin_method == "manual":
self.signin_strategy = ManualSignIn(account, password)
else:
raise ValueError("Unrecognised 'signin_method', options are: 'app' or 'manual'. "
"See: https://developer.1password.com/docs/cli/verify")

def get_uuid(self, docname: str, vault: str = "Private") -> str: # pragma: no cover
"""
Expand Down
Loading