Skip to content

Commit

Permalink
Merge branch 'master' into konflux/references/master
Browse files Browse the repository at this point in the history
  • Loading branch information
Ellen-Yi-Dong authored Oct 15, 2024
2 parents 22144b4 + 3b6c724 commit ddfaf9e
Show file tree
Hide file tree
Showing 29 changed files with 1,781 additions and 281 deletions.
19 changes: 18 additions & 1 deletion deploy/rbac-clowdapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ objects:
value: ${PRINCIPAL_USER_DOMAIN}
- name: PRINCIPAL_CLEANUP_DELETION_ENABLED_UMB
value: ${PRINCIPAL_CLEANUP_DELETION_ENABLED_UMB}
- name: PRINCIPAL_CLEANUP_UPDATE_ENABLED_UMB
value: ${PRINCIPAL_CLEANUP_UPDATE_ENABLED_UMB}
- name: UMB_JOB_ENABLED
value: ${UMB_JOB_ENABLED}

Expand Down Expand Up @@ -470,6 +472,10 @@ objects:
value: ${V2_MIGRATION_APP_EXCLUDE_LIST}
- name: V2_MIGRATION_RESOURCE_EXCLUDE_LIST
value: ${V2_MIGRATION_RESOURCE_EXCLUDE_LIST}
- name: V2_BOOTSTRAP_TENANT
value: ${V2_BOOTSTRAP_TENANT}
- name: V1_BOOTSTRAP_ADD_USER_ID
value: ${V1_BOOTSTRAP_ADD_USER_ID}
- name: RBAC_DESTRUCTIVE_API_ENABLED_UNTIL
value: ${RBAC_DESTRUCTIVE_API_ENABLED_UNTIL}
- name: RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL
Expand Down Expand Up @@ -754,6 +760,14 @@ parameters:
- description: Resources (by namespace:name) exclude list for v2 migration (resource definitions only)
name: V2_MIGRATION_RESOURCE_EXCLUDE_LIST
value: rbac:workspace
- description: Whether or not to bootstrap tenants using V2 logic (i.e. creating workspaces and relations).
name: V2_BOOTSTRAP_TENANT
value: 'False'
- description: |
Whether or not to add user IDs to existing principals within V1 logic. Once V2 tenant bootstrapping is used,
this flag is ignored and user IDs will be added regardless.
name: V1_BOOTSTRAP_ADD_USER_ID
value: rbac:workspace
- description: Timestamp expiration allowance on destructive actions through the internal RBAC API
name: RBAC_DESTRUCTIVE_API_ENABLED_UNTIL
value: ''
Expand Down Expand Up @@ -921,10 +935,13 @@ parameters:
RBAC currently expects all principals to either come from itself (cross-account),
or from a single identity infrastructure domain (identity header, SSO, BOP).
This defines that single domain.
value: 'redhat.com'
value: 'redhat'
- name: PRINCIPAL_CLEANUP_DELETION_ENABLED_UMB
description: Allow cleanup job to delete principals via messages from UMB
value: 'False'
- name: PRINCIPAL_CLEANUP_UPDATE_ENABLED_UMB
description: Allow cleanup job to update principals via messages from UMB
value: 'False'
- name: UMB_JOB_ENABLE
description: Temp env to enable the UMB job
value: 'True'
Expand Down
16 changes: 9 additions & 7 deletions rbac/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
"""API models for import organization."""
from typing import Optional

from django.db import models
from django.db.models import Q

Expand Down Expand Up @@ -60,14 +62,14 @@ class Meta:
class User:
"""A request User. Might also represent a service account."""

username = None
account = None
admin = False
username: Optional[str] = None
account: Optional[str] = None
admin: bool = False
access = {}
system = False
is_active = True
org_id = None
user_id = None
system: bool = False
is_active: bool = True
org_id: Optional[str] = None
user_id: Optional[str] = None
# Service account properties.
bearer_token: str = ""
client_id: str = ""
Expand Down
25 changes: 18 additions & 7 deletions rbac/management/group/definer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@

"""Handler for system defined group."""
import logging
from typing import Union
from typing import Optional, Tuple, Union
from uuid import uuid4

from django.conf import settings
from django.db import transaction
from django.db.models.query import QuerySet
from django.utils.translation import gettext as _
Expand All @@ -43,7 +44,7 @@
logger = logging.getLogger(__name__) # pylint: disable=invalid-name


def seed_group():
def seed_group() -> Tuple[Group, Group]:
"""Create or update default group."""
public_tenant = Tenant.objects.get(tenant_name="public")
with transaction.atomic():
Expand Down Expand Up @@ -78,30 +79,40 @@ def seed_group():
update_group_roles(admin_group, admin_roles, public_tenant)
logger.info("Finished seeding default org admin group %s.", name)

return group, admin_group

def set_system_flag_before_update(group, tenant, user):

def set_system_flag_before_update(group: Group, tenant, user) -> Optional[Group]:
"""Update system flag on default groups."""
if group.system:
group = clone_default_group_in_public_schema(group, tenant)
group = clone_default_group_in_public_schema(group, tenant) # type: ignore
group_flag_change_notification_handler(user, group)
return group


def clone_default_group_in_public_schema(group, tenant):
def clone_default_group_in_public_schema(group, tenant) -> Optional[Group]:
"""Clone the default group for a tenant into the public schema."""
if settings.PRINCIPAL_CLEANUP_UPDATE_ENABLED_UMB:
# TODO: bootstrap the tenant to get the mapping
# use this for uuid instead and to remove the default role binding tuple
group_uuid = uuid4()
else:
group_uuid = uuid4()

public_tenant = Tenant.objects.get(tenant_name="public")
tenant_default_policy = group.policies.get(system=True)
group.name = "Custom default access"
group.system = False
group.tenant = tenant
group.uuid = uuid4()
group.uuid = group_uuid
clear_pk(group)
clear_pk(tenant_default_policy)
tenant_default_policy.uuid = uuid4()
tenant_default_policy.name = "System Policy for Group {}".format(group.uuid)
tenant_default_policy.tenant = tenant
if Group.objects.filter(name=group.name, platform_default=group.platform_default, tenant=tenant):
return
# TODO: returning none can break other code
return None
public_default_roles = Role.objects.filter(platform_default=True, tenant=public_tenant)

group.save()
Expand Down
30 changes: 29 additions & 1 deletion rbac/management/group/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

"""Model for group management."""
import logging
from typing import Optional, Union
from uuid import uuid4

from django.conf import settings
Expand All @@ -25,12 +26,14 @@
from django.utils import timezone
from internal.integration import chrome_handlers
from internal.integration import sync_handlers
from kessel.relations.v1beta1.common_pb2 import Relationship
from management.cache import AccessCache
from management.principal.model import Principal
from management.rbac_fields import AutoDateTimeField
from management.role.model import Role
from migration_tool.utils import create_relationship

from api.models import TenantAwareModel
from api.models import TenantAwareModel, User


logger = logging.getLogger(__name__) # pylint: disable=invalid-name
Expand All @@ -49,6 +52,31 @@ class Group(TenantAwareModel):
system = models.BooleanField(default=False)
admin_default = models.BooleanField(default=False)

@staticmethod
def relationship_to_principal_for_group(
group: "Group", principal: Union[Principal, User]
) -> Optional[Relationship]:
"""Create a relationship between a group and a principal given a Principal or User."""
if isinstance(principal, Principal):
id = principal.principal_resource_id()
if id is None:
return None
elif (user_id := principal.user_id) is not None:
id = Principal.user_id_to_principal_resource_id(user_id)
else:
return None
return create_relationship(("rbac", "group"), str(group.uuid), ("rbac", "principal"), id, "member")

@staticmethod
def relationship_to_user_id_for_group(group_uuid: str, user_id: str) -> Relationship:
"""Create a relationship between a group and a user ID."""
id = Principal.user_id_to_principal_resource_id(user_id)
return create_relationship(("rbac", "group"), group_uuid, ("rbac", "principal"), id, "member")

def relationship_to_principal(self, principal: Union[Principal, User]) -> Optional[Relationship]:
"""Create a relationship between a group and a principal given a Principal or User."""
return Group.relationship_to_principal_for_group(self, principal)

def roles(self):
"""Roles for a group."""
return Role.objects.filter(policies__in=self.__policy_ids()).distinct()
Expand Down
17 changes: 8 additions & 9 deletions rbac/management/group/relation_api_dual_write_group_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from uuid import uuid4

from django.conf import settings
from management.group.model import Group
from management.models import Workspace
from management.principal.model import Principal
from management.role.model import BindingMapping, Role
Expand All @@ -32,7 +33,6 @@
ReplicationEventType,
)
from migration_tool.models import V2boundresource, V2role, V2rolebinding
from migration_tool.utils import create_relationship

from api.models import Tenant

Expand All @@ -42,6 +42,8 @@
class RelationApiDualWriteGroupHandler:
"""Class to handle Dual Write API related operations."""

group: Group

def __init__(
self,
group,
Expand All @@ -60,7 +62,7 @@ def __init__(
self.default_workspace = Workspace.objects.get(tenant=self.tenant, type=Workspace.Types.DEFAULT)
self.event_type = event_type
self.user_domain = settings.PRINCIPAL_USER_DOMAIN
self._replicator = replicator if replicator else OutboxReplicator(group)
self._replicator = replicator if replicator else OutboxReplicator()
except Exception as e:
raise DualWriteException(e)

Expand All @@ -72,17 +74,13 @@ def _generate_member_relations(self):
"""Generate user-groups relations."""
relations = []
for principal in self.principals:
if principal.user_id is None:
relationship = self.group.relationship_to_principal(principal)
if relationship is None:
logger.warning(
"[Dual Write] Principal(uuid=%s) does not have user_id. Skipping replication.", principal.uuid
)
continue
principal_id = f"{self.user_domain}:{principal.user_id}"
relations.append(
create_relationship(
("rbac", "group"), str(self.group.uuid), ("rbac", "principal"), principal_id, "member"
)
)
relations.append(relationship)

return relations

Expand Down Expand Up @@ -112,6 +110,7 @@ def _replicate(self):
self._replicator.replicate(
ReplicationEvent(
event_type=self.event_type,
info={"group_uuid": str(self.group.uuid)},
# TODO: need to think about partitioning
# Maybe resource id
partition_key="rbactodo",
Expand Down
50 changes: 50 additions & 0 deletions rbac/management/migrations/0055_tenantmapping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Generated by Django 4.2.15 on 2024-10-09 16:20

from django.db import migrations, models
import django.db.models.deletion
import uuid


class Migration(migrations.Migration):

dependencies = [
("api", "0014_auto_20220726_1743"),
("management", "0054_bindingmapping"),
]

operations = [
migrations.CreateModel(
name="TenantMapping",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"default_group_uuid",
models.UUIDField(default=uuid.uuid4, editable=False),
),
(
"default_admin_group_uuid",
models.UUIDField(default=uuid.uuid4, editable=False),
),
(
"default_role_binding_uuid",
models.UUIDField(default=uuid.uuid4, editable=False),
),
(
"default_admin_role_binding_uuid",
models.UUIDField(default=uuid.uuid4, editable=False),
),
(
"tenant",
models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to="api.tenant"),
),
],
),
]
Loading

0 comments on commit ddfaf9e

Please sign in to comment.