Use the instance service key for registry JWT signing
This commit is contained in:
parent
a4aa5cc02a
commit
8887f09ba8
26 changed files with 457 additions and 278 deletions
|
@ -1,20 +0,0 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDVDCCAjwCCQDNYtlT1+tGbzANBgkqhkiG9w0BAQUFADBsMQswCQYDVQQGEwJV
|
||||
UzERMA8GA1UECBMITmV3IFlvcmsxETAPBgNVBAcTCE5ldyBZb3JrMRQwEgYDVQQK
|
||||
EwtDb3JlT1MsIEluYzENMAsGA1UECxMEUXVheTESMBAGA1UEAxMJMTI3LjAuMC4x
|
||||
MB4XDTE2MDUyMzE1MjUxOVoXDTI2MDUyMTE1MjUxOVowbDELMAkGA1UEBhMCVVMx
|
||||
ETAPBgNVBAgTCE5ldyBZb3JrMREwDwYDVQQHEwhOZXcgWW9yazEUMBIGA1UEChML
|
||||
Q29yZU9TLCBJbmMxDTALBgNVBAsTBFF1YXkxEjAQBgNVBAMTCTEyNy4wLjAuMTCC
|
||||
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKRvOt/XGNIovlr1BWxl2oqs
|
||||
KDlgnESj6bFENDjs9+YLrB3mSWX6w4Dk2IdNU0EKHeVnnsAuBs83jaFsIVJxrC99
|
||||
ndv0PaejBovUbWyYN3zCMur8iNGse/FT4WRqks2m0Wr0jmEAX5piX/eWo/7OQdea
|
||||
wNAGyH7wE0voMpyVSZMBmxRw07zWnwWBihvhOiiCnXZh32GQMplq0wxk4DkBf3hC
|
||||
SEaAqsFHKfEFPxVXfdPGeiKKK+P2SAh+uN4miJpGf7Xkuj/Mmzxr1ajNczhPT6OM
|
||||
pw0R3h/mok1S8zcp8lN/eDdKwjMeP4Rx+Lc0cRluZNa8otq9qYPNSCIkvsSz5b8C
|
||||
AwEAATANBgkqhkiG9w0BAQUFAAOCAQEAZaaD8fLWEh4RGZ7X38IM/ocwDKaXWpDp
|
||||
0EC3KMEuar1MET3MtVIXy/k/BLr0HmLRQ2KSV3wFfyOInseVeCvIcKZZo/JF28gR
|
||||
LJVBcjExSIr6X8RoPgmKt7AdjlUjPV5XpRzDpfYcMaqpjJa75x6RoxC2ybh5Apyk
|
||||
EzL3Naysk6TVPi5ckUYMLfw3JEbCeaEY4KNwVgsNcs447EcBxwGHTBqGOYtpIfku
|
||||
SMas81oniMo9LMKv19Bn1oOforaqh8P2c57yregDsCDmP6j0gqkYjhJFCj5JNAKK
|
||||
KT35QIfTbVFeCXAoLw0+o9Ma1Q+j7LfwdxnikUHNVZmlmjQmTBMwqg==
|
||||
-----END CERTIFICATE-----
|
|
@ -1,27 +0,0 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEApG8639cY0ii+WvUFbGXaiqwoOWCcRKPpsUQ0OOz35gusHeZJ
|
||||
ZfrDgOTYh01TQQod5WeewC4GzzeNoWwhUnGsL32d2/Q9p6MGi9RtbJg3fMIy6vyI
|
||||
0ax78VPhZGqSzabRavSOYQBfmmJf95aj/s5B15rA0AbIfvATS+gynJVJkwGbFHDT
|
||||
vNafBYGKG+E6KIKddmHfYZAymWrTDGTgOQF/eEJIRoCqwUcp8QU/FVd908Z6Ioor
|
||||
4/ZICH643iaImkZ/teS6P8ybPGvVqM1zOE9Po4ynDRHeH+aiTVLzNynyU394N0rC
|
||||
Mx4/hHH4tzRxGW5k1ryi2r2pg81IIiS+xLPlvwIDAQABAoIBAANdV0oPP63FMafw
|
||||
zYybRO6DeUs7Q9dPt09uQtdLWgM2B+6QsL3KdMelZxzVozd4eoYgKaprBq6kx1wf
|
||||
N0tVkh1ip6FBjSVp+49O6HJJZxFBdANE6ZPIwLx+Z+VDHP/iQvS6TlODy3EARFBv
|
||||
n6luFQDRZNKc4OtgBDUQakCz+U5tuJLqoR8wk/WGQP4FJiZlVwJqNPXMA1A2Mrri
|
||||
n6WkhfpB30Z5dl9zsR+zJRbwRBjgJCYN37YC7zdHRfIhBPBvDT+8ApR50BGvPGN3
|
||||
sLQuH2FsskbgPsIrWMfCxtWr2xbw028GOe7TSjEG63EG7oGAT0O2eQmAcuPc4Dqj
|
||||
Urn8saECgYEA2LkCe6MysmOtattC/gi3B/rIoOCd+4l9yTnW7S7nk/hdeOzxyqX1
|
||||
P7OgVeoYLLk3UJy3qTrNDnc0eGTJz0XyPhLlX0f9lduiSMH92XpNsBG7ngnyMCQF
|
||||
eAZz8ZlDZC39I8y9CzdcHSLxuHKmQ9jhgUm+EIuf8OlrkjchPdE06i8CgYEAwjxG
|
||||
cDA5X1hKYgQTObq245vR3txkvETmLVB7hWkjWLzR//a4hXHJT1fg2LxD5EMtCKZ2
|
||||
WXKhcy3tbja+c/IEI1L1wA2v/aWlEvi9n354EQ1QzkvCBDFP5enLnItAUzJQ0IgE
|
||||
dtSUskK+li8aY2LB0EPt0eJmYU0cZUJXbl/ZKXECgYAAtttjPO512A5CQ+a8n5q6
|
||||
1ADFRvg+U/2uJBqpPXZV7oOgWmeRm2prg1QL9HGP9CxSf7G7RQ5X9dyeaPahUEG0
|
||||
IqvO3JXhYI/wXXNQvC51XhmYM8AwmG3ML3lCWpb2RZCIBay51Lzg+7SAPyB9KMHV
|
||||
g0C1HUCxspNAMB5T7dSW0QKBgGkxRaCarWeypE4jENpyAXyRNf8xcyj3U4V1EgB1
|
||||
qVv0nvK2BsbWkgTzfeVDSK2FqA0IQg49Y6zCUdUfttOKXa1Xz5ocj5SaMiVtKx0G
|
||||
3DW39WxUYRXuMuw8SzZTwBmOpW/aSjik9ob4WMlzZyIuKPMG5vSFXZcSsO8yF7HC
|
||||
HRUxAoGBAKtCRLT9I5Ap37gWT8W6AAZygoUqhlYO9qygQrBDaJsHj0ZSHM0TO3ig
|
||||
Bwq/UxDHBKFV3hmqx5Zmpoa9ZrURb4cBw/+TLq2ppXPLEU+XmEVmqL2323Vyr/Ih
|
||||
CAIVWFsY3EGQL7TArOfag+v0Nxq3pypOhjweqIWEMDg+gV2+GHhQ
|
||||
-----END RSA PRIVATE KEY-----
|
Binary file not shown.
1
test/data/test.kid
Normal file
1
test/data/test.kid
Normal file
|
@ -0,0 +1 @@
|
|||
test_service_key
|
|
@ -1,27 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEogIBAAKCAQEAvzy/peQEnNvIebJ+3QGtry6e8wPOlDBfQ5V5Yh9r07igUE5U
|
||||
GtUM8Yo/2Re4dtOoFwesPPFvwwu65Qp7L1XZda3KlOncCYmQ0/AHEKlyA07BBbJV
|
||||
YvJ9yg+m/94W0fzUgFTZJ18UnjUip7LT4MPkGANrMpZeI1hGVihOHIHjGR/kpMdd
|
||||
W862gCRwdFm4Hw+Wbt0UwXzhpr31CGY/Mzf6mwhzEk6Q0QjQC7R4zvj9Oi6Q1r1C
|
||||
ipPpdZLYDF+joSxA9ZKHiLCDH42kQu88WDNcdXSvg5bs6+4L0SvWwUpGLHPDOG5J
|
||||
ENM3cujv3/Pbx5QIQnWNOisMiLQsudWtf5V1sQIDAQABAoIBAA4QlbfJsV0n/PKW
|
||||
YiY2/WMo9p/A4+yaMidyUt8YmIGVzpSZbi4bBTyugkuhJvv2TSKEefJxf1rE/hXi
|
||||
U3UDx16UTZXuLTS2XWR4/swG6k+79w5IM2d3ljDKPeoLl3oSMj7N/rqaj5WjKs1S
|
||||
paqePaRWfAfYb0wCLgogJL6L/vvV8BillimzIWqXyfGlRux7oM5auy7rZ98dq5Tc
|
||||
f2wQ05W6oyK65pI1h8X9CMOdaBPxuhb+2w0sj3kICV4ZBksWqVFvqdDiAXQWev9M
|
||||
w+xVDorfKtcbspGh9RxwMqkRDqB+EBmLrLNPyYk/OUQ12vch9/8+L8W0xecRw6Qx
|
||||
rZjT0MECgYEA4rRTZUx1Gq38ltdBE1wTDMohO1m/q5rE4+iQQJEv8mMNOxYNf/KN
|
||||
4PR+57kXf6subM7J6iIBOFgNiA36fa+cJVGMj28y3BsbTmPdXAzRh3dv67dOwqvM
|
||||
ssjFBmFbLqZz3a8qXyQ85QgThGT3Yreq+GOjfHmwYBiBCCMPJM0Zn1UCgYEA1/Mg
|
||||
HX0d9SuIReLSFKDb9vLwdGeaAkm2o8qj14M2k6lPg8G5B6zDD57rs3e0QV0xG7UL
|
||||
qwyahKENXUO9FLqzWk7HjJJiH11A4jR8kP42IKCpvwDVkQE3uLg9ONUCBBu9KA2h
|
||||
6LQM0q5aEQHv+3PsgeQECwV+8X4Gz0sS65ChJO0CgYEA0OZlqoSHrCwDA2QavSIt
|
||||
E632bWBINHMLVw/oTPb8fZg0iuvJSMtTXaUug4yVULmGsBDlEnB/O1I4NdTbq5F0
|
||||
ixbYNRu8fAImaVewlK/jK7ctVMG3O79fgqdqlnSDtzr+rZpJqx4TVuDYSzlWlIq2
|
||||
aug1r+/aTNKHo93aiIjOQXkCf3bwcb/MKbPfRi83vn2eG4joRYfXh/u6nd2YvqT0
|
||||
oBq0Jhdrm32eqdDwtuEiDSXzLhkUnliXmIN0MOgtZvcD3cTfnwjNlz2vHw132yQA
|
||||
388YrmWFEBvNj+Mtloq2x2V74bMtzv9cK7PeU70KVCMqthjUfWWUoVZhE18Y+lLE
|
||||
Vf0CgYEAzAlbU3OiVr36x5fTcPmHi7925cZpsaLKp/DwSIfHCp/oBI2UwZu4Q44h
|
||||
FR+JaKhCkmIquxrkdM8tWGotMoPKVvakMZtuebMJTI/C/tt4MBCdibM7JqgZ1Olr
|
||||
t2ujgUTujkXSCS8SSGS3KKvKoDquowJwqnDS8h2+WVfPgGJYOfk=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAyqdQgnelhAPMSeyH0kr3UGePK9oFOmNfwD0Ymnh7YYXr21VH
|
||||
WwyM2eVW3cnLd9KXywDFtGSe9oFDbnOuMCdUowdkBcaHju+isbv5KEbNSoy/T2Ri
|
||||
p+6L0cY63YzcMJzv1nEYztYXS8wz76pSK81BKBCLapqOCmcPeCvV9yaoFZYvZEsX
|
||||
Cl5jjXN3iujSzSF5Z6PpNFlJWTErMT2Z4QfbDKX2Nw6vJN6JnGpTNHZvgvcyNX8v
|
||||
kSgVpQ8DFnFkBEx54PvRV5KpHAq6AsJxKONMo11idQS2PfCNpa2hvz9O6UZe+eIX
|
||||
8jPo5NW8TuGZJumbdPT/nxTDLfCqfiZboeI0PwIDAQABAoIBAHVJhLUd3jObpx6Z
|
||||
wLobHSvx49Dza9cxMHeoZJbyaCY3Rhw5LQUrLFHoA/B1HEeLIMMi/Um8eqwcgBRq
|
||||
60N/X+LDIkadclNtqfHH4xpGcAZXk1m1tcuPqmiMnAEhx0ZzbfPknQEIs47w7pYl
|
||||
M02ai71OZgIa1V57614XsMxMGTf0HadsmqC0cxLQ21UxROzCvv49N26ZTav7aLwl
|
||||
1yW+scP/lo2HH6VJFTNJduOBgmnnMVIEhYLHa26lsf3biARf8TyV2xupex7s46iD
|
||||
RegXZzzAlHx8/qkFoTfENNefAbBX87E+r1gs9zmWEo+2DaKYxmuDTqAbk107c1Jo
|
||||
XQ59MRECgYEA0utdbhdgL0ll69tV4wGdZb49VTCzfmGrab8X+8Hy6aFzUDYqIsJD
|
||||
1l5e7fPxY+MqeP4XoJ7q5RAqIBAXSubds743RElMxvLsiy6Q/HuZ5ICxjUZ2Amop
|
||||
mItcGG9RXaiqXAKHk6ZMgFhO3/NAVv2it+XnP3uLucAgmqh7Wp7ML2MCgYEA9fet
|
||||
kYirz32ZAvxWDrTapQjSDkyCAaCZGB+BQ5paBLeMzTIcVHiu2aggCJrc4YoqB91D
|
||||
JHlynZhvxOK0m1KXHDnbPn9YqwsVZDTIU4PnpC0KEj357VujXDH/tD0ggzrm5ruQ
|
||||
4o0SpfavI7MAe0vUlv46x+CfIzSq+kPRenrRBHUCgYEAyCAIk1fcrKFg8ow3jt/O
|
||||
X2ZFPZqrBMRZZ0mo0PiyqljFWBs8maRnx3PdcLvgk11MxGabNozy5YsT3T5HS4uI
|
||||
Wm6mc8V08uQ16s2xRc9lMnmlfh2YBSyD8ThxlsGwm0RY+FpyF3dX6QNhO37L0n5w
|
||||
MTsT0pk/92xDw1sPR+maZW8CgYBp8GJ2k1oExUDZE1vxe53MhS8L75HzJ3uo8zDW
|
||||
sC1jaLchThr7mvscThh1/FV0YvDVcExR8mkWTaieMVK+r2TcSGMQ2QKUsPJmtYEu
|
||||
z1o+0RNMZhs2S0jiFbrfo5BUVVNMP68YlNBaYRRwGNH1SOTon9kra6i/HhkiL4GS
|
||||
8kECXQKBgAs/DqfCobJsIMi7TDcG1FkQEKwPmnKmh8rDX3665gCRz7rOoIG8u05A
|
||||
J6pQqrUrPRI+AAtVM4nW0z4KE07ruTJ/8wapTErm/5Bp5bikiaHy7NY2kHj3hVwr
|
||||
KYh700ZUPV9vd+xUpfTNoVyvV2tu4QnG8ihKII6vfCPItEpE8glo
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -1 +0,0 @@
|
|||
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/PL+l5ASc28h5sn7dAa2vLp7zA86UMF9DlXliH2vTuKBQTlQa1Qzxij/ZF7h206gXB6w88W/DC7rlCnsvVdl1rcqU6dwJiZDT8AcQqXIDTsEFslVi8n3KD6b/3hbR/NSAVNknXxSeNSKnstPgw+QYA2syll4jWEZWKE4cgeMZH+Skx11bzraAJHB0WbgfD5Zu3RTBfOGmvfUIZj8zN/qbCHMSTpDRCNALtHjO+P06LpDWvUKKk+l1ktgMX6OhLED1koeIsIMfjaRC7zxYM1x1dK+Dluzr7gvRK9bBSkYsc8M4bkkQ0zdy6O/f89vHlAhCdY06KwyItCy51a1/lXWx jzelinskie@hanazawa
|
|
@ -15,10 +15,8 @@ from cachetools import lru_cache
|
|||
from flask import request, jsonify, abort
|
||||
from flask.blueprints import Blueprint
|
||||
from flask.ext.testing import LiveServerTestCase
|
||||
from cryptography.x509 import load_pem_x509_certificate
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
from app import app, storage
|
||||
from app import app, storage, instance_keys
|
||||
from data.database import close_db_filter, configure, DerivedStorageForImage, QueueItem, Image
|
||||
from data import model
|
||||
from endpoints.v1 import v1_bp
|
||||
|
@ -30,7 +28,7 @@ from initdb import wipe_database, initialize_database, populate_database
|
|||
from endpoints.csrf import generate_csrf_token
|
||||
from tempfile import NamedTemporaryFile
|
||||
from jsonschema import validate as validate_schema
|
||||
from util.security import strictjwt
|
||||
from util.security.registry_jwt import decode_bearer_token
|
||||
|
||||
import endpoints.decorated
|
||||
import json
|
||||
|
@ -1824,36 +1822,19 @@ class V1LoginTests(V1RegistryLoginMixin, LoginTests, RegistryTestCaseMixin, Base
|
|||
|
||||
class V2LoginTests(V2RegistryLoginMixin, LoginTests, RegistryTestCaseMixin, BaseRegistryMixin, LiveServerTestCase):
|
||||
""" Tests for V2 login. """
|
||||
|
||||
@staticmethod
|
||||
@lru_cache(maxsize=1)
|
||||
def load_public_key(certificate_file_path):
|
||||
with open(certificate_file_path) as cert_file:
|
||||
cert_obj = load_pem_x509_certificate(cert_file.read(), default_backend())
|
||||
return cert_obj.public_key()
|
||||
|
||||
|
||||
def do_logincheck(self, username, password, scope, expected_actions=[], expect_success=True,
|
||||
**kwargs):
|
||||
# Perform login to get an auth token.
|
||||
response = self.do_login(username, password, scope, expect_success=expect_success, **kwargs)
|
||||
|
||||
if not expect_success:
|
||||
return
|
||||
|
||||
# Validate the returned JWT.
|
||||
# Validate the returned token.
|
||||
encoded = response.json()['token']
|
||||
token = 'Bearer ' + encoded
|
||||
|
||||
expected_issuer = app.config['JWT_AUTH_TOKEN_ISSUER']
|
||||
audience = app.config['SERVER_HOSTNAME']
|
||||
|
||||
max_signed_s = app.config.get('JWT_AUTH_MAX_FRESH_S', 3660)
|
||||
certificate_file_path = app.config['JWT_AUTH_CERTIFICATE_PATH']
|
||||
|
||||
public_key = V2LoginTests.load_public_key(certificate_file_path)
|
||||
|
||||
max_exp = strictjwt.exp_max_s_option(max_signed_s)
|
||||
payload = strictjwt.decode(encoded, public_key, algorithms=['RS256'], audience=audience,
|
||||
issuer=expected_issuer, options=max_exp)
|
||||
payload = decode_bearer_token(token, instance_keys)
|
||||
self.assertIsNotNone(payload)
|
||||
|
||||
if scope is None:
|
||||
self.assertEquals(0, len(payload['access']))
|
||||
|
|
|
@ -2,15 +2,15 @@ import unittest
|
|||
import time
|
||||
import jwt
|
||||
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
|
||||
from app import app
|
||||
from app import app, instance_keys
|
||||
from data import model
|
||||
from data.database import ServiceKeyApprovalType
|
||||
from initdb import setup_database_for_testing, finished_database_for_testing
|
||||
from endpoints.v2.v2auth import TOKEN_VALIDITY_LIFETIME_S
|
||||
from auth.registry_jwt_auth import identity_from_bearer_token, load_public_key, InvalidJWTException
|
||||
from auth.registry_jwt_auth import identity_from_bearer_token, InvalidJWTException
|
||||
from util.morecollections import AttrDict
|
||||
from util.security.registry_jwt import (_load_certificate_bytes, _load_private_key, ANONYMOUS_SUB,
|
||||
build_context_and_subject)
|
||||
from util.security.registry_jwt import (ANONYMOUS_SUB, build_context_and_subject,
|
||||
decode_bearer_token, generate_bearer_token)
|
||||
|
||||
|
||||
TEST_AUDIENCE = app.config['SERVER_HOSTNAME']
|
||||
|
@ -19,20 +19,18 @@ MAX_SIGNED_S = 3660
|
|||
|
||||
|
||||
class TestRegistryV2Auth(unittest.TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(TestRegistryV2Auth, self).__init__(*args, **kwargs)
|
||||
self.public_key = None
|
||||
|
||||
def setUp(self):
|
||||
certificate_file_path = app.config['JWT_AUTH_CERTIFICATE_PATH']
|
||||
self.public_key = load_public_key(certificate_file_path)
|
||||
setup_database_for_testing(self)
|
||||
|
||||
def tearDown(self):
|
||||
finished_database_for_testing(self)
|
||||
|
||||
def _generate_token_data(self, access=[], audience=TEST_AUDIENCE, user=TEST_USER, iat=None,
|
||||
exp=None, nbf=None, iss=app.config['JWT_AUTH_TOKEN_ISSUER']):
|
||||
exp=None, nbf=None, iss=None):
|
||||
|
||||
_, subject = build_context_and_subject(user, None, None)
|
||||
return {
|
||||
'iss': iss,
|
||||
'iss': iss or instance_keys.service_name,
|
||||
'aud': audience,
|
||||
'nbf': nbf if nbf is not None else int(time.time()),
|
||||
'iat': iat if iat is not None else int(time.time()),
|
||||
|
@ -41,28 +39,25 @@ class TestRegistryV2Auth(unittest.TestCase):
|
|||
'access': access,
|
||||
}
|
||||
|
||||
def _generate_token(self, token_data):
|
||||
def _generate_token(self, token_data, key_id=None, private_key=None, skip_header=False, alg=None):
|
||||
key_id = key_id or instance_keys.local_key_id
|
||||
private_key = private_key or instance_keys.local_private_key
|
||||
|
||||
certificate = _load_certificate_bytes(app.config['JWT_AUTH_CERTIFICATE_PATH'])
|
||||
if alg == "none":
|
||||
private_key = None
|
||||
|
||||
token_headers = {
|
||||
'x5c': [certificate],
|
||||
'kid': key_id,
|
||||
}
|
||||
|
||||
private_key = _load_private_key(app.config['JWT_AUTH_PRIVATE_KEY_PATH'])
|
||||
token_data = jwt.encode(token_data, private_key, 'RS256', headers=token_headers)
|
||||
if skip_header:
|
||||
token_headers = {}
|
||||
|
||||
token_data = jwt.encode(token_data, private_key, alg or 'RS256', headers=token_headers)
|
||||
return 'Bearer {0}'.format(token_data)
|
||||
|
||||
def _parse_token(self, token):
|
||||
return identity_from_bearer_token(token, MAX_SIGNED_S, self.public_key)[0]
|
||||
|
||||
def _generate_public_key(self):
|
||||
key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=1024,
|
||||
backend=default_backend()
|
||||
)
|
||||
return key.public_key()
|
||||
return identity_from_bearer_token(token)[0]
|
||||
|
||||
def test_accepted_token(self):
|
||||
token = self._generate_token(self._generate_token_data())
|
||||
|
@ -100,12 +95,6 @@ class TestRegistryV2Auth(unittest.TestCase):
|
|||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(token)
|
||||
|
||||
def test_bad_signature(self):
|
||||
token = self._generate_token(self._generate_token_data())
|
||||
other_public_key = self._generate_public_key()
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
identity_from_bearer_token(token, MAX_SIGNED_S, other_public_key)
|
||||
|
||||
def test_audience(self):
|
||||
token_data = self._generate_token_data(audience='someotherapp')
|
||||
token = self._generate_token(token_data)
|
||||
|
@ -171,7 +160,6 @@ class TestRegistryV2Auth(unittest.TestCase):
|
|||
|
||||
def test_iss(self):
|
||||
token_data = self._generate_token_data(iss='badissuer')
|
||||
|
||||
token = self._generate_token(token_data)
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(token)
|
||||
|
@ -181,9 +169,94 @@ class TestRegistryV2Auth(unittest.TestCase):
|
|||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(no_iss_token)
|
||||
|
||||
def test_missing_header(self):
|
||||
token_data = self._generate_token_data()
|
||||
missing_header_token = self._generate_token(token_data, skip_header=True)
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(missing_header_token)
|
||||
|
||||
def test_invalid_key(self):
|
||||
token_data = self._generate_token_data()
|
||||
invalid_key_token = self._generate_token(token_data, key_id='someunknownkey')
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(invalid_key_token)
|
||||
|
||||
def test_expired_key(self):
|
||||
token_data = self._generate_token_data()
|
||||
expired_key_token = self._generate_token(token_data, key_id='kid7')
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(expired_key_token)
|
||||
|
||||
def test_mixing_keys(self):
|
||||
token_data = self._generate_token_data()
|
||||
|
||||
# Create a new key for testing.
|
||||
p, key = model.service_keys.generate_service_key(instance_keys.service_name, None, kid='newkey',
|
||||
name='newkey', metadata={})
|
||||
|
||||
private_key = p.exportKey('PEM')
|
||||
|
||||
# Test first with the new valid, but unapproved key.
|
||||
unapproved_key_token = self._generate_token(token_data, key_id='newkey', private_key=private_key)
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(unapproved_key_token)
|
||||
|
||||
# Approve the key and try again.
|
||||
admin_user = model.user.get_user('devtable')
|
||||
model.service_keys.approve_service_key(key.kid, admin_user, ServiceKeyApprovalType.SUPERUSER)
|
||||
|
||||
valid_token = self._generate_token(token_data, key_id='newkey', private_key=private_key)
|
||||
|
||||
identity = self._parse_token(valid_token)
|
||||
self.assertEqual(identity.id, TEST_USER.username)
|
||||
self.assertEqual(0, len(identity.provides))
|
||||
|
||||
# Try using a different private key with the existing key ID.
|
||||
bad_private_token = self._generate_token(token_data, key_id='newkey', private_key=instance_keys.local_private_key)
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(bad_private_token)
|
||||
|
||||
# Try using a different key ID with the existing private key.
|
||||
kid_mismatch_token = self._generate_token(token_data, key_id=instance_keys.local_key_id, private_key=private_key)
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(kid_mismatch_token)
|
||||
|
||||
# Delete the new key.
|
||||
key.delete_instance(recursive=True)
|
||||
|
||||
# Ensure it still works (via the cache.)
|
||||
deleted_key_token = self._generate_token(token_data, key_id='newkey', private_key=private_key)
|
||||
identity = self._parse_token(deleted_key_token)
|
||||
self.assertEqual(identity.id, TEST_USER.username)
|
||||
self.assertEqual(0, len(identity.provides))
|
||||
|
||||
# Break the cache.
|
||||
instance_keys.clear_cache()
|
||||
|
||||
# Ensure the key no longer works.
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(deleted_key_token)
|
||||
|
||||
def test_bad_token(self):
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token("some random token here")
|
||||
|
||||
def test_bad_bearer_token(self):
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token("Bearer: sometokenhere")
|
||||
|
||||
def test_bad_bearer_newline_token(self):
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token("\nBearer: dGVzdA")
|
||||
|
||||
def test_ensure_no_none(self):
|
||||
token_data = self._generate_token_data()
|
||||
none_token = self._generate_token(token_data, alg='none', private_key=None)
|
||||
|
||||
with self.assertRaises(InvalidJWTException):
|
||||
self._parse_token(none_token)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
unittest.main()
|
||||
|
||||
|
|
|
@ -56,8 +56,6 @@ class TestConfig(DefaultConfig):
|
|||
FEATURE_BITTORRENT = True
|
||||
FEATURE_ACI_CONVERSION = True
|
||||
|
||||
INSTANCE_SERVICE_KEY_LOCATION = 'test/data/test.pem'
|
||||
|
||||
CLOUDWATCH_NAMESPACE = None
|
||||
|
||||
FEATURE_SECURITY_SCANNER = True
|
||||
|
@ -73,5 +71,5 @@ class TestConfig(DefaultConfig):
|
|||
GPG2_PRIVATE_KEY_FILENAME = 'test/data/signing-private.gpg'
|
||||
GPG2_PUBLIC_KEY_FILENAME = 'test/data/signing-public.gpg'
|
||||
|
||||
JWT_AUTH_CERTIFICATE_PATH = 'test/data/registry_v2_auth.crt'
|
||||
JWT_AUTH_PRIVATE_KEY_PATH = 'test/data/registry_v2_auth_private.key'
|
||||
INSTANCE_SERVICE_KEY_KID_LOCATION = 'test/data/test.kid'
|
||||
INSTANCE_SERVICE_KEY_LOCATION = 'test/data/test.pem'
|
||||
|
|
Reference in a new issue