keys ui WIP

This commit is contained in:
Joseph Schorr 2016-04-01 13:55:29 -04:00 committed by Jimmy Zelinskie
parent dc593c0197
commit 11ff3e9b59
25 changed files with 1154 additions and 74 deletions

View file

@ -5,6 +5,7 @@ import logging
import os
import string
from calendar import timegm
from datetime import datetime
from hashlib import sha256
from random import SystemRandom
@ -22,7 +23,7 @@ from auth.permissions import SuperUserPermission
from endpoints.api import (ApiResource, nickname, resource, validate_json_request,
internal_only, require_scope, show_if, parse_args,
query_param, abort, require_fresh_login, path_param, verify_not_prod,
page_support)
page_support, log_action)
from endpoints.api.logs import get_logs, get_aggregate_logs
from data import model
from data.database import ServiceKeyApprovalType
@ -148,6 +149,8 @@ def org_view(org):
def user_view(user, password=None):
user_data = {
'kind': 'user',
'name': user.username,
'username': user.username,
'email': user.email,
'verified': user.verified,
@ -506,7 +509,7 @@ class SuperUserServiceKeyManagement(ApiResource):
""" Resource for managing service keys."""
schemas = {
'CreateServiceKey': {
'id': 'PutServiceKey',
'id': 'CreateServiceKey',
'type': 'object',
'description': 'Description of creation of a service key',
'required': ['service', 'expiration'],
@ -523,9 +526,13 @@ class SuperUserServiceKeyManagement(ApiResource):
'type': 'object',
'description': 'The key/value pairs of this key\'s metadata',
},
'notes': {
'type': 'string',
'description': 'If specified, the extra notes for the key',
},
'expiration': {
'description': 'The expiration date as a unix timestamp',
'anyOf': [{'type': 'string'}, {'type': 'null'}],
'anyOf': [{'type': 'number'}, {'type': 'null'}],
},
},
},
@ -536,46 +543,72 @@ class SuperUserServiceKeyManagement(ApiResource):
@require_scope(scopes.SUPERUSER)
def get(self):
if SuperUserPermission().can():
keys = model.service_keys.list_keys()
return jsonify({'keys': [key_view(key) for key in keys]})
keys = model.service_keys.list_all_keys()
return jsonify({
'keys': [key_view(key) for key in keys],
})
abort(403)
@require_fresh_login
@verify_not_prod
@nickname('createServiceKey')
@require_scope(scopes.SUPERUSER)
@validate_json_request('PutServiceKey')
@validate_json_request('CreateServiceKey')
def post(self):
if SuperUserPermission().can():
body = request.get_json()
# Ensure we have a valid expiration date if specified.
expiration_date = body.get('expiration', None)
if expiration_date == '':
expiration_date = None
if expiration_date is not None:
try:
expiration_date = datetime.utcfromtimestamp(float(expiration_date))
except ValueError:
abort(400)
# Create the metadata for the key.
user = get_authenticated_user()
metadata = body.get('metadata', {})
metadata.update({
'created_by': 'Quay SuperUser Panel',
'created_by': 'Quay Superuser Panel',
'creator': user.username,
'ip': request.remote_addr,
})
# Generate the private key but *do not save it on the server anywhere*.
private_key = RSA.generate(2048)
jwk = RSAKey(key=private_key.publickey()).serialize()
kid = sha256(json.dumps(canonicalize(jwk), separators=(',', ':'))).hexdigest()
# Create the service key.
model.service_keys.create_service_key(body.get('name', ''), kid, body['service'], jwk,
metadata, expiration_date)
model.service_keys.approve_service_key(kid, user, ServiceKeyApprovalType.SUPERUSER)
return jsonify({'public_key': private_key.publickey().exportKey('PEM'),
'private_key': private_key.exportKey('PEM')})
# Auto-approve the service key.
model.service_keys.approve_service_key(kid, user, ServiceKeyApprovalType.SUPERUSER,
notes=body.get('notes', ''))
# Log the creation and auto-approval of the service key.
key_log_metadata = {
'kid': kid,
'preshared': True,
'service': body['service'],
'name': body.get('name', ''),
'expiration_date': expiration_date,
'auto_approved': True,
}
log_action('service_key_create', user.username, key_log_metadata)
log_action('service_key_approve', user.username, key_log_metadata)
return jsonify({
'kid': kid,
'name': body.get('name', ''),
'public_key': private_key.publickey().exportKey('PEM'),
'private_key': private_key.exportKey('PEM'),
})
abort(403)
@ -590,7 +623,6 @@ class SuperUserServiceKeyUpdater(ApiResource):
'id': 'PutServiceKey',
'type': 'object',
'description': 'Description of updates for a service key',
'required': ['name', 'metadata', 'expiration'],
'properties': {
'name': {
'type': 'string',
@ -602,28 +634,78 @@ class SuperUserServiceKeyUpdater(ApiResource):
},
'expiration': {
'description': 'The expiration date as a unix timestamp',
'anyOf': [{'type': 'string'}, {'type': 'null'}],
'anyOf': [{'type': 'number'}, {'type': 'null'}],
},
},
},
}
@require_fresh_login
@verify_not_prod
@nickname('putServiceKey')
@nickname('updateServiceKey')
@require_scope(scopes.SUPERUSER)
@validate_json_request('PutServiceKey')
def put(self, kid):
if SuperUserPermission().can():
body = request.get_json()
try:
key = model.service_keys.get_service_key(kid)
except model.service_keys.ServiceKeyDoesNotExist:
abort(404)
expiration_date = body['expiration']
if expiration_date is not None and expiration_date != '':
try:
expiration_date = datetime.utcfromtimestamp(float(expiration_date))
except ValueError:
abort(400)
user = get_authenticated_user()
model.service_keys.update_service_key(body['name'], kid, body['metadata'], expiration_date)
key_log_metadata = {
'kid': key.kid,
'service': key.service,
'name': body.get('name', key.name),
'expiration_date': key.expiration_date,
}
if 'expiration' in body:
expiration_date = body['expiration']
if expiration_date is not None and expiration_date != '':
try:
expiration_date = datetime.utcfromtimestamp(float(expiration_date))
except ValueError:
abort(400)
key_log_metadata.update({
'old_expiration_date': key.expiration_date,
'expiration_date': expiration_date,
})
log_action('service_key_extend', user.username, key_log_metadata)
model.service_keys.set_key_expiration(kid, expiration_date)
if 'name' in body or 'metadata' in body:
model.service_keys.update_service_key(kid, body.get('name'), body.get('metadata'))
log_action('service_key_modify', user.username, key_log_metadata)
return jsonify(key_view(model.service_keys.get_service_key(kid)))
abort(403)
@require_fresh_login
@verify_not_prod
@nickname('deleteServiceKey')
@require_scope(scopes.SUPERUSER)
def delete(self, kid):
if SuperUserPermission().can():
key = model.service_keys.delete_service_key(kid)
key_log_metadata = {
'kid': kid,
'service': key.service,
'name': key.name,
'created_date': key.created_date,
'expiration_date': key.expiration_date,
}
user = get_authenticated_user()
log_action('service_key_delete', user.username, key_log_metadata)
return make_response('', 201)
abort(403)
@ -634,19 +716,36 @@ class SuperUserServiceKeyUpdater(ApiResource):
class SuperUserServiceKeyApproval(ApiResource):
""" Resource for approving service keys. """
schemas = {
'ApproveServiceKey': {
'id': 'ApproveServiceKey',
'type': 'object',
'description': 'Information for approving service keys',
'properties': {
'notes': {
'type': 'string',
'description': 'Optional approval notes',
},
},
},
}
@require_fresh_login
@verify_not_prod
@nickname('approveServiceKey')
@require_scope(scopes.SUPERUSER)
def put(self, kid):
if SuperUserPermission().can():
notes = request.get_json().get('notes', '')
approver = get_authenticated_user()
try:
model.service_keys.approve_service_key(kid, approver, ServiceKeyApprovalType.SUPERUSER)
model.service_keys.approve_service_key(kid, approver, ServiceKeyApprovalType.SUPERUSER,
notes=notes)
except model.ServiceKeyDoesNotExist:
abort(404)
except model.ServiceKeyAlreadyApproved:
pass
return make_response('', 200)
return make_response('', 201)
abort(403)