key server: misc cleanup to get it working
This commit is contained in:
parent
c0ab45d335
commit
50ad1bb6b1
2 changed files with 37 additions and 11 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
from calendar import timegm
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
|
@ -32,8 +33,8 @@ def _notify_superusers(key):
|
||||||
'service': key.service,
|
'service': key.service,
|
||||||
'jwk': key.jwk,
|
'jwk': key.jwk,
|
||||||
'metadata': key.metadata,
|
'metadata': key.metadata,
|
||||||
'created_date': key.created_date,
|
'created_date': timegm(key.created_date.utctimetuple()),
|
||||||
'expiration_date': key.expiration_date,
|
'expiration_date': timegm(key.created_date.utctimetuple()),
|
||||||
}
|
}
|
||||||
|
|
||||||
superusers = User.select().where(User.username << app.config['SUPER_USERS'])
|
superusers = User.select().where(User.username << app.config['SUPER_USERS'])
|
||||||
|
@ -136,4 +137,7 @@ def list_service_keys(service):
|
||||||
|
|
||||||
|
|
||||||
def get_service_key(kid, service=None):
|
def get_service_key(kid, service=None):
|
||||||
|
try:
|
||||||
return _list_service_keys_query(kid=kid, service=service).get()
|
return _list_service_keys_query(kid=kid, service=service).get()
|
||||||
|
except ServiceKey.DoesNotExist:
|
||||||
|
raise ServiceKeyDoesNotExist
|
||||||
|
|
|
@ -1,18 +1,24 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
import jwt
|
import jwt
|
||||||
|
|
||||||
from flask import Blueprint, jsonify, abort, request, make_response
|
from flask import Blueprint, jsonify, abort, request, make_response
|
||||||
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicNumbers
|
||||||
|
from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePublicNumbers
|
||||||
from cryptography.hazmat.backends import default_backend
|
from cryptography.hazmat.backends import default_backend
|
||||||
|
from jwkest.jwk import keyrep, RSAKey, ECKey
|
||||||
|
|
||||||
import data.model
|
import data.model
|
||||||
import data.model.service_keys
|
import data.model.service_keys
|
||||||
|
|
||||||
from app import app
|
from app import app
|
||||||
|
from auth.registry_jwt_auth import TOKEN_REGEX
|
||||||
from util.security import strictjwt
|
from util.security import strictjwt
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
key_server = Blueprint('key_server', __name__)
|
key_server = Blueprint('key_server', __name__)
|
||||||
|
|
||||||
JWT_HEADER_NAME = 'Authorization'
|
JWT_HEADER_NAME = 'Authorization'
|
||||||
|
@ -36,9 +42,18 @@ def _validate_jwk(jwk, kid):
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
|
|
||||||
|
def _jwk_dict_to_public_key(jwk):
|
||||||
|
jwkest_key = keyrep(jwk)
|
||||||
|
if isinstance(jwkest_key, RSAKey):
|
||||||
|
pycrypto_key = jwkest_key.key
|
||||||
|
return RSAPublicNumbers(e=pycrypto_key.e, n=pycrypto_key.n).public_key(default_backend())
|
||||||
|
elif isinstance(jwkest_key, ECKey):
|
||||||
|
x, y = jwkest_key.get_key()
|
||||||
|
return EllipticCurvePublicNumbers(x, y, jwkest_key.curve).public_key(default_backend())
|
||||||
|
|
||||||
|
|
||||||
def _validate_jwt(encoded_jwt, jwk, service):
|
def _validate_jwt(encoded_jwt, jwk, service):
|
||||||
public_key = RSAPublicNumbers(e=jwk.e,
|
public_key = _jwk_dict_to_public_key(jwk)
|
||||||
n=jwk.n).public_key(default_backend())
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
strictjwt.decode(encoded_jwt, public_key, algorithms=['RS256'],
|
strictjwt.decode(encoded_jwt, public_key, algorithms=['RS256'],
|
||||||
|
@ -49,6 +64,7 @@ def _validate_jwt(encoded_jwt, jwk, service):
|
||||||
|
|
||||||
def _signer_kid(encoded_jwt):
|
def _signer_kid(encoded_jwt):
|
||||||
decoded_jwt = jwt.decode(encoded_jwt, verify=False)
|
decoded_jwt = jwt.decode(encoded_jwt, verify=False)
|
||||||
|
logger.debug(decoded_jwt)
|
||||||
return decoded_jwt.get('signer_kid', None)
|
return decoded_jwt.get('signer_kid', None)
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,20 +104,24 @@ def put_service_keys(service, kid):
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
jwk = request.json()
|
jwk = request.get_json()
|
||||||
except ValueError:
|
except ValueError:
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
encoded_jwt = request.headers.get(JWT_HEADER_NAME, None)
|
logger.debug(jwk)
|
||||||
if not encoded_jwt:
|
|
||||||
|
jwt_header = request.headers.get(JWT_HEADER_NAME, '')
|
||||||
|
match = TOKEN_REGEX.match(jwt_header)
|
||||||
|
if match is None:
|
||||||
abort(400)
|
abort(400)
|
||||||
|
encoded_jwt = match.group(1)
|
||||||
|
|
||||||
_validate_jwk(jwk, kid)
|
_validate_jwk(jwk, kid)
|
||||||
|
|
||||||
metadata = {'ip': request.remote_addr}
|
metadata = {'ip': request.remote_addr}
|
||||||
signer_kid = _signer_kid(encoded_jwt)
|
signer_kid = _signer_kid(encoded_jwt)
|
||||||
|
|
||||||
if kid == signer_kid or signer_kid == '':
|
if kid == signer_kid or signer_kid is None:
|
||||||
# The key is self-signed. Create a new instance and await approval.
|
# The key is self-signed. Create a new instance and await approval.
|
||||||
_validate_jwt(encoded_jwt, jwk, service)
|
_validate_jwt(encoded_jwt, jwk, service)
|
||||||
data.model.service_keys.create_service_key('', kid, service, jwk, metadata, expiration_date)
|
data.model.service_keys.create_service_key('', kid, service, jwk, metadata, expiration_date)
|
||||||
|
@ -125,9 +145,11 @@ def put_service_keys(service, kid):
|
||||||
|
|
||||||
@key_server.route('/services/<service>/keys/<kid>', methods=['DELETE'])
|
@key_server.route('/services/<service>/keys/<kid>', methods=['DELETE'])
|
||||||
def delete_service_key(service, kid):
|
def delete_service_key(service, kid):
|
||||||
encoded_jwt = request.headers.get(JWT_HEADER_NAME, None)
|
jwt_header = request.headers.get(JWT_HEADER_NAME, '')
|
||||||
if not encoded_jwt:
|
match = TOKEN_REGEX.match(jwt_header)
|
||||||
|
if match is None:
|
||||||
abort(400)
|
abort(400)
|
||||||
|
encoded_jwt = match.group(1)
|
||||||
|
|
||||||
signer_kid = _signer_kid(encoded_jwt)
|
signer_kid = _signer_kid(encoded_jwt)
|
||||||
signer_key = _signer_key(service, signer_kid)
|
signer_key = _signer_key(service, signer_kid)
|
||||||
|
|
Reference in a new issue