summaryrefslogtreecommitdiff
path: root/tests/unit/test_auth.py
blob: 6d35d761e9415a37c73ab7807d38343da97c0ff3 (plain)
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
# Copyright 2020 OpenStack Foundation
# Copyright 2020 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import json
from unittest import mock
import os.path
import jwt
from io import StringIO
import time

from zuul.driver import auth

from tests.base import BaseTestCase, FIXTURE_DIR

with open(os.path.join(FIXTURE_DIR,
                       'auth/openid-configuration.json'), 'r') as well_known:
    FAKE_WELL_KNOWN_CONFIG = json.loads(well_known.read())


algo = jwt.algorithms.RSAAlgorithm(jwt.algorithms.RSAAlgorithm.SHA256)
with open(os.path.join(FIXTURE_DIR,
                       'auth/oidc-key'), 'r') as k:
    OIDC_PRIVATE_KEY = algo.prepare_key(k.read())
with open(os.path.join(FIXTURE_DIR,
                       'auth/oidc-key.pub'), 'r') as k:
    pub_key = algo.prepare_key(k.read())
    pub_jwk = algo.to_jwk(pub_key)
    key = {
        "kid": "OwO",
        "use": "sig",
        "alg": "RS256"
    }
    key.update(json.loads(pub_jwk))
    # not present in keycloak jwks
    if "key_ops" in key:
        del key["key_ops"]
    FAKE_CERTS = {
        "keys": [
            key
        ]
    }


class FakeResponse:
    def __init__(self, json_dict):
        self._json = json_dict

    def json(self):
        return self._json


def mock_get(url, params=None, **kwargs):
    if url == ("https://my.oidc.provider/auth/realms/realm-one/"
               ".well-known/openid-configuration"):
        return FakeResponse(FAKE_WELL_KNOWN_CONFIG)
    else:
        raise Exception("Unknown URL %s" % url)


def mock_urlopen(url, *args, **kwargs):
    if hasattr(url, 'full_url'):
        # Like a urllib.Request object
        url = url.full_url
    if url == ("https://my.oidc.provider/auth/realms/realm-one/"
               "protocol/openid-connect/certs"):
        io = StringIO()
        json.dump(FAKE_CERTS, io)
        io.seek(0)
        return io
    else:
        raise Exception("Unknown URL %s" % url)


class TestOpenIDConnectAuthenticator(BaseTestCase):
    def test_decodeToken(self):
        """Test the decoding workflow"""
        config = {
            'issuer_id': FAKE_WELL_KNOWN_CONFIG['issuer'],
            'client_id': 'zuul-app',
            'realm': 'realm-one',
        }
        OIDCAuth = auth.jwt.OpenIDConnectAuthenticator(**config)
        payload = {
            'iss': FAKE_WELL_KNOWN_CONFIG['issuer'],
            'aud': config['client_id'],
            'exp': int(time.time()) + 3600,
            'sub': 'someone'
        }
        token = jwt.encode(
            payload,
            OIDC_PRIVATE_KEY,
            algorithm='RS256',
            headers={'kid': 'OwO'})
        with mock.patch('requests.get', side_effect=mock_get):
            # patching call in PyJWKClient's fetch_data
            with mock.patch('urllib.request.urlopen',
                            side_effect=mock_urlopen):
                decoded = OIDCAuth.decodeToken(token)
                for claim in payload.keys():
                    self.assertEqual(payload[claim], decoded[claim])