canonicalize json

This commit is contained in:
Jimmy Zelinskie 2016-03-29 14:49:07 -04:00 committed by Jimmy Zelinskie
parent f406942984
commit 97ae800e6c
3 changed files with 36 additions and 16 deletions

View file

@ -5,7 +5,6 @@ import logging
import os import os
import string import string
from collections import OrderedDict
from datetime import datetime from datetime import datetime
from hashlib import sha256 from hashlib import sha256
from random import SystemRandom from random import SystemRandom
@ -27,6 +26,7 @@ from endpoints.api import (ApiResource, nickname, resource, validate_json_reques
from endpoints.api.logs import get_logs, get_aggregate_logs from endpoints.api.logs import get_logs, get_aggregate_logs
from data import model from data import model
from data.database import ServiceKeyApprovalType from data.database import ServiceKeyApprovalType
from util import canonicalize
from util.useremails import send_confirmation_email, send_recovery_email from util.useremails import send_confirmation_email, send_recovery_email
@ -543,9 +543,7 @@ class SuperUserServiceKeyManagement(ApiResource):
private_key = RSA.generate(2048) private_key = RSA.generate(2048)
jwk = RSAKey(key=private_key.publickey()).serialize() jwk = RSAKey(key=private_key.publickey()).serialize()
canonical_jwk = OrderedDict(sorted(jwk.items())) kid = sha256(json.dumps(canonicalize(jwk), separators=(',', ':'))).hexdigest()
kid = sha256(json.dumps(canonical_jwk)).hexdigest()
model.service_keys.create_service_key(body.get('name', ''), kid, body['service'], jwk, model.service_keys.create_service_key(body.get('name', ''), kid, body['service'], jwk,
metadata, expiration_date) metadata, expiration_date)

View file

@ -1,5 +1,3 @@
import json
from datetime import datetime from datetime import datetime
import jwt import jwt
@ -68,7 +66,14 @@ def get_service_keys(service):
@key_server.route('/services/<service>/keys/<kid>', methods=['GET']) @key_server.route('/services/<service>/keys/<kid>', methods=['GET'])
def get_service_key(kid): def get_service_key(kid):
key = data.model.service_keys.get_service_key(kid) try:
key = data.model.service_keys.get_service_key(kid)
except data.model.ServiceKeyDoesNotExist:
abort(404)
if key.approval is None:
abort(404)
return jsonify(key.jwk) return jsonify(key.jwk)
@ -92,26 +97,30 @@ def put_service_keys(service, kid):
_validate_jwk(jwk, kid) _validate_jwk(jwk, kid)
metadata = {'ip': request.remote_addr}
signer_kid = _signer_kid(encoded_jwt) signer_kid = _signer_kid(encoded_jwt)
signer_key = _signer_key(signer_kid)
if kid == signer_kid:
# The key is self-signed. Create a new instance and await approval.
_validate_jwt(encoded_jwt, jwk, service)
data.model.service_keys.create_service_key('', kid, service, jwk, metadata, expiration_date)
return make_response('', 202)
metadata.update({'created_by': 'Key Rotation'})
signer_key = _signer_key(signer_kid)
signer_jwk = signer_key.jwk
if signer_key.service != service: if signer_key.service != service:
abort(403) abort(403)
_validate_jwt(encoded_jwt, signer_key.jwk, service) _validate_jwt(encoded_jwt, signer_jwk, service)
metadata = {
'ip': request.remote_addr,
'created_by': 'Key Rotation',
'rotated_by': json.dumps(signer_key),
}
try: try:
data.model.service_keys.replace_service_key(kid, jwk, metadata, expiration_date) data.model.service_keys.replace_service_key(kid, jwk, metadata, expiration_date)
except data.model.ServiceKeyDoesNotExist: except data.model.ServiceKeyDoesNotExist:
abort(404) abort(404)
return make_response('', 200)
@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):

View file

@ -1,3 +1,6 @@
import collections
def get_app_url(config): def get_app_url(config):
""" Returns the application's URL, based on the given config. """ """ Returns the application's URL, based on the given config. """
return '%s://%s' % (config['PREFERRED_URL_SCHEME'], config['SERVER_HOSTNAME']) return '%s://%s' % (config['PREFERRED_URL_SCHEME'], config['SERVER_HOSTNAME'])
@ -16,3 +19,13 @@ def slash_join(*args):
args = [rmslash(path) for path in args] args = [rmslash(path) for path in args]
return '/'.join(args) return '/'.join(args)
def canonicalize(json_obj):
""" Returns a JSON object sorted by key. """
if isinstance(json_obj, collections.MutableMapping):
sorted_obj = sorted({key: canonicalize(val) for key, val in json_obj}.items())
return collections.OrderedDict(sorted_obj)
elif isinstance(json_obj, (list, tuple)):
return [canonicalize(val) for val in json_obj]
return json_obj