EDUCATOR-4498 | Add optional output-file option to generate_jwt_signing_key command.
This commit is contained in:
committed by
Alex Dusenbery
parent
ad2444204a
commit
ba2f0725ee
@@ -10,6 +10,7 @@ import random
|
||||
import string
|
||||
from argparse import RawTextHelpFormatter
|
||||
|
||||
import yaml
|
||||
from Cryptodome.PublicKey import RSA
|
||||
from django.conf import settings
|
||||
from django.core.management.base import BaseCommand
|
||||
@@ -66,6 +67,12 @@ class Command(BaseCommand):
|
||||
dest='add_previous_public_keys',
|
||||
help='Whether to NOT add the previous set of public keys to the new public key set',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--output-file',
|
||||
action='store',
|
||||
type=str,
|
||||
help='Optional YML file in which output should be stored. Needs to be absolute path.',
|
||||
)
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
@@ -88,8 +95,15 @@ class Command(BaseCommand):
|
||||
options['key_size'],
|
||||
options['key_id'] or self._generate_key_id(options['key_id_size']),
|
||||
)
|
||||
self._output_public_keys(jwk_key, options['add_previous_public_keys'])
|
||||
self._output_private_keys(jwk_key)
|
||||
public_keys = self._output_public_keys(jwk_key, options['add_previous_public_keys'])
|
||||
private_keys = self._output_private_keys(jwk_key)
|
||||
if options['output_file']:
|
||||
jwt_auth_data = {
|
||||
'JWT_AUTH': public_keys,
|
||||
}
|
||||
jwt_auth_data['JWT_AUTH'].update(private_keys)
|
||||
with open(options['output_file'], 'w') as f_out: # pylint: disable=open-builtin
|
||||
yaml.safe_dump(jwt_auth_data, stream=f_out)
|
||||
|
||||
def _generate_key_id(self, size, chars=string.ascii_uppercase + string.digits):
|
||||
return ''.join(random.choice(chars) for _ in range(size))
|
||||
@@ -120,6 +134,9 @@ class Command(BaseCommand):
|
||||
)
|
||||
print(" ")
|
||||
print(" COMMON_JWT_PUBLIC_SIGNING_JWK_SET: '{}'".format(serialized_public_keys))
|
||||
return {
|
||||
'COMMON_JWT_PUBLIC_SIGNING_JWK_SET': serialized_public_keys,
|
||||
}
|
||||
|
||||
def _add_previous_public_keys(self, public_keys):
|
||||
previous_signing_keys = settings.JWT_AUTH.get('JWT_PUBLIC_SIGNING_JWK_SET')
|
||||
@@ -144,3 +161,7 @@ class Command(BaseCommand):
|
||||
print(" EDXAPP_JWT_PRIVATE_SIGNING_JWK: '{}'".format(serialized_keypair_json))
|
||||
print(" ")
|
||||
print(" EDXAPP_JWT_SIGNING_ALGORITHM: 'RS512'")
|
||||
return {
|
||||
'EDXAPP_JWT_PRIVATE_SIGNING_JWK': serialized_keypair_json,
|
||||
'EDXAPP_JWT_SIGNING_ALGORITHM': 'RS512',
|
||||
}
|
||||
|
||||
@@ -4,11 +4,14 @@ Tests the ``generate_jwt_signing_key`` management command.
|
||||
# pylint: disable=missing-docstring
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from StringIO import StringIO
|
||||
|
||||
import ddt
|
||||
import yaml
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase
|
||||
from mock import patch
|
||||
@@ -43,13 +46,18 @@ class TestGenerateJwtSigningKey(TestCase):
|
||||
)
|
||||
self.assertEqual(log_message_exists, expected_to_exist)
|
||||
|
||||
def _assert_key_output(self, output_stream):
|
||||
def _assert_key_output(self, output_stream, filename):
|
||||
expected_in_output = (
|
||||
'EDXAPP_JWT_PRIVATE_SIGNING_JWK', 'EDXAPP_JWT_SIGNING_ALGORITHM', 'COMMON_JWT_PUBLIC_SIGNING_JWK_SET'
|
||||
)
|
||||
for expected in expected_in_output:
|
||||
self.assertIn(expected, output_stream.getvalue())
|
||||
|
||||
with open(filename) as file_obj: # pylint: disable=open-builtin
|
||||
output_from_yaml = yaml.safe_load(file_obj)
|
||||
for expected in expected_in_output:
|
||||
self.assertIn(expected, output_from_yaml['JWT_AUTH'])
|
||||
|
||||
def _assert_presence_of_old_keys(self, mock_log, add_previous_public_keys):
|
||||
self._assert_log_message(mock_log, 'Old JWT_PUBLIC_SIGNING_JWK_SET', expected_to_exist=add_previous_public_keys)
|
||||
|
||||
@@ -73,11 +81,14 @@ class TestGenerateJwtSigningKey(TestCase):
|
||||
command_options['key_id'] = TEST_KEY_IDENTIFIER
|
||||
if key_id_size:
|
||||
command_options['key_id_size'] = key_id_size
|
||||
_, filename = tempfile.mkstemp(suffix='.yml')
|
||||
command_options['output_file'] = filename
|
||||
|
||||
with self._captured_output() as (output_stream, _):
|
||||
with patch(LOGGER) as mock_log:
|
||||
call_command(COMMAND_NAME, **command_options)
|
||||
|
||||
self._assert_key_output(output_stream)
|
||||
self._assert_key_output(output_stream, filename)
|
||||
self._assert_presence_of_old_keys(mock_log, add_previous_public_keys)
|
||||
self._assert_presence_of_key_id(mock_log, output_stream, provide_key_id, key_id_size)
|
||||
os.remove(filename)
|
||||
|
||||
Reference in New Issue
Block a user