From 23125241c8d34d8dbf34d402abf82641350c0fd3 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Tue, 9 Nov 2021 08:07:51 +0100 Subject: [PATCH 1/4] Allow global maintainers/owners to add product types --- docs/content/en/usage/permissions.md | 8 +- dojo/api_v2/permissions.py | 4 +- dojo/authorization/authorization.py | 43 +++++++++-- .../authorization/authorization_decorators.py | 17 ++++- dojo/authorization/roles_permissions.py | 15 ++++ dojo/endpoint/queries.py | 15 +--- dojo/engagement/queries.py | 9 +-- dojo/finding/queries.py | 15 +--- dojo/finding_group/queries.py | 9 +-- dojo/fixtures/dojo_testdata.json | 76 +++++++++++++++++++ dojo/jira_link/views.py | 3 +- dojo/models.py | 4 +- dojo/product/queries.py | 43 +++-------- dojo/product_type/queries.py | 12 +-- dojo/product_type/views.py | 5 +- dojo/test/queries.py | 15 +--- .../authorization/test_authorization.py | 17 ++++- dojo/unittests/test_rest_framework.py | 24 ++++++ 18 files changed, 225 insertions(+), 109 deletions(-) diff --git a/docs/content/en/usage/permissions.md b/docs/content/en/usage/permissions.md index bb157048428..f4a91a4dd2b 100644 --- a/docs/content/en/usage/permissions.md +++ b/docs/content/en/usage/permissions.md @@ -9,7 +9,7 @@ draft: false * Administrators (aka super users) have no limitations in the system. They can change all settings, manage users and have read and write access to all data. * Staff users can add Product Types, and have access to data according to their role in a Product or Product Type. There is the parameter `AUTHORIZATION_STAFF_OVERRIDE` in the settings to give all staff users full access to all Products and Product Types. -* Guest users have limited functionality available. They cannot add Product Types but have access to data according to their role in a Product or Product Type +* Regular users have limited functionality available. They cannot add Product Types but have access to data according to their role in a Product or Product Type ## Product and Product Type permissions @@ -19,7 +19,7 @@ Users can be assigned as members to Products and Product Types, giving them one | | Reader | Writer | Maintainer | Owner | API Importer | |-----------------------------|:------:|:------:|:----------:|:-----:|:------------:| -| Add Product Type 1) | | | | | | +| Add Product Type | | | 1) |1) | | | View Product Type | x | x | x | x | x | | Remove yourself as a member | x | x | x | x | | | Manage Product Type members | | | x | x | | @@ -73,7 +73,7 @@ Users can be assigned as members to Products and Product Types, giving them one | Delete Note | | (x) 2) | x | x | | -1) Every staff user and administrator can add Product Types. Guest users are not allowed to add Product Types. +1) Every staff user and administrator can add Product Types. Regular users are not allowed to add Product Types, unless they are Global Owner or Maintainer. 2) Every user is allowed to delete his own notes. @@ -105,7 +105,7 @@ The membership of a group itself has a role that determines what permissions the | Add Group member as Owner | | | x | | Delete Group | | | x | -1) Every staff user and administrator can add groups. Guest users are not allowed to add groups. +1) Every staff user and administrator can add groups. Regular users are not allowed to add groups. The permissions to manage the roles of Products and Product types for a group is defined by the role of the user in the respective Product or Product Type. diff --git a/dojo/api_v2/permissions.py b/dojo/api_v2/permissions.py index 5e99f104ffb..0c80e2d18bf 100644 --- a/dojo/api_v2/permissions.py +++ b/dojo/api_v2/permissions.py @@ -5,7 +5,7 @@ from dojo.models import Endpoint, Engagement, Finding, Product_Type, Product, Test, Dojo_Group from django.shortcuts import get_object_or_404 from rest_framework import permissions, serializers -from dojo.authorization.authorization import user_has_permission +from dojo.authorization.authorization import user_has_global_permission, user_has_permission from dojo.authorization.roles_permissions import Permissions @@ -215,7 +215,7 @@ def has_object_permission(self, request, view, obj): class UserHasProductTypePermission(permissions.BasePermission): def has_permission(self, request, view): if request.method == 'POST': - return request.user.is_staff + return user_has_global_permission(request.user, Permissions.Product_Type_Add) else: return True diff --git a/dojo/authorization/authorization.py b/dojo/authorization/authorization.py index 86e213305e6..dfa6a312f77 100644 --- a/dojo/authorization/authorization.py +++ b/dojo/authorization/authorization.py @@ -1,7 +1,7 @@ from django.core.exceptions import PermissionDenied from django.conf import settings from dojo.request_cache import cache_for_request -from dojo.authorization.roles_permissions import Permissions, Roles, get_roles_with_permissions +from dojo.authorization.roles_permissions import Permissions, Roles, get_global_roles_with_permissions, get_roles_with_permissions from dojo.models import Product_Type, Product_Type_Member, Product, Product_Member, Engagement, \ Test, Finding, Endpoint, Finding_Group, Product_Group, Product_Type_Group, Dojo_Group, Dojo_Group_Member, \ Languages, App_Analysis, Stub_Finding, Product_API_Scan_Configuration @@ -17,11 +17,8 @@ def user_has_permission(user, obj, permission): if isinstance(obj, Product_Type) or isinstance(obj, Product): # Global roles are only relevant for product types, products and their dependent objects - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return True - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return True if isinstance(obj, Product_Type): # Check if the user has a role for the product type with the requested permissions @@ -95,11 +92,33 @@ def user_has_permission(user, obj, permission): format(type(obj).__name__, permission)) +def user_has_global_permission(user, permission): + if user.is_superuser: + return True + + if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: + return True + + if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_global_permission(user.global_role.role.id, permission): + return True + + for group in get_groups(user): + if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_global_permission(group.global_role.role.id, permission): + return True + + return False + + def user_has_permission_or_403(user, obj, permission): if not user_has_permission(user, obj, permission): raise PermissionDenied +def user_has_global_permission_or_403(user, permission): + if not user_has_global_permission(user, permission): + raise PermissionDenied + + def get_roles_for_permission(permission): if not Permissions.has_value(permission): raise PermissionDoesNotExistError('Permission {} does not exist'.format(permission)) @@ -119,9 +138,23 @@ def role_has_permission(role, permission): raise RoleDoesNotExistError('Role {} does not exist'.format(role)) roles = get_roles_with_permissions() permissions = roles.get(role) + if not permissions: + return False return permission in permissions +def role_has_global_permission(role, permission): + if role is None: + return False + if not Roles.has_value(role): + raise RoleDoesNotExistError('Role {} does not exist'.format(role)) + roles = get_global_roles_with_permissions() + permissions = roles.get(role) + if permissions and permission in permissions: + return True + return role_has_permission(role, permission) + + class NoAuthorizationImplementedError(Exception): def __init__(self, message): self.message = message diff --git a/dojo/authorization/authorization_decorators.py b/dojo/authorization/authorization_decorators.py index e0a72607905..971d37843dc 100644 --- a/dojo/authorization/authorization_decorators.py +++ b/dojo/authorization/authorization_decorators.py @@ -2,7 +2,7 @@ from django.conf import settings from django.core.exceptions import PermissionDenied from django.shortcuts import get_object_or_404 -from dojo.authorization.authorization import user_has_permission_or_403 +from dojo.authorization.authorization import user_has_global_permission_or_403, user_has_permission_or_403 from dojo.user.helper import user_is_authorized as legacy_check @@ -39,3 +39,18 @@ def _wrapped(request, *args, **kwargs): return func(request, *args, **kwargs) return _wrapped + + +def user_has_global_permission(permission, func=None): + """Decorator for functions that ensures the user has a permission + """ + + if func is None: + return functools.partial(user_has_global_permission, permission) + + @functools.wraps(func) + def _wrapped(request, *args, **kwargs): + user_has_global_permission_or_403(request.user, permission) + return func(request, *args, **kwargs) + + return _wrapped diff --git a/dojo/authorization/roles_permissions.py b/dojo/authorization/roles_permissions.py index 698232d78a9..03f943b92ea 100644 --- a/dojo/authorization/roles_permissions.py +++ b/dojo/authorization/roles_permissions.py @@ -33,6 +33,7 @@ class Permissions(IntEnum): Product_Type_Member_Add_Owner = 1005 Product_Type_Edit = 1006 Product_Type_Delete = 1007 + Product_Type_Add = 1008 Product_View = 1102 Product_Member_Delete = 1103 @@ -448,3 +449,17 @@ def get_roles_with_permissions(): Permissions.Product_API_Scan_Configuration_Delete } } + + +def get_global_roles_with_permissions(): + """ + Extra permissions for global roles, on top of the permissions granted to the "normal" roles above. + """ + return { + Roles.Maintainer: { + Permissions.Product_Type_Add + }, + Roles.Owner: { + Permissions.Product_Type_Add + } + } diff --git a/dojo/endpoint/queries.py b/dojo/endpoint/queries.py index 597967a956b..f1c5019d4a1 100644 --- a/dojo/endpoint/queries.py +++ b/dojo/endpoint/queries.py @@ -3,8 +3,7 @@ from django.db.models import Exists, OuterRef, Q from dojo.models import Endpoint, Endpoint_Status, Product_Member, Product_Type_Member, \ Product_Group, Product_Type_Group -from dojo.authorization.authorization import get_roles_for_permission, role_has_permission, \ - get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission def get_authorized_endpoints(permission, queryset=None, user=None): @@ -27,13 +26,9 @@ def get_authorized_endpoints(permission, queryset=None, user=None): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return endpoints - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return endpoints - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return endpoints - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), @@ -87,13 +82,9 @@ def get_authorized_endpoint_status(permission, queryset=None, user=None): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return endpoint_status - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return endpoint_status - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return endpoint_status - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('endpoint__product__prod_type_id'), diff --git a/dojo/engagement/queries.py b/dojo/engagement/queries.py index ca131c13c1e..ca42383250c 100644 --- a/dojo/engagement/queries.py +++ b/dojo/engagement/queries.py @@ -3,8 +3,7 @@ from django.db.models import Exists, OuterRef, Q from dojo.models import Engagement, Product_Member, Product_Type_Member, \ Product_Group, Product_Type_Group -from dojo.authorization.authorization import get_roles_for_permission, role_has_permission, \ - get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission def get_authorized_engagements(permission): @@ -20,13 +19,9 @@ def get_authorized_engagements(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Engagement.objects.all() - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Engagement.objects.all() - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Engagement.objects.all() - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), diff --git a/dojo/finding/queries.py b/dojo/finding/queries.py index 0a882154a29..3afc11ae75b 100644 --- a/dojo/finding/queries.py +++ b/dojo/finding/queries.py @@ -3,8 +3,7 @@ from django.db.models import Exists, OuterRef, Q from dojo.models import Finding, Product_Member, Product_Type_Member, Stub_Finding, \ Product_Group, Product_Type_Group -from dojo.authorization.authorization import get_roles_for_permission, role_has_permission, \ - get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission def get_authorized_findings(permission, queryset=None, user=None): @@ -27,13 +26,9 @@ def get_authorized_findings(permission, queryset=None, user=None): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return findings - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return findings - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return findings - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('test__engagement__product__prod_type_id'), @@ -82,13 +77,9 @@ def get_authorized_stub_findings(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Stub_Finding.objects.all() - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Stub_Finding.objects.all() - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Stub_Finding.objects.all() - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('test__engagement__product__prod_type_id'), diff --git a/dojo/finding_group/queries.py b/dojo/finding_group/queries.py index ae08e6897c5..4ac1582e369 100644 --- a/dojo/finding_group/queries.py +++ b/dojo/finding_group/queries.py @@ -3,8 +3,7 @@ from django.db.models import Exists, OuterRef, Q from dojo.models import Finding_Group, Product_Member, Product_Type_Member, \ Product_Group, Product_Type_Group -from dojo.authorization.authorization import get_roles_for_permission, role_has_permission, \ - get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission def get_authorized_finding_groups(permission, queryset=None, user=None): @@ -27,13 +26,9 @@ def get_authorized_finding_groups(permission, queryset=None, user=None): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return finding_groups - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return finding_groups - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return finding_groups - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('test__engagement__product__prod_type_id'), diff --git a/dojo/fixtures/dojo_testdata.json b/dojo/fixtures/dojo_testdata.json index d826a686736..bf7d5517609 100644 --- a/dojo/fixtures/dojo_testdata.json +++ b/dojo/fixtures/dojo_testdata.json @@ -71,6 +71,42 @@ "date_joined": "2018-04-13T07:59:51.527Z" } }, + { + "pk": 5, + "model": "auth.user", + "fields": { + "username": "user4", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "last_login": null, + "groups": [], + "user_permissions": [], + "password": "pbkdf2_sha256$36000$pe8Ff8HrBPac$Lb3ee6/R9z/aL9nM+D2AXWTpIt9Pa9kcLueXxYNy1ZY=", + "email": "", + "date_joined": "2018-04-13T07:59:51.527Z" + } + }, + { + "pk": 6, + "model": "auth.user", + "fields": { + "username": "user5", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": false, + "is_staff": false, + "last_login": null, + "groups": [], + "user_permissions": [], + "password": "pbkdf2_sha256$36000$pe8Ff8HrBPac$Lb3ee6/R9z/aL9nM+D2AXWTpIt9Pa9kcLueXxYNy1ZY=", + "email": "", + "date_joined": "2018-04-13T07:59:51.527Z" + } + }, { "pk": "2dqr18yqu9mzb87abk0okid75w2clakl", "model": "sessions.session", @@ -2193,6 +2229,30 @@ "created": "2018-04-16T06:54:35.940Z" } }, + { + "pk": "184770c4c3256aba904297610fbb4da3fa15ba34", + "model": "authtoken.token", + "fields": { + "user": 4, + "created": "2018-04-16T06:54:35.933Z" + } + }, + { + "pk": "184770c4c3256aba904297610fbb4da3fa15ba35", + "model": "authtoken.token", + "fields": { + "user": 5, + "created": "2018-04-16T06:54:35.933Z" + } + }, + { + "pk": "184770c4c3256aba904297610fbb4da3fa15ba36", + "model": "authtoken.token", + "fields": { + "user": 6, + "created": "2018-04-16T06:54:35.933Z" + } + }, { "pk": "1", "model": "dojo.dojo_group", @@ -2244,6 +2304,22 @@ "role": 4 } }, + { + "pk": 2, + "model": "dojo.global_role", + "fields": { + "user": 6, + "role": 4 + } + }, + { + "pk": 3, + "model": "dojo.global_role", + "fields": { + "user": 5, + "role": 5 + } + }, { "model": "dojo.language_type", "pk": 1, diff --git a/dojo/jira_link/views.py b/dojo/jira_link/views.py index aab0d86a8a9..c747d0a8599 100644 --- a/dojo/jira_link/views.py +++ b/dojo/jira_link/views.py @@ -249,6 +249,7 @@ def express_new_jira(request): issue_id = jform.cleaned_data.get('issue_key') key_url = jira_server.strip('/') + '/rest/api/latest/issue/' + issue_id + '/transitions?expand=transitions.fields' response = jira._session.get(key_url).json() + logger.debug('Retrieved JIRA issue succesfully') open_key = close_key = None for node in response['transitions']: if node['to']['statusCategory']['name'] == 'To Do': @@ -259,7 +260,7 @@ def express_new_jira(request): logger.exception(e) # already logged in jira_helper messages.add_message(request, messages.ERROR, - 'Unable to find Open/Close ID\'s. They will need to be found manually', + 'Unable to find Open/Close ID\'s (invalid issue key specified?). They will need to be found manually', extra_tags='alert-danger') return render(request, 'dojo/new_jira.html', {'jform': jform}) diff --git a/dojo/models.py b/dojo/models.py index 03745893263..0e816cc3cb3 100755 --- a/dojo/models.py +++ b/dojo/models.py @@ -3700,7 +3700,9 @@ def enable_disable_auditlog(enable=True): admin.site.register(System_Settings, System_SettingsAdmin) admin.site.register(CWE) admin.site.register(Regulation) -admin.site.register(Notifications) +admin.site.register(Global_Role) +admin.site.register(Role) +admin.site.register(Dojo_Group) # SonarQube Integration admin.site.register(Sonarqube_Issue) diff --git a/dojo/product/queries.py b/dojo/product/queries.py index 0db1322e4e3..590a3a0406f 100644 --- a/dojo/product/queries.py +++ b/dojo/product/queries.py @@ -4,8 +4,9 @@ from dojo.models import Product, Product_Member, Product_Type_Member, App_Analysis, \ DojoMeta, Product_Group, Product_Type_Group, Languages, Engagement_Presets, \ Product_API_Scan_Configuration -from dojo.authorization.authorization import get_roles_for_permission, user_has_permission, \ - role_has_permission, get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission, user_has_permission, \ + role_has_permission + from dojo.group.queries import get_authorized_groups from dojo.authorization.roles_permissions import Permissions @@ -25,13 +26,9 @@ def get_authorized_products(permission, user=None): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Product.objects.all().order_by('name') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Product.objects.all().order_by('name') - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Product.objects.all().order_by('name') - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('prod_type_id'), @@ -98,7 +95,7 @@ def get_authorized_product_members(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Product_Member.objects.all().select_related('role') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Product_Member.objects.all().select_related('role') products = get_authorized_products(permission) @@ -153,13 +150,9 @@ def get_authorized_app_analysis(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return App_Analysis.objects.all().order_by('name') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return App_Analysis.objects.all().order_by('name') - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return App_Analysis.objects.all().order_by('name') - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), @@ -208,13 +201,9 @@ def get_authorized_dojo_meta(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return DojoMeta.objects.all().order_by('name') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return DojoMeta.objects.all().order_by('name') - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return DojoMeta.objects.all().order_by('name') - roles = get_roles_for_permission(permission) product_authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), @@ -319,13 +308,9 @@ def get_authorized_languages(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Languages.objects.all().order_by('language') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Languages.objects.all().order_by('language') - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Languages.objects.all().order_by('language') - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), @@ -374,13 +359,9 @@ def get_authorized_engagement_presets(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Engagement_Presets.objects.all().order_by('title') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Engagement_Presets.objects.all().order_by('title') - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Engagement_Presets.objects.all().order_by('title') - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), @@ -429,13 +410,9 @@ def get_authorized_product_api_scan_configurations(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Product_API_Scan_Configuration.objects.all() - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Product_API_Scan_Configuration.objects.all() - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Product_API_Scan_Configuration.objects.all() - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('product__prod_type_id'), diff --git a/dojo/product_type/queries.py b/dojo/product_type/queries.py index 4f9475b4325..e9584860fbe 100644 --- a/dojo/product_type/queries.py +++ b/dojo/product_type/queries.py @@ -2,8 +2,8 @@ from django.db.models import Exists, OuterRef, Q from django.conf import settings from dojo.models import Product_Type, Product_Type_Member, Product_Type_Group -from dojo.authorization.authorization import get_roles_for_permission, user_has_permission, \ - role_has_permission, get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission, user_has_permission, \ + role_has_permission from dojo.group.queries import get_authorized_groups from dojo.authorization.roles_permissions import Permissions @@ -21,13 +21,9 @@ def get_authorized_product_types(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Product_Type.objects.all().order_by('name') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Product_Type.objects.all().order_by('name') - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Product_Type.objects.all().order_by('name') - roles = get_roles_for_permission(permission) authorized_roles = Product_Type_Member.objects.filter(product_type=OuterRef('pk'), user=user, @@ -79,7 +75,7 @@ def get_authorized_product_type_members(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Product_Type_Member.objects.all().select_related('role') - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Product_Type_Member.objects.all().select_related('role') product_types = get_authorized_product_types(permission) diff --git a/dojo/product_type/views.py b/dojo/product_type/views.py index 292cc5c1df4..776ce2440a8 100644 --- a/dojo/product_type/views.py +++ b/dojo/product_type/views.py @@ -3,7 +3,6 @@ from django.contrib.admin.utils import NestedObjects from django.db import DEFAULT_DB_ALIAS from django.contrib import messages -from django.contrib.auth.decorators import user_passes_test from django.urls import reverse from django.http import HttpResponseRedirect from django.shortcuts import render, get_object_or_404 @@ -19,7 +18,7 @@ from django.conf import settings from dojo.authorization.authorization import user_has_permission from dojo.authorization.roles_permissions import Permissions -from dojo.authorization.authorization_decorators import user_is_authorized +from dojo.authorization.authorization_decorators import user_has_global_permission, user_is_authorized from dojo.product_type.queries import get_authorized_product_types, get_authorized_members_for_product_type, \ get_authorized_groups_for_product_type from dojo.product.queries import get_authorized_products @@ -70,7 +69,7 @@ def prefetch_for_product_type(prod_types): return prefetch_prod_types -@user_passes_test(lambda u: u.is_staff) +@user_has_global_permission(Permissions.Product_Type_Add) def add_product_type(request): form = Product_TypeForm() if request.method == 'POST': diff --git a/dojo/test/queries.py b/dojo/test/queries.py index c2b88ba3e9e..7560fa7dc7e 100644 --- a/dojo/test/queries.py +++ b/dojo/test/queries.py @@ -3,8 +3,7 @@ from django.db.models import Exists, OuterRef, Q from dojo.models import Test, Product_Member, Product_Type_Member, Test_Import, \ Product_Group, Product_Type_Group -from dojo.authorization.authorization import get_roles_for_permission, role_has_permission, \ - get_groups +from dojo.authorization.authorization import get_roles_for_permission, user_has_global_permission def get_authorized_tests(permission, product=None): @@ -24,13 +23,9 @@ def get_authorized_tests(permission, product=None): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Test.objects.all() - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Test.objects.all() - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Test.objects.all() - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('engagement__product__prod_type_id'), @@ -82,13 +77,9 @@ def get_authorized_test_imports(permission): if user.is_staff and settings.AUTHORIZATION_STAFF_OVERRIDE: return Test_Import.objects.all() - if hasattr(user, 'global_role') and user.global_role.role is not None and role_has_permission(user.global_role.role.id, permission): + if user_has_global_permission(user, permission): return Test_Import.objects.all() - for group in get_groups(user): - if hasattr(group, 'global_role') and group.global_role.role is not None and role_has_permission(group.global_role.role.id, permission): - return Test_Import.objects.all() - roles = get_roles_for_permission(permission) authorized_product_type_roles = Product_Type_Member.objects.filter( product_type=OuterRef('test__engagement__product__prod_type_id'), diff --git a/dojo/unittests/authorization/test_authorization.py b/dojo/unittests/authorization/test_authorization.py index 2acd048826a..2319ebbb561 100644 --- a/dojo/unittests/authorization/test_authorization.py +++ b/dojo/unittests/authorization/test_authorization.py @@ -6,7 +6,7 @@ Test, Finding, Endpoint, Dojo_Group, Product_Group, Product_Type_Group, Role, Global_Role, Dojo_Group_Member, \ Languages, App_Analysis, Stub_Finding import dojo.authorization.authorization -from dojo.authorization.authorization import role_has_permission, get_roles_for_permission, \ +from dojo.authorization.authorization import role_has_permission, get_roles_for_permission, user_has_global_permission, \ user_has_permission_or_403, user_has_permission, \ RoleDoesNotExistError, PermissionDoesNotExistError from dojo.authorization.roles_permissions import Permissions, Roles @@ -131,6 +131,13 @@ def setUpTestData(cls): cls.group_member.user = cls.user4 cls.group_member.role = Role.objects.get(id=Roles.Writer) + cls.user5 = Dojo_User() + cls.user5.id = 5 + cls.global_role_user = Global_Role() + cls.global_role_user.id = 5 + cls.global_role_user.user = cls.user5 + cls.global_role_user.role = Role.objects.get(id=Roles.Owner) + def test_role_has_permission_exception(self): with self.assertRaisesMessage(RoleDoesNotExistError, 'Role 9999 does not exist'): @@ -497,6 +504,14 @@ def test_user_has_global_role_success(self): result = user_has_permission(self.user2, self.product, Permissions.Product_View) self.assertTrue(result) + def test_user_has_global_role_global_permission_no_permission(self): + result = user_has_global_permission(self.user2, Permissions.Product_Type_Add) + self.assertFalse(result) + + def test_user_has_global_role_global_permission_success(self): + result = user_has_global_permission(self.user5, Permissions.Product_Type_Add) + self.assertTrue(result) + @patch('dojo.models.Dojo_Group.objects') def test_user_in_group_with_global_role_no_permission(self, mock_foo): mock_foo.select_related.return_value = mock_foo diff --git a/dojo/unittests/test_rest_framework.py b/dojo/unittests/test_rest_framework.py index 4530fda6e88..f46b5e0b4e5 100644 --- a/dojo/unittests/test_rest_framework.py +++ b/dojo/unittests/test_rest_framework.py @@ -482,6 +482,18 @@ def setUp_not_authorized(self): self.client = APIClient() self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) + def setUp_global_reader(self): + testuser = User.objects.get(id=5) + token = Token.objects.get(user=testuser) + self.client = APIClient() + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) + + def setUp_global_owner(self): + testuser = User.objects.get(id=6) + token = Token.objects.get(user=testuser) + self.client = APIClient() + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token.key) + @skipIfNotSubclass(ListModelMixin) def test_list_not_authorized(self): if not self.object_permission: @@ -1476,6 +1488,18 @@ def test_create_not_authorized(self): response = self.client.post(self.url, self.payload) self.assertEqual(403, response.status_code, response.content[:1000]) + def test_create_not_authorized_reader(self): + self.setUp_global_reader() + + response = self.client.post(self.url, self.payload) + self.assertEqual(403, response.status_code, response.content[:1000]) + + def test_create_authorized_owner(self): + self.setUp_global_owner() + + response = self.client.post(self.url, self.payload) + self.assertEqual(201, response.status_code, response.content[:1000]) + class DojoGroupsTest(BaseClass.RESTEndpointTest): fixtures = ['dojo_testdata.json'] From faa48491cb39976111143912d8be51fbbc6c7173 Mon Sep 17 00:00:00 2001 From: Valentijn Scholten Date: Wed, 10 Nov 2021 12:16:38 +0100 Subject: [PATCH 2/4] add permission check to templates --- dojo/authorization/authorization_decorators.py | 4 ++-- dojo/templates/base.html | 2 +- dojo/templates/dojo/product_type.html | 2 +- dojo/templates/dojo/view_product_type.html | 2 +- dojo/templatetags/authorization_tags.py | 7 ++++++- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/dojo/authorization/authorization_decorators.py b/dojo/authorization/authorization_decorators.py index 971d37843dc..76fca372695 100644 --- a/dojo/authorization/authorization_decorators.py +++ b/dojo/authorization/authorization_decorators.py @@ -7,7 +7,7 @@ def user_is_authorized(model, permission, arg, legacy_permission=None, lookup="pk", func=None): - """Decorator for functions that ensures the user has permission on an object. + """Decorator for functions that ensures the user has an object permission. """ if func is None: @@ -42,7 +42,7 @@ def _wrapped(request, *args, **kwargs): def user_has_global_permission(permission, func=None): - """Decorator for functions that ensures the user has a permission + """Decorator for functions that ensures the user has a (global) permission """ if func is None: diff --git a/dojo/templates/base.html b/dojo/templates/base.html index cd18c77fb65..9e62de09d26 100644 --- a/dojo/templates/base.html +++ b/dojo/templates/base.html @@ -208,7 +208,7 @@
  • Add Product
  • {% endif %}
  • All Product Types
  • - {% if request.user.is_staff %} + {% if "Product_Type_Add"|has_permission %}
  • Add Product Type
  • {% endif %} diff --git a/dojo/templates/dojo/product_type.html b/dojo/templates/dojo/product_type.html index fd790f3f3d3..d230db72043 100644 --- a/dojo/templates/dojo/product_type.html +++ b/dojo/templates/dojo/product_type.html @@ -20,7 +20,7 @@

    diff --git a/dojo/templates/dojo/product_type.html b/dojo/templates/dojo/product_type.html index d230db72043..b4749e40326 100644 --- a/dojo/templates/dojo/product_type.html +++ b/dojo/templates/dojo/product_type.html @@ -20,7 +20,7 @@