This repository was archived by the owner on Jul 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcredentials.py
More file actions
100 lines (83 loc) · 4.5 KB
/
credentials.py
File metadata and controls
100 lines (83 loc) · 4.5 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
# -*- python-indent-offset: 4 -*-
'''
credentials: utilities for interacting with the user's keyring/keychain.
This is used by CasicsDB and other functions to retrieve user login & password
for the CASICS database, as well as host and port info for the database server.
'''
__version__ = '1.0.0'
__author__ = 'Michael Hucka <mhucka@caltech.edu>'
__email__ = 'mhucka@caltech.edu'
__license__ = 'GPLv3'
import getpass
import keyring
# Credentials/keyring functions
# .............................................................................
# Explanation about the weird way this is done: the Python keyring module
# only offers a single function for setting a value; ostensibly, this is
# intended to store a password associated with an identifier (a user name),
# and this identifier is expected to be obtained some other way, such as by
# using the current user's computer login name. This poses 2 problems for us:
#
# 1. The user may want to use a different user name for the remote service,
# so we can't assume the user's computer login name is the same. We also
# don't want to ask for the remote user name every time we need the
# information, because that can end up presenting a dialog to the user every
# time, which quickly becomes unbearably annoying. This means we can't use
# a user-generated identifer to access the keyring value -- we have to
# invent a value, and then store the user's name for the remote service as
# part of the value we store. (Here, we use the fake user name "username" to
# access the value stored in the user's keyring for a given service.)
#
# 2. We need to store several pieces of information, not just a password,
# but the Python keyring module interface (and presumably most system
# keychains) does not allow anything but a string value. The hackacious
# solution taken here is to concatenate several values into a single string
# used as the actual value stored. The individual values are separated by a
# character that is unlikely to be part of any user-typed value.
def get_credentials(service, user=None):
'''Looks up the user's credentials for the given 'service' using the
keyring/keychain facility on this computer. If 'user' is None, this uses
the fake user named "credentials". The latter makes it possible to access a
service with a different user login name than the user's current login
name without having to ask the user for the alternative name every time.
'''
value = keyring.get_password(service, user if user else 'credentials')
return _decode(value) if value else (None, None, None, None)
def save_credentials(service, user, pswd, host=None, port=None):
'''Saves the user, password, host and port info for 'service'.'''
user = user if user else ''
pswd = pswd if pswd else ''
host = host if host else ''
port = port if port else ''
keyring.set_password(service, 'credentials', _encode(user, pswd, host, port))
def obtain_credentials(service, display_name,
user=None, pswd=None, host=None, port=None,
default_host=None, default_port=None):
'''As the user for credentials for 'service'.'''
(s_user, s_pswd, s_host, s_port) = (None, None, None, None)
if service:
# If we're given a service, retrieve the stored (if any) for defaults.
(s_user, s_pswd, s_host, s_port) = get_credentials(service)
if host is not -1 and not host:
host = s_host or input("{} host (default: {}): ".format(display_name,
default_host))
host = host or default_host
if port is not -1 and not port:
port = s_port or input("{} port (default: {}): ".format(display_name,
default_port))
port = port or default_port
if not user:
user = s_user or input("{} user name: ".format(display_name))
if not pswd:
pswd = s_pswd or getpass.getpass('{} password: '.format(display_name))
return (user, pswd, host, port)
_sep = ''
'''Character used to separate multiple actual values stored as a single
encoded value string. This character is deliberately chosen to be something
very unlikely to be part of a legitimate string value typed by user at a
shell prompt, because control-c is normally used to interrupt programs.
'''
def _encode(user, pswd, host, port):
return '{}{}{}{}{}{}{}'.format(user, _sep, pswd, _sep, host, _sep, port)
def _decode(value_string):
return tuple(value_string.split(_sep))