refactor approval service key to not need approver
This commit is contained in:
parent
7edf679670
commit
cc9bedbeb9
11 changed files with 331 additions and 13 deletions
|
@ -155,7 +155,7 @@ def test_mixing_keys_e2e(initialized_db):
|
||||||
|
|
||||||
# Approve the key and try again.
|
# Approve the key and try again.
|
||||||
admin_user = model.user.get_user('devtable')
|
admin_user = model.user.get_user('devtable')
|
||||||
model.service_keys.approve_service_key(key.kid, admin_user, ServiceKeyApprovalType.SUPERUSER)
|
model.service_keys.approve_service_key(key.kid, ServiceKeyApprovalType.SUPERUSER, approver=admin_user)
|
||||||
|
|
||||||
valid_token = _token(token_data, key_id='newkey', private_key=private_key)
|
valid_token = _token(token_data, key_id='newkey', private_key=private_key)
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,17 @@ import pathvalidate
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from flask import request, jsonify
|
from flask import request, jsonify, make_response
|
||||||
|
|
||||||
|
from data.database import ServiceKeyApprovalType
|
||||||
|
from data.model import ServiceKeyDoesNotExist
|
||||||
from util.config.validator import EXTRA_CA_DIRECTORY
|
from util.config.validator import EXTRA_CA_DIRECTORY
|
||||||
|
|
||||||
from config_app.config_endpoints.exception import InvalidRequest
|
from config_app.config_endpoints.exception import InvalidRequest
|
||||||
from config_app.config_endpoints.api import resource, ApiResource, nickname
|
from config_app.config_endpoints.api import resource, ApiResource, nickname
|
||||||
from config_app.config_endpoints.api.superuser_models_pre_oci import pre_oci_model
|
from config_app.config_endpoints.api.superuser_models_pre_oci import pre_oci_model
|
||||||
from config_app.config_util.ssl import load_certificate, CertInvalidException
|
from config_app.config_util.ssl import load_certificate, CertInvalidException
|
||||||
from config_app.c_app import config_provider, INIT_SCRIPTS_LOCATION
|
from config_app.c_app import app, config_provider, INIT_SCRIPTS_LOCATION
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
@ -146,4 +148,44 @@ class SuperUserServiceKeyManagement(ApiResource):
|
||||||
'keys': [key.to_dict() for key in keys],
|
'keys': [key.to_dict() for key in keys],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@resource('/v1/superuser/approvedkeys/<kid>')
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
@nickname('approveServiceKey')
|
||||||
|
@validate_json_request('ApproveServiceKey')
|
||||||
|
def post(self, kid):
|
||||||
|
notes = request.get_json().get('notes', '')
|
||||||
|
approver = app.config.get('SUPER_USERS', [])[0] # get the first superuser created in the config tool
|
||||||
|
try:
|
||||||
|
key = pre_oci_model.approve_service_key(kid, ServiceKeyApprovalType.SUPERUSER, notes=notes)
|
||||||
|
|
||||||
|
# Log the approval of the service key.
|
||||||
|
key_log_metadata = {
|
||||||
|
'kid': kid,
|
||||||
|
'service': key.service,
|
||||||
|
'name': key.name,
|
||||||
|
'expiration_date': key.expiration_date,
|
||||||
|
}
|
||||||
|
|
||||||
|
log_action('service_key_approve', None, key_log_metadata)
|
||||||
|
except ServiceKeyDoesNotExist:
|
||||||
|
raise NotFound()
|
||||||
|
except ServiceKeyAlreadyApproved:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return make_response('', 201)
|
||||||
|
|
|
@ -17,6 +17,13 @@ def _create_key(key):
|
||||||
return ServiceKey(key.name, key.kid, key.service, key.jwk, key.metadata, key.created_date, key.expiration_date,
|
return ServiceKey(key.name, key.kid, key.service, key.jwk, key.metadata, key.created_date, key.expiration_date,
|
||||||
key.rotation_duration, approval)
|
key.rotation_duration, approval)
|
||||||
|
|
||||||
|
class ServiceKeyDoesNotExist(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ServiceKeyAlreadyApproved(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class PreOCIModel(SuperuserDataInterface):
|
class PreOCIModel(SuperuserDataInterface):
|
||||||
"""
|
"""
|
||||||
|
@ -27,5 +34,14 @@ class PreOCIModel(SuperuserDataInterface):
|
||||||
keys = model.service_keys.list_all_keys()
|
keys = model.service_keys.list_all_keys()
|
||||||
return [_create_key(key) for key in keys]
|
return [_create_key(key) for key in keys]
|
||||||
|
|
||||||
|
def approve_service_key(self, kid, approval_type, notes=''):
|
||||||
|
try:
|
||||||
|
key = model.service_keys.approve_service_key(kid, approval_type, notes=notes)
|
||||||
|
return _create_key(key)
|
||||||
|
except model.ServiceKeyDoesNotExist:
|
||||||
|
raise ServiceKeyDoesNotExist
|
||||||
|
except model.ServiceKeyAlreadyApproved:
|
||||||
|
raise ServiceKeyAlreadyApproved
|
||||||
|
|
||||||
|
|
||||||
pre_oci_model = PreOCIModel()
|
pre_oci_model = PreOCIModel()
|
||||||
|
|
|
@ -46,6 +46,7 @@ export class KubeDeployModalComponent {
|
||||||
this.errorMessage = `Could cycle the deployments with the new configuration. Error: ${err.toString()}`;
|
this.errorMessage = `Could cycle the deployments with the new configuration. Error: ${err.toString()}`;
|
||||||
})
|
})
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
|
console.log(err)
|
||||||
this.state = 'error';
|
this.state = 'error';
|
||||||
this.errorMessage = `Could not deploy the configuration. Error: ${err.toString()}`;
|
this.errorMessage = `Could not deploy the configuration. Error: ${err.toString()}`;
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
<div class="request-service-key-dialog-element">
|
||||||
|
<!-- Modal message dialog -->
|
||||||
|
<div class="co-dialog modal fade">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" ng-show="!working">×</button>
|
||||||
|
<h4 class="modal-title">Create key for service {{ requestKeyInfo.service }}</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body" ng-show="working">
|
||||||
|
<div class="cor-loader"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body" ng-show="!working">
|
||||||
|
<!-- Step 0 -->
|
||||||
|
<div ng-show="step == 0">
|
||||||
|
<table class="co-option-table">
|
||||||
|
<tr>
|
||||||
|
<td><input type="radio" id="automaticKey" ng-model="requestKind" value="automatic"></td>
|
||||||
|
<td>
|
||||||
|
<label for="automaticKey">Have the service provide a key</label>
|
||||||
|
<div class="help-text">Recommended for <code>{{ requestKeyInfo.service }}</code> installations where the single instance is setup now.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><input type="radio" id="presharedKey" ng-model="requestKind" value="preshared"></td>
|
||||||
|
<td>
|
||||||
|
<label for="presharedKey">Generate shared key</label>
|
||||||
|
<div class="help-text">Recommended for <code>{{ requestKeyInfo.service }}</code> installations where the instances are dynamically started.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 1 (automatic) -->
|
||||||
|
<div ng-show="step == 1 && requestKind == 'automatic'" style="text-align: center">
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
Please start the <code>{{ requestKeyInfo.service }}</code> service now, configured for <a href="https://github.com/coreos/jwtproxy#autogenerated-private-key" ng-safenewtab>autogenerated private key</a>. The key approval process will continue automatically once the service connects to Quay.
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 20px;">
|
||||||
|
Waiting for service to connect
|
||||||
|
</div>
|
||||||
|
<div style="margin-top: 10px; margin-bottom: 20px;">
|
||||||
|
<div class="cor-loader-inline"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 2 (automatic) -->
|
||||||
|
<div ng-show="step == 2 && requestKind == 'automatic'" style="text-align: center">
|
||||||
|
A key for service <code>{{ requestKeyInfo.service }}</code> has been automatically generated, approved and saved in the service's keystore.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 1 (generate) -->
|
||||||
|
<div ng-show="step == 1 && requestKind == 'preshared'">
|
||||||
|
<form name="createForm" ng-submit="createPresharedKey()">
|
||||||
|
<table class="co-form-table">
|
||||||
|
<tr>
|
||||||
|
<td><label for="create-key-name">Key Name:</label></td>
|
||||||
|
<td>
|
||||||
|
<input class="form-control" name="create-key-name" type="text" ng-model="preshared.name" placeholder="Friendly Key Name">
|
||||||
|
<span class="co-help-text">
|
||||||
|
A friendly name for the key for later reference.
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="create-key-expiration">Expiration date (optional):</label></td>
|
||||||
|
<td>
|
||||||
|
<span class="datetime-picker" datetime="preshared.expiration"></span>
|
||||||
|
<span class="co-help-text">
|
||||||
|
The date and time that the key expires. If left blank, the key will never expire.
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td><label for="create-key-notes">Approval Notes (optional):</label></td>
|
||||||
|
<td>
|
||||||
|
<markdown-input content="preshared.notes"
|
||||||
|
can-write="true"
|
||||||
|
(content-changed)="updateNotes($event.content)"
|
||||||
|
field-title="notes"></markdown-input>
|
||||||
|
<span class="co-help-text">
|
||||||
|
Optional notes for additional human-readable information about why the key was created.
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Step 2 (generate) -->
|
||||||
|
<div ng-show="step == 2 && requestKind == 'preshared'">
|
||||||
|
<div class="co-alert co-alert-warning">
|
||||||
|
The following key has been generated for service <code>{{ requestKeyInfo.service }}</code>.
|
||||||
|
<br><br>
|
||||||
|
Please copy the key's ID and copy/download the key's private contents and place it in the directory with the service's configuration.
|
||||||
|
<br><br>
|
||||||
|
<strong>Once this dialog is closed this private key will not be accessible anywhere else!</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label>Key ID:</label>
|
||||||
|
<div class="copy-box" value="createdKey.kid"></div>
|
||||||
|
|
||||||
|
<label>Private Key (PEM):</label>
|
||||||
|
<textarea class="key-display form-control" onclick="this.focus();this.select()" readonly>{{ createdKey.private_key }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" ng-show="!working">
|
||||||
|
<button type="button" class="btn btn-primary" ng-show="step == 1 && requestKind == 'preshared'"
|
||||||
|
ng-disabled="createForm.$invalid"
|
||||||
|
ng-click="createPresharedKey()">
|
||||||
|
Generate Key
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary" ng-show="step == 0 && requestKind == 'preshared'"
|
||||||
|
ng-click="showGenerate()">
|
||||||
|
Continue
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary" ng-show="step == 0 && requestKind == 'automatic'"
|
||||||
|
ng-click="startApproval()">
|
||||||
|
Start Approval
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary" ng-click="downloadPrivateKey(createdKey)" ng-if="createdKey && isDownloadSupported()">
|
||||||
|
<i class="fa fa-download"></i> Download Private Key
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal" ng-show="step == 2">Close</button>
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal" ng-show="step != 2">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.modal-content -->
|
||||||
|
</div><!-- /.modal-dialog -->
|
||||||
|
</div><!-- /.modal -->
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* An element which displays a dialog for requesting or creating a service key.
|
||||||
|
*/
|
||||||
|
angular.module('quay').directive('requestServiceKeyDialog', function () {
|
||||||
|
var directiveDefinitionObject = {
|
||||||
|
priority: 0,
|
||||||
|
templateUrl: '/static/directives/request-service-key-dialog.html',
|
||||||
|
replace: false,
|
||||||
|
transclude: true,
|
||||||
|
restrict: 'C',
|
||||||
|
scope: {
|
||||||
|
'requestKeyInfo': '=requestKeyInfo',
|
||||||
|
'keyCreated': '&keyCreated'
|
||||||
|
},
|
||||||
|
controller: function($scope, $element, $timeout, ApiService) {
|
||||||
|
var handleNewKey = function(key) {
|
||||||
|
var data = {
|
||||||
|
'notes': 'Approved during setup of service ' + key.service
|
||||||
|
};
|
||||||
|
|
||||||
|
var params = {
|
||||||
|
'kid': key.kid
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.approveServiceKey(data, params).then(function(resp) {
|
||||||
|
$scope.keyCreated({'key': key});
|
||||||
|
$scope.step = 2;
|
||||||
|
}, ApiService.errorDisplay('Could not approve service key'));
|
||||||
|
};
|
||||||
|
|
||||||
|
var checkKeys = function() {
|
||||||
|
var isShown = ($element.find('.modal').data('bs.modal') || {}).isShown;
|
||||||
|
if (!isShown) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: filter by service.
|
||||||
|
ApiService.listServiceKeys().then(function(resp) {
|
||||||
|
var keys = resp['keys'];
|
||||||
|
for (var i = 0; i < keys.length; ++i) {
|
||||||
|
var key = keys[i];
|
||||||
|
if (key.service == $scope.requestKeyInfo.service && !key.approval && key.rotation_duration) {
|
||||||
|
handleNewKey(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$timeout(checkKeys, 1000);
|
||||||
|
}, ApiService.errorDisplay('Could not list service keys'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.show = function() {
|
||||||
|
$scope.working = false;
|
||||||
|
$scope.step = 0;
|
||||||
|
$scope.requestKind = null;
|
||||||
|
$scope.preshared = {
|
||||||
|
'name': $scope.requestKeyInfo.service + ' Service Key',
|
||||||
|
'notes': 'Created during setup for service `' + $scope.requestKeyInfo.service + '`'
|
||||||
|
};
|
||||||
|
|
||||||
|
$element.find('.modal').modal({});
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.hide = function() {
|
||||||
|
$scope.loading = false;
|
||||||
|
$element.find('.modal').modal('hide');
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.showGenerate = function() {
|
||||||
|
$scope.step = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.startApproval = function() {
|
||||||
|
$scope.step = 1;
|
||||||
|
checkKeys();
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.isDownloadSupported = function() {
|
||||||
|
var isSafari = /^((?!chrome).)*safari/i.test(navigator.userAgent);
|
||||||
|
if (isSafari) {
|
||||||
|
// Doesn't work properly in Safari, sadly.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try { return !!new Blob(); } catch(e) {}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.downloadPrivateKey = function(key) {
|
||||||
|
var blob = new Blob([key.private_key]);
|
||||||
|
FileSaver.saveAs(blob, key.service + '.pem');
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.createPresharedKey = function() {
|
||||||
|
$scope.working = true;
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
'name': $scope.preshared.name,
|
||||||
|
'service': $scope.requestKeyInfo.service,
|
||||||
|
'expiration': $scope.preshared.expiration || null,
|
||||||
|
'notes': $scope.preshared.notes
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiService.createServiceKey(data).then(function(resp) {
|
||||||
|
$scope.working = false;
|
||||||
|
$scope.step = 2;
|
||||||
|
$scope.createdKey = resp;
|
||||||
|
$scope.keyCreated({'key': resp});
|
||||||
|
}, ApiService.errorDisplay('Could not create service key'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.updateNotes = function(content) {
|
||||||
|
$scope.preshared.notes = content;
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.$watch('requestKeyInfo', function(info) {
|
||||||
|
if (info && info.service) {
|
||||||
|
$scope.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return directiveDefinitionObject;
|
||||||
|
});
|
|
@ -145,7 +145,7 @@ def set_key_expiration(kid, expiration_date):
|
||||||
service_key.save()
|
service_key.save()
|
||||||
|
|
||||||
|
|
||||||
def approve_service_key(kid, approver, approval_type, notes=''):
|
def approve_service_key(kid, approval_type, approver=None, notes=''):
|
||||||
try:
|
try:
|
||||||
with db_transaction():
|
with db_transaction():
|
||||||
key = db_for_update(ServiceKey.select().where(ServiceKey.kid == kid)).get()
|
key = db_for_update(ServiceKey.select().where(ServiceKey.kid == kid)).get()
|
||||||
|
|
|
@ -118,7 +118,7 @@ class PreOCIModel(SuperuserDataInterface):
|
||||||
|
|
||||||
def approve_service_key(self, kid, approver, approval_type, notes=''):
|
def approve_service_key(self, kid, approver, approval_type, notes=''):
|
||||||
try:
|
try:
|
||||||
key = model.service_keys.approve_service_key(kid, approver, approval_type, notes=notes)
|
key = model.service_keys.approve_service_key(kid, approval_type, approver=approver, notes=notes)
|
||||||
return _create_key(key)
|
return _create_key(key)
|
||||||
except model.ServiceKeyDoesNotExist:
|
except model.ServiceKeyDoesNotExist:
|
||||||
raise ServiceKeyDoesNotExist
|
raise ServiceKeyDoesNotExist
|
||||||
|
|
|
@ -161,8 +161,7 @@ def __generate_service_key(kid, name, user, timestamp, approval_type, expiration
|
||||||
rotation_duration=rotation_duration)
|
rotation_duration=rotation_duration)
|
||||||
|
|
||||||
if approval_type is not None:
|
if approval_type is not None:
|
||||||
model.service_keys.approve_service_key(key.kid, user, approval_type,
|
model.service_keys.approve_service_key(key.kid, approval_type, notes='The **test** approval')
|
||||||
notes='The **test** approval')
|
|
||||||
|
|
||||||
key_metadata = {
|
key_metadata = {
|
||||||
'kid': kid,
|
'kid': kid,
|
||||||
|
@ -820,7 +819,7 @@ def populate_database(minimal=False, with_storage=False):
|
||||||
key = model.service_keys.create_service_key('test_service_key', 'test_service_key', 'quay',
|
key = model.service_keys.create_service_key('test_service_key', 'test_service_key', 'quay',
|
||||||
_TEST_JWK, {}, None)
|
_TEST_JWK, {}, None)
|
||||||
|
|
||||||
model.service_keys.approve_service_key(key.kid, new_user_1, ServiceKeyApprovalType.SUPERUSER,
|
model.service_keys.approve_service_key(key.kid, ServiceKeyApprovalType.SUPERUSER,
|
||||||
notes='Test service key for local/test registry testing')
|
notes='Test service key for local/test registry testing')
|
||||||
|
|
||||||
# Add an app specific token.
|
# Add an app specific token.
|
||||||
|
|
|
@ -559,7 +559,7 @@ class KeyServerTestCase(EndpointTestCase):
|
||||||
}, data=jwk, expected_code=403)
|
}, data=jwk, expected_code=403)
|
||||||
|
|
||||||
# Approve the key.
|
# Approve the key.
|
||||||
model.service_keys.approve_service_key('kid420', 1, ServiceKeyApprovalType.SUPERUSER)
|
model.service_keys.approve_service_key('kid420', ServiceKeyApprovalType.SUPERUSER, approver=1)
|
||||||
|
|
||||||
# Rotate that new key
|
# Rotate that new key
|
||||||
with assert_action_logged('service_key_rotate'):
|
with assert_action_logged('service_key_rotate'):
|
||||||
|
@ -598,7 +598,7 @@ class KeyServerTestCase(EndpointTestCase):
|
||||||
def test_attempt_delete_service_key_with_expired_key(self):
|
def test_attempt_delete_service_key_with_expired_key(self):
|
||||||
# Generate two keys, approving the first.
|
# Generate two keys, approving the first.
|
||||||
private_key, _ = model.service_keys.generate_service_key('sample_service', None, kid='first')
|
private_key, _ = model.service_keys.generate_service_key('sample_service', None, kid='first')
|
||||||
model.service_keys.approve_service_key('first', 1, ServiceKeyApprovalType.SUPERUSER)
|
model.service_keys.approve_service_key('first', ServiceKeyApprovalType.SUPERUSER, approver=1)
|
||||||
model.service_keys.generate_service_key('sample_service', None, kid='second')
|
model.service_keys.generate_service_key('sample_service', None, kid='second')
|
||||||
|
|
||||||
# Mint a JWT with our test payload
|
# Mint a JWT with our test payload
|
||||||
|
@ -661,7 +661,7 @@ class KeyServerTestCase(EndpointTestCase):
|
||||||
expected_code=403, service='sample_service', kid='kid321')
|
expected_code=403, service='sample_service', kid='kid321')
|
||||||
|
|
||||||
# Approve the second key.
|
# Approve the second key.
|
||||||
model.service_keys.approve_service_key('kid123', 1, ServiceKeyApprovalType.SUPERUSER)
|
model.service_keys.approve_service_key('kid123', ServiceKeyApprovalType.SUPERUSER, approver=1)
|
||||||
|
|
||||||
# Using the credentials of our approved key, delete our unapproved key
|
# Using the credentials of our approved key, delete our unapproved key
|
||||||
with assert_action_logged('service_key_delete'):
|
with assert_action_logged('service_key_delete'):
|
||||||
|
|
|
@ -18,8 +18,7 @@ def generate_key(service, name, expiration_date=None, notes=None):
|
||||||
metadata=metadata,
|
metadata=metadata,
|
||||||
name=name)
|
name=name)
|
||||||
# Auto-approve the service key.
|
# Auto-approve the service key.
|
||||||
model.service_keys.approve_service_key(key.kid, None, ServiceKeyApprovalType.AUTOMATIC,
|
model.service_keys.approve_service_key(key.kid, ServiceKeyApprovalType.AUTOMATIC, notes=notes or '')
|
||||||
notes=notes or '')
|
|
||||||
|
|
||||||
# Log the creation and auto-approval of the service key.
|
# Log the creation and auto-approval of the service key.
|
||||||
key_log_metadata = {
|
key_log_metadata = {
|
||||||
|
|
Reference in a new issue