-
Notifications
You must be signed in to change notification settings - Fork 21
Expand file tree
/
Copy pathcredentials.py
More file actions
120 lines (95 loc) · 3.53 KB
/
credentials.py
File metadata and controls
120 lines (95 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import base64
import os
try:
import keyring
except ImportError:
keyring = None
from upcloud_api.errors import UpCloudClientError
ENV_KEY_USERNAME = "UPCLOUD_USERNAME"
ENV_KEY_PASSWORD = "UPCLOUD_PASSWORD" # noqa: S105
ENV_KEY_TOKEN = "UPCLOUD_TOKEN" # noqa: S105
KEYRING_SERVICE_NAME = "UpCloud"
KEYRING_TOKEN_USER = ""
KEYRING_GO_PREFIX = "go-keyring-base64:"
def _parse_keyring_value(value: str) -> str:
if value.startswith(KEYRING_GO_PREFIX):
value = value[len(KEYRING_GO_PREFIX) :]
return base64.b64decode(value).decode()
return value
def _read_keyring_value(username: str) -> str:
if keyring is None:
return None
value = keyring.get_password(KEYRING_SERVICE_NAME, username)
try:
return _parse_keyring_value(value) if value else None
except Exception:
raise UpCloudClientError(
f"Failed to read keyring value for {username}. Ensure that the value saved to the system keyring is correct."
) from None
class Credentials:
"""
Class for handling UpCloud API credentials.
"""
def __init__(self, username: str = None, password: str = None, token: str = None):
"""
Initializes the Credentials object with username, password and/or token. Use `parse` method to read credentials from environment variables or keyring.
"""
self._username = username
self._password = password
self._token = token
@property
def authorization(self) -> str:
"""
Returns the authorization header value based on the provided credentials.
"""
if self._token:
return f"Bearer {self._token}"
credentials = f"{self._username}:{self._password}".encode()
encoded_credentials = base64.b64encode(credentials).decode()
return f"Basic {encoded_credentials}"
@property
def dict(self) -> dict:
"""
Returns the credentials as a dictionary.
"""
return {
"username": self._username,
"password": self._password,
"token": self._token,
}
@property
def is_defined(self) -> bool:
"""
Checks if the credentials are defined.
"""
return bool(self._username and self._password or self._token)
def _read_from_env(self):
if not self._username:
self._username = os.getenv(ENV_KEY_USERNAME)
if not self._password:
self._password = os.getenv(ENV_KEY_PASSWORD)
if not self._token:
self._token = os.getenv(ENV_KEY_TOKEN)
def _read_from_keyring(self):
if self._username and not self._password:
self._password = _read_keyring_value(self._username)
if self.is_defined:
return
self._token = _read_keyring_value(KEYRING_TOKEN_USER)
@classmethod
def parse(cls, username: str = None, password: str = None, token: str = None):
"""
Parses credentials from the provided parameters, environment variables or the system keyring.
"""
credentials = cls(username, password, token)
if credentials.is_defined:
return credentials
credentials._read_from_env()
if credentials.is_defined:
return credentials
credentials._read_from_keyring()
if credentials.is_defined:
return credentials
raise UpCloudClientError(
f"Credentials not found. These must be set in configuration, via environment variables or in the system keyring ({KEYRING_SERVICE_NAME})"
)