/
connection.py
153 lines (128 loc) · 5.37 KB
/
connection.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# Copyright (c) 2017 The Regents of the University of Michigan
# All rights reserved.
# This software is licensed under the BSD 3-Clause License.
import subprocess
import logging
from os.path import expanduser
from deprecation import deprecated
import pymongo
from .errors import ConfigError
from ..version import __version__
PYMONGO_2 = pymongo.version_tuple[0] == 2
logger = logging.getLogger(__name__)
AUTH_NONE = 'none'
AUTH_SCRAM_SHA_1 = 'SCRAM-SHA-1'
AUTH_SSL = 'SSL'
AUTH_SSL_x509 = 'SSL-x509'
"""
THIS MODULE IS DEPRECATED!
"""
@deprecated(deprecated_in="1.3", removed_in="2.0", current_version=__version__,
details="The connection module is deprecated.")
def get_subject_from_certificate(fn_certificate): # pragma no cover
try:
cert_txt = subprocess.check_output(
['openssl', 'x509', '-in', fn_certificate,
'-inform', 'PEM', '-subject', '-nameopt', 'RFC2253']).decode()
except subprocess.CalledProcessError:
msg = "Unable to retrieve subject from certificate '{}'."
raise RuntimeError(msg.format(fn_certificate))
else:
lines = cert_txt.split('\n')
assert lines[0].startswith('subject=')
return lines[0][len('subject='):].strip()
@deprecated(deprecated_in="1.3", removed_in="2.0", current_version=__version__,
details="The connection module is deprecated.")
def raise_unsupported_auth_mechanism(mechanism):
msg = "Auth mechanism '{}' not supported."
raise ValueError(msg.format(mechanism))
@deprecated(deprecated_in="1.3", removed_in="2.0", current_version=__version__,
details="The connection module is deprecated.")
class DBClientConnector(object):
def __init__(self, host_config, **kwargs):
self._config = host_config
self._client = None
self._kwargs = kwargs
@property
def client(self):
if self._client is None:
raise RuntimeError("Client not connected.")
else:
return self._client
@property
def host(self):
return self._config['url']
@property
def config(self):
return dict(self._config)
def _config_get(self, key, default=None):
return self._config.get(key, default)
def _config_get_required(self, key):
try:
return self._config[key]
except KeyError as e:
raise ConfigError("Missing required key '{}'.".format(e))
def _connect_pymongo3(self, host):
logger.debug("Connecting with pymongo3.")
forwarded_parameters = (
'socketTimeoutMS', 'connectTimeoutMS', 'serverSelectionTimeoutMS',
'w', 'wtimeout', 'replicaSet')
parameters = self._kwargs
for parameter in forwarded_parameters:
if parameter in self._config:
parameters[parameter] = self._config_get(parameter)
auth_mechanism = self._config_get('auth_mechanism', AUTH_NONE)
if auth_mechanism in (AUTH_NONE, AUTH_SCRAM_SHA_1):
client = pymongo.MongoClient(
host,
read_preference=getattr(
pymongo.read_preferences.ReadPreference,
self._config_get('read_preference', 'PRIMARY')),
** parameters)
else:
raise_unsupported_auth_mechanism(auth_mechanism)
self._client = client
def connect(self, host=None):
if host is None:
host = self._config_get_required('url')
logger.debug("Connecting to host '{host}'.".format(
host=self._config_get_required('url')))
if PYMONGO_2:
raise RuntimeError("pymongo version 2.x is no longer supported.")
else:
self._connect_pymongo3(host)
def authenticate(self):
auth_mechanism = self._config_get('auth_mechanism', AUTH_NONE)
logger.debug("Authenticating: mechanism={}".format(auth_mechanism))
if auth_mechanism == AUTH_SCRAM_SHA_1:
db_auth = self.client[self._config.get('db_auth', 'admin')]
username = self._config_get_required('username')
msg = "Authenticating user '{user}' with database '{db}'."
logger.debug(msg.format(user=username, db=db_auth))
db_auth.authenticate(
username,
self._config_get_required('password'),
mechanism=AUTH_SCRAM_SHA_1)
elif auth_mechanism in (AUTH_SSL, AUTH_SSL_x509): # pragma no cover
certificate_subject = get_subject_from_certificate(
expanduser(self._config_get_required('ssl_certfile')))
logger.debug("Authenticating: user={}".format(certificate_subject))
db_external = self.client['$external']
db_external.authenticate(
certificate_subject, mechanism='MONGODB-X509')
elif auth_mechanism == AUTH_NONE:
pass
else:
raise_unsupported_auth_mechanism(auth_mechanism)
def logout(self):
auth_mechanism = self._config_get('auth_mechanism', AUTH_NONE)
if auth_mechanism == AUTH_SCRAM_SHA_1:
db_auth = self.client['admin']
db_auth.logout()
elif auth_mechanism in (AUTH_SSL, AUTH_SSL_x509): # pragma no cover
db_external = self.client['$external']
db_external.logout()
elif auth_mechanism == AUTH_NONE:
pass
else:
raise_unsupported_auth_mechanism(auth_mechanism)