diff --git a/rbac/internal/integration_views.py b/rbac/internal/integration_views.py new file mode 100644 index 000000000..0381963ea --- /dev/null +++ b/rbac/internal/integration_views.py @@ -0,0 +1,61 @@ +# +# Copyright 2020 Red Hat, Inc. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +"""View for OCM group/role API.""" +from audioop import reverse +import datetime +import json +import logging + +import pytz +from django.conf import settings +from django.db import transaction +from django.db.migrations.recorder import MigrationRecorder +from django.http import Http404, HttpResponse +from django.shortcuts import redirect, reverse +from management import views +from management.cache import TenantCache +from management.models import Group, Role + + +from api.models import Tenant + + +logger = logging.getLogger(__name__) +TENANTS = TenantCache() + +def groups(request, account_number): + username = request.GET.get("username") + if username: + base_url = reverse("group-list") + url = f'{base_url}?principals={username}' + return redirect(url) + else: + return Http404 + +def groups_for_principal(request, account_number, username): + base_url = reverse("group-list") + url = f'{base_url}?principals={username}' + return redirect(url) + +def roles_from_group(request, account_number, uuid): + return redirect("group-roles", uuid=uuid) + +def roles_for_group(request, account_number, username, uuid): + base_url = reverse("group-roles", kwargs={'uuid': uuid}) + url = f'{base_url}?principals={username}' + return redirect(url) \ No newline at end of file diff --git a/rbac/internal/middleware.py b/rbac/internal/middleware.py index 29e77ec02..663f1def3 100644 --- a/rbac/internal/middleware.py +++ b/rbac/internal/middleware.py @@ -22,11 +22,14 @@ from django.conf import settings from django.http import HttpResponseForbidden +from django.shortcuts import get_object_or_404 +from django.urls import resolve from django.utils.deprecation import MiddlewareMixin from api.common import RH_IDENTITY_HEADER -from api.models import User +from api.models import User, Tenant from api.serializers import extract_header +from rbac.middleware import IdentityHeaderMiddleware logger = logging.getLogger(__name__) @@ -58,8 +61,14 @@ def process_request(self, request): logger.error("Malformed X-RH-Identity header.") return HttpResponseForbidden() + if "integration" in resolve(request.path).url_name: + return IdentityHeaderMiddleware.process_request(self, request) + request.user = user def process_response(self, request, response): """Process responses for internal identity middleware.""" return response + + def get_tenant(self, request): + request.tenant = get_object_or_404(Tenant, tenant_name=self.tenant_re.match(request.path_info).group('tenant_id')) \ No newline at end of file diff --git a/rbac/internal/urls.py b/rbac/internal/urls.py index 496b89cf5..4a9006bbd 100644 --- a/rbac/internal/urls.py +++ b/rbac/internal/urls.py @@ -19,13 +19,17 @@ from django.urls import path -from . import views +from . import integration_views, views from .views import trigger_error urlpatterns = [ path("api/tenant/unmodified/", views.list_unmodified_tenants), path("api/tenant/", views.list_tenants), path("api/tenant//", views.tenant_view), + path("api/tenant//groups/", integration_views.groups, name="integration-groups"), + path("api/tenant//groups//roles/", integration_views.roles_from_group, name="integration-group-roles"), + path("api/tenant//principal//groups/", integration_views.groups_for_principal, name="integration-princ-groups"), + path("api/tenant//principal//groups//roles/", integration_views.roles_for_group, name="integration-princ-roles"), path("api/migrations/run/", views.run_migrations), path("api/migrations/progress/", views.migration_progress), path("api/seeds/run/", views.run_seeds), diff --git a/rbac/management/group/view.py b/rbac/management/group/view.py index d2de3e876..d18b47c61 100644 --- a/rbac/management/group/view.py +++ b/rbac/management/group/view.py @@ -96,16 +96,31 @@ def roles_filter(self, queryset, field, values): queryset = queryset.filter(policies__roles__name__icontains=role_name) return queryset + def principal_filter(self, queryset, field, values): + """Filter for groups containing principals.""" + if not values: + key = "groups_filter" + message = "No principals provided to filter groups!" + error = {key: [_(message)]} + raise serializers.ValidationError(error) + principals = [value.lower() for value in values.split(",")] + + for principal in principals: + queryset = queryset.filter(principals__username__icontains=principal) + + return queryset + name = filters.CharFilter(field_name="name", method="name_filter") role_names = filters.CharFilter(field_name="role_names", method="roles_filter") uuid = filters.CharFilter(field_name="uuid", method="uuid_filter") + principals = filters.CharFilter(field_name="principals", method="principal_filter") system = filters.BooleanFilter(field_name="system") platform_default = filters.BooleanFilter(field_name="platform_default") admin_default = filters.BooleanFilter(field_name="admin_default") class Meta: model = Group - fields = ["name", "role_names", "uuid"] + fields = ["name", "role_names", "uuid", "principals"] class GroupViewSet( diff --git a/rbac/rbac/dev_middleware.py b/rbac/rbac/dev_middleware.py index 6c4e33692..80125d30d 100644 --- a/rbac/rbac/dev_middleware.py +++ b/rbac/rbac/dev_middleware.py @@ -41,8 +41,8 @@ def process_request(self, request): # pylint: disable=no-self-use "identity": { "account_number": "10001", "org_id": "11111", - "type": "User", - "user": { + "type": "Associate", + "associate": { "username": "user_dev", "email": "user_dev@foo.com", "is_org_admin": True, diff --git a/rbac/rbac/middleware.py b/rbac/rbac/middleware.py index 65fa04f9a..d9c58d38a 100644 --- a/rbac/rbac/middleware.py +++ b/rbac/rbac/middleware.py @@ -74,7 +74,7 @@ class IdentityHeaderMiddleware(MiddlewareMixin): header = RH_IDENTITY_HEADER - def get_tenant(self, model, hostname, request): + def get_tenant(self, request): """Override the tenant selection logic.""" if settings.AUTHENTICATE_WITH_ORG_ID: tenant_name = create_tenant_name(request.user.account) @@ -184,7 +184,11 @@ def process_request(self, request): # pylint: disable=R1710 _, json_rh_auth = extract_header(request, self.header) user.account = json_rh_auth.get("identity", {})["account_number"] user.org_id = json_rh_auth.get("identity", {}).get("org_id") - user_info = json_rh_auth.get("identity", {}).get("user", {}) + usertype = json_rh_auth.get("identity", {}).get("type") + if usertype.lower() == "associate": + user_info = json_rh_auth.get("identity", {}).get("associate", {}) + else: + user_info = json_rh_auth.get("identity", {}).get("user", {}) user.username = user_info["username"] user.admin = user_info.get("is_org_admin") user.internal = user_info.get("is_internal") @@ -239,7 +243,7 @@ def process_request(self, request): # pylint: disable=R1710 raise error if user.username and (user.account or user.org_id): request.user = user - request.tenant = self.get_tenant(model=None, hostname=None, request=request) + request.tenant = self.get_tenant(request=request) def process_response(self, request, response): # pylint: disable=no-self-use """Process response for identity middleware.