Add backend ability to generate sec keys in config app
This commit is contained in:
parent
0bc22d810a
commit
9faba7f5c3
3 changed files with 243 additions and 165 deletions
|
@ -2,6 +2,7 @@ import logging
|
||||||
import pathvalidate
|
import pathvalidate
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from flask import request, jsonify, make_response
|
from flask import request, jsonify, make_response
|
||||||
|
|
||||||
|
@ -149,6 +150,64 @@ class SuperUserServiceKeyManagement(ApiResource):
|
||||||
'keys': [key.to_dict() for key in keys],
|
'keys': [key.to_dict() for key in keys],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@nickname('createServiceKey')
|
||||||
|
@validate_json_request('CreateServiceKey')
|
||||||
|
def post(self):
|
||||||
|
body = request.get_json()
|
||||||
|
|
||||||
|
# Ensure we have a valid expiration date if specified.
|
||||||
|
expiration_date = body.get('expiration', None)
|
||||||
|
if expiration_date is not None:
|
||||||
|
try:
|
||||||
|
expiration_date = datetime.utcfromtimestamp(float(expiration_date))
|
||||||
|
except ValueError as ve:
|
||||||
|
raise InvalidRequest('Invalid expiration date: %s' % ve)
|
||||||
|
|
||||||
|
if expiration_date <= datetime.now():
|
||||||
|
raise InvalidRequest('Expiration date cannot be in the past')
|
||||||
|
|
||||||
|
# Create the metadata for the key.
|
||||||
|
# Since we don't have logins in the config app, we'll just get any of the superusers
|
||||||
|
user = config_provider.get_config().get('SUPER_USERS', [None])[0]
|
||||||
|
if user is None:
|
||||||
|
raise InvalidRequest('No super users exist, cannot create service key without approver')
|
||||||
|
|
||||||
|
metadata = body.get('metadata', {})
|
||||||
|
metadata.update({
|
||||||
|
'created_by': 'Quay Superuser Panel',
|
||||||
|
'creator': user,
|
||||||
|
'ip': request.remote_addr,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Generate a key with a private key that we *never save*.
|
||||||
|
(private_key, key_id) = pre_oci_model.generate_service_key(body['service'], expiration_date,
|
||||||
|
metadata=metadata,
|
||||||
|
name=body.get('name', ''))
|
||||||
|
# Auto-approve the service key.
|
||||||
|
pre_oci_model.approve_service_key(key_id, ServiceKeyApprovalType.SUPERUSER,
|
||||||
|
notes=body.get('notes', ''))
|
||||||
|
|
||||||
|
# Log the creation and auto-approval of the service key.
|
||||||
|
key_log_metadata = {
|
||||||
|
'kid': key_id,
|
||||||
|
'preshared': True,
|
||||||
|
'service': body['service'],
|
||||||
|
'name': body.get('name', ''),
|
||||||
|
'expiration_date': expiration_date,
|
||||||
|
'auto_approved': True,
|
||||||
|
}
|
||||||
|
|
||||||
|
log_action('service_key_create', None, key_log_metadata)
|
||||||
|
log_action('service_key_approve', None, key_log_metadata)
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'kid': key_id,
|
||||||
|
'name': body.get('name', ''),
|
||||||
|
'service': body['service'],
|
||||||
|
'public_key': private_key.publickey().exportKey('PEM'),
|
||||||
|
'private_key': private_key.exportKey('PEM'),
|
||||||
|
})
|
||||||
|
|
||||||
@resource('/v1/superuser/approvedkeys/<kid>')
|
@resource('/v1/superuser/approvedkeys/<kid>')
|
||||||
class SuperUserServiceKeyApproval(ApiResource):
|
class SuperUserServiceKeyApproval(ApiResource):
|
||||||
""" Resource for approving service keys. """
|
""" Resource for approving service keys. """
|
||||||
|
|
|
@ -43,5 +43,10 @@ class PreOCIModel(SuperuserDataInterface):
|
||||||
except model.ServiceKeyAlreadyApproved:
|
except model.ServiceKeyAlreadyApproved:
|
||||||
raise ServiceKeyAlreadyApproved
|
raise ServiceKeyAlreadyApproved
|
||||||
|
|
||||||
|
def generate_service_key(self, service, expiration_date, kid=None, name='', metadata=None, rotation_duration=None):
|
||||||
|
(private_key, key) = model.service_keys.generate_service_key(service, expiration_date, metadata=metadata, name=name)
|
||||||
|
|
||||||
|
return private_key, key.kid
|
||||||
|
|
||||||
|
|
||||||
pre_oci_model = PreOCIModel()
|
pre_oci_model = PreOCIModel()
|
||||||
|
|
|
@ -27,6 +27,19 @@ export type MarkdownSymbol = 'heading1'
|
||||||
export type BrowserPlatform = "firefox"
|
export type BrowserPlatform = "firefox"
|
||||||
| "chrome";
|
| "chrome";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constant representing current browser platform. Used for determining available features.
|
||||||
|
* TODO Only rudimentary implementation, should prefer specific feature detection strategies instead.
|
||||||
|
*/
|
||||||
|
export const browserPlatform: BrowserPlatform = (() => {
|
||||||
|
if (navigator.userAgent.toLowerCase().indexOf('firefox') != -1) {
|
||||||
|
return 'firefox';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'chrome';
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dynamically fetch and register a new language with Highlight.js
|
* Dynamically fetch and register a new language with Highlight.js
|
||||||
*/
|
*/
|
||||||
|
@ -90,6 +103,7 @@ highlightedLanguages.forEach((langName) => addHighlightedLanguage(langName));
|
||||||
declarations: [],
|
declarations: [],
|
||||||
providers: [
|
providers: [
|
||||||
{provide: 'markdownConverter', useValue: new Converter({extensions: [<any>showdownHighlight]})},
|
{provide: 'markdownConverter', useValue: new Converter({extensions: [<any>showdownHighlight]})},
|
||||||
|
{provide: 'BrowserPlatform', useValue: browserPlatform},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class MarkdownModule {
|
export class MarkdownModule {
|
||||||
|
|
Reference in a new issue