Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 5bd669f

Browse files
authored
Merge pull request #535 from Zlopez/fix_tests
Fix tests
2 parents 15bb124 + 317397a commit 5bd669f

File tree

200 files changed

+4733
-4894
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

200 files changed

+4733
-4894
lines changed

fedmsg/__init__.py

+10-3
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@
1919
#
2020
""" Federated Message Bus Client API """
2121

22-
import inspect
2322
import threading
2423
import functools
2524

2625
import fedmsg.core
2726
import fedmsg.config
2827

28+
# The python3.11 doesn't have getargspec anymore
29+
# getarcspec is deprecated till python 3.0
30+
# Let's try to first import one otherwise the other
31+
try:
32+
from inspect import getfullargspec as inspect_getargs
33+
except ImportError:
34+
from inspect import getargspec as inspect_getargs
35+
2936
__local = threading.local()
3037

3138
__all__ = [
@@ -64,7 +71,7 @@ def init(**kw):
6471

6572
def API_function(doc=None):
6673
def api_function(func):
67-
scrub = inspect.getfullargspec(func).args
74+
scrub = inspect_getargs(func).args
6875

6976
@functools.wraps(func)
7077
def _wrapper(*args, **kw):
@@ -76,7 +83,7 @@ def _wrapper(*args, **kw):
7683
del config_overrides[arg]
7784

7885
init(**config_overrides)
79-
assert(__local.__context)
86+
assert __local.__context
8087

8188
return func(*args, **kw)
8289

fedmsg/crypto/x509.py

+1-186
Original file line numberDiff line numberDiff line change
@@ -20,24 +20,10 @@
2020
""" ``fedmsg.crypto.x509`` - X.509 backend for :mod:`fedmsg.crypto`. """
2121

2222
import logging
23-
import os
24-
import tempfile
2523
import warnings
2624

27-
from requests.exceptions import RequestException
2825
import six
29-
try:
30-
# Else we need M2Crypto and m2ext
31-
import M2Crypto
32-
import m2ext
33-
_m2crypto = True
34-
except ImportError:
35-
_m2crypto = False
36-
37-
from . import utils
3826
from .x509_ng import _cryptography, sign as _crypto_sign, validate as _crypto_validate
39-
import fedmsg.crypto # noqa: E402
40-
import fedmsg.encoding # noqa: E402
4127

4228

4329
_log = logging.getLogger(__name__)
@@ -58,181 +44,10 @@ def _disabled_validate(*args, **kwargs):
5844
' and "pyopenssl") or "m2crypto" are not available.')
5945

6046

61-
def _m2crypto_sign(message, ssldir=None, certname=None, **config):
62-
""" Insert two new fields into the message dict and return it.
63-
64-
Those fields are:
65-
66-
- 'signature' - the computed RSA message digest of the JSON repr.
67-
- 'certificate' - the base64 X509 certificate of the sending host.
68-
"""
69-
if ssldir is None or certname is None:
70-
error = "You must set the ssldir and certname keyword arguments."
71-
raise ValueError(error)
72-
73-
message['crypto'] = 'x509'
74-
75-
certificate = M2Crypto.X509.load_cert(
76-
"%s/%s.crt" % (ssldir, certname)).as_pem()
77-
# Opening this file requires elevated privileges in stg/prod.
78-
rsa_private = M2Crypto.RSA.load_key(
79-
"%s/%s.key" % (ssldir, certname))
80-
81-
digest = M2Crypto.EVP.MessageDigest('sha1')
82-
digest.update(fedmsg.encoding.dumps(message))
83-
84-
signature = rsa_private.sign(digest.digest())
85-
86-
# Return a new dict containing the pairs in the original message as well
87-
# as the new authn fields.
88-
return dict(message.items() + [
89-
('signature', signature.encode('base64').decode('ascii')),
90-
('certificate', certificate.encode('base64').decode('ascii')),
91-
])
92-
93-
94-
def _m2crypto_validate(message, ssldir=None, **config):
95-
""" Return true or false if the message is signed appropriately.
96-
97-
Four things must be true:
98-
99-
1) The X509 cert must be signed by our CA
100-
2) The cert must not be in our CRL.
101-
3) We must be able to verify the signature using the RSA public key
102-
contained in the X509 cert.
103-
4) The topic of the message and the CN on the cert must appear in the
104-
:ref:`conf-routing-policy` dict.
105-
106-
"""
107-
108-
if ssldir is None:
109-
raise ValueError("You must set the ssldir keyword argument.")
110-
111-
def fail(reason):
112-
_log.warn("Failed validation. %s" % reason)
113-
return False
114-
115-
# Some sanity checking
116-
for field in ['signature', 'certificate']:
117-
if field not in message:
118-
return fail("No %r field found." % field)
119-
if not isinstance(message[field], six.text_type):
120-
_log.error('msg[%r] is not a unicode string' % field)
121-
try:
122-
# Make an effort to decode it, it's very likely utf-8 since that's what
123-
# is hardcoded throughout fedmsg. Worst case scenario is it'll cause a
124-
# validation error when there shouldn't be one.
125-
message[field] = message[field].decode('utf-8')
126-
except UnicodeError as e:
127-
_log.error("Unable to decode the message '%s' field: %s", field, str(e))
128-
return False
129-
130-
# Peal off the auth datums
131-
signature = message['signature'].decode('base64')
132-
certificate = message['certificate'].decode('base64')
133-
message = fedmsg.crypto.strip_credentials(message)
134-
135-
# Build an X509 object
136-
cert = M2Crypto.X509.load_cert_string(certificate)
137-
138-
# Validate the cert. Make sure it is signed by our CA.
139-
# validate_certificate will one day be a part of M2Crypto.SSL.Context
140-
# https://bugzilla.osafoundation.org/show_bug.cgi?id=11690
141-
142-
ca_location = config.get('ca_cert_location', 'https://fedoraproject.org/fedmsg/ca.crt')
143-
crl_location = config.get('crl_location', 'https://fedoraproject.org/fedmsg/crl.pem')
144-
fd, cafile = tempfile.mkstemp()
145-
try:
146-
ca_certificate, crl = utils.load_certificates(ca_location, crl_location)
147-
os.write(fd, ca_certificate.encode('ascii'))
148-
os.fsync(fd)
149-
ctx = m2ext.SSL.Context()
150-
ctx.load_verify_locations(cafile=cafile)
151-
if not ctx.validate_certificate(cert):
152-
ca_certificate, crl = utils.load_certificates(
153-
ca_location, crl_location, invalidate_cache=True)
154-
with open(cafile, 'w') as f:
155-
f.write(ca_certificate)
156-
ctx = m2ext.SSL.Context()
157-
ctx.load_verify_locations(cafile=cafile)
158-
if not ctx.validate_certificate(cert):
159-
return fail("X509 certificate is not valid.")
160-
except (IOError, RequestException) as e:
161-
_log.error(str(e))
162-
return False
163-
finally:
164-
os.close(fd)
165-
os.remove(cafile)
166-
167-
if crl:
168-
try:
169-
fd, crlfile = tempfile.mkstemp(text=True)
170-
os.write(fd, crl.encode('ascii'))
171-
os.fsync(fd)
172-
crl = M2Crypto.X509.load_crl(crlfile)
173-
finally:
174-
os.close(fd)
175-
os.remove(crlfile)
176-
# FIXME -- We need to check that the CRL is signed by our own CA.
177-
# See https://bugzilla.osafoundation.org/show_bug.cgi?id=12954#c2
178-
# if not ctx.validate_certificate(crl):
179-
# return fail("X509 CRL is not valid.")
180-
181-
# FIXME -- we check the CRL, but by doing string comparison ourselves.
182-
# This is not what we want to be doing.
183-
# There is a patch into M2Crypto to handle this for us. We should use it
184-
# once its integrated upstream.
185-
# See https://bugzilla.osafoundation.org/show_bug.cgi?id=12954#c2
186-
revoked_serials = [long(line.split(': ')[1].strip(), base=16)
187-
for line in crl.as_text().split('\n')
188-
if 'Serial Number:' in line]
189-
if cert.get_serial_number() in revoked_serials:
190-
subject = cert.get_subject()
191-
192-
signer = '(no CN)'
193-
if subject.nid.get('CN'):
194-
entry = subject.get_entries_by_nid(subject.nid['CN'])[0]
195-
if entry:
196-
signer = entry.get_data().as_text()
197-
198-
return fail("X509 cert %r, %r is in the Revocation List (CRL)" % (
199-
signer, cert.get_serial_number()))
200-
201-
# If the cert is good, then test to see if the signature in the messages
202-
# matches up with the provided cert.
203-
rsa_public = cert.get_pubkey().get_rsa()
204-
digest = M2Crypto.EVP.MessageDigest('sha1')
205-
digest.update(fedmsg.encoding.dumps(message))
206-
try:
207-
if not rsa_public.verify(digest.digest(), signature):
208-
raise M2Crypto.RSA.RSAError("RSA signature failed to validate.")
209-
except M2Crypto.RSA.RSAError as e:
210-
return fail(str(e))
211-
212-
# Now we know that the cert is valid. The message is *authenticated*.
213-
# * Next step: Authorization *
214-
215-
# Load our policy from the config dict.
216-
routing_policy = config.get('routing_policy', {})
217-
218-
# Determine the name of the signer of the message.
219-
# This will be something like "shell-pkgs01.stg.phx2.fedoraproject.org"
220-
subject = cert.get_subject()
221-
signer = subject.get_entries_by_nid(subject.nid['CN'])[0]\
222-
.get_data().as_text()
223-
224-
return utils.validate_policy(
225-
message.get('topic'), signer, routing_policy, config.get('routing_nitpicky', False))
226-
227-
228-
# Maintain the ``sign`` and ``validate`` APIs while preferring cryptography and
229-
# pyOpenSSL over M2Crypto.
47+
# Maintain the ``sign`` and ``validate`` APIs
23048
if _cryptography:
23149
sign = _crypto_sign
23250
validate = _crypto_validate
233-
elif _m2crypto:
234-
sign = _m2crypto_sign
235-
validate = _m2crypto_validate
23651
else:
23752
sign = _disabled_sign
23853
validate = _disabled_validate

fedmsg/tests/consumers/test_relay.py

+40-31
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,40 @@ class TestSigningRelayConsumer(unittest.TestCase):
1212
def setUp(self):
1313
self.hub = mock.Mock()
1414
self.signing_cert = (
15-
u'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVekNDQTd5Z0F3SUJBZ0lCRHpBTkJna3Fo\n'
16-
u'a2lHOXcwQkFRVUZBRENCb0RFTE1Ba0dBMVVFQmhNQ1ZWTXgKQ3pBSkJnTlZCQWdUQWs1RE1SQXdE\n'
17-
u'Z1lEVlFRSEV3ZFNZV3hsYVdkb01SY3dGUVlEVlFRS0V3NUdaV1J2Y21FZwpVSEp2YW1WamRERVBN\n'
18-
u'QTBHQTFVRUN4TUdabVZrYlhObk1ROHdEUVlEVlFRREV3Wm1aV1J0YzJjeER6QU5CZ05WCkJDa1RC\n'
19-
u'bVpsWkcxelp6RW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBWm1Wa2IzSmhjSEp2YW1WamRD\n'
20-
u'NXYKY21jd0hoY05NVEl3TnpFMU1qRXhPRFV5V2hjTk1qSXdOekV6TWpFeE9EVXlXakNCNGpFTE1B\n'
21-
u'a0dBMVVFQmhNQwpWVk14Q3pBSkJnTlZCQWdUQWs1RE1SQXdEZ1lEVlFRSEV3ZFNZV3hsYVdkb01S\n'
22-
u'Y3dGUVlEVlFRS0V3NUdaV1J2CmNtRWdVSEp2YW1WamRERVBNQTBHQTFVRUN4TUdabVZrYlhObk1U\n'
23-
u'QXdMZ1lEVlFRREV5ZHphR1ZzYkMxd1lXTnIKWVdkbGN6QXhMbkJvZURJdVptVmtiM0poY0hKdmFt\n'
24-
u'VmpkQzV2Y21jeE1EQXVCZ05WQkNrVEozTm9aV3hzTFhCaApZMnRoWjJWek1ERXVjR2g0TWk1bVpX\n'
25-
u'UnZjbUZ3Y205cVpXTjBMbTl5WnpFbU1DUUdDU3FHU0liM0RRRUpBUllYCllXUnRhVzVBWm1Wa2Iz\n'
26-
u'SmhjSEp2YW1WamRDNXZjbWN3Z1o4d0RRWUpLb1pJaHZjTkFRRUJCUUFEZ1kwQU1JR0oKQW9HQkFN\n'
27-
u'RUlKNURzZ0VsaG5XMENLcnNpc1UvV0svUFBrSkNST0N0WnBwQXZha0dDVHhVU1RoWDhpZmVsVjVa\n'
28-
u'dwp1T1dCWDlxTHg2cGJzNHhodnVrVDkwUHphYUlKR24xeUpjVnZLTDYzS1I1SCtZNXdOamJLREhY\n'
29-
u'ZlBuM0J1Z0hSCmRzdnV0Yi9Fa3hNM3NYbnRpZWY0K2ZWVGsyanZiTXFsYmEvWHc4cXBsRWxqMXFm\n'
30-
u'aEFnTUJBQUdqZ2dGWE1JSUIKVXpBSkJnTlZIUk1FQWpBQU1DMEdDV0NHU0FHRytFSUJEUVFnRmg1\n'
31-
u'RllYTjVMVkpUUVNCSFpXNWxjbUYwWldRZwpRMlZ5ZEdsbWFXTmhkR1V3SFFZRFZSME9CQllFRkUw\n'
32-
u'Zmh6czZhWjViVDJVNjZzUjNrUG1LdzBGYk1JSFZCZ05WCkhTTUVnYzB3Z2NxQUZBQ1lwZFhueEZV\n'
33-
u'T2hLTm4vbVpLRnVBRUZkMGhvWUdtcElHak1JR2dNUXN3Q1FZRFZRUUcKRXdKVlV6RUxNQWtHQTFV\n'
34-
u'RUNCTUNUa014RURBT0JnTlZCQWNUQjFKaGJHVnBaMmd4RnpBVkJnTlZCQW9URGtabApaRzl5WVNC\n'
35-
u'UWNtOXFaV04wTVE4d0RRWURWUVFMRXdabVpXUnRjMmN4RHpBTkJnTlZCQU1UQm1abFpHMXpaekVQ\n'
36-
u'Ck1BMEdBMVVFS1JNR1ptVmtiWE5uTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQm1aV1J2\n'
37-
u'Y21Gd2NtOXEKWldOMExtOXlaNElKQUk3cktOaXBFNTE4TUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVG\n'
38-
u'QndNQ01Bc0dBMVVkRHdRRQpBd0lIZ0RBTkJna3Foa2lHOXcwQkFRVUZBQU9CZ1FCK3RlWFNCV0pQ\n'
39-
u'VWlLMDBEYWl4RmF6ZThSUW01S1ZBQjBRCkRSdDdqcDdRcVViZHd2ZWhvU3NKODVDYnZLazhYZ0Ey\n'
40-
u'UW16RFdhRzRhcklrQUVCWGFkNjlyMkZmMTMzTmQxQlEKeGZGRGRWdXFyeE9HeXJwazhyOFAxYmJJ\n'
41-
u'YjRNb09aUVQxbGFGTFUzZjNJUVNIYW93RkRuZ0V0azlZUzRpSEhrWQora3FlRnczYmhRPT0KLS0t\n'
42-
u'LS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\n'
15+
u'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYakNDQThlZ0F3SUJBZ0lCR'
16+
u'HpBTkJna3Fo\na2lHOXcwQkFRc0ZBRENCb0RFTE1Ba0dBMVVFQmhNQ1ZWTXgKQ3pB'
17+
u'SkJnTlZCQWdUQWs1RE1SQXdE\nZ1lEVlFRSEV3ZFNZV3hsYVdkb01SY3dGUVlEVlF'
18+
u'RS0V3NUdaV1J2Y21FZwpVSEp2YW1WamRERVBN\nQTBHQTFVRUN4TUdabVZrYlhObk'
19+
u'1ROHdEUVlEVlFRREV3Wm1aV1J0YzJjeER6QU5CZ05WCkJDa1RC\nbVpsWkcxelp6R'
20+
u'W1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBWm1Wa2IzSmhjSEp2YW1WamRD\n'
21+
u'NXYKY21jd0hoY05Nak13TWpJd01URTBNekV5V2hjTk16TXdNakUzTVRFME16RXlXa'
22+
u'kNCNGpFTE1B\na0dBMVVFQmhNQwpWVk14Q3pBSkJnTlZCQWdUQWs1RE1SQXdEZ1lE'
23+
u'VlFRSEV3ZFNZV3hsYVdkb01S\nY3dGUVlEVlFRS0V3NUdaV1J2CmNtRWdVSEp2YW1'
24+
u'WamRERVBNQTBHQTFVRUN4TUdabVZrYlhObk1U\nQXdMZ1lEVlFRREV5ZHphR1ZzYk'
25+
u'Mxd1lXTnIKWVdkbGN6QXhMbkJvZURJdVptVmtiM0poY0hKdmFt\nVmpkQzV2Y21je'
26+
u'E1EQXVCZ05WQkNrVEozTm9aV3hzTFhCaApZMnRoWjJWek1ERXVjR2g0TWk1bVpX\n'
27+
u'UnZjbUZ3Y205cVpXTjBMbTl5WnpFbU1DUUdDU3FHU0liM0RRRUpBUllYCllXUnRhV'
28+
u'zVBWm1Wa2Iz\nSmhjSEp2YW1WamRDNXZjbWN3Z1o4d0RRWUpLb1pJaHZjTkFRRUJC'
29+
u'UUFEZ1kwQU1JR0oKQW9HQkFL\nWUhDV1VhaWo4YlFub25ZVVYwOEdnWWYvWnRSRlB'
30+
u'IVG9vYnkzQ3Z0Tk5Nc2JETkxnZEhxRUU2WHFs\nSApXM3FWaDNFaktJRHZDdmtzOU'
31+
u't0endRY0pXZVdXLy9qMVozR1ZZMzZ1WS9vUDAvMEl0dmtaRTZZ\ndHkxNFZ5clI5C'
32+
u'nZmL3NIVlllTFc2N2FIdW0xYVc3VElMZWxHVE1VUDJQWnNPdk0vNjlLNWpEMzhv\n'
33+
u'RkFnTUJBQUdqZ2dGaU1JSUIKWGpBSkJnTlZIUk1FQWpBQU1DMEdDV0NHU0FHRytFS'
34+
u'UJEUVFnRmg1\nRllYTjVMVkpUUVNCSFpXNWxjbUYwWldRZwpRMlZ5ZEdsbWFXTmhk'
35+
u'R1V3SFFZRFZSME9CQllFRkFz\nZWpLMG9SanVuOWREL0pCZHlZRzJ2VmRJZk1JSGd'
36+
u'CZ05WCkhTTUVnZGd3Z2RXQUZJVTdDN1dHSWpM\nZHZmamhmSW5ESUszS1JMVTRvWU'
37+
u'dtcElHak1JR2dNUXN3Q1FZRFZRUUcKRXdKVlV6RUxNQWtHQTFV\nRUNCTUNUa014R'
38+
u'URBT0JnTlZCQWNUQjFKaGJHVnBaMmd4RnpBVkJnTlZCQW9URGtabApaRzl5WVNC\n'
39+
u'UWNtOXFaV04wTVE4d0RRWURWUVFMRXdabVpXUnRjMmN4RHpBTkJnTlZCQU1UQm1ab'
40+
u'FpHMXpaekVQ\nCk1BMEdBMVVFS1JNR1ptVmtiWE5uTVNZd0pBWUpLb1pJaHZjTkFR'
41+
u'a0JGaGRoWkcxcGJrQm1aV1J2\nY21Gd2NtOXEKWldOMExtOXlaNElVTVJoZm1seSt'
42+
u'4bVFlL1NDek43S3lGdnZnd1BZd0V3WURWUjBs\nQkF3d0NnWUlLd1lCQlFVSApBd0'
43+
u'l3Q3dZRFZSMFBCQVFEQWdlQU1BMEdDU3FHU0liM0RRRUJDd1VB\nQTRHQkFGVkVEa'
44+
u'U93U2k2aWxEMTRiRCtZCmxjT3Rxc0FLY1o2cEpzalRiYVlacDdFRVpUd1NhZEJs\n'
45+
u'cmtrZThTRkZzQUtnKzREVXU3ejF2Q0NVSHhSeEI1Z0oKYytNM1lrdW9OQXlOZThHY'
46+
u'VZBbGNBSHo5\ndm95TW05cWhSbHlFa2pIaWNYcWRsK00wOXJlYmJBK1YyNVcyYzk0'
47+
u'aApETXA1ZWUvalRFSkI3eEpj\nNjdNNW5KbWkKLS0tLS1FTkQgQ0VSVElGSUNBVEU'
48+
u'tLS0tLQo=\n'
4349
)
4450
self.hub.config = {
4551
'fedmsg.consumers.relay.enabled': True,
@@ -63,9 +69,12 @@ def test_message_signed(self):
6369
expected_msg = {
6470
'my': 'msg',
6571
'crypto': 'x509',
66-
'signature': (u'kyZ496SD+qgufonX9lqV/4L/o3s0+j4j5RaeMzhRIIGhfk6/RIEtl1DW73xbo+'
67-
u'Xs2STbidFyz7Yt\n6IUb3/U+8Io0CTTbIyQvcvtof/a3EdmbnZtOQ93VfnXXkn'
68-
u'6m76yVcFnQDicagY/600KmfNCDAwve\nI6+B9va/q10CBloMLkE=\n'),
72+
'signature': (
73+
u'cM41fBCf5vWoYQvI9mlVofIJZ/djKXAk+4s8EltzSeW+xWCqJ/EnCTHj'
74+
u'ZcNGY09CeJRoDcq0Yc1y\nc8QR6IT7JCSlwkc1Iqj+5SKE/REm6AN5Xd'
75+
u'3jqJHZSWMqKqhxvlzwaWEFI1nNp3qhxWDJNmUKEqIU\nUwl+6CntIulR'
76+
u'f/5fiO8=\n'
77+
),
6978
'certificate': self.signing_cert,
7079
}
7180
consumer = relay.SigningRelayConsumer(self.hub)

fedmsg/tests/crypto/test_utils.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def test_remote_cert(self):
122122

123123
with mock.patch.dict('fedmsg.crypto.utils._cached_certificates', clear=True):
124124
ca, crl = utils.load_certificates(location)
125-
self.assertEqual((expected_cert, None), utils._cached_certificates[location])
125+
self.assertEqual((str(expected_cert), None), utils._cached_certificates[location])
126126
self.assertEqual(expected_cert, ca)
127127
self.assertTrue(crl is None)
128128

0 commit comments

Comments
 (0)