sensu-plugins-mongodb-mrtrotl 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +1 -0
- data/LICENSE +22 -0
- data/README.md +27 -0
- data/bin/check-mongodb-metric.rb +144 -0
- data/bin/check-mongodb-query-count.rb +267 -0
- data/bin/check-mongodb.py +1644 -0
- data/bin/check-mongodb.rb +5 -0
- data/bin/metrics-mongodb-replication.rb +254 -0
- data/bin/metrics-mongodb.rb +133 -0
- data/lib/bson/__init__.py +1347 -0
- data/lib/bson/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/_helpers.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/binary.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/code.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/codec_options.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/dbref.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/decimal128.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/int64.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/json_util.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/max_key.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/min_key.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/objectid.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/raw_bson.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/regex.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/son.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/timestamp.cpython-310.pyc +0 -0
- data/lib/bson/__pycache__/tz_util.cpython-310.pyc +0 -0
- data/lib/bson/_cbson.cpython-310-x86_64-linux-gnu.so +0 -0
- data/lib/bson/_helpers.py +41 -0
- data/lib/bson/binary.py +364 -0
- data/lib/bson/code.py +101 -0
- data/lib/bson/codec_options.py +414 -0
- data/lib/bson/codec_options.pyi +100 -0
- data/lib/bson/dbref.py +133 -0
- data/lib/bson/decimal128.py +314 -0
- data/lib/bson/errors.py +35 -0
- data/lib/bson/int64.py +39 -0
- data/lib/bson/json_util.py +874 -0
- data/lib/bson/max_key.py +55 -0
- data/lib/bson/min_key.py +55 -0
- data/lib/bson/objectid.py +286 -0
- data/lib/bson/py.typed +2 -0
- data/lib/bson/raw_bson.py +175 -0
- data/lib/bson/regex.py +135 -0
- data/lib/bson/son.py +208 -0
- data/lib/bson/timestamp.py +124 -0
- data/lib/bson/tz_util.py +52 -0
- data/lib/gridfs/__init__.py +1015 -0
- data/lib/gridfs/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/gridfs/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/gridfs/__pycache__/grid_file.cpython-310.pyc +0 -0
- data/lib/gridfs/errors.py +33 -0
- data/lib/gridfs/grid_file.py +907 -0
- data/lib/gridfs/py.typed +2 -0
- data/lib/pymongo/__init__.py +185 -0
- data/lib/pymongo/__pycache__/__init__.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/_csot.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/aggregation.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/auth.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/auth_aws.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/bulk.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/change_stream.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/client_options.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/client_session.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/collation.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/collection.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/command_cursor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/common.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/compression_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/cursor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/daemon.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/database.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/driver_info.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/encryption.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/encryption_options.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/errors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/event_loggers.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/hello.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/helpers.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/max_staleness_selectors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/message.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/mongo_client.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/monitor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/monitoring.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/network.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ocsp_cache.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ocsp_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/operations.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/periodic_executor.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/pool.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/pyopenssl_context.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/read_concern.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/read_preferences.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/response.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/results.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/saslprep.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_api.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_description.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_selectors.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/server_type.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/settings.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/socket_checker.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/srv_resolver.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ssl_context.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/ssl_support.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/topology.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/topology_description.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/typings.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/uri_parser.cpython-310.pyc +0 -0
- data/lib/pymongo/__pycache__/write_concern.cpython-310.pyc +0 -0
- data/lib/pymongo/_cmessage.cpython-310-x86_64-linux-gnu.so +0 -0
- data/lib/pymongo/_csot.py +118 -0
- data/lib/pymongo/aggregation.py +229 -0
- data/lib/pymongo/auth.py +549 -0
- data/lib/pymongo/auth_aws.py +94 -0
- data/lib/pymongo/bulk.py +513 -0
- data/lib/pymongo/change_stream.py +457 -0
- data/lib/pymongo/client_options.py +302 -0
- data/lib/pymongo/client_session.py +1112 -0
- data/lib/pymongo/collation.py +224 -0
- data/lib/pymongo/collection.py +3204 -0
- data/lib/pymongo/command_cursor.py +353 -0
- data/lib/pymongo/common.py +984 -0
- data/lib/pymongo/compression_support.py +149 -0
- data/lib/pymongo/cursor.py +1345 -0
- data/lib/pymongo/daemon.py +141 -0
- data/lib/pymongo/database.py +1202 -0
- data/lib/pymongo/driver_info.py +42 -0
- data/lib/pymongo/encryption.py +884 -0
- data/lib/pymongo/encryption_options.py +221 -0
- data/lib/pymongo/errors.py +365 -0
- data/lib/pymongo/event_loggers.py +221 -0
- data/lib/pymongo/hello.py +219 -0
- data/lib/pymongo/helpers.py +259 -0
- data/lib/pymongo/max_staleness_selectors.py +114 -0
- data/lib/pymongo/message.py +1440 -0
- data/lib/pymongo/mongo_client.py +2144 -0
- data/lib/pymongo/monitor.py +440 -0
- data/lib/pymongo/monitoring.py +1801 -0
- data/lib/pymongo/network.py +311 -0
- data/lib/pymongo/ocsp_cache.py +87 -0
- data/lib/pymongo/ocsp_support.py +372 -0
- data/lib/pymongo/operations.py +507 -0
- data/lib/pymongo/periodic_executor.py +183 -0
- data/lib/pymongo/pool.py +1660 -0
- data/lib/pymongo/py.typed +2 -0
- data/lib/pymongo/pyopenssl_context.py +383 -0
- data/lib/pymongo/read_concern.py +75 -0
- data/lib/pymongo/read_preferences.py +609 -0
- data/lib/pymongo/response.py +109 -0
- data/lib/pymongo/results.py +217 -0
- data/lib/pymongo/saslprep.py +113 -0
- data/lib/pymongo/server.py +247 -0
- data/lib/pymongo/server_api.py +170 -0
- data/lib/pymongo/server_description.py +285 -0
- data/lib/pymongo/server_selectors.py +153 -0
- data/lib/pymongo/server_type.py +32 -0
- data/lib/pymongo/settings.py +159 -0
- data/lib/pymongo/socket_checker.py +104 -0
- data/lib/pymongo/srv_resolver.py +126 -0
- data/lib/pymongo/ssl_context.py +39 -0
- data/lib/pymongo/ssl_support.py +99 -0
- data/lib/pymongo/topology.py +890 -0
- data/lib/pymongo/topology_description.py +639 -0
- data/lib/pymongo/typings.py +39 -0
- data/lib/pymongo/uri_parser.py +624 -0
- data/lib/pymongo/write_concern.py +129 -0
- data/lib/pymongo-4.2.0.dist-info/INSTALLER +1 -0
- data/lib/pymongo-4.2.0.dist-info/LICENSE +201 -0
- data/lib/pymongo-4.2.0.dist-info/METADATA +250 -0
- data/lib/pymongo-4.2.0.dist-info/RECORD +167 -0
- data/lib/pymongo-4.2.0.dist-info/REQUESTED +0 -0
- data/lib/pymongo-4.2.0.dist-info/WHEEL +6 -0
- data/lib/pymongo-4.2.0.dist-info/top_level.txt +3 -0
- data/lib/sensu-plugins-mongodb/metrics.rb +391 -0
- data/lib/sensu-plugins-mongodb/version.rb +9 -0
- data/lib/sensu-plugins-mongodb.rb +1 -0
- metadata +407 -0
@@ -0,0 +1,383 @@
|
|
1
|
+
# Copyright 2019-present MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
4
|
+
# may not use this file except in compliance with the License. You
|
5
|
+
# may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
# implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
|
15
|
+
"""A CPython compatible SSLContext implementation wrapping PyOpenSSL's
|
16
|
+
context.
|
17
|
+
"""
|
18
|
+
|
19
|
+
import socket as _socket
|
20
|
+
import ssl as _stdlibssl
|
21
|
+
import sys as _sys
|
22
|
+
import time as _time
|
23
|
+
from errno import EINTR as _EINTR
|
24
|
+
from ipaddress import ip_address as _ip_address
|
25
|
+
|
26
|
+
from cryptography.x509 import load_der_x509_certificate as _load_der_x509_certificate
|
27
|
+
from OpenSSL import SSL as _SSL
|
28
|
+
from OpenSSL import crypto as _crypto
|
29
|
+
from service_identity import CertificateError as _SICertificateError
|
30
|
+
from service_identity import VerificationError as _SIVerificationError
|
31
|
+
from service_identity.pyopenssl import verify_hostname as _verify_hostname
|
32
|
+
from service_identity.pyopenssl import verify_ip_address as _verify_ip_address
|
33
|
+
|
34
|
+
from pymongo.errors import ConfigurationError as _ConfigurationError
|
35
|
+
from pymongo.errors import _CertificateError
|
36
|
+
from pymongo.ocsp_cache import _OCSPCache
|
37
|
+
from pymongo.ocsp_support import _load_trusted_ca_certs, _ocsp_callback
|
38
|
+
from pymongo.socket_checker import SocketChecker as _SocketChecker
|
39
|
+
from pymongo.socket_checker import _errno_from_exception
|
40
|
+
|
41
|
+
try:
|
42
|
+
import certifi
|
43
|
+
|
44
|
+
_HAVE_CERTIFI = True
|
45
|
+
except ImportError:
|
46
|
+
_HAVE_CERTIFI = False
|
47
|
+
|
48
|
+
PROTOCOL_SSLv23 = _SSL.SSLv23_METHOD
|
49
|
+
# Always available
|
50
|
+
OP_NO_SSLv2 = _SSL.OP_NO_SSLv2
|
51
|
+
OP_NO_SSLv3 = _SSL.OP_NO_SSLv3
|
52
|
+
OP_NO_COMPRESSION = _SSL.OP_NO_COMPRESSION
|
53
|
+
# This isn't currently documented for PyOpenSSL
|
54
|
+
OP_NO_RENEGOTIATION = getattr(_SSL, "OP_NO_RENEGOTIATION", 0)
|
55
|
+
|
56
|
+
# Always available
|
57
|
+
HAS_SNI = True
|
58
|
+
IS_PYOPENSSL = True
|
59
|
+
|
60
|
+
# Base Exception class
|
61
|
+
SSLError = _SSL.Error
|
62
|
+
|
63
|
+
# https://github.com/python/cpython/blob/v3.8.0/Modules/_ssl.c#L2995-L3002
|
64
|
+
_VERIFY_MAP = {
|
65
|
+
_stdlibssl.CERT_NONE: _SSL.VERIFY_NONE,
|
66
|
+
_stdlibssl.CERT_OPTIONAL: _SSL.VERIFY_PEER,
|
67
|
+
_stdlibssl.CERT_REQUIRED: _SSL.VERIFY_PEER | _SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
|
68
|
+
}
|
69
|
+
|
70
|
+
_REVERSE_VERIFY_MAP = dict((value, key) for key, value in _VERIFY_MAP.items())
|
71
|
+
|
72
|
+
|
73
|
+
# For SNI support. According to RFC6066, section 3, IPv4 and IPv6 literals are
|
74
|
+
# not permitted for SNI hostname.
|
75
|
+
def _is_ip_address(address):
|
76
|
+
try:
|
77
|
+
_ip_address(address)
|
78
|
+
return True
|
79
|
+
except (ValueError, UnicodeError): # noqa: B014
|
80
|
+
return False
|
81
|
+
|
82
|
+
|
83
|
+
# According to the docs for Connection.send it can raise
|
84
|
+
# WantX509LookupError and should be retried.
|
85
|
+
BLOCKING_IO_ERRORS = (_SSL.WantReadError, _SSL.WantWriteError, _SSL.WantX509LookupError)
|
86
|
+
|
87
|
+
|
88
|
+
def _ragged_eof(exc):
|
89
|
+
"""Return True if the OpenSSL.SSL.SysCallError is a ragged EOF."""
|
90
|
+
return exc.args == (-1, "Unexpected EOF")
|
91
|
+
|
92
|
+
|
93
|
+
# https://github.com/pyca/pyopenssl/issues/168
|
94
|
+
# https://github.com/pyca/pyopenssl/issues/176
|
95
|
+
# https://docs.python.org/3/library/ssl.html#notes-on-non-blocking-sockets
|
96
|
+
class _sslConn(_SSL.Connection):
|
97
|
+
def __init__(self, ctx, sock, suppress_ragged_eofs):
|
98
|
+
self.socket_checker = _SocketChecker()
|
99
|
+
self.suppress_ragged_eofs = suppress_ragged_eofs
|
100
|
+
super(_sslConn, self).__init__(ctx, sock)
|
101
|
+
|
102
|
+
def _call(self, call, *args, **kwargs):
|
103
|
+
timeout = self.gettimeout()
|
104
|
+
if timeout:
|
105
|
+
start = _time.monotonic()
|
106
|
+
while True:
|
107
|
+
try:
|
108
|
+
return call(*args, **kwargs)
|
109
|
+
except BLOCKING_IO_ERRORS as exc:
|
110
|
+
if isinstance(exc, _SSL.WantReadError):
|
111
|
+
want_read = True
|
112
|
+
want_write = False
|
113
|
+
elif isinstance(exc, _SSL.WantWriteError):
|
114
|
+
want_read = False
|
115
|
+
want_write = True
|
116
|
+
else:
|
117
|
+
want_read = True
|
118
|
+
want_write = True
|
119
|
+
self.socket_checker.select(self, want_read, want_write, timeout)
|
120
|
+
if timeout and _time.monotonic() - start > timeout:
|
121
|
+
raise _socket.timeout("timed out")
|
122
|
+
continue
|
123
|
+
|
124
|
+
def do_handshake(self, *args, **kwargs):
|
125
|
+
return self._call(super(_sslConn, self).do_handshake, *args, **kwargs)
|
126
|
+
|
127
|
+
def recv(self, *args, **kwargs):
|
128
|
+
try:
|
129
|
+
return self._call(super(_sslConn, self).recv, *args, **kwargs)
|
130
|
+
except _SSL.SysCallError as exc:
|
131
|
+
# Suppress ragged EOFs to match the stdlib.
|
132
|
+
if self.suppress_ragged_eofs and _ragged_eof(exc):
|
133
|
+
return b""
|
134
|
+
raise
|
135
|
+
|
136
|
+
def recv_into(self, *args, **kwargs):
|
137
|
+
try:
|
138
|
+
return self._call(super(_sslConn, self).recv_into, *args, **kwargs) # type: ignore
|
139
|
+
except _SSL.SysCallError as exc:
|
140
|
+
# Suppress ragged EOFs to match the stdlib.
|
141
|
+
if self.suppress_ragged_eofs and _ragged_eof(exc):
|
142
|
+
return 0
|
143
|
+
raise
|
144
|
+
|
145
|
+
def sendall(self, buf, flags=0):
|
146
|
+
view = memoryview(buf)
|
147
|
+
total_length = len(buf)
|
148
|
+
total_sent = 0
|
149
|
+
sent = 0
|
150
|
+
while total_sent < total_length:
|
151
|
+
try:
|
152
|
+
sent = self._call(
|
153
|
+
super(_sslConn, self).send, view[total_sent:], flags # type: ignore
|
154
|
+
)
|
155
|
+
# XXX: It's not clear if this can actually happen. PyOpenSSL
|
156
|
+
# doesn't appear to have any interrupt handling, nor any interrupt
|
157
|
+
# errors for OpenSSL connections.
|
158
|
+
except (IOError, OSError) as exc: # noqa: B014
|
159
|
+
if _errno_from_exception(exc) == _EINTR:
|
160
|
+
continue
|
161
|
+
raise
|
162
|
+
# https://github.com/pyca/pyopenssl/blob/19.1.0/src/OpenSSL/SSL.py#L1756
|
163
|
+
# https://www.openssl.org/docs/man1.0.2/man3/SSL_write.html
|
164
|
+
if sent <= 0:
|
165
|
+
raise Exception("Connection closed")
|
166
|
+
total_sent += sent
|
167
|
+
|
168
|
+
|
169
|
+
class _CallbackData(object):
|
170
|
+
"""Data class which is passed to the OCSP callback."""
|
171
|
+
|
172
|
+
def __init__(self):
|
173
|
+
self.trusted_ca_certs = None
|
174
|
+
self.check_ocsp_endpoint = None
|
175
|
+
self.ocsp_response_cache = _OCSPCache()
|
176
|
+
|
177
|
+
|
178
|
+
class SSLContext(object):
|
179
|
+
"""A CPython compatible SSLContext implementation wrapping PyOpenSSL's
|
180
|
+
context.
|
181
|
+
"""
|
182
|
+
|
183
|
+
__slots__ = ("_protocol", "_ctx", "_callback_data", "_check_hostname")
|
184
|
+
|
185
|
+
def __init__(self, protocol):
|
186
|
+
self._protocol = protocol
|
187
|
+
self._ctx = _SSL.Context(self._protocol)
|
188
|
+
self._callback_data = _CallbackData()
|
189
|
+
self._check_hostname = True
|
190
|
+
# OCSP
|
191
|
+
# XXX: Find a better place to do this someday, since this is client
|
192
|
+
# side configuration and wrap_socket tries to support both client and
|
193
|
+
# server side sockets.
|
194
|
+
self._callback_data.check_ocsp_endpoint = True
|
195
|
+
self._ctx.set_ocsp_client_callback(callback=_ocsp_callback, data=self._callback_data)
|
196
|
+
|
197
|
+
@property
|
198
|
+
def protocol(self):
|
199
|
+
"""The protocol version chosen when constructing the context.
|
200
|
+
This attribute is read-only.
|
201
|
+
"""
|
202
|
+
return self._protocol
|
203
|
+
|
204
|
+
def __get_verify_mode(self):
|
205
|
+
"""Whether to try to verify other peers' certificates and how to
|
206
|
+
behave if verification fails. This attribute must be one of
|
207
|
+
ssl.CERT_NONE, ssl.CERT_OPTIONAL or ssl.CERT_REQUIRED.
|
208
|
+
"""
|
209
|
+
return _REVERSE_VERIFY_MAP[self._ctx.get_verify_mode()]
|
210
|
+
|
211
|
+
def __set_verify_mode(self, value):
|
212
|
+
"""Setter for verify_mode."""
|
213
|
+
|
214
|
+
def _cb(connobj, x509obj, errnum, errdepth, retcode):
|
215
|
+
# It seems we don't need to do anything here. Twisted doesn't,
|
216
|
+
# and OpenSSL's SSL_CTX_set_verify let's you pass NULL
|
217
|
+
# for the callback option. It's weird that PyOpenSSL requires
|
218
|
+
# this.
|
219
|
+
return retcode
|
220
|
+
|
221
|
+
self._ctx.set_verify(_VERIFY_MAP[value], _cb)
|
222
|
+
|
223
|
+
verify_mode = property(__get_verify_mode, __set_verify_mode)
|
224
|
+
|
225
|
+
def __get_check_hostname(self):
|
226
|
+
return self._check_hostname
|
227
|
+
|
228
|
+
def __set_check_hostname(self, value):
|
229
|
+
if not isinstance(value, bool):
|
230
|
+
raise TypeError("check_hostname must be True or False")
|
231
|
+
self._check_hostname = value
|
232
|
+
|
233
|
+
check_hostname = property(__get_check_hostname, __set_check_hostname)
|
234
|
+
|
235
|
+
def __get_check_ocsp_endpoint(self):
|
236
|
+
return self._callback_data.check_ocsp_endpoint
|
237
|
+
|
238
|
+
def __set_check_ocsp_endpoint(self, value):
|
239
|
+
if not isinstance(value, bool):
|
240
|
+
raise TypeError("check_ocsp must be True or False")
|
241
|
+
self._callback_data.check_ocsp_endpoint = value
|
242
|
+
|
243
|
+
check_ocsp_endpoint = property(__get_check_ocsp_endpoint, __set_check_ocsp_endpoint)
|
244
|
+
|
245
|
+
def __get_options(self):
|
246
|
+
# Calling set_options adds the option to the existing bitmask and
|
247
|
+
# returns the new bitmask.
|
248
|
+
# https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_options
|
249
|
+
return self._ctx.set_options(0)
|
250
|
+
|
251
|
+
def __set_options(self, value):
|
252
|
+
# Explcitly convert to int, since newer CPython versions
|
253
|
+
# use enum.IntFlag for options. The values are the same
|
254
|
+
# regardless of implementation.
|
255
|
+
self._ctx.set_options(int(value))
|
256
|
+
|
257
|
+
options = property(__get_options, __set_options)
|
258
|
+
|
259
|
+
def load_cert_chain(self, certfile, keyfile=None, password=None):
|
260
|
+
"""Load a private key and the corresponding certificate. The certfile
|
261
|
+
string must be the path to a single file in PEM format containing the
|
262
|
+
certificate as well as any number of CA certificates needed to
|
263
|
+
establish the certificate's authenticity. The keyfile string, if
|
264
|
+
present, must point to a file containing the private key. Otherwise
|
265
|
+
the private key will be taken from certfile as well.
|
266
|
+
"""
|
267
|
+
# Match CPython behavior
|
268
|
+
# https://github.com/python/cpython/blob/v3.8.0/Modules/_ssl.c#L3930-L3971
|
269
|
+
# Password callback MUST be set first or it will be ignored.
|
270
|
+
if password:
|
271
|
+
|
272
|
+
def _pwcb(max_length, prompt_twice, user_data):
|
273
|
+
# XXX:We could check the password length against what OpenSSL
|
274
|
+
# tells us is the max, but we can't raise an exception, so...
|
275
|
+
# warn?
|
276
|
+
return password.encode("utf-8")
|
277
|
+
|
278
|
+
self._ctx.set_passwd_cb(_pwcb)
|
279
|
+
self._ctx.use_certificate_chain_file(certfile)
|
280
|
+
self._ctx.use_privatekey_file(keyfile or certfile)
|
281
|
+
self._ctx.check_privatekey()
|
282
|
+
|
283
|
+
def load_verify_locations(self, cafile=None, capath=None):
|
284
|
+
"""Load a set of "certification authority"(CA) certificates used to
|
285
|
+
validate other peers' certificates when `~verify_mode` is other than
|
286
|
+
ssl.CERT_NONE.
|
287
|
+
"""
|
288
|
+
self._ctx.load_verify_locations(cafile, capath)
|
289
|
+
# Manually load the CA certs when get_verified_chain is not available (pyopenssl<20).
|
290
|
+
if not hasattr(_SSL.Connection, "get_verified_chain"):
|
291
|
+
self._callback_data.trusted_ca_certs = _load_trusted_ca_certs(cafile)
|
292
|
+
|
293
|
+
def _load_certifi(self):
|
294
|
+
"""Attempt to load CA certs from certifi."""
|
295
|
+
if _HAVE_CERTIFI:
|
296
|
+
self.load_verify_locations(certifi.where())
|
297
|
+
else:
|
298
|
+
raise _ConfigurationError(
|
299
|
+
"tlsAllowInvalidCertificates is False but no system "
|
300
|
+
"CA certificates could be loaded. Please install the "
|
301
|
+
"certifi package, or provide a path to a CA file using "
|
302
|
+
"the tlsCAFile option"
|
303
|
+
)
|
304
|
+
|
305
|
+
def _load_wincerts(self, store):
|
306
|
+
"""Attempt to load CA certs from Windows trust store."""
|
307
|
+
cert_store = self._ctx.get_cert_store()
|
308
|
+
oid = _stdlibssl.Purpose.SERVER_AUTH.oid
|
309
|
+
for cert, encoding, trust in _stdlibssl.enum_certificates(store): # type: ignore
|
310
|
+
if encoding == "x509_asn":
|
311
|
+
if trust is True or oid in trust:
|
312
|
+
cert_store.add_cert(
|
313
|
+
_crypto.X509.from_cryptography(_load_der_x509_certificate(cert))
|
314
|
+
)
|
315
|
+
|
316
|
+
def load_default_certs(self):
|
317
|
+
"""A PyOpenSSL version of load_default_certs from CPython."""
|
318
|
+
# PyOpenSSL is incapable of loading CA certs from Windows, and mostly
|
319
|
+
# incapable on macOS.
|
320
|
+
# https://www.pyopenssl.org/en/stable/api/ssl.html#OpenSSL.SSL.Context.set_default_verify_paths
|
321
|
+
if _sys.platform == "win32":
|
322
|
+
try:
|
323
|
+
for storename in ("CA", "ROOT"):
|
324
|
+
self._load_wincerts(storename)
|
325
|
+
except PermissionError:
|
326
|
+
# Fall back to certifi
|
327
|
+
self._load_certifi()
|
328
|
+
elif _sys.platform == "darwin":
|
329
|
+
self._load_certifi()
|
330
|
+
self._ctx.set_default_verify_paths()
|
331
|
+
|
332
|
+
def set_default_verify_paths(self):
|
333
|
+
"""Specify that the platform provided CA certificates are to be used
|
334
|
+
for verification purposes."""
|
335
|
+
# Note: See PyOpenSSL's docs for limitations, which are similar
|
336
|
+
# but not that same as CPython's.
|
337
|
+
self._ctx.set_default_verify_paths()
|
338
|
+
|
339
|
+
def wrap_socket(
|
340
|
+
self,
|
341
|
+
sock,
|
342
|
+
server_side=False,
|
343
|
+
do_handshake_on_connect=True,
|
344
|
+
suppress_ragged_eofs=True,
|
345
|
+
server_hostname=None,
|
346
|
+
session=None,
|
347
|
+
):
|
348
|
+
"""Wrap an existing Python socket sock and return a TLS socket
|
349
|
+
object.
|
350
|
+
"""
|
351
|
+
ssl_conn = _sslConn(self._ctx, sock, suppress_ragged_eofs)
|
352
|
+
if session:
|
353
|
+
ssl_conn.set_session(session)
|
354
|
+
if server_side is True:
|
355
|
+
ssl_conn.set_accept_state()
|
356
|
+
else:
|
357
|
+
# SNI
|
358
|
+
if server_hostname and not _is_ip_address(server_hostname):
|
359
|
+
# XXX: Do this in a callback registered with
|
360
|
+
# SSLContext.set_info_callback? See Twisted for an example.
|
361
|
+
ssl_conn.set_tlsext_host_name(server_hostname.encode("idna"))
|
362
|
+
if self.verify_mode != _stdlibssl.CERT_NONE:
|
363
|
+
# Request a stapled OCSP response.
|
364
|
+
ssl_conn.request_ocsp()
|
365
|
+
ssl_conn.set_connect_state()
|
366
|
+
# If this wasn't true the caller of wrap_socket would call
|
367
|
+
# do_handshake()
|
368
|
+
if do_handshake_on_connect:
|
369
|
+
# XXX: If we do hostname checking in a callback we can get rid
|
370
|
+
# of this call to do_handshake() since the handshake
|
371
|
+
# will happen automatically later.
|
372
|
+
ssl_conn.do_handshake()
|
373
|
+
# XXX: Do this in a callback registered with
|
374
|
+
# SSLContext.set_info_callback? See Twisted for an example.
|
375
|
+
if self.check_hostname and server_hostname is not None:
|
376
|
+
try:
|
377
|
+
if _is_ip_address(server_hostname):
|
378
|
+
_verify_ip_address(ssl_conn, server_hostname)
|
379
|
+
else:
|
380
|
+
_verify_hostname(ssl_conn, server_hostname)
|
381
|
+
except (_SICertificateError, _SIVerificationError) as exc:
|
382
|
+
raise _CertificateError(str(exc))
|
383
|
+
return ssl_conn
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Copyright 2015 MongoDB, Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License",
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""Tools for working with read concerns."""
|
16
|
+
|
17
|
+
from typing import Any, Dict, Optional
|
18
|
+
|
19
|
+
|
20
|
+
class ReadConcern(object):
|
21
|
+
"""ReadConcern
|
22
|
+
|
23
|
+
:Parameters:
|
24
|
+
- `level`: (string) The read concern level specifies the level of
|
25
|
+
isolation for read operations. For example, a read operation using a
|
26
|
+
read concern level of ``majority`` will only return data that has been
|
27
|
+
written to a majority of nodes. If the level is left unspecified, the
|
28
|
+
server default will be used.
|
29
|
+
|
30
|
+
.. versionadded:: 3.2
|
31
|
+
|
32
|
+
"""
|
33
|
+
|
34
|
+
def __init__(self, level: Optional[str] = None) -> None:
|
35
|
+
if level is None or isinstance(level, str):
|
36
|
+
self.__level = level
|
37
|
+
else:
|
38
|
+
raise TypeError("level must be a string or None.")
|
39
|
+
|
40
|
+
@property
|
41
|
+
def level(self) -> Optional[str]:
|
42
|
+
"""The read concern level."""
|
43
|
+
return self.__level
|
44
|
+
|
45
|
+
@property
|
46
|
+
def ok_for_legacy(self) -> bool:
|
47
|
+
"""Return ``True`` if this read concern is compatible with
|
48
|
+
old wire protocol versions."""
|
49
|
+
return self.level is None or self.level == "local"
|
50
|
+
|
51
|
+
@property
|
52
|
+
def document(self) -> Dict[str, Any]:
|
53
|
+
"""The document representation of this read concern.
|
54
|
+
|
55
|
+
.. note::
|
56
|
+
:class:`ReadConcern` is immutable. Mutating the value of
|
57
|
+
:attr:`document` does not mutate this :class:`ReadConcern`.
|
58
|
+
"""
|
59
|
+
doc = {}
|
60
|
+
if self.__level:
|
61
|
+
doc["level"] = self.level
|
62
|
+
return doc
|
63
|
+
|
64
|
+
def __eq__(self, other: Any) -> bool:
|
65
|
+
if isinstance(other, ReadConcern):
|
66
|
+
return self.document == other.document
|
67
|
+
return NotImplemented
|
68
|
+
|
69
|
+
def __repr__(self):
|
70
|
+
if self.level:
|
71
|
+
return "ReadConcern(%s)" % self.level
|
72
|
+
return "ReadConcern()"
|
73
|
+
|
74
|
+
|
75
|
+
DEFAULT_READ_CONCERN = ReadConcern()
|