Skip to content

Commit

Permalink
Merge pull request RedHatInsights#1146 from petracihalova/destructive…
Browse files Browse the repository at this point in the history
…_operations

Set a finer control over destructive operations
  • Loading branch information
petracihalova authored Jul 24, 2024
2 parents fc7292e + a194e94 commit 853033d
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 16 deletions.
11 changes: 8 additions & 3 deletions deploy/rbac-clowdapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,10 @@ objects:
value: ${BYPASS_BOP_VERIFICATION}
- name: ROLE_CREATE_ALLOW_LIST
value: ${ROLE_CREATE_ALLOW_LIST}
- name: RBAC_DESTRUCTIVE_ENABLED_UNTIL
value: ${RBAC_DESTRUCTIVE_ENABLED_UNTIL}
- name: RBAC_DESTRUCTIVE_API_ENABLED_UNTIL
value: ${RBAC_DESTRUCTIVE_API_ENABLED_UNTIL}
- name: RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL
value: ${RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL}
- name: CLOWDER_ENABLED
value: ${CLOWDER_ENABLED}
- name: APP_NAMESPACE
Expand Down Expand Up @@ -727,7 +729,10 @@ parameters:
name: ROLE_CREATE_ALLOW_LIST
value: cost-management,remediations,inventory,drift,policies,advisor,catalog,approval,vulnerability,compliance,automation-analytics,notifications,patch,integrations,ros,staleness,config-manager,idmsvc
- description: Timestamp expiration allowance on destructive actions through the internal RBAC API
name: RBAC_DESTRUCTIVE_ENABLED_UNTIL
name: RBAC_DESTRUCTIVE_API_ENABLED_UNTIL
value: ''
- description: Timestamp expiration allowance on destructive actions through the seeding job
name: RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL
value: ''
- description: Image tag
name: IMAGE_TAG
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ services:
- PRINCIPAL_PROXY_SERVICE_PATH=${PRINCIPAL_PROXY_SERVICE_PATH}
- PRINCIPAL_PROXY_SERVICE_SOURCE_CERT=${PRINCIPAL_PROXY_SERVICE_SOURCE_CERT-False}
- PRINCIPAL_PROXY_SERVICE_SSL_VERIFY=${PRINCIPAL_PROXY_SERVICE_SSL_VERIFY-False}
- RBAC_DESTRUCTIVE_ENABLED_UNTIL=${RBAC_DESTRUCTIVE_ENABLED_UNTIL}
- RBAC_DESTRUCTIVE_API_ENABLED_UNTIL=${RBAC_DESTRUCTIVE_API_ENABLED_UNTIL}
- RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL=${RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL}
privileged: true
ports:
- 9080:8080
Expand Down
9 changes: 7 additions & 2 deletions rbac/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@
from django.conf import settings


def destructive_ok():
def destructive_ok(operation_type):
"""Determine if it's ok to run destructive operations."""
now = datetime.datetime.utcnow().replace(tzinfo=pytz.UTC)
return now < settings.INTERNAL_DESTRUCTIVE_API_OK_UNTIL
if operation_type == "api":
return now < settings.INTERNAL_DESTRUCTIVE_API_OK_UNTIL
if operation_type == "seeding":
return now < settings.DESTRUCTIVE_SEEDING_OK_UNTIL

return False
8 changes: 4 additions & 4 deletions rbac/internal/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def tenant_view(request, org_id):
"""
logger.info(f"Tenant view: {request.method} {request.user.username}")
if request.method == "DELETE":
if not destructive_ok():
if not destructive_ok("api"):
return HttpResponse("Destructive operations disallowed.", status=400)

tenant_obj = get_object_or_404(Tenant, org_id=org_id)
Expand Down Expand Up @@ -391,7 +391,7 @@ def invalid_default_admin_groups(request):
}
return HttpResponse(json.dumps(payload), content_type="application/json")
if request.method == "DELETE":
if not destructive_ok():
if not destructive_ok("api"):
return HttpResponse("Destructive operations disallowed.", status=400)
invalid_default_admin_groups_list.delete()
return HttpResponse(status=204)
Expand All @@ -405,7 +405,7 @@ def role_removal(request):
"""
logger.info(f"Role removal: {request.method} {request.user.username}")
if request.method == "DELETE":
if not destructive_ok():
if not destructive_ok("api"):
return HttpResponse("Destructive operations disallowed.", status=400)

role_name = request.GET.get("name")
Expand Down Expand Up @@ -433,7 +433,7 @@ def permission_removal(request):
"""
logger.info(f"Permission removal: {request.method} {request.user.username}")
if request.method == "DELETE":
if not destructive_ok():
if not destructive_ok("api"):
return HttpResponse("Destructive operations disallowed.", status=400)

permission = request.GET.get("permission")
Expand Down
4 changes: 2 additions & 2 deletions rbac/management/role/definer.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def seed_roles():
# Find roles in DB but not in config
roles_to_delete = Role.objects.filter(system=True).exclude(id__in=current_role_ids)
logger.info(f"The following '{roles_to_delete.count()}' roles(s) eligible for removal: {roles_to_delete.values()}")
if destructive_ok():
if destructive_ok("seeding"):
logger.info(f"Removing the following role(s): {roles_to_delete.values()}")
# Actually remove roles no longer in config
with transaction.atomic():
Expand Down Expand Up @@ -202,7 +202,7 @@ def seed_permissions():
logger.info(
f"The following '{perms_to_delete.count()}' permission(s) eligible for removal: {perms_to_delete.values()}"
)
if destructive_ok():
if destructive_ok("seeding"):
logger.info(f"Removing the following permissions(s): {perms_to_delete.values()}")
# Actually remove perms no longer in DB
with transaction.atomic():
Expand Down
13 changes: 12 additions & 1 deletion rbac/rbac/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,15 @@
GROUP_SEEDING_ENABLED = ENVIRONMENT.bool("GROUP_SEEDING_ENABLED", default=True)
MAX_SEED_THREADS = ENVIRONMENT.int("MAX_SEED_THREADS", default=None)

try:
DESTRUCTIVE_SEEDING_OK_UNTIL = parse_dt(
os.environ.get("RBAC_DESTRUCTIVE_SEEDING_ENABLED_UNTIL", "not-a-real-time")
)
if DESTRUCTIVE_SEEDING_OK_UNTIL.tzinfo is None:
DESTRUCTIVE_SEEDING_OK_UNTIL = DESTRUCTIVE_SEEDING_OK_UNTIL.replace(tzinfo=pytz.UTC)
except ValueError as e:
DESTRUCTIVE_SEEDING_OK_UNTIL = datetime.datetime(1970, 1, 1, tzinfo=pytz.UTC)

# disable log messages less than CRITICAL when running unit tests.
if len(sys.argv) > 1 and sys.argv[1] == "test":
logging.disable(logging.CRITICAL)
Expand All @@ -367,7 +376,9 @@
INTERNAL_API_PATH_PREFIXES = ["/_private/"]

try:
INTERNAL_DESTRUCTIVE_API_OK_UNTIL = parse_dt(os.environ.get("RBAC_DESTRUCTIVE_ENABLED_UNTIL", "not-a-real-time"))
INTERNAL_DESTRUCTIVE_API_OK_UNTIL = parse_dt(
os.environ.get("RBAC_DESTRUCTIVE_API_ENABLED_UNTIL", "not-a-real-time")
)
if INTERNAL_DESTRUCTIVE_API_OK_UNTIL.tzinfo is None:
INTERNAL_DESTRUCTIVE_API_OK_UNTIL = INTERNAL_DESTRUCTIVE_API_OK_UNTIL.replace(tzinfo=pytz.UTC)
except ValueError as e:
Expand Down
14 changes: 12 additions & 2 deletions tests/core/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,19 @@ def invalid_destructive_time():
@override_settings(INTERNAL_DESTRUCTIVE_API_OK_UNTIL=valid_destructive_time())
def test_destructive_ok_true(self):
"""Test that it's true when within date range."""
self.assertEqual(destructive_ok(), True)
self.assertEqual(destructive_ok("api"), True)

@override_settings(INTERNAL_DESTRUCTIVE_API_OK_UNTIL=invalid_destructive_time())
def test_destructive_ok_false(self):
"""Test that it's false when not within date range."""
self.assertEqual(destructive_ok(), False)
self.assertEqual(destructive_ok("api"), False)

@override_settings(DESTRUCTIVE_SEEDING_OK_UNTIL=valid_destructive_time())
def test_destructive_ok_true(self):
"""Test that it's true when within date range."""
self.assertEqual(destructive_ok("seeding"), True)

@override_settings(DESTRUCTIVE_SEEDING_OK_UNTIL=invalid_destructive_time())
def test_destructive_ok_false(self):
"""Test that it's false when not within date range."""
self.assertEqual(destructive_ok("seeding"), False)
2 changes: 1 addition & 1 deletion tests/internal/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ def test_delete_selective_roles_disallowed(self):
@override_settings(INTERNAL_DESTRUCTIVE_API_OK_UNTIL=valid_destructive_time())
def test_delete_selective_roles(self):
"""Test that we can delete selective roles when allowed and no roles."""
# No name speicified
# No name specified
response = self.client.delete(f"/_private/api/utils/role/", **self.request.META)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

Expand Down

0 comments on commit 853033d

Please sign in to comment.