From b4e7a088cf4c38f431d48e7b8020baaf6e71098e Mon Sep 17 00:00:00 2001 From: jackie / Andrea Ida Malkah Klaura <jackie@diebin.at> Date: Sun, 11 Apr 2021 14:19:27 +0200 Subject: [PATCH] add management command to create OIDC client --- .../management/commands/create_oidc_client.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 program/management/commands/create_oidc_client.py diff --git a/program/management/commands/create_oidc_client.py b/program/management/commands/create_oidc_client.py new file mode 100644 index 00000000..b60503ca --- /dev/null +++ b/program/management/commands/create_oidc_client.py @@ -0,0 +1,101 @@ +from django.core.management.base import BaseCommand, CommandError +import json, sys, random, string +from oidc_provider.models import Client, ResponseType + + +class Command(BaseCommand): + help = 'Sets up an OIDC client / relaying party. For details check out the' + \ + 'section on Relying Parties at https://django-oidc-provider.readthedocs.io' + + def add_arguments(self, parser): + parser.add_argument('name', type=str, + help='A label that you associate with this client') + parser.add_argument('client_type', type=str, choices=['public', 'confidential'], + help='The type of client can be either public or confidential') + parser.add_argument('--no-require-consent', dest='require_consent', action='store_false', + help='By default user consent is required. Use this to skip user consent.') + parser.add_argument('--no-reuse-consent', dest='reuse_consent', action='store_false', + help='By default user consent will be reused. Use this if the user should provide consent on every login.') + parser.set_defaults(require_consent=True, reuse_consent=True) + parser.add_argument('-u', '--redirect-uri', type=str, action='append', + help='Redirect URI after successful authentication. Can be used more than once.') + parser.add_argument('-p', '--post-logout-redirect', type=str, action='append', + help='Post logout redirect URI. Can be used more than once.') + parser.add_argument('-s', '--scope', type=str, action='append', + help='Authorized scope values for this client. Can be used more than once.') + parser.add_argument('-r', dest='response_types', action='append', + choices=['code', 'id_token', 'id_token token', 'code token', 'code id_token', 'code id_token token'], + help='The type of response the client will get.') + parser.add_argument('-i', '--id-only', dest='id_only', action='store_true', + help='Do not print anything else then the ID of the newly created client '+\ + '(and the client secret in case of confidential clients).') + parser.set_defaults(id_only=False) + + + def handle(self, *args, **options): + # generate a new client ID and secret + client_id = False + counter = 0 + while not client_id: + client_id = random.randint(100000, 999999) + counter += 1 + if counter > 10000: + raise CommandError('Could not find a free client_id. Already'+\ + ' tried 10000 times. There seems to be something seriously'+\ + ' wrong with your setup. Please inspect manually.') + try: + Client.objects.get(client_id=client_id) + except Client.DoesNotExist: + pass + else: + client_id = False + + client_secret = '' + if options['client_type'] == 'confidential': + client_secret = ''.join(random.SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(32)) + + # initialize lists if no option was provided + if options['redirect_uri'] is None: + options['redirect_uri'] = [] + if options['post_logout_redirect'] is None: + options['post_logout_redirect'] = [] + if options['scope'] is None: + options['scope'] = [] + + if not options["id_only"]: + self.stdout.write(f'Creating client with name {options["name"]}') + try: + c = Client( + client_id=client_id, + client_secret=client_secret, + name=options['name'], client_type=options['client_type'], + redirect_uris=options['redirect_uri'], + require_consent=options['require_consent'], + reuse_consent=options['reuse_consent'], + post_logout_redirect_uris=options['post_logout_redirect'], + scope=options['scope'], + ) + c.save() + except: + raise CommandError('Could not create an OpenID connect client' +\ + f' due to the following error: {sys.exc_info()}') + + + if options['response_types']: + try: + for r_value in options['response_types']: + r = ResponseType.objects.get(value=r_value) + c.response_types.add(r) + except: + raise CommandError('Client was stored, but could not set response_types'+\ + f' due to the following error: {sys.exc_info()}') + + if options["id_only"]: + if options['client_type'] == 'confidential': + self.stdout.write(f'{c.client_id} {c.client_secret}') + else: + self.stdout.write(f'{c.client_id}') + else: + self.stdout.write(f'Successfully created new OIDC client, with ID: {c.client_id}') + if options['client_type'] == 'confidential': + self.stdout.write(f'The secret for this confidential client is: {c.client_secret}') -- GitLab