From 927a545f41b6ca8490e12de63ca0d346043884e9 Mon Sep 17 00:00:00 2001 From: Les Begnaud Date: Thu, 5 Dec 2019 09:41:11 -0600 Subject: [PATCH 1/8] adjust groups and users startup scripts to allow custom codename filter --- initializers/groups.yml | 14 ++++++++++---- initializers/users.yml | 8 ++++---- startup_scripts/000_users.py | 27 ++++++++++++++++++++------- startup_scripts/010_groups.py | 24 ++++++++++++++++++------ 4 files changed, 52 insertions(+), 21 deletions(-) diff --git a/initializers/groups.yml b/initializers/groups.yml index 7bdd0a7..fc9ff28 100644 --- a/initializers/groups.yml +++ b/initializers/groups.yml @@ -7,10 +7,16 @@ # writers: # users: # - writer +## specify explicit permission codenames or codename filter functions and filters to match on # permissions: -# - add_device -# - change_device # - delete_device -# - add_virtualmachine -# - change_virtualmachine # - delete_virtualmachine +# - codename__startswith: +# - add_ +# - change_ +# vm_managers: +# - codename__endswith: +# - _virtualmachine +# creators: +# - codename__startswith: +# - add_ diff --git a/initializers/users.yml b/initializers/users.yml index 64c005c..a7a57a3 100644 --- a/initializers/users.yml +++ b/initializers/users.yml @@ -4,10 +4,10 @@ # password: reader # writer: # password: writer +## specify explicit permission codenames or codename filter functions and filters to match on # permissions: -# - add_device -# - change_device # - delete_device -# - add_virtualmachine -# - change_virtualmachine # - delete_virtualmachine +# - codename__startswith: +# - add_ +# - change_ diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index a1340a1..0037ee1 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -25,10 +25,23 @@ with file.open('r') as stream: if user_details.get('api_token', 0): Token.objects.create(user=user, key=user_details['api_token']) - user_permissions = user_details.get('permissions', []) - if user_permissions: - user.user_permissions.clear() - for permission_codename in user_details.get('permissions', []): - for permission in Permission.objects.filter(codename=permission_codename): - user.user_permissions.add(permission) - user.save() + yaml_permissions = user_details.get('permissions', []) + permission_object = user + if yaml_permissions: + permission_object.permissions.clear() + for yaml_permission in yaml_permissions: + if isinstance(yaml_permission,dict): + # assume this is the specific codename filter function instead of an exact codename + permission_codename_function = list(yaml_permission.keys())[0] + permission_codenames = yaml_permission[permission_codename_function] + else: + permission_codename_function = 'codename' + permission_codenames = list({yaml_permission}) + + # supports either one codename from the permissions list, or multiple codenames in a codename_function dict + for permission_codename in permission_codenames: + # supports non-unique permission codenames + for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): + permission_object.permissions.add(permission) + + permission_object.save() diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py index e68a5f0..49b6678 100644 --- a/startup_scripts/010_groups.py +++ b/startup_scripts/010_groups.py @@ -24,9 +24,21 @@ with file.open('r') as stream: if user: user.groups.add(group) - group_permissions = group_details.get('permissions', []) - if group_permissions: - group.permissions.clear() - for permission_codename in group_details.get('permissions', []): - for permission in Permission.objects.filter(codename=permission_codename): - group.permissions.add(permission) + yaml_permissions = group_details.get('permissions', []) + permission_object = group + if yaml_permissions: + permission_object.permissions.clear() + for yaml_permission in yaml_permissions: + if isinstance(yaml_permission,dict): + # assume this is the specific codename filter function instead of an exact codename + permission_codename_function = list(yaml_permission.keys())[0] + permission_codenames = yaml_permission[permission_codename_function] + else: + permission_codename_function = 'codename' + permission_codenames = list({yaml_permission}) + + # supports either one codename from the permissions list, or multiple codenames in a codename_function dict + for permission_codename in permission_codenames: + # supports non-unique permission codenames + for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): + permission_object.permissions.add(permission) From cce4370d4141db79b1df37fdcf905b4e6b6d1189 Mon Sep 17 00:00:00 2001 From: Les Begnaud Date: Thu, 5 Dec 2019 09:44:40 -0600 Subject: [PATCH 2/8] add permission example --- initializers/groups.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/initializers/groups.yml b/initializers/groups.yml index fc9ff28..6d0665f 100644 --- a/initializers/groups.yml +++ b/initializers/groups.yml @@ -17,6 +17,9 @@ # vm_managers: # - codename__endswith: # - _virtualmachine +# device_managers: +# - codename__contains: +# - device # creators: # - codename__startswith: # - add_ From aa0d2a6e01b7ace9e81f91a56b09eae844352278 Mon Sep 17 00:00:00 2001 From: Les Begnaud Date: Wed, 11 Dec 2019 10:14:38 -0600 Subject: [PATCH 3/8] simplify yml definitions to use wildcard syntax --- initializers/groups.yml | 16 ++++++---------- initializers/users.yml | 7 +++---- startup_scripts/000_users.py | 19 ++++++++----------- startup_scripts/010_groups.py | 19 ++++++++----------- 4 files changed, 25 insertions(+), 36 deletions(-) diff --git a/initializers/groups.yml b/initializers/groups.yml index 6d0665f..d471fe3 100644 --- a/initializers/groups.yml +++ b/initializers/groups.yml @@ -7,19 +7,15 @@ # writers: # users: # - writer -## specify explicit permission codenames or codename filter functions and filters to match on +## specify explicit permission codenames or include wildcard to match multiple permissions # permissions: # - delete_device # - delete_virtualmachine -# - codename__startswith: -# - add_ -# - change_ +# - add_* +# - change_* # vm_managers: -# - codename__endswith: -# - _virtualmachine +# - *_virtualmachine # device_managers: -# - codename__contains: -# - device +# - *device* # creators: -# - codename__startswith: -# - add_ +# - add_* diff --git a/initializers/users.yml b/initializers/users.yml index a7a57a3..ad654b7 100644 --- a/initializers/users.yml +++ b/initializers/users.yml @@ -4,10 +4,9 @@ # password: reader # writer: # password: writer -## specify explicit permission codenames or codename filter functions and filters to match on +## specify explicit permission codenames or include wildcard to match multiple permissions # permissions: # - delete_device # - delete_virtualmachine -# - codename__startswith: -# - add_ -# - change_ +# - add_* +# - change_* diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index 0037ee1..cb04a16 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -30,18 +30,15 @@ with file.open('r') as stream: if yaml_permissions: permission_object.permissions.clear() for yaml_permission in yaml_permissions: - if isinstance(yaml_permission,dict): - # assume this is the specific codename filter function instead of an exact codename - permission_codename_function = list(yaml_permission.keys())[0] - permission_codenames = yaml_permission[permission_codename_function] + if '*' in yaml_permission: + permission_codename_function = 'codename__iregex' + permission_codename = '^' + yaml_permission.replace('*','.*') + '$' else: permission_codename_function = 'codename' - permission_codenames = list({yaml_permission}) - - # supports either one codename from the permissions list, or multiple codenames in a codename_function dict - for permission_codename in permission_codenames: - # supports non-unique permission codenames - for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): - permission_object.permissions.add(permission) + permission_codename = yaml_permission + + # supports non-unique permission codenames + for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): + permission_object.permissions.add(permission) permission_object.save() diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py index 49b6678..990e065 100644 --- a/startup_scripts/010_groups.py +++ b/startup_scripts/010_groups.py @@ -29,16 +29,13 @@ with file.open('r') as stream: if yaml_permissions: permission_object.permissions.clear() for yaml_permission in yaml_permissions: - if isinstance(yaml_permission,dict): - # assume this is the specific codename filter function instead of an exact codename - permission_codename_function = list(yaml_permission.keys())[0] - permission_codenames = yaml_permission[permission_codename_function] + if '*' in yaml_permission: + permission_codename_function = 'codename__iregex' + permission_codename = '^' + yaml_permission.replace('*','.*') + '$' else: permission_codename_function = 'codename' - permission_codenames = list({yaml_permission}) - - # supports either one codename from the permissions list, or multiple codenames in a codename_function dict - for permission_codename in permission_codenames: - # supports non-unique permission codenames - for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): - permission_object.permissions.add(permission) + permission_codename = yaml_permission + + # supports non-unique permission codenames + for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): + permission_object.permissions.add(permission) From f4e243d5ad55c56090164e14fc68173b002eaa78 Mon Sep 17 00:00:00 2001 From: Les Begnaud Date: Wed, 11 Dec 2019 10:21:28 -0600 Subject: [PATCH 4/8] update example to note yaml restriction --- initializers/groups.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/initializers/groups.yml b/initializers/groups.yml index d471fe3..67a9290 100644 --- a/initializers/groups.yml +++ b/initializers/groups.yml @@ -14,8 +14,9 @@ # - add_* # - change_* # vm_managers: -# - *_virtualmachine +## yaml doesn't allow starting a key with an asterisk without explicit use of single quote +# - '*_virtualmachine' # device_managers: -# - *device* +# - '*device*' # creators: # - add_* From a2c06026d57ff2a5160df9eed116ba713e019e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Fri, 31 Jan 2020 11:35:25 +0100 Subject: [PATCH 5/8] Ajdust indents in __main__.py ... so that the match the style of the other python code in this project --- startup_scripts/__main__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/startup_scripts/__main__.py b/startup_scripts/__main__.py index 50d2613..b13b8dd 100644 --- a/startup_scripts/__main__.py +++ b/startup_scripts/__main__.py @@ -7,12 +7,12 @@ from os.path import dirname, abspath this_dir = dirname(abspath(__file__)) def filename(f): - return f.name + return f.name with scandir(dirname(abspath(__file__))) as it: - for f in sorted(it, key = filename): - if f.name.startswith('__') or not f.is_file(): - continue - - print(f"Running {f.path}") - runpy.run_path(f.path) + for f in sorted(it, key = filename): + if f.name.startswith('__') or not f.is_file(): + continue + + print(f"Running {f.path}") + runpy.run_path(f.path) From ba3176f140973b6bad219269a24467e6aa1b3a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Fri, 31 Jan 2020 11:37:05 +0100 Subject: [PATCH 6/8] Added missing keywords to the yaml ... and moved some documentatory comments to the beginning of the file. --- initializers/groups.yml | 17 +++++++++++++++-- initializers/users.yml | 13 ++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/initializers/groups.yml b/initializers/groups.yml index 67a9290..b91ef39 100644 --- a/initializers/groups.yml +++ b/initializers/groups.yml @@ -1,3 +1,15 @@ +## To list all permissions, run: +## +## docker-compose run --rm --entrypoint /bin/bash netbox +## $ ./manage.py migrate +## $ ./manage.py shell +## > from django.contrib.auth.models import Permission +## > print('\n'.join([p.codename for p in Permission.objects.all()])) +## +## Permission lists support wildcards. See the examples below. +## +## Examples: + # applications: # users: # - technical_user @@ -7,16 +19,17 @@ # writers: # users: # - writer -## specify explicit permission codenames or include wildcard to match multiple permissions # permissions: # - delete_device # - delete_virtualmachine # - add_* # - change_* # vm_managers: -## yaml doesn't allow starting a key with an asterisk without explicit use of single quote +# permissions: # - '*_virtualmachine' # device_managers: +# permissions: # - '*device*' # creators: +# permissions: # - add_* diff --git a/initializers/users.yml b/initializers/users.yml index ad654b7..2aea62e 100644 --- a/initializers/users.yml +++ b/initializers/users.yml @@ -1,10 +1,21 @@ +## To list all permissions, run: +## +## docker-compose run --rm --entrypoint /bin/bash netbox +## $ ./manage.py migrate +## $ ./manage.py shell +## > from django.contrib.auth.models import Permission +## > print('\n'.join([p.codename for p in Permission.objects.all()])) +## +## Permission lists support wildcards. See the examples below. +## +## Examples: + # technical_user: # api_token: 0123456789technicaluser789abcdef01234567 # must be looooong! # reader: # password: reader # writer: # password: writer -## specify explicit permission codenames or include wildcard to match multiple permissions # permissions: # - delete_device # - delete_virtualmachine From 69ef7b78272654ea8e3610da8b30eb981e391275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Fri, 31 Jan 2020 11:39:05 +0100 Subject: [PATCH 7/8] Removed the eval from the code ... and changed it to make it work with the latest Netbox version. --- startup_scripts/000_users.py | 22 ++++++++++------------ startup_scripts/010_groups.py | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index cb04a16..446aaeb 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -20,25 +20,23 @@ with file.open('r') as stream: username = username, password = user_details.get('password', 0) or User.objects.make_random_password) - print("👤 Created user ",username) + print("👤 Created user",username) if user_details.get('api_token', 0): Token.objects.create(user=user, key=user_details['api_token']) yaml_permissions = user_details.get('permissions', []) - permission_object = user + subject = user.user_permissions if yaml_permissions: - permission_object.permissions.clear() + subject.clear() for yaml_permission in yaml_permissions: if '*' in yaml_permission: - permission_codename_function = 'codename__iregex' - permission_codename = '^' + yaml_permission.replace('*','.*') + '$' + permission_filter = '^' + yaml_permission.replace('*','.*') + '$' + permissions = Permission.objects.filter(codename__iregex=permission_filter) + print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") else: - permission_codename_function = 'codename' - permission_codename = yaml_permission + permissions = Permission.objects.filter(codename=yaml_permission) + print(" ⚿ Granting permission", yaml_permission) - # supports non-unique permission codenames - for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): - permission_object.permissions.add(permission) - - permission_object.save() + for permission in permissions: + subject.add(permission) diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py index 990e065..0a35387 100644 --- a/startup_scripts/010_groups.py +++ b/startup_scripts/010_groups.py @@ -25,17 +25,17 @@ with file.open('r') as stream: user.groups.add(group) yaml_permissions = group_details.get('permissions', []) - permission_object = group + subject = group.permissions if yaml_permissions: - permission_object.permissions.clear() + subject.clear() for yaml_permission in yaml_permissions: if '*' in yaml_permission: - permission_codename_function = 'codename__iregex' - permission_codename = '^' + yaml_permission.replace('*','.*') + '$' + permission_filter = '^' + yaml_permission.replace('*','.*') + '$' + permissions = Permission.objects.filter(codename__iregex=permission_filter) + print(" ⚿ Granting", permissions.count(), "permissions matching '" + yaml_permission + "'") else: - permission_codename_function = 'codename' - permission_codename = yaml_permission - - # supports non-unique permission codenames - for permission in eval('Permission.objects.filter(' + permission_codename_function + '=permission_codename)'): - permission_object.permissions.add(permission) + permissions = Permission.objects.filter(codename=yaml_permission) + print(" ⚿ Granting permission", yaml_permission) + + for permission in permissions: + subject.add(permission) From 3d80cc5a722c80285be4b803ecbdde60e0199f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=A4der?= Date: Sun, 2 Feb 2020 09:48:02 +0100 Subject: [PATCH 8/8] Tiny code refactoring --- startup_scripts/000_users.py | 2 +- startup_scripts/010_groups.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/startup_scripts/000_users.py b/startup_scripts/000_users.py index 446aaeb..6962df0 100644 --- a/startup_scripts/000_users.py +++ b/startup_scripts/000_users.py @@ -26,8 +26,8 @@ with file.open('r') as stream: Token.objects.create(user=user, key=user_details['api_token']) yaml_permissions = user_details.get('permissions', []) - subject = user.user_permissions if yaml_permissions: + subject = user.user_permissions subject.clear() for yaml_permission in yaml_permissions: if '*' in yaml_permission: diff --git a/startup_scripts/010_groups.py b/startup_scripts/010_groups.py index 0a35387..08fb4bf 100644 --- a/startup_scripts/010_groups.py +++ b/startup_scripts/010_groups.py @@ -25,8 +25,8 @@ with file.open('r') as stream: user.groups.add(group) yaml_permissions = group_details.get('permissions', []) - subject = group.permissions if yaml_permissions: + subject = group.permissions subject.clear() for yaml_permission in yaml_permissions: if '*' in yaml_permission: