Skip to content

Commit

Permalink
Merge pull request #535 from Zlopez/fix_tests
Browse files Browse the repository at this point in the history
Fix tests
  • Loading branch information
Zlopez authored Feb 22, 2023
2 parents 15bb124 + 317397a commit 5bd669f
Show file tree
Hide file tree
Showing 200 changed files with 4,733 additions and 4,894 deletions.
13 changes: 10 additions & 3 deletions fedmsg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@
#
""" Federated Message Bus Client API """

import inspect
import threading
import functools

import fedmsg.core
import fedmsg.config

# The python3.11 doesn't have getargspec anymore
# getarcspec is deprecated till python 3.0
# Let's try to first import one otherwise the other
try:
from inspect import getfullargspec as inspect_getargs
except ImportError:
from inspect import getargspec as inspect_getargs

__local = threading.local()

__all__ = [
Expand Down Expand Up @@ -64,7 +71,7 @@ def init(**kw):

def API_function(doc=None):
def api_function(func):
scrub = inspect.getfullargspec(func).args
scrub = inspect_getargs(func).args

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

init(**config_overrides)
assert(__local.__context)
assert __local.__context

return func(*args, **kw)

Expand Down
187 changes: 1 addition & 186 deletions fedmsg/crypto/x509.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,10 @@
""" ``fedmsg.crypto.x509`` - X.509 backend for :mod:`fedmsg.crypto`. """

import logging
import os
import tempfile
import warnings

from requests.exceptions import RequestException
import six
try:
# Else we need M2Crypto and m2ext
import M2Crypto
import m2ext
_m2crypto = True
except ImportError:
_m2crypto = False

from . import utils
from .x509_ng import _cryptography, sign as _crypto_sign, validate as _crypto_validate
import fedmsg.crypto # noqa: E402
import fedmsg.encoding # noqa: E402


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


def _m2crypto_sign(message, ssldir=None, certname=None, **config):
""" Insert two new fields into the message dict and return it.
Those fields are:
- 'signature' - the computed RSA message digest of the JSON repr.
- 'certificate' - the base64 X509 certificate of the sending host.
"""
if ssldir is None or certname is None:
error = "You must set the ssldir and certname keyword arguments."
raise ValueError(error)

message['crypto'] = 'x509'

certificate = M2Crypto.X509.load_cert(
"%s/%s.crt" % (ssldir, certname)).as_pem()
# Opening this file requires elevated privileges in stg/prod.
rsa_private = M2Crypto.RSA.load_key(
"%s/%s.key" % (ssldir, certname))

digest = M2Crypto.EVP.MessageDigest('sha1')
digest.update(fedmsg.encoding.dumps(message))

signature = rsa_private.sign(digest.digest())

# Return a new dict containing the pairs in the original message as well
# as the new authn fields.
return dict(message.items() + [
('signature', signature.encode('base64').decode('ascii')),
('certificate', certificate.encode('base64').decode('ascii')),
])


def _m2crypto_validate(message, ssldir=None, **config):
""" Return true or false if the message is signed appropriately.
Four things must be true:
1) The X509 cert must be signed by our CA
2) The cert must not be in our CRL.
3) We must be able to verify the signature using the RSA public key
contained in the X509 cert.
4) The topic of the message and the CN on the cert must appear in the
:ref:`conf-routing-policy` dict.
"""

if ssldir is None:
raise ValueError("You must set the ssldir keyword argument.")

def fail(reason):
_log.warn("Failed validation. %s" % reason)
return False

# Some sanity checking
for field in ['signature', 'certificate']:
if field not in message:
return fail("No %r field found." % field)
if not isinstance(message[field], six.text_type):
_log.error('msg[%r] is not a unicode string' % field)
try:
# Make an effort to decode it, it's very likely utf-8 since that's what
# is hardcoded throughout fedmsg. Worst case scenario is it'll cause a
# validation error when there shouldn't be one.
message[field] = message[field].decode('utf-8')
except UnicodeError as e:
_log.error("Unable to decode the message '%s' field: %s", field, str(e))
return False

# Peal off the auth datums
signature = message['signature'].decode('base64')
certificate = message['certificate'].decode('base64')
message = fedmsg.crypto.strip_credentials(message)

# Build an X509 object
cert = M2Crypto.X509.load_cert_string(certificate)

# Validate the cert. Make sure it is signed by our CA.
# validate_certificate will one day be a part of M2Crypto.SSL.Context
# https://bugzilla.osafoundation.org/show_bug.cgi?id=11690

ca_location = config.get('ca_cert_location', 'https://fedoraproject.org/fedmsg/ca.crt')
crl_location = config.get('crl_location', 'https://fedoraproject.org/fedmsg/crl.pem')
fd, cafile = tempfile.mkstemp()
try:
ca_certificate, crl = utils.load_certificates(ca_location, crl_location)
os.write(fd, ca_certificate.encode('ascii'))
os.fsync(fd)
ctx = m2ext.SSL.Context()
ctx.load_verify_locations(cafile=cafile)
if not ctx.validate_certificate(cert):
ca_certificate, crl = utils.load_certificates(
ca_location, crl_location, invalidate_cache=True)
with open(cafile, 'w') as f:
f.write(ca_certificate)
ctx = m2ext.SSL.Context()
ctx.load_verify_locations(cafile=cafile)
if not ctx.validate_certificate(cert):
return fail("X509 certificate is not valid.")
except (IOError, RequestException) as e:
_log.error(str(e))
return False
finally:
os.close(fd)
os.remove(cafile)

if crl:
try:
fd, crlfile = tempfile.mkstemp(text=True)
os.write(fd, crl.encode('ascii'))
os.fsync(fd)
crl = M2Crypto.X509.load_crl(crlfile)
finally:
os.close(fd)
os.remove(crlfile)
# FIXME -- We need to check that the CRL is signed by our own CA.
# See https://bugzilla.osafoundation.org/show_bug.cgi?id=12954#c2
# if not ctx.validate_certificate(crl):
# return fail("X509 CRL is not valid.")

# FIXME -- we check the CRL, but by doing string comparison ourselves.
# This is not what we want to be doing.
# There is a patch into M2Crypto to handle this for us. We should use it
# once its integrated upstream.
# See https://bugzilla.osafoundation.org/show_bug.cgi?id=12954#c2
revoked_serials = [long(line.split(': ')[1].strip(), base=16)
for line in crl.as_text().split('\n')
if 'Serial Number:' in line]
if cert.get_serial_number() in revoked_serials:
subject = cert.get_subject()

signer = '(no CN)'
if subject.nid.get('CN'):
entry = subject.get_entries_by_nid(subject.nid['CN'])[0]
if entry:
signer = entry.get_data().as_text()

return fail("X509 cert %r, %r is in the Revocation List (CRL)" % (
signer, cert.get_serial_number()))

# If the cert is good, then test to see if the signature in the messages
# matches up with the provided cert.
rsa_public = cert.get_pubkey().get_rsa()
digest = M2Crypto.EVP.MessageDigest('sha1')
digest.update(fedmsg.encoding.dumps(message))
try:
if not rsa_public.verify(digest.digest(), signature):
raise M2Crypto.RSA.RSAError("RSA signature failed to validate.")
except M2Crypto.RSA.RSAError as e:
return fail(str(e))

# Now we know that the cert is valid. The message is *authenticated*.
# * Next step: Authorization *

# Load our policy from the config dict.
routing_policy = config.get('routing_policy', {})

# Determine the name of the signer of the message.
# This will be something like "shell-pkgs01.stg.phx2.fedoraproject.org"
subject = cert.get_subject()
signer = subject.get_entries_by_nid(subject.nid['CN'])[0]\
.get_data().as_text()

return utils.validate_policy(
message.get('topic'), signer, routing_policy, config.get('routing_nitpicky', False))


# Maintain the ``sign`` and ``validate`` APIs while preferring cryptography and
# pyOpenSSL over M2Crypto.
# Maintain the ``sign`` and ``validate`` APIs
if _cryptography:
sign = _crypto_sign
validate = _crypto_validate
elif _m2crypto:
sign = _m2crypto_sign
validate = _m2crypto_validate
else:
sign = _disabled_sign
validate = _disabled_validate
71 changes: 40 additions & 31 deletions fedmsg/tests/consumers/test_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,40 @@ class TestSigningRelayConsumer(unittest.TestCase):
def setUp(self):
self.hub = mock.Mock()
self.signing_cert = (
u'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVVekNDQTd5Z0F3SUJBZ0lCRHpBTkJna3Fo\n'
u'a2lHOXcwQkFRVUZBRENCb0RFTE1Ba0dBMVVFQmhNQ1ZWTXgKQ3pBSkJnTlZCQWdUQWs1RE1SQXdE\n'
u'Z1lEVlFRSEV3ZFNZV3hsYVdkb01SY3dGUVlEVlFRS0V3NUdaV1J2Y21FZwpVSEp2YW1WamRERVBN\n'
u'QTBHQTFVRUN4TUdabVZrYlhObk1ROHdEUVlEVlFRREV3Wm1aV1J0YzJjeER6QU5CZ05WCkJDa1RC\n'
u'bVpsWkcxelp6RW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBWm1Wa2IzSmhjSEp2YW1WamRD\n'
u'NXYKY21jd0hoY05NVEl3TnpFMU1qRXhPRFV5V2hjTk1qSXdOekV6TWpFeE9EVXlXakNCNGpFTE1B\n'
u'a0dBMVVFQmhNQwpWVk14Q3pBSkJnTlZCQWdUQWs1RE1SQXdEZ1lEVlFRSEV3ZFNZV3hsYVdkb01S\n'
u'Y3dGUVlEVlFRS0V3NUdaV1J2CmNtRWdVSEp2YW1WamRERVBNQTBHQTFVRUN4TUdabVZrYlhObk1U\n'
u'QXdMZ1lEVlFRREV5ZHphR1ZzYkMxd1lXTnIKWVdkbGN6QXhMbkJvZURJdVptVmtiM0poY0hKdmFt\n'
u'VmpkQzV2Y21jeE1EQXVCZ05WQkNrVEozTm9aV3hzTFhCaApZMnRoWjJWek1ERXVjR2g0TWk1bVpX\n'
u'UnZjbUZ3Y205cVpXTjBMbTl5WnpFbU1DUUdDU3FHU0liM0RRRUpBUllYCllXUnRhVzVBWm1Wa2Iz\n'
u'SmhjSEp2YW1WamRDNXZjbWN3Z1o4d0RRWUpLb1pJaHZjTkFRRUJCUUFEZ1kwQU1JR0oKQW9HQkFN\n'
u'RUlKNURzZ0VsaG5XMENLcnNpc1UvV0svUFBrSkNST0N0WnBwQXZha0dDVHhVU1RoWDhpZmVsVjVa\n'
u'dwp1T1dCWDlxTHg2cGJzNHhodnVrVDkwUHphYUlKR24xeUpjVnZLTDYzS1I1SCtZNXdOamJLREhY\n'
u'ZlBuM0J1Z0hSCmRzdnV0Yi9Fa3hNM3NYbnRpZWY0K2ZWVGsyanZiTXFsYmEvWHc4cXBsRWxqMXFm\n'
u'aEFnTUJBQUdqZ2dGWE1JSUIKVXpBSkJnTlZIUk1FQWpBQU1DMEdDV0NHU0FHRytFSUJEUVFnRmg1\n'
u'RllYTjVMVkpUUVNCSFpXNWxjbUYwWldRZwpRMlZ5ZEdsbWFXTmhkR1V3SFFZRFZSME9CQllFRkUw\n'
u'Zmh6czZhWjViVDJVNjZzUjNrUG1LdzBGYk1JSFZCZ05WCkhTTUVnYzB3Z2NxQUZBQ1lwZFhueEZV\n'
u'T2hLTm4vbVpLRnVBRUZkMGhvWUdtcElHak1JR2dNUXN3Q1FZRFZRUUcKRXdKVlV6RUxNQWtHQTFV\n'
u'RUNCTUNUa014RURBT0JnTlZCQWNUQjFKaGJHVnBaMmd4RnpBVkJnTlZCQW9URGtabApaRzl5WVNC\n'
u'UWNtOXFaV04wTVE4d0RRWURWUVFMRXdabVpXUnRjMmN4RHpBTkJnTlZCQU1UQm1abFpHMXpaekVQ\n'
u'Ck1BMEdBMVVFS1JNR1ptVmtiWE5uTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQm1aV1J2\n'
u'Y21Gd2NtOXEKWldOMExtOXlaNElKQUk3cktOaXBFNTE4TUJNR0ExVWRKUVFNTUFvR0NDc0dBUVVG\n'
u'QndNQ01Bc0dBMVVkRHdRRQpBd0lIZ0RBTkJna3Foa2lHOXcwQkFRVUZBQU9CZ1FCK3RlWFNCV0pQ\n'
u'VWlLMDBEYWl4RmF6ZThSUW01S1ZBQjBRCkRSdDdqcDdRcVViZHd2ZWhvU3NKODVDYnZLazhYZ0Ey\n'
u'UW16RFdhRzRhcklrQUVCWGFkNjlyMkZmMTMzTmQxQlEKeGZGRGRWdXFyeE9HeXJwazhyOFAxYmJJ\n'
u'YjRNb09aUVQxbGFGTFUzZjNJUVNIYW93RkRuZ0V0azlZUzRpSEhrWQora3FlRnczYmhRPT0KLS0t\n'
u'LS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\n'
u'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUVYakNDQThlZ0F3SUJBZ0lCR'
u'HpBTkJna3Fo\na2lHOXcwQkFRc0ZBRENCb0RFTE1Ba0dBMVVFQmhNQ1ZWTXgKQ3pB'
u'SkJnTlZCQWdUQWs1RE1SQXdE\nZ1lEVlFRSEV3ZFNZV3hsYVdkb01SY3dGUVlEVlF'
u'RS0V3NUdaV1J2Y21FZwpVSEp2YW1WamRERVBN\nQTBHQTFVRUN4TUdabVZrYlhObk'
u'1ROHdEUVlEVlFRREV3Wm1aV1J0YzJjeER6QU5CZ05WCkJDa1RC\nbVpsWkcxelp6R'
u'W1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBWm1Wa2IzSmhjSEp2YW1WamRD\n'
u'NXYKY21jd0hoY05Nak13TWpJd01URTBNekV5V2hjTk16TXdNakUzTVRFME16RXlXa'
u'kNCNGpFTE1B\na0dBMVVFQmhNQwpWVk14Q3pBSkJnTlZCQWdUQWs1RE1SQXdEZ1lE'
u'VlFRSEV3ZFNZV3hsYVdkb01S\nY3dGUVlEVlFRS0V3NUdaV1J2CmNtRWdVSEp2YW1'
u'WamRERVBNQTBHQTFVRUN4TUdabVZrYlhObk1U\nQXdMZ1lEVlFRREV5ZHphR1ZzYk'
u'Mxd1lXTnIKWVdkbGN6QXhMbkJvZURJdVptVmtiM0poY0hKdmFt\nVmpkQzV2Y21je'
u'E1EQXVCZ05WQkNrVEozTm9aV3hzTFhCaApZMnRoWjJWek1ERXVjR2g0TWk1bVpX\n'
u'UnZjbUZ3Y205cVpXTjBMbTl5WnpFbU1DUUdDU3FHU0liM0RRRUpBUllYCllXUnRhV'
u'zVBWm1Wa2Iz\nSmhjSEp2YW1WamRDNXZjbWN3Z1o4d0RRWUpLb1pJaHZjTkFRRUJC'
u'UUFEZ1kwQU1JR0oKQW9HQkFL\nWUhDV1VhaWo4YlFub25ZVVYwOEdnWWYvWnRSRlB'
u'IVG9vYnkzQ3Z0Tk5Nc2JETkxnZEhxRUU2WHFs\nSApXM3FWaDNFaktJRHZDdmtzOU'
u't0endRY0pXZVdXLy9qMVozR1ZZMzZ1WS9vUDAvMEl0dmtaRTZZ\ndHkxNFZ5clI5C'
u'nZmL3NIVlllTFc2N2FIdW0xYVc3VElMZWxHVE1VUDJQWnNPdk0vNjlLNWpEMzhv\n'
u'RkFnTUJBQUdqZ2dGaU1JSUIKWGpBSkJnTlZIUk1FQWpBQU1DMEdDV0NHU0FHRytFS'
u'UJEUVFnRmg1\nRllYTjVMVkpUUVNCSFpXNWxjbUYwWldRZwpRMlZ5ZEdsbWFXTmhk'
u'R1V3SFFZRFZSME9CQllFRkFz\nZWpLMG9SanVuOWREL0pCZHlZRzJ2VmRJZk1JSGd'
u'CZ05WCkhTTUVnZGd3Z2RXQUZJVTdDN1dHSWpM\nZHZmamhmSW5ESUszS1JMVTRvWU'
u'dtcElHak1JR2dNUXN3Q1FZRFZRUUcKRXdKVlV6RUxNQWtHQTFV\nRUNCTUNUa014R'
u'URBT0JnTlZCQWNUQjFKaGJHVnBaMmd4RnpBVkJnTlZCQW9URGtabApaRzl5WVNC\n'
u'UWNtOXFaV04wTVE4d0RRWURWUVFMRXdabVpXUnRjMmN4RHpBTkJnTlZCQU1UQm1ab'
u'FpHMXpaekVQ\nCk1BMEdBMVVFS1JNR1ptVmtiWE5uTVNZd0pBWUpLb1pJaHZjTkFR'
u'a0JGaGRoWkcxcGJrQm1aV1J2\nY21Gd2NtOXEKWldOMExtOXlaNElVTVJoZm1seSt'
u'4bVFlL1NDek43S3lGdnZnd1BZd0V3WURWUjBs\nQkF3d0NnWUlLd1lCQlFVSApBd0'
u'l3Q3dZRFZSMFBCQVFEQWdlQU1BMEdDU3FHU0liM0RRRUJDd1VB\nQTRHQkFGVkVEa'
u'U93U2k2aWxEMTRiRCtZCmxjT3Rxc0FLY1o2cEpzalRiYVlacDdFRVpUd1NhZEJs\n'
u'cmtrZThTRkZzQUtnKzREVXU3ejF2Q0NVSHhSeEI1Z0oKYytNM1lrdW9OQXlOZThHY'
u'VZBbGNBSHo5\ndm95TW05cWhSbHlFa2pIaWNYcWRsK00wOXJlYmJBK1YyNVcyYzk0'
u'aApETXA1ZWUvalRFSkI3eEpj\nNjdNNW5KbWkKLS0tLS1FTkQgQ0VSVElGSUNBVEU'
u'tLS0tLQo=\n'
)
self.hub.config = {
'fedmsg.consumers.relay.enabled': True,
Expand All @@ -63,9 +69,12 @@ def test_message_signed(self):
expected_msg = {
'my': 'msg',
'crypto': 'x509',
'signature': (u'kyZ496SD+qgufonX9lqV/4L/o3s0+j4j5RaeMzhRIIGhfk6/RIEtl1DW73xbo+'
u'Xs2STbidFyz7Yt\n6IUb3/U+8Io0CTTbIyQvcvtof/a3EdmbnZtOQ93VfnXXkn'
u'6m76yVcFnQDicagY/600KmfNCDAwve\nI6+B9va/q10CBloMLkE=\n'),
'signature': (
u'cM41fBCf5vWoYQvI9mlVofIJZ/djKXAk+4s8EltzSeW+xWCqJ/EnCTHj'
u'ZcNGY09CeJRoDcq0Yc1y\nc8QR6IT7JCSlwkc1Iqj+5SKE/REm6AN5Xd'
u'3jqJHZSWMqKqhxvlzwaWEFI1nNp3qhxWDJNmUKEqIU\nUwl+6CntIulR'
u'f/5fiO8=\n'
),
'certificate': self.signing_cert,
}
consumer = relay.SigningRelayConsumer(self.hub)
Expand Down
2 changes: 1 addition & 1 deletion fedmsg/tests/crypto/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def test_remote_cert(self):

with mock.patch.dict('fedmsg.crypto.utils._cached_certificates', clear=True):
ca, crl = utils.load_certificates(location)
self.assertEqual((expected_cert, None), utils._cached_certificates[location])
self.assertEqual((str(expected_cert), None), utils._cached_certificates[location])
self.assertEqual(expected_cert, ca)
self.assertTrue(crl is None)

Expand Down
Loading

0 comments on commit 5bd669f

Please sign in to comment.