Skip to content

Commit

Permalink
Merge branch 'RedHatInsights:master' into log_delete_group_role
Browse files Browse the repository at this point in the history
  • Loading branch information
Ellen-Yi-Dong authored Aug 7, 2024
2 parents 01d459d + 8c1e2b7 commit 8e4ed64
Show file tree
Hide file tree
Showing 14 changed files with 199 additions and 192 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,4 @@ docs/source/specs/output-dir/
dev/*
/scripts/ephemeral/config.yaml
docs/source/specs/typespec/node_modules/
docs/source/specs/typespec/tsp-output/
docs/source/specs/typespec/tsp-output/
4 changes: 2 additions & 2 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ watchtower = "==3.0.0"
boto3 = "==1.24.24"
celery = "==5.3.0b2"
redis = "==5.0.0"
relations-grpc-clients-python-kessel-project = "==0.3.1"
relations-grpc-clients-python-kessel-project = "==0.3.2"
sqlparse = "==0.5.0"
django-extensions = "==3.2.1"
python-dateutil = "==2.8.2"
ecs-logging = "==2.0.0"
app-common-python = "==0.2.5"
sentry-sdk = "==1.18.0"
sentry-sdk = "==2.8.0"
psycopg2 = "==2.9.5"
kafka-python = "==2.0.2"
psycopg2-binary = "==2.9.5"
Expand Down
191 changes: 91 additions & 100 deletions Pipfile.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions docs/source/specs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3305,7 +3305,7 @@
},
"ServiceAccount": {
"required": [
"clientID",
"clientId",
"username",
"type"
],
Expand All @@ -3320,7 +3320,7 @@
"service-account"
]
},
"clientID": {
"clientId": {
"type": "string",
"example": "fe593ba0-9c62-013c-1dc2-6aa2427b506a"
},
Expand All @@ -3345,7 +3345,7 @@
"ServiceAccountIn": {
"required": [
"type",
"clientID"
"clientId"
],
"properties": {
"type": {
Expand All @@ -3354,7 +3354,7 @@
"service-account"
]
},
"clientID": {
"clientId": {
"type": "string",
"example": "fe593ba0-9c62-013c-1dc2-6aa2427b506a"
}
Expand Down
17 changes: 13 additions & 4 deletions rbac/internal/specs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,15 @@
"modified"
]
}
},
{
"name": "system",
"in": "query",
"required": false,
"description": "Parameter for filtering resource by system flag.",
"schema": {
"type": "boolean"
}
}
],
"responses": {
Expand Down Expand Up @@ -1495,7 +1504,7 @@
},
"ServiceAccount": {
"required": [
"clientID",
"clientId",
"username",
"type"
],
Expand All @@ -1510,7 +1519,7 @@
"service-account"
]
},
"clientID": {
"clientId": {
"type": "string",
"example": "fe593ba0-9c62-013c-1dc2-6aa2427b506a"
},
Expand All @@ -1535,7 +1544,7 @@
"ServiceAccountIn": {
"required": [
"type",
"clientID"
"clientId"
],
"properties": {
"type": {
Expand All @@ -1544,7 +1553,7 @@
"service-account"
]
},
"clientID": {
"clientId": {
"type": "string",
"example": "fe593ba0-9c62-013c-1dc2-6aa2427b506a"
}
Expand Down
25 changes: 12 additions & 13 deletions rbac/management/group/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,15 @@
ROLES_KEY = "roles"
EXCLUDE_KEY = "exclude"
ORDERING_PARAM = "order_by"
NAME_KEY = "name"
PRINCIPAL_TYPE_KEY = "principal_type"
PRINCIPAL_USERNAME_KEY = "principal_username"
VALID_ROLE_ORDER_FIELDS = list(RoleViewSet.ordering_fields)
ROLE_DISCRIMINATOR_KEY = "role_discriminator"
SERVICE_ACCOUNT_CLIENT_IDS_KEY = "service_account_client_ids"
SERVICE_ACCOUNT_DESCRIPTION_KEY = "service_account_description"
SERVICE_ACCOUNT_NAME_KEY = "service_account_name"
SERVICE_ACCOUNT_USERNAME_FORMAT = "service-account-{clientID}"
SERVICE_ACCOUNT_USERNAME_FORMAT = "service-account-{clientId}"
TYPE_SERVICE_ACCOUNT = "service-account"
VALID_EXCLUDE_VALUES = ["true", "false"]
VALID_GROUP_ROLE_FILTERS = ["role_name", "role_description", "role_display_name", "role_system"]
Expand Down Expand Up @@ -444,14 +445,14 @@ def add_service_accounts(
# Organize them by their client ID.
it_service_accounts_by_client_ids: dict[str, dict] = {}
for it_sa in it_service_accounts:
it_service_accounts_by_client_ids[it_sa["clientID"]] = it_sa
it_service_accounts_by_client_ids[it_sa["clientId"]] = it_sa

# Make sure that the service accounts the user specified are visible by them.
it_sa_client_ids = it_service_accounts_by_client_ids.keys()
invalid_service_accounts: set = set()
for specified_sa in service_accounts:
if specified_sa["clientID"] not in it_sa_client_ids:
invalid_service_accounts.add(specified_sa["clientID"])
if specified_sa["clientId"] not in it_sa_client_ids:
invalid_service_accounts.add(specified_sa["clientId"])

# If we have any invalid service accounts, notify the user.
if len(invalid_service_accounts) > 0:
Expand All @@ -463,15 +464,15 @@ def add_service_accounts(
# Fetch the service account from our database to add it to the group. If it doesn't exist, we create
# it.
for specified_sa in service_accounts:
client_id = specified_sa["clientID"]
client_id = specified_sa["clientId"]
try:
principal = Principal.objects.get(
username__iexact=SERVICE_ACCOUNT_USERNAME_FORMAT.format(clientID=client_id),
username__iexact=SERVICE_ACCOUNT_USERNAME_FORMAT.format(clientId=client_id),
tenant=tenant,
)
except Principal.DoesNotExist:
principal = Principal.objects.create(
username=SERVICE_ACCOUNT_USERNAME_FORMAT.format(clientID=client_id),
username=SERVICE_ACCOUNT_USERNAME_FORMAT.format(clientId=client_id),
service_account_id=client_id,
type=TYPE_SERVICE_ACCOUNT,
tenant=tenant,
Expand All @@ -483,7 +484,7 @@ def add_service_accounts(
group_principal_change_notification_handler(
self.request.user,
group,
SERVICE_ACCOUNT_USERNAME_FORMAT.format(clientID=client_id),
SERVICE_ACCOUNT_USERNAME_FORMAT.format(clientId=client_id),
"added",
)

Expand Down Expand Up @@ -1069,10 +1070,8 @@ def obtain_roles(self, request, group):
roles = group.roles_with_access() if exclude == "false" else self.obtain_roles_with_exclusion(request, group)
filtered_roles = self.filtered_roles(roles, request)
annotated_roles = filtered_roles.annotate(policyCount=Count("policies", distinct=True))
# add default order by name
order_field = "name"
if ORDERING_PARAM in request.query_params:
order_field = request.query_params.get(ORDERING_PARAM)

order_field = request.query_params.get(ORDERING_PARAM, NAME_KEY)
ordered_roles = self.order_queryset(annotated_roles, VALID_ROLE_ORDER_FIELDS, order_field)
return [RoleMinimumSerializer(role).data for role in ordered_roles]

Expand All @@ -1093,7 +1092,7 @@ def remove_service_accounts(
request_id = getattr(self.request, "req_id", None)
logger.info(
f"[Request_id: {request_id}] remove_service_accounts({service_accounts}),"
"Group:{group.name},OrgId:{org_id},Acct:{account_name}"
f"Group:{group.name},OrgId:{org_id},Acct:{user.account}"
)

# Fetch the tenant from the database.
Expand Down
6 changes: 3 additions & 3 deletions rbac/management/principal/it_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def _is_service_account_valid(self, user: User, client_id: str) -> bool:
service_accounts: list[dict] = self.request_service_accounts(bearer_token=user.bearer_token)

for sa in service_accounts:
if client_id == sa.get("clientID"):
if client_id == sa.get("clientId"):
return True

return False
Expand Down Expand Up @@ -451,7 +451,7 @@ def _transform_incoming_payload(self, service_account_from_it_service: dict) ->
created_at = service_account_from_it_service.get("createdAt")

if client_id:
service_account["clientID"] = client_id
service_account["clientId"] = client_id

if name:
service_account["name"] = name
Expand Down Expand Up @@ -484,7 +484,7 @@ def _merge_principals_it_service_accounts(

for it_service_account in it_service_accounts:
try:
sa_principal = service_account_principals[it_service_account["clientID"]]
sa_principal = service_account_principals[it_service_account["clientId"]]

if username_only and username_only == "true":
service_accounts.append({"username": sa_principal.username}) # type: ignore
Expand Down
10 changes: 5 additions & 5 deletions rbac/management/principal/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ class PrincipalInputSerializer(serializers.Serializer):
"""Serializer for the Principal model."""

username = serializers.CharField(required=False, max_length=150)
clientID = serializers.UUIDField(required=False, source="service_account_id")
clientId = serializers.UUIDField(required=False, source="service_account_id")
type = serializers.CharField(required=False)

def validate(self, data: OrderedDict):
"""
Assert that the correct fields are specified.
Assert that when the specified type is 'service-account', the corresponding 'clientID' field
Assert that when the specified type is 'service-account', the corresponding 'clientId' field
has been specified.
"""
# If the "type" has not been specified, we assume it is a user principal.
Expand All @@ -57,7 +57,7 @@ def validate(self, data: OrderedDict):
return data
elif data["type"] == "service-account":
if "service_account_id" not in data:
raise ValidationError(code="missing", message="the clientID field is required for service accounts")
raise ValidationError(code="missing", message="the clientId field is required for service accounts")

return data
else:
Expand All @@ -68,13 +68,13 @@ def validate(self, data: OrderedDict):
class Meta:
"""Metadata for the serializer."""

fields = ("username", "clientID", "type")
fields = ("username", "clientId", "type")


class ServiceAccountSerializer(serializers.Serializer):
"""Serializer for Service Account."""

clientID = serializers.UUIDField()
clientId = serializers.UUIDField()
name = serializers.CharField()
description = serializers.CharField(allow_null=True, required=False)
owner = serializers.CharField()
Expand Down
7 changes: 4 additions & 3 deletions rbac/management/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
policies_for_principal,
queryset_by_id,
roles_for_principal,
validate_and_get_key,
)
from rest_framework import permissions, serializers
from rest_framework.request import Request
Expand Down Expand Up @@ -97,7 +98,7 @@ def _gather_group_querysets(request, args, kwargs):
"""Decide which groups to provide for request."""
username = request.query_params.get("username")

scope = request.query_params.get(SCOPE_KEY, ORG_ID_SCOPE)
scope = validate_and_get_key(request.query_params, SCOPE_KEY, VALID_SCOPES, ORG_ID_SCOPE)
if scope != ORG_ID_SCOPE and not username:
return get_object_principal_queryset(request, scope, Group)

Expand Down Expand Up @@ -149,7 +150,7 @@ def annotate_roles_with_counts(queryset):

def get_role_queryset(request) -> QuerySet:
"""Obtain the queryset for roles."""
scope = request.query_params.get(SCOPE_KEY, ORG_ID_SCOPE)
scope = validate_and_get_key(request.query_params, SCOPE_KEY, VALID_SCOPES, ORG_ID_SCOPE)
public_tenant = Tenant.objects.get(tenant_name="public")
base_query = annotate_roles_with_counts(Role.objects.prefetch_related("access")).filter(
tenant__in=[request.tenant, public_tenant]
Expand Down Expand Up @@ -213,7 +214,7 @@ def get_role_queryset(request) -> QuerySet:

def get_policy_queryset(request):
"""Obtain the queryset for policies."""
scope = request.query_params.get(SCOPE_KEY, ORG_ID_SCOPE)
scope = validate_and_get_key(request.query_params, SCOPE_KEY, VALID_SCOPES, ORG_ID_SCOPE)
if scope != ORG_ID_SCOPE:
return get_object_principal_queryset(request, scope, Policy)

Expand Down
6 changes: 3 additions & 3 deletions rbac/management/role/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
from management.group.model import Group
from management.notifications.notification_handlers import role_obj_change_notification_handler
from management.serializer_override_mixin import SerializerCreateOverrideMixin
from management.utils import filter_queryset_by_tenant, get_principal
from management.utils import filter_queryset_by_tenant, get_principal, validate_and_get_key
from rest_framework import serializers

from api.models import Tenant
from .model import Access, Permission, ResourceDefinition, Role
from ..querysets import PRINCIPAL_SCOPE
from ..querysets import ORG_ID_SCOPE, PRINCIPAL_SCOPE, SCOPE_KEY, VALID_SCOPES

ALLOWED_OPERATIONS = ["in", "equal"]
FILTER_FIELDS = {"key", "value", "operation"}
Expand Down Expand Up @@ -329,7 +329,7 @@ def obtain_applications(obj):

def obtain_groups_in(obj, request):
"""Shared function to get the groups the roles is in."""
scope_param = request.query_params.get("scope")
scope_param = validate_and_get_key(request.query_params, SCOPE_KEY, VALID_SCOPES, ORG_ID_SCOPE)
username_param = request.query_params.get("username")
policy_ids = list(obj.policies.values_list("id", flat=True))

Expand Down
23 changes: 15 additions & 8 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
amqp==5.2.0; python_version >= '3.6'
app-common-python==0.2.5
asgiref==3.8.1; python_version >= '3.8'
astunparse==1.6.3
async-timeout==4.0.3; python_full_version <= '3.11.2'
billiard==4.2.0; python_version >= '3.7'
boto3==1.24.24; python_version >= '3.7'
Expand All @@ -14,7 +15,7 @@ click==8.1.7; python_version >= '3.7'
click-didyoumean==0.3.1; python_full_version >= '3.6.2'
click-plugins==1.1.1
click-repl==0.3.0; python_version >= '3.6'
cryptography==42.0.8; python_version >= '3.7'
cryptography==43.0.0; python_version >= '3.7'
django==4.2.14; python_version >= '3.8'
django-cors-headers==3.13.0; python_version >= '3.7'
django-environ==0.10.0; python_version >= '3.5' and python_version < '4'
Expand All @@ -25,39 +26,45 @@ django-tenants==3.5.0
djangorestframework==3.15.2; python_version >= '3.8'
djangorestframework-csv==2.1.1
ecs-logging==2.0.0; python_version >= '3.6'
grpcio==1.64.1
grpcio-status==1.64.1
googleapis-common-protos==1.63.2; python_version >= '3.7'
grpcio==1.64.1; python_version >= '3.8'
grpcio-status==1.64.1; python_version >= '3.8'
grpcio-tools==1.64.1; python_version >= '3.8'
gunicorn==22.0.0; python_version >= '3.7'
idna==3.7; python_version >= '3.5'
jinja2==3.1.4; python_version >= '3.7'
jmespath==1.0.1; python_version >= '3.7'
joserfc==0.8.0; python_version >= '3.8'
kafka-python==2.0.2
kombu==5.3.7; python_version >= '3.8'
kombu==5.4.0rc3; python_version >= '3.8'
markupsafe==2.1.5; python_version >= '3.7'
packaging==24.1; python_version >= '3.8'
prometheus-client==0.15.0; python_version >= '3.6'
prompt-toolkit==3.0.47; python_full_version >= '3.7.0'
protoc-gen-validate==1.0.4
protobuf==5.28.0rc1; python_version >= '3.8'
protoc-gen-validate==1.0.4; python_version >= '3.6'
psycopg2==2.9.5; python_version >= '3.6'
psycopg2-binary==2.9.5; python_version >= '3.6'
pycparser==2.22; python_version >= '3.8'
python-dateutil==2.8.2; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
pytz==2022.2.1
redis==5.0.0; python_version >= '3.7'
relations-grpc-clients-python-kessel-project==0.3.1
relations-grpc-clients-python-kessel-project==0.3.2; python_version >= '3.9'
requests==2.32.3; python_version >= '3.8'
s3transfer==0.6.2; python_version >= '3.7'
sentry-sdk==1.18.0
sentry-sdk==2.8.0; python_version >= '3.6'
setuptools==72.1.0; python_version >= '3.8'
six==1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'
sqlparse==0.5.0; python_version >= '3.8'
stompest==2.3.0
typing-extensions==4.12.2; python_version < '3.11'
typing-extensions==4.12.1; python_version < '3.11'
tzdata==2022.2; python_version >= '2'
unicodecsv==0.14.1
urllib3==1.26.19; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'
validate-email==1.3
vine==5.1.0; python_version >= '3.6'
watchtower==3.0.0; python_version >= '3.6'
wcwidth==0.2.13
wheel==0.44.0; python_version >= '3.8'
whitenoise==6.4.0; python_version >= '3.7'
xmltodict==0.13.0; python_version >= '3.4'
Loading

0 comments on commit 8e4ed64

Please sign in to comment.