switch from optparse to argparse for cli (#24866)

This commit is contained in:
Emmanuel Cazenave 2018-07-10 18:45:43 +02:00
parent a6e24b6855
commit 6aaa191217
9 changed files with 209 additions and 211 deletions

View File

@ -1,7 +1,6 @@
from __future__ import unicode_literals
import getpass
from optparse import make_option
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError
@ -12,14 +11,16 @@ from django.core.exceptions import MultipleObjectsReturned
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
)
help = "Change a user's password for django.contrib.auth."
requires_system_checks = False
def add_arguments(self, parser):
parser.add_argument('username', nargs='?', type=str)
parser.add_argument(
'--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".')
def _get_pass(self, prompt="Password: "):
p = getpass.getpass(prompt=force_str(prompt))
if not p:
@ -27,12 +28,8 @@ class Command(BaseCommand):
return p
def handle(self, *args, **options):
if len(args) > 1:
raise CommandError("need exactly one or zero arguments for username")
if args:
username, = args
else:
username = options['username']
if not username:
username = getpass.getuser()
UserModel = get_user_model()

View File

@ -1,12 +1,12 @@
import logging
from django.core.management.base import NoArgsCommand
from django.core.management.base import BaseCommand
from django.db import models
class Command(NoArgsCommand):
class Command(BaseCommand):
help = 'Clean expired models of authentic2.'
def handle_noargs(self, **options):
def handle(self, **options):
log = logging.getLogger(__name__)
for app in models.get_apps():

View File

@ -1,4 +1,3 @@
from optparse import make_option
import logging
import datetime
@ -20,25 +19,31 @@ def print_table(table):
print line
class Command(BaseCommand):
args = '<clean_threshold>'
help = '''Clean unused accounts'''
option_list = BaseCommand.option_list + (
make_option("--alert-thresholds",
help='list of durations before sending an alert '
'message for unused account, default is none',
default = None),
make_option("--period", type='int',
help='period between two calls to '
'clean-unused-accounts as days, default is 1',
default=1),
make_option("--fake", action='store_true', help='do nothing',
default=False),
make_option("--filter", help='filter to apply to the user queryset, '
'the Django filter key and value are separated by character =', action='append', default=[]),
make_option('--from-email', default=settings.DEFAULT_FROM_EMAIL,
help='sender address for notifications, default is DEFAULT_FROM_EMAIL from settings'),
)
def add_arguments(self, parser):
parser.add_argument('clean_threshold', type=int)
parser.add_argument(
"--alert-thresholds",
help='list of durations before sending an alert '
'message for unused account, default is none',
default=None)
parser.add_argument(
"--period", type=int,
help='period between two calls to '
'clean-unused-accounts as days, default is 1',
default=1
)
parser.add_argument("--fake", action='store_true', help='do nothing', default=False)
parser.add_argument(
"--filter", help='filter to apply to the user queryset, '
'the Django filter key and value are separated by character =', action='append',
default=[]
)
parser.add_argument(
'--from-email', default=settings.DEFAULT_FROM_EMAIL,
help='sender address for notifications, default is DEFAULT_FROM_EMAIL from settings'
)
def handle(self, *args, **options):
log = logging.getLogger(__name__)
@ -48,15 +53,11 @@ class Command(BaseCommand):
log.exception('failure while cleaning unused accounts')
def clean_unused_acccounts(self, *args, **options):
if len(args) < 1:
raise CommandError('missing clean_threshold')
if options['period'] < 1:
raise CommandError('period must be > 0')
try:
clean_threshold = int(args[0])
if clean_threshold < 1:
raise ValueError()
except ValueError:
clean_threshold = options['clean_threshold']
if clean_threshold < 1:
raise CommandError('clean_threshold must be an integer > 0')
if options['verbosity'] == '0':

View File

@ -1,9 +1,9 @@
import argparse
import logging
from optparse import make_option
import json
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
@ -79,67 +79,64 @@ class DjangoUserLDIFParser(ldif.LDIFParser):
with file(self.options['result'], 'w') as f:
json.dump(self.json, f)
def extra_attribute_parse(option, opt_str, value, parser):
ldap_attribute, django_attribute = value
try:
attribute = Attribute.objects.get(name=django_attribute)
except Attribute.DoesNotExist:
raise CommandError('django attribute %s does not exist' % django_attribute)
parser.values.extra_attribute[ldap_attribute] = attribute
class ExtraAttributeAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
ldap_attribute, django_attribute = values
try:
attribute = Attribute.objects.get(name=django_attribute)
except Attribute.DoesNotExist:
raise argparse.ArgumentTypeError(
'django attribute %s does not exist' % django_attribute)
res = getattr(namespace, self.dest, {})
res[ldap_attribute] = attribute
setattr(namespace, self.dest, res)
class Command(BaseCommand):
'''Load LDAP ldif file'''
can_import_django_settings = True
requires_model_validation = True
option_list = BaseCommand.option_list + (
make_option('--first-name',
default='givenName',
help='attribute used to set the first name'),
make_option('--last-name',
default='sn',
help='attribute used to set the last name'),
make_option('--email',
default='mail',
help='attribute used to set the email'),
make_option('--username',
default='uid',
help='attribute used to set the username'),
make_option('--password',
default='userPassword',
help='attribute to extract the password from, OpenLDAP hashing algorithm are recognized'),
make_option('--object-class',
default='inetOrgPerson',
help='object class of records to load'),
make_option('--extra-attribute',
default={},
action='callback',
nargs=2,
type='string',
callback=extra_attribute_parse,
help='object class of records to load'),
make_option('--result',
default=None,
help='file to store a JSON log of created users'),
make_option('--fake',
action='store_true',
help='file to store a JSON log of created users'),
make_option('--realm',
default=None,
help='realm for the new users'),
make_option('--callback',
default=None,
help='python file containing a function callback(user, dn, entry, options, dump) it can return models that will be saved'),
make_option('--callback-arg',
action='append',
help='arguments for the callback'),
)
args = '<ldif_file...>'
help = 'Load/update LDIF files as users'
def add_arguments(self, parser):
parser.add_argument('ldif_file', nargs='+')
parser.add_argument(
'--first-name', default='givenName', help='attribute used to set the first name')
parser.add_argument(
'--last-name', default='sn', help='attribute used to set the last name')
parser.add_argument('--email', default='mail', help='attribute used to set the email')
parser.add_argument('--username', default='uid', help='attribute used to set the username')
parser.add_argument(
'--password', default='userPassword',
help='attribute to extract the password from, '
'OpenLDAP hashing algorithm are recognized'
)
parser.add_argument(
'--object-class', default='inetOrgPerson', help='object class of records to load')
parser.add_argument(
'--extra-attribute', default={}, action=ExtraAttributeAction, nargs=2,
help='object class of records to load'
)
parser.add_argument(
'--result', default=None, help='file to store a JSON log of created users')
parser.add_argument(
'--fake', action='store_true', help='file to store a JSON log of created users'
)
parser.add_argument('--realm', default=None, help='realm for the new users')
parser.add_argument(
'--callback', default=None,
help='python file containing a function callback(user, dn, entry, options, dump)'
' it can return models that will be saved'
)
parser.add_argument('--callback-arg', action='append', help='arguments for the callback')
@atomic
def handle(self, *args, **options):
options['verbosity'] = int(options['verbosity'])
for arg in args:
ldif_files = options.pop('ldif_file')
for arg in ldif_files:
f = file(arg)
parser = DjangoUserLDIFParser(f, options=options, command=self)
parser.parse()

View File

@ -1,5 +1,4 @@
import getpass
from optparse import make_option
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand, CommandError
@ -9,14 +8,16 @@ from authentic2.utils import generate_password
from authentic2.models import PasswordReset
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".'),
)
help = "Reset a user's password for django.contrib.auth."
require_model_validation = False
def add_arguments(self, parser):
parser.add_argument('username', nargs='?', type=str)
parser.add_argument(
'--database', action='store', dest='database',
default=DEFAULT_DB_ALIAS, help='Specifies the database to use. Default is "default".')
def _get_pass(self, prompt="Password: "):
p = getpass.getpass(prompt=prompt)
if not p:
@ -24,12 +25,8 @@ class Command(BaseCommand):
return p
def handle(self, *args, **options):
if len(args) > 1:
raise CommandError("need exactly one or zero arguments for username")
if args:
username, = args
else:
username = options['username']
if not username:
username = getpass.getuser()
UserModel = get_user_model()

View File

@ -1,4 +1,3 @@
from optparse import make_option
import sys
import xml.etree.ElementTree as etree
import os
@ -227,60 +226,47 @@ class Command(BaseCommand):
can_import_django_settings = True
output_transaction = True
requires_model_validation = True
option_list = BaseCommand.option_list + (
make_option(
'--idp',
action='store_true',
dest='idp',
default=False,
help='Do nothing'),
make_option(
'--sp',
action='store_true',
dest='sp',
default=False,
help='Do nothing'),
make_option(
'--sp-policy',
dest='sp_policy',
default=None,
help='SAML2 service provider options policy'),
make_option(
'--delete',
action='store_true',
dest='delete',
default=False,
help='Delete all providers defined in the metadata file (kind of uninstall)'),
make_option(
'--ignore-errors',
action='store_true',
dest='ignore-errors',
default=False,
help='If loading of one EntityDescriptor fails, continue loading'),
make_option(
'--source',
dest='source',
default=None,
help='Tag the loaded providers with the given source string, \
existing providers with the same tag will be removed if they do not exist\
anymore in the metadata file.'),
make_option(
'--reset-attributes',
action='store_true',
default=False,
help = 'Load the specified SAMLv2 metadata file'
def add_arguments(self, parser):
parser.add_argument('metadata_file_path')
parser.add_argument(
'--idp', action='store_true', dest='idp', default=False, help='Do nothing')
parser.add_argument(
'--sp', action='store_true', dest='sp', default=False, help='Do nothing')
parser.add_argument(
'--sp-policy', dest='sp_policy', default=None,
help='SAML2 service provider options policy'
)
parser.add_argument(
'--delete', action='store_true', dest='delete', default=False,
help='Delete all providers defined in the metadata file (kind of uninstall)'
)
parser.add_argument(
'--ignore-errors', action='store_true', dest='ignore-errors', default=False,
help='If loading of one EntityDescriptor fails, continue loading'
)
parser.add_argument(
'--source', dest='source', default=None,
help='Tag the loaded providers with the given source string, '
'existing providers with the same tag will be removed if they do not exist '
'anymore in the metadata file.'
)
parser.add_argument(
'--reset-attributes', action='store_true', default=False,
help='When loading shibboleth attribute filter policies, start by '
'removing all existing SAML attributes for each provider'),
make_option(
'--dont-load-attribute-consuming-service',
dest='load_attribute_consuming_service',
default=True,
action='store_false',
'removing all existing SAML attributes for each provider'
)
parser.add_argument(
'--dont-load-attribute-consuming-service', dest='load_attribute_consuming_service',
default=True, action='store_false',
help='Prevent loading of the attribute policy from '
'AttributeConsumingService nodes in the metadata file.'),
make_option(
'AttributeConsumingService nodes in the metadata file.'
)
parser.add_argument(
'--shibboleth-attribute-filter-policy',
dest='attribute-filter-policy',
default=None,
dest='attribute-filter-policy', default=None,
help='''Path to a file containing an Attribute Filter Policy for the
Shibboleth IdP, that will be used to configure SAML attributes for
each provider. The following schema is supported:
@ -295,40 +281,34 @@ each provider. The following schema is supported:
</AttributeFilterPolicy>
Any other kind of attribute filter policy is unsupported.
'''),
make_option(
'--create-disabled',
dest='create-disabled',
action='store_true',
default=False,
help='When creating a new provider, make it disabled by default.'),
)
args = '<metadata_file>'
help = 'Load the specified SAMLv2 metadata file'
'''
)
parser.add_argument(
'--create-disabled', dest='create-disabled', action='store_true', default=False,
help='When creating a new provider, make it disabled by default.'
)
@commit_on_success
def handle(self, *args, **options):
verbosity = int(options['verbosity'])
source = options['source']
if not args:
raise CommandError('No metadata file on the command line')
metadata_file_path = options['metadata_file_path']
# Check sources
try:
if source is not None:
source.decode('ascii')
except:
raise CommandError('--source MUST be an ASCII string value')
if args[0].startswith('http://') or args[0].startswith('https://'):
response = requests.get(args[0])
if metadata_file_path.startswith('http://') or metadata_file_path.startswith('https://'):
response = requests.get(metadata_file_path)
if not response.ok:
raise CommandError('Unable to open url %s' % args[0])
raise CommandError('Unable to open url %s' % metadata_file_path)
metadata_file = StringIO(response.content)
else:
try:
metadata_file = file(args[0])
metadata_file = file(metadata_file_path)
except:
raise CommandError('Unable to open file %s' % args[0])
raise CommandError('Unable to open file %s' % metadata_file_path)
try:
doc = etree.parse(metadata_file)

View File

@ -1,6 +1,5 @@
import json
import pprint
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
@ -17,29 +16,41 @@ class Command(BaseCommand):
'''Load LDAP ldif file'''
can_import_django_settings = True
requires_model_validation = True
option_list = BaseCommand.option_list + (
make_option('--issuer', help='do automatic registration of the issuer'),
make_option('--openid-configuration', help='file containing the OpenID Connect '
'configuration of the OP'),
make_option('--claim-mapping', default=[], action='append',
help='mapping from claim to attribute'),
make_option('--delete-claim', default=[], action='append',
help='delete mapping from claim to attribute'),
make_option('--client-id', help='registered client ID'),
make_option('--client-secret', help='register client secret'),
make_option('--scope', default=[], action='append',
help='extra scopes, openid is automatic'),
make_option('--no-verify', default=False, action='store_true',
help='do not verify TLS certificates'),
make_option('--show', default=False, action='store_true',
help='show provider configuration'),
make_option('--ou-slug', help='slug of the ou, if absent default ou is used'),
)
args = '<name>'
help = 'Register an OpenID Connect OP'
def add_arguments(self, parser):
parser.add_argument('name')
parser.add_argument('--issuer', help='do automatic registration of the issuer')
parser.add_argument(
'--openid-configuration',
help='file containing the OpenID Connect '
'configuration of the OP'
)
parser.add_argument(
'--claim-mapping', default=[], action='append',
help='mapping from claim to attribute'
)
parser.add_argument(
'--delete-claim', default=[], action='append',
help='delete mapping from claim to attribute'
)
parser.add_argument('--client-id', help='registered client ID')
parser.add_argument('--client-secret', help='register client secret')
parser.add_argument(
'--scope', default=[], action='append',
help='extra scopes, openid is automatic')
parser.add_argument(
'--no-verify', default=False, action='store_true',
help='do not verify TLS certificates'
)
parser.add_argument(
'--show', default=False, action='store_true',
help='show provider configuration')
parser.add_argument('--ou-slug', help='slug of the ou, if absent default ou is used')
@atomic
def handle(self, name, *args, **options):
def handle(self, *args, **options):
name = options['name']
openid_configuration = options.get('openid_configuration')
issuer = options.get('issuer')
if openid_configuration:

View File

@ -1,5 +1,3 @@
from optparse import make_option
try:
import ldap
from ldap.dn import str2dn, dn2str
@ -26,23 +24,21 @@ class Command(BaseCommand):
can_import_django_settings = True
output_transaction = True
requires_model_validation = True
option_list = BaseCommand.option_list + (
make_option('--fake',
action='store_true',
default=False,
help='Do nothing, just simulate'),
make_option('--batch-size',
action='store',
type='int',
default=200,
help='Batch size'),
)
def add_arguments(self, parser):
parser.add_argument('target_resource', nargs='*')
parser.add_argument(
'--fake', action='store_true', default=False, help='Do nothing, just simulate'
)
parser.add_argument(
'--batch-size', action='store', type='int', default=200, help='Batch size'
)
def handle(self, *args, **options):
ressources = app_settings.RESSOURCES
if args:
if options['target_resource']:
ressources = [ressource for ressource in ressources
if ressource.get('name') in args]
if ressource.get('name') in options['target_resource']]
for ressource in ressources:
self.sync_ressource(ressource, **options)

View File

@ -5,7 +5,7 @@ import json
from django.core import management
import py
from authentic2.models import DeletedUser
from authentic2.models import Attribute, DeletedUser
from authentic2_auth_oidc.models import OIDCProvider
from django_rbac.utils import get_ou_model
@ -55,6 +55,24 @@ def test_load_ldif(db, monkeypatch, tmpdir):
management.call_command(
'load-ldif', ldif.strpath, result='result', extra_attribute={'ldap_attr': 'first_name'})
# test ExtraAttributeAction
class MockPArser(object):
def __init__(self, *args, **kwargs):
self.users = []
assert len(args) == 1
assert isinstance(args[0], file)
assert kwargs['options']['extra_attribute'] == {
'ldap_attr': Attribute.objects.get(name='first_name')}
assert kwargs['options']['result'] == 'result'
def parse(self):
pass
monkeypatch.setattr(oidc_cmd, 'DjangoUserLDIFParser', MockPArser)
management.call_command(
'load-ldif', '--extra-attribute', 'ldap_attr', 'first_name',
'--result', 'result', ldif.strpath)
def test_oidc_register_issuer(db, tmpdir, monkeypatch):
oidc_conf_f = py.path.local(__file__).dirpath('openid_configuration.json')
@ -79,7 +97,8 @@ def test_oidc_register_issuer(db, tmpdir, monkeypatch):
oidc_conf = py.path.local(__file__).dirpath('openid_configuration.json').strpath
management.call_command(
'oidc-register-issuer', 'somename', openid_configuration=oidc_conf, issuer='issuer')
'oidc-register-issuer', '--openid-configuration', oidc_conf, '--issuer', 'issuer',
'somename')
provider = OIDCProvider.objects.get(name='somename')
assert provider.issuer == 'issuer'