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

Ca 210 fix access control model in api #67

Merged
merged 52 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
0a89b03
Allow static files in development environments
nas-tabchiche Feb 22, 2024
fd28045
Merge branch 'main' into CA-210-fix-access-control-model-in-api
nas-tabchiche Feb 22, 2024
0b85bf1
Add library related permissions
nas-tabchiche Feb 22, 2024
3adf310
fix RBACPermissions.has_permission
nas-tabchiche Feb 22, 2024
aa5435c
Update Folder.get_folder method
nas-tabchiche Feb 22, 2024
b2d9119
Fix collection-level API permissions
nas-tabchiche Feb 22, 2024
a4aee25
Fix folder-level create permissions
nas-tabchiche Feb 22, 2024
1b8417e
Update API tests
nas-tabchiche Feb 22, 2024
240d5cb
Fix logout permission classes
nas-tabchiche Feb 22, 2024
948d429
Enforce library related permissions
nas-tabchiche Feb 22, 2024
7deb954
Allow static files in development environments
nas-tabchiche Feb 22, 2024
6b70e47
Add library related permissions
nas-tabchiche Feb 22, 2024
23df0dd
fix RBACPermissions.has_permission
nas-tabchiche Feb 22, 2024
847e786
Update Folder.get_folder method
nas-tabchiche Feb 22, 2024
907debc
Fix collection-level API permissions
nas-tabchiche Feb 22, 2024
21b0940
Fix folder-level create permissions
nas-tabchiche Feb 22, 2024
156d2e5
Update API tests
nas-tabchiche Feb 22, 2024
5d3c57c
Fix logout permission classes
nas-tabchiche Feb 22, 2024
241efe2
Enforce library related permissions
nas-tabchiche Feb 22, 2024
8437b27
simplify IAM model
eric-intuitem Feb 22, 2024
c9420c5
try fix pytest (wip)
eric-intuitem Feb 23, 2024
5c75d2a
Merge branch 'CA-210-fix-access-control-model-in-api' of github.com:i…
nas-tabchiche Feb 23, 2024
602924d
Fix regression in assessment forms
nas-tabchiche Feb 23, 2024
b983ba1
Merge pull request #68 from intuitem/hotfix/assessment-creation-regre…
eric-intuitem Feb 23, 2024
a3991a5
Allow static files in development environments
nas-tabchiche Feb 22, 2024
a55afd5
Add library related permissions
nas-tabchiche Feb 22, 2024
73540e6
fix RBACPermissions.has_permission
nas-tabchiche Feb 22, 2024
3c6638b
Update Folder.get_folder method
nas-tabchiche Feb 22, 2024
cc1bbb6
Fix collection-level API permissions
nas-tabchiche Feb 22, 2024
8765190
Fix folder-level create permissions
nas-tabchiche Feb 22, 2024
26dd0d5
Update API tests
nas-tabchiche Feb 22, 2024
cd3146f
Fix logout permission classes
nas-tabchiche Feb 22, 2024
3bcc9e1
Enforce library related permissions
nas-tabchiche Feb 22, 2024
f48e52d
Add library related permissions
nas-tabchiche Feb 22, 2024
a1ba64a
fix RBACPermissions.has_permission
nas-tabchiche Feb 22, 2024
0dec96b
Fix collection-level API permissions
nas-tabchiche Feb 22, 2024
dc795d5
Fix folder-level create permissions
nas-tabchiche Feb 22, 2024
db04938
simplify IAM model
eric-intuitem Feb 22, 2024
60d812d
try fix pytest (wip)
eric-intuitem Feb 23, 2024
83aac98
fix pytest
eric-intuitem Feb 23, 2024
8f893bb
Merge branch 'CA-210-fix-access-control-model-in-api' of https://gith…
eric-intuitem Feb 23, 2024
465a9ed
rationalize library permissions
eric-intuitem Feb 23, 2024
5cc0d74
Update API tests
nas-tabchiche Feb 22, 2024
e2864a1
Enforce library related permissions
nas-tabchiche Feb 22, 2024
082b04f
fix RBACPermissions.has_permission
nas-tabchiche Feb 22, 2024
9614fa8
simplify IAM model
eric-intuitem Feb 22, 2024
1851b34
try fix pytest (wip)
eric-intuitem Feb 23, 2024
5ee2aee
rationalize library permissions
eric-intuitem Feb 23, 2024
4124df1
fix call to is_access_allowed
eric-intuitem Feb 23, 2024
a0630b8
Merge branch 'CA-210-fix-access-control-model-in-api' of https://gith…
eric-intuitem Feb 23, 2024
9e541e6
Fix library read error
eric-intuitem Feb 23, 2024
b83c4fc
cleaning
eric-intuitem Feb 23, 2024
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
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ __pycache__
*.DS_Store
*~$*
staticfiles/*
static/
*.mo
.env
.vscode
Expand Down
7 changes: 7 additions & 0 deletions backend/app_tests/api/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,13 @@ def create_object(
# Uses the API endpoint to create an object with authentication
response = authenticated_client.post(url, build_params, format=query_format)

if fails:
# Asserts that the object was not created
assert (
response.status_code == expected_status
), f"{verbose_name} can not be created with authentication"
return

# Asserts that the object was created successfully
assert (
response.status_code == expected_status
Expand Down
27 changes: 22 additions & 5 deletions backend/app_tests/api/test_api_requirement_assessments.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import pytest
from rest_framework.status import HTTP_400_BAD_REQUEST
from rest_framework.test import APIClient
from core.models import (
ComplianceAssessment,
Expand Down Expand Up @@ -41,7 +42,9 @@ def test_get_requirement_assessments(self, authenticated_client):
project=Project.objects.create(name="test", folder=folder),
framework=Framework.objects.all()[0],
),
"requirement": RequirementNode.objects.create(name="test", folder=folder, assessable=False),
"requirement": RequirementNode.objects.create(
name="test", folder=folder, assessable=False
),
},
)

Expand Down Expand Up @@ -76,7 +79,9 @@ def test_update_requirement_assessments(self, authenticated_client):
project=Project.objects.create(name="test", folder=folder),
framework=Framework.objects.all()[0],
),
"requirement": RequirementNode.objects.create(name="test", folder=folder, assessable=False),
"requirement": RequirementNode.objects.create(
name="test", folder=folder, assessable=False
),
},
{
"status": REQUIREMENT_ASSESSMENT_STATUS2,
Expand Down Expand Up @@ -113,14 +118,18 @@ def test_get_requirement_assessments(self, authenticated_client):
},
{
"folder": str(folder.id),
"compliance_assessment": {"id": str(compliance_assessment.id), "str": compliance_assessment.name},
"compliance_assessment": {
"id": str(compliance_assessment.id),
"str": compliance_assessment.name,
},
"requirement": str(RequirementNode.objects.all()[0].id),
},
-1,
)

def test_create_requirement_assessments(self, authenticated_client):
"""test to create requirement assessments with the API with authentication"""
"""nobody has permission to do that, so it will fail"""

EndpointTestsQueries.Auth.import_object(authenticated_client, "Framework")
folder = Folder.objects.create(name="test")
Expand All @@ -144,9 +153,14 @@ def test_create_requirement_assessments(self, authenticated_client):
"security_measures": [str(security_measure.id)],
},
{
"compliance_assessment": {"id": str(compliance_assessment.id), "str": compliance_assessment.name}
"compliance_assessment": {
"id": str(compliance_assessment.id),
"str": compliance_assessment.name,
}
},
base_count=-1,
fails=True,
expected_status=HTTP_400_BAD_REQUEST
)

def test_update_requirement_assessments(self, authenticated_client):
Expand Down Expand Up @@ -189,7 +203,10 @@ def test_update_requirement_assessments(self, authenticated_client):
},
{
"folder": str(Folder.get_root_folder().id),
"compliance_assessment": {"id": str(compliance_assessment.id), "str": compliance_assessment.name},
"compliance_assessment": {
"id": str(compliance_assessment.id),
"str": compliance_assessment.name,
},
"requirement": str(RequirementNode.objects.all()[0].id),
},
)
Expand Down
2 changes: 1 addition & 1 deletion backend/cal/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.2 on 2024-02-10 02:00
# Generated by Django 5.0.2 on 2024-02-23 00:51

from django.db import migrations, models

Expand Down
12 changes: 8 additions & 4 deletions backend/ciso_assistant/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,14 @@ def set_ciso_assistant_url(_, __, event_dict):
"PAGE_SIZE": PAGINATE_BY,
}

# templates are still used to send email
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
"rest_framework.renderers.BrowsableAPIRenderer"
)
if DEBUG:
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
"rest_framework.renderers.BrowsableAPIRenderer"
)
INSTALLED_APPS.append("django.contrib.staticfiles")
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "static"

TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
Expand Down
55 changes: 28 additions & 27 deletions backend/core/apps.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
from ciso_assistant.settings import CISO_ASSISTANT_SUPERUSER_EMAIL


def startup():
"""Implement CISO Assistant 1.0 default Roles and User Groups"""
def startup(**kwargs):
"""
Implement CISO Assistant 1.0 default Roles and User Groups during migrate
This makes sure root folder and global groups are defined before any other object is created
Create superuser if CISO_ASSISTANT_SUPERUSER_EMAIL defined
"""

from ciso_assistant.settings import (
CISO_ASSISTANT_SUPERUSER_EMAIL,
)
from django.contrib.auth.models import Permission
from iam.models import Folder, Role, RoleAssignment, User, UserGroup
print("post-migrate handler: initialize database")

auditor_permissions = Permission.objects.filter(
codename__in=[
Expand All @@ -28,6 +32,7 @@ def startup():
"view_requirement",
"view_evidence",
"view_framework",
"view_library",
]
)

Expand All @@ -51,6 +56,7 @@ def startup():
"view_requirement",
"view_evidence",
"view_framework",
"view_library",
]
)

Expand Down Expand Up @@ -98,6 +104,7 @@ def startup():
"view_riskmatrix",
"view_requirement",
"view_framework",
"view_library",
]
)

Expand Down Expand Up @@ -150,6 +157,7 @@ def startup():
"delete_evidence",
"view_requirement",
"view_framework",
"view_library",
]
)

Expand Down Expand Up @@ -227,23 +235,14 @@ def startup():
"delete_framework",
"view_requirementnode",
"view_requirementlevel", # Permits to see the object on api by an admin
"view_library",
"add_library",
"delete_library",
"backup",
"restore",
]
)

# if superuser defined and does not exist, then create it
if (
CISO_ASSISTANT_SUPERUSER_EMAIL
and not User.objects.filter(email=CISO_ASSISTANT_SUPERUSER_EMAIL).exists()
):
try:
User.objects.create_superuser(
email=CISO_ASSISTANT_SUPERUSER_EMAIL, is_superuser=True
)
except Exception as e:
print(e) #NOTE: Add this exception in the logger
# if root folder does not exist, then create it
if not Folder.objects.filter(content_type=Folder.ContentType.ROOT).exists():
Folder.objects.create(
Expand Down Expand Up @@ -308,12 +307,18 @@ def startup():
folder=Folder.get_root_folder(),
)
ra2.perimeter_folders.add(global_validators.folder)
# add any superuser to the global administrors group, in case it is not yet done
for superuser in User.objects.filter(is_superuser=True):
UserGroup.objects.get(name="BI-UG-ADM").user_set.add(superuser)
# fix administrator role, to facilitate migrations
administrator = Role.objects.filter(name="BI-RL-ADM").first()
administrator.permissions.set(administrator_permissions)

# if superuser defined and does not exist, then create it
if (
CISO_ASSISTANT_SUPERUSER_EMAIL
and not User.objects.filter(email=CISO_ASSISTANT_SUPERUSER_EMAIL).exists()
):
try:
User.objects.create_superuser(
email=CISO_ASSISTANT_SUPERUSER_EMAIL, is_superuser=True
)
except Exception as e:
print(e) #NOTE: Add this exception in the logger


class CoreConfig(AppConfig):
Expand All @@ -322,8 +327,4 @@ class CoreConfig(AppConfig):
verbose_name = "Core"

def ready(self):
import os

if os.environ.get("RUN_MAIN"):
"""Only called in main, not during makemigrations or migrate"""
startup()
post_migrate.connect(startup, sender=self)
2 changes: 1 addition & 1 deletion backend/core/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.2 on 2024-02-10 02:00
# Generated by Django 5.0.2 on 2024-02-23 00:51

import core.validators
import uuid
Expand Down
22 changes: 20 additions & 2 deletions backend/core/migrations/0002_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 5.0.2 on 2024-02-10 02:00
# Generated by Django 5.0.2 on 2024-02-23 00:51

import django.db.models.deletion
import iam.models
Expand Down Expand Up @@ -52,6 +52,11 @@ class Migration(migrations.Migration):
name='framework',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.framework', verbose_name='Framework'),
),
migrations.AddField(
model_name='library',
name='dependencies',
field=models.ManyToManyField(blank=True, to='core.library', verbose_name='Dependencies'),
),
migrations.AddField(
model_name='library',
name='folder',
Expand All @@ -75,7 +80,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='requirementassessment',
name='compliance_assessment',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.complianceassessment', verbose_name='Compliance assessment'),
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='requirement_assessments', to='core.complianceassessment', verbose_name='Compliance assessment'),
),
migrations.AddField(
model_name='requirementassessment',
Expand Down Expand Up @@ -207,6 +212,19 @@ class Migration(migrations.Migration):
name='security_measures',
field=models.ManyToManyField(blank=True, related_name='requirement_assessments', to='core.securitymeasure', verbose_name='Security measures'),
),
migrations.CreateModel(
name='Policy',
fields=[
],
options={
'verbose_name': 'Policy',
'verbose_name_plural': 'Policies',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('core.securitymeasure',),
),
migrations.AddField(
model_name='threat',
name='folder',
Expand Down
24 changes: 0 additions & 24 deletions backend/core/migrations/0003_library_dependencies_and_more.py

This file was deleted.

26 changes: 0 additions & 26 deletions backend/core/migrations/0004_policy.py

This file was deleted.

4 changes: 2 additions & 2 deletions backend/core/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ class RBACPermissions(permissions.DjangoObjectPermissions):
}

def has_permission(self, request: Request, view) -> bool:
""" we don't need this check, as we have queryset for list and serializers for create
see https://www.django-rest-framework.org/api-guide/permissions/ """
return True

def has_object_permission(self, request: Request, view, obj):
Expand All @@ -30,8 +32,6 @@ def has_object_permission(self, request: Request, view, obj):
if not perms:
return False
_codename = perms[0].split(".")[1]
if queryset.model == User:
return RoleAssignment.has_permission(user=request.user, codename=_codename)
return RoleAssignment.is_access_allowed(
user=request.user,
perm=Permission.objects.get(codename=_codename),
Expand Down
Loading
Loading