Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: define PA role and permissions + update tests #693

Merged
merged 3 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions license_manager/apps/api/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,3 @@ class CanRetireUser(permissions.BasePermission):

def has_permission(self, request, view):
return request.user.username == settings.RETIREMENT_SERVICE_WORKER_USERNAME or request.user.is_superuser


class IsInProvisioningAdminGroup(permissions.BasePermission):
"""
Grant access to those users only who are part of the license provisiioning django group
"""
ALLOWED_API_GROUPS = ['provisioning_admins_group']
message = 'Access denied: You do not have the necessary permissions to access this.'

def has_permission(self, request, view):
return (
super().has_permission(request, view) and (
request.user.groups.filter(name__in=self.ALLOWED_API_GROUPS).exists()
)
)
1 change: 0 additions & 1 deletion license_manager/apps/api/v1/tests/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

# Constants for subscriptions API tests
SUBSCRIPTION_RENEWAL_DAYS_OFFSET = 500
PROVISIONING_ADMINS_GROUP = "provisioning_admins_group"

ADMIN_ROLES = {
'system_role': constants.SYSTEM_ENTERPRISE_ADMIN_ROLE,
Expand Down
214 changes: 106 additions & 108 deletions license_manager/apps/api/v1/tests/test_views.py

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions license_manager/apps/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import extend_schema, extend_schema_view
from edx_rbac.decorators import permission_required
from edx_rbac.mixins import PermissionRequiredForListingMixin
from edx_rbac.mixins import (
PermissionRequiredForListingMixin,
PermissionRequiredMixin,
)
from edx_rest_framework_extensions.auth.jwt.authentication import (
JwtAuthentication,
)
Expand All @@ -30,10 +33,7 @@
from license_manager.apps.api.filters import LicenseFilter
from license_manager.apps.api.mixins import UserDetailsFromJwtMixin
from license_manager.apps.api.models import BulkEnrollmentJob
from license_manager.apps.api.permissions import (
CanRetireUser,
IsInProvisioningAdminGroup,
)
from license_manager.apps.api.permissions import CanRetireUser
from license_manager.apps.api.tasks import (
create_braze_aliases_task,
execute_post_revocation_tasks,
Expand Down Expand Up @@ -311,13 +311,15 @@ def _check_subscription_licenses(self, customer_agreement, plan, user_email):
),
)
class CustomerAgreementProvisioningAdminViewset(
PermissionRequiredMixin,
viewsets.GenericViewSet,
mixins.CreateModelMixin,
mixins.RetrieveModelMixin
):
""" Viewset for Provisioning Admins write operations."""
authentication_classes = [JwtAuthentication, SessionAuthentication]
permission_classes = [permissions.IsAuthenticated, IsInProvisioningAdminGroup]
permission_classes = [permissions.IsAuthenticated]
permission_required = constants.SUBSCRIPTIONS_CUSTOMER_AGREEMENT_PROVISIONING_ADMIN_ACCESS_PERMISSION
lookup_field = 'uuid'
lookup_url_kwarg = 'customer_agreement_uuid'
serializer_class = serializers.CustomerAgreementSerializer
Expand Down Expand Up @@ -460,13 +462,15 @@ def list(self, request, *args, **kwargs):
),
)
class SubscriptionPlanProvisioningAdminViewset(
PermissionRequiredMixin,
mixins.CreateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet
):
""" Viewset for Provisioning Admins write operations."""
authentication_classes = [JwtAuthentication, SessionAuthentication]
permission_classes = [permissions.IsAuthenticated, IsInProvisioningAdminGroup]
permission_classes = [permissions.IsAuthenticated]
permission_required = constants.SUBSCRIPTIONS_PROVISIONING_ADMIN_ACCESS_PERMISSION
lookup_field = 'uuid'
lookup_url_kwarg = 'subscription_uuid' # URL keyword for the lookup field

Expand Down
9 changes: 9 additions & 0 deletions license_manager/apps/subscriptions/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,23 @@ class SegmentEvents:
# Role-based access control
SUBSCRIPTIONS_ADMIN_ROLE = 'enterprise_subscriptions_admin'
SUBSCRIPTIONS_LEARNER_ROLE = 'enterprise_subscriptions_learner'
# Role-based access control - Provisioning admins
PROVISIONING_SUBSCRIPTION_ADMIN_ROLE = 'provisioning_subscription_admin'
PROVISIONING_CUSTOMER_AGREEMENT_ADMIN_ROLE = 'provisioning_customer_agreement_admin'

SYSTEM_ENTERPRISE_ADMIN_ROLE = 'enterprise_admin'
SYSTEM_ENTERPRISE_LEARNER_ROLE = 'enterprise_learner'
SYSTEM_ENTERPRISE_OPERATOR_ROLE = 'enterprise_openedx_operator'
SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE = 'enterprise_provisioning_admin'

SUBSCRIPTIONS_ADMIN_ACCESS_PERMISSION = 'subscriptions.has_admin_access'
SUBSCRIPTIONS_ADMIN_LEARNER_ACCESS_PERMISSION = 'subscriptions.has_learner_or_admin_access'

# Provisioning admins permissions
SUBSCRIPTIONS_PROVISIONING_ADMIN_ACCESS_PERMISSION = 'subscriptions.has_provisioning_admin_access'
SUBSCRIPTIONS_CUSTOMER_AGREEMENT_PROVISIONING_ADMIN_ACCESS_PERMISSION = \
'agreements.has_provisioning_admin_access'

# Subsidy constants
PERCENTAGE_DISCOUNT_TYPE = 'percentage'
LICENSE_DISCOUNT_VALUE = 100 # Represents a 100% off value
Expand Down
44 changes: 44 additions & 0 deletions license_manager/apps/subscriptions/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,47 @@ def has_explicit_access_to_subscriptions_learner(user, enterprise_customer_uuid)
constants.SUBSCRIPTIONS_ADMIN_LEARNER_ACCESS_PERMISSION,
has_admin_access | has_learner_access,
)


@rules.predicate
def has_implicit_access_to_subscription_plan_provisioning(user, enterprise_customer_uuid): # pylint: disable=unused-argument
"""
Check that if request user has implicit access as PROVISIONING_SUBSCRIPTION_ADMIN_ROLE.
Returns:
boolean: whether the request user has access.
"""
return request_user_has_implicit_access_via_jwt(
get_decoded_jwt(crum.get_current_request()),
constants.PROVISIONING_SUBSCRIPTION_ADMIN_ROLE,
str(enterprise_customer_uuid),
)


# Grants access permission if the user is a provisioning admin
rules.add_perm(
constants.SUBSCRIPTIONS_PROVISIONING_ADMIN_ACCESS_PERMISSION,
has_implicit_access_to_subscription_plan_provisioning,
)


@rules.predicate
def has_implicit_access_to_customer_agreement_provisioning(user, enterprise_customer_uuid): # pylint: disable=unused-argument
"""
Check that if request user has implicit access as PROVISIONING_SUBSCRIPTION_ADMIN_ROLE.
Returns:
boolean: whether the request user has access.
"""
return request_user_has_implicit_access_via_jwt(
get_decoded_jwt(crum.get_current_request()),
constants.PROVISIONING_CUSTOMER_AGREEMENT_ADMIN_ROLE,
str(enterprise_customer_uuid),
)


# Grants access permission if the user is a provisioning admin
rules.add_perm(
constants.SUBSCRIPTIONS_CUSTOMER_AGREEMENT_PROVISIONING_ADMIN_ACCESS_PERMISSION,
has_implicit_access_to_customer_agreement_provisioning,
)
7 changes: 7 additions & 0 deletions license_manager/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
from corsheaders.defaults import default_headers as corsheaders_default_headers

from license_manager.apps.subscriptions.constants import (
PROVISIONING_SUBSCRIPTION_ADMIN_ROLE,
PROVISIONING_CUSTOMER_AGREEMENT_ADMIN_ROLE,
SUBSCRIPTIONS_ADMIN_ROLE,
SUBSCRIPTIONS_LEARNER_ROLE,
SYSTEM_ENTERPRISE_ADMIN_ROLE,
SYSTEM_ENTERPRISE_LEARNER_ROLE,
SYSTEM_ENTERPRISE_OPERATOR_ROLE,
SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE,
)
from license_manager.settings.utils import get_logger_config

Expand Down Expand Up @@ -405,6 +408,10 @@
SYSTEM_ENTERPRISE_OPERATOR_ROLE: [SUBSCRIPTIONS_ADMIN_ROLE],
SYSTEM_ENTERPRISE_ADMIN_ROLE: [SUBSCRIPTIONS_ADMIN_ROLE],
SYSTEM_ENTERPRISE_LEARNER_ROLE: [SUBSCRIPTIONS_LEARNER_ROLE],
SYSTEM_ENTERPRISE_PROVISIONING_ADMIN_ROLE: [
PROVISIONING_SUBSCRIPTION_ADMIN_ROLE,
PROVISIONING_CUSTOMER_AGREEMENT_ADMIN_ROLE,
]
}

SOCIAL_MEDIA_FOOTER_URLS = os.environ.get('SOCIAL_MEDIA_FOOTER_URLS', '')
Expand Down
Loading