-
-
Notifications
You must be signed in to change notification settings - Fork 150
/
SecretService.py
124 lines (107 loc) · 4.48 KB
/
SecretService.py
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
121
122
123
124
from contextlib import closing
import logging
from ..util import properties
from ..backend import KeyringBackend
from ..credentials import SimpleCredential
from ..errors import (
InitError,
PasswordDeleteError,
ExceptionRaisedContext,
KeyringLocked,
)
try:
import secretstorage
import secretstorage.exceptions as exceptions
except ImportError:
pass
except AttributeError:
# See https://github.com/jaraco/keyring/issues/296
pass
log = logging.getLogger(__name__)
class Keyring(KeyringBackend):
"""Secret Service Keyring"""
appid = 'Python keyring library'
@properties.ClassProperty
@classmethod
def priority(cls):
with ExceptionRaisedContext() as exc:
secretstorage.__name__
if exc:
raise RuntimeError("SecretStorage required")
if secretstorage.__version_tuple__[0] < 3:
raise RuntimeError("SecretStorage 3.0 or newer required")
try:
with closing(secretstorage.dbus_init()) as connection:
list(secretstorage.get_all_collections(connection))
except exceptions.SecretStorageException as e:
raise RuntimeError("Unable to initialize SecretService: %s" % e)
return 5
def get_preferred_collection(self):
"""If self.preferred_collection contains a D-Bus path,
the collection at that address is returned. Otherwise,
the default collection is returned.
"""
bus = secretstorage.dbus_init()
try:
if hasattr(self, 'preferred_collection'):
collection = secretstorage.Collection(bus, self.preferred_collection)
else:
collection = secretstorage.get_default_collection(bus)
except exceptions.SecretStorageException as e:
raise InitError("Failed to create the collection: %s." % e)
if collection.is_locked():
collection.unlock()
if collection.is_locked(): # User dismissed the prompt
raise KeyringLocked("Failed to unlock the collection!")
return collection
def unlock(self, item):
if hasattr(item, 'unlock'):
item.unlock()
if item.is_locked(): # User dismissed the prompt
raise KeyringLocked('Failed to unlock the item!')
def get_password(self, service, username):
"""Get password of the username for the service
"""
collection = self.get_preferred_collection()
with closing(collection.connection):
items = collection.search_items({"username": username, "service": service})
for item in items:
self.unlock(item)
return item.get_secret().decode('utf-8')
def set_password(self, service, username, password):
"""Set password for the username of the service
"""
collection = self.get_preferred_collection()
attributes = {
"application": self.appid,
"service": service,
"username": username,
}
label = "Password for '{}' on '{}'".format(username, service)
with closing(collection.connection):
collection.create_item(label, attributes, password, replace=True)
def delete_password(self, service, username):
"""Delete the stored password (only the first one)
"""
collection = self.get_preferred_collection()
with closing(collection.connection):
items = collection.search_items({"username": username, "service": service})
for item in items:
return item.delete()
raise PasswordDeleteError("No such password!")
def get_credential(self, service, username):
"""Gets the first username and password for a service.
Returns a Credential instance
The username can be omitted, but if there is one, it will use get_password
and return a SimpleCredential containing the username and password
Otherwise, it will return the first username and password combo that it finds.
"""
if username:
return SimpleCredential(username, self.get_password(service, username))
collection = self.get_preferred_collection()
with closing(collection.connection):
items = collection.search_items({"service": service})
for item in items:
self.unlock(item)
username = item.get_attributes().get("username")
return SimpleCredential(username, item.get_secret().decode('utf-8'))