Skip to content

Commit

Permalink
Merge branch 'main' into CA-203-API-tests-for-user-permissions-and-roles
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-intuitem committed Feb 25, 2024
2 parents 267257d + 8784c4d commit fec86f6
Show file tree
Hide file tree
Showing 48 changed files with 1,658 additions and 1,285 deletions.
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Read the [full article](https://intuitem.com/blog/we-are-going-open-source/) abo
- CMMC v2
- PSPF

Checkout the [library](/library/libraries/) for the Domain Specific Language used and how you can define your own.
Checkout the [library](/library/libraries/) and [tools](/tools/) for the Domain Specific Language used and how you can define your own.

### Coming soon

Expand Down Expand Up @@ -75,6 +75,8 @@ You can then reach CISO Assistant using your web brower at [https://localhost:84

For the following executions, use "docker-compose up" directly.

If you want to restart a fresh install, simply delete the db directory, where the database is stored.

## Setting up CISO Assistant for development

### Requirements
Expand Down Expand Up @@ -132,7 +134,7 @@ export EMAIL_HOST_USER_RESCUE=<XXX>
export EMAIL_HOST_PASSWORD_RESCUE=<XXX>
export EMAIL_USE_TLS_RESCUE=True

# You can define the email of the first superuser, useful for automation
# You can define the email of the first superuser, useful for automation. A mail is sent to the superuser for password initlization
export CISO_SUPERUSER_EMAIL=<XXX>

# By default, Django secret key is generated randomly at each start of CISO Assistant. This is convenient for quick test,
Expand Down Expand Up @@ -267,6 +269,25 @@ python manage.py migrate

These migration files should be tracked by version control.

## Test harness

To run API tests on the backend, simply type "pytest" in a shell in the backend folder.

To run functional tests on the frontend, do the following actions:
- in the backend folder, launch the following commands:
```shell
[email protected] DJANGO_SUPERUSER_PASSWORD=1234 python manage.py createsuperuser --noinput
CISO_ASSISTANT_URL=http://localhost:4173 python manage.py runserver
```
- in parallel, in the frontend folder, launch the following command:
```shell
PUBLIC_BACKEND_API_URL=http://localhost:8000/api npx playwright test
```

For tests requiring mail sending, it is necessary to launch mailhog in a separate terminal.

The goal of the test harness is to prevent any regression, i.e. all the tests shall be successful. This is achieved for API tests, and will be soon achieved for functional tests.

## Built With

- [Django](https://www.djangoproject.com/) - Python Web Development Framework
Expand Down
9 changes: 5 additions & 4 deletions backend/core/apps.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
from ciso_assistant.settings import CISO_ASSISTANT_SUPERUSER_EMAIL

import os

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 django.contrib.auth.models import Permission
from iam.models import Folder, Role, RoleAssignment, User, UserGroup
print("post-migrate handler: initialize database")
print("startup handler: initialize database", kwargs)

auditor_permissions = Permission.objects.filter(
codename__in=[
Expand Down Expand Up @@ -329,4 +328,6 @@ class CoreConfig(AppConfig):
verbose_name = "Core"

def ready(self):
post_migrate.connect(startup, sender=self)
# avoid post_migrate handler if we are in the main, as it interferes with restore
if not os.environ.get('RUN_MAIN'):
post_migrate.connect(startup, sender=self)
10 changes: 4 additions & 6 deletions backend/core/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -516,16 +516,16 @@ def aggregate_risks_per_field(
count = (
RiskScenario.objects.filter(id__in=object_ids_view)
.filter(residual_level=i)
.filter(risk_assessment__risk_matrix__name=["name"])
# .filter(risk_assessment__risk_matrix__name=["name"])
.count()
)
) # What the second filter does ? Is this usefull ?
else:
count = (
RiskScenario.objects.filter(id__in=object_ids_view)
.filter(current_level=i)
.filter(risk_assessment__risk_matrix__name=["name"])
# .filter(risk_assessment__risk_matrix__name=["name"])
.count()
)
) # What the second filter does ? Is this usefull ?

if "count" not in values[m["risk"][i][field]]:
values[m["risk"][i][field]]["count"] = count
Expand All @@ -551,7 +551,6 @@ def risks_count_per_level(user: User, risk_assessments: list | None = None):
residual_level.append(
{"name": r[0], "value": r[1]["count"], "color": r[1]["color"], "localName": camel_case(r[0])}
)

return {"current": current_level, "residual": residual_level}


Expand Down Expand Up @@ -709,7 +708,6 @@ def risk_status(user: User, risk_assessment_list):
names.append(str(risk_assessment.project) + " " + str(risk_assessment.version))

y_max_rsk = max(max_tmp, default=0) + 1
print("y_max_rsk: ", y_max_rsk)

return {
"names": names,
Expand Down
5 changes: 3 additions & 2 deletions backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ def impact(self, request, pk):
return Response(choices)

@action(detail=False, name="Get risk count per level")
def count_per_level(self, request): # _per_level
def count_per_level(self, request):
return Response({"results": risks_count_per_level(request.user)})


Expand Down Expand Up @@ -766,9 +766,10 @@ class UserFilter(df.FilterSet):
is_approver = df.BooleanFilter(method="filter_approver", label="Approver")

def filter_approver(self, queryset, name, value):
""" we don't know yet which folders will be used, so filter on any folder"""
approvers_id = []
for candidate in User.objects.all():
if RoleAssignment.has_permission(candidate, "approve_riskacceptance"):
if 'approve_riskacceptance' in candidate.permissions:
approvers_id.append(candidate.id)
if value:
return queryset.filter(id__in=approvers_id)
Expand Down
6 changes: 5 additions & 1 deletion backend/iam/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,11 @@ def get_user_groups(self):

@property
def has_backup_permission(self) -> bool:
return RoleAssignment.has_permission(self, "backup")
return RoleAssignment.is_access_allowed(
user=self,
perm=Permission.objects.get(codename="backup"),
folder=Folder.get_root_folder(),
)

@property
def edit_url(self) -> str:
Expand Down
2 changes: 1 addition & 1 deletion backend/serdes/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@

urlpatterns = [
path("dump-db/", login_required(views.dump_db_view), name="dump-db"),
path("load-backup/", views.LoadBackupView.as_view(), name="load-backup"),
path("load-backup/", login_required(views.LoadBackupView.as_view()), name="load-backup"),
]
2 changes: 2 additions & 0 deletions backend/serdes/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ class LoadBackupView(APIView):
serializer_class = LoadBackupSerializer

def post(self, request, *args, **kwargs):
if not is_admin_check(request.user):
return Response(status=status.HTTP_403_FORBIDDEN)
if request.data:
sys.stdin = io.StringIO(json.dumps(request.data[1]))
request.session.flush()
Expand Down
70 changes: 63 additions & 7 deletions frontend/messages/en.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"addButton": "Add {model}",
"associatedObject": "Associated {model}",
"french": "French",
"english": "English",
"addThreat": "New threat",
"addSecurityFunction": "New security function",
"addSecurityMeasure": "New security measure",
"addAsset": "New asset",
"addRiskAssessment": "New risk assessment",
"addRiskScenario": "New risk scenario",
"addRiskAcceptance": "New risk acceptance",
"addComplianceAssessment": "New compliance assessment",
"addEvidence": "New evidence",
"addDomain": "New domain",
"addProject": "New project",
"addUser": "New user",
"associatedThreats": "Associated threats",
"associatedSecurityFunctions": "Associated security functions",
"associatedSecurityMeasures": "Associated security measures",
"associatedAssets": "Associated assets",
"associatedRiskAssessments": "Associated risk assessments",
"associatedRiskScenarios": "Associated risk scenarios",
"associatedRiskAcceptances": "Associated risk acceptances",
"associatedComplianceAssessments": "Associated compliance assessments",
"associatedEvidences": "Associated evidences",
"associatedDomains": "Associated domains",
"associatedProjects": "Associated projects",
"associatedUsers": "Associated users",
"home": "Home",
"edit": "Edit",
"overview": "Overview",
Expand Down Expand Up @@ -125,7 +147,7 @@
"statistics": "Statistics",
"myProjects": "My projects",
"scenarios": "Scenarios",
"assignedObjects": "Assigned to {number} {object}",
"assignedProjects": "Assigned to {number} project{s}",
"currentRiskLevelPerScenario": "Current risk level per risk scenario",
"residualRiskLevelPerScenario": "Residual risk level per risk scenario",
"securityMeasuresStatus": "Security measures status",
Expand All @@ -147,7 +169,8 @@
"upcoming": "Upcoming",
"today": "Today",
"actionRequested": "Action requested",
"noObjectYet": "No {object} yet",
"noRiskAcceptanceYet": "No risk acceptance yet",
"noSecurityMeasureYet": "No security measure yet",
"authors": "Authors",
"reviewers": "Reviewers",
"process": "Process",
Expand All @@ -161,7 +184,7 @@
"pendingMeasures": "Your pending measures",
"orderdByRankingScore": "Ordered by ranking score",
"rankingScore": "Ranking score",
"noPendingObject": "No pending {object}",
"noPendingSecurityMeasure": "No pending security measure",
"rankingScoreDefintion": "Ranking score is an adaptive metric that combines the information of effort and current risk level, and crosses it with the other data to assist you for the prioritization",
"actions": "Actions",
"projectsSummaryEmpty": "Projects summary is empty",
Expand All @@ -172,6 +195,39 @@
"measureOpen": "Measure: open",
"measureProgress": "Measure: in progress",
"measureHold": "Measure: on hold",
"measureDone": "Measure: done"

"measureDone": "Measure: done",
"monday": "Monday",
"tuesday": "Tuesday",
"wednesday": "Wednesday",
"thursday": "Thursday",
"friday": "Friday",
"saturday": "Saturday",
"sunday": "Sunday",
"january": "January",
"february": "February",
"march": "March",
"april": "April",
"may": "May",
"june": "June",
"july": "July",
"august": "August",
"september": "September",
"october": "October",
"november": "November",
"december": "December",
"errorsFound": "error{s} found",
"warningsFound": "warning{s} found",
"infosFound": "info{s} found",
"remediationPlan": "Remediation plan",
"treatmentPlan": "Treatment plan",
"asPDF": "as PDF",
"asCSV": "as CSV",
"draft": "Draft",
"riskMatrixView": "Risk matrix view",
"currentInMatrixView": "Current",
"residualInMatrixView": "Residual",
"probability": "Probability",
"riskLevels": "Risk levels",
"cancel": "Cancel",
"save": "Save"
}
72 changes: 64 additions & 8 deletions frontend/messages/fr.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,31 @@
{
"$schema": "https://inlang.com/schema/inlang-message-format",
"addButton": "Ajouter {determinant} {model}",
"associatedObject": "{model} associé{e}s",
"french": "Français",
"english": "Anglais",
"addThreat": "Ajouter une menace",
"addSecurityFunction": "Ajouter une fonction de sécurité",
"addSecurityMeasure": "Ajouter une mesure de sécurité",
"addAsset": "Ajouter un bien sensible",
"addRiskAssessment": "Ajouter une évaluation de risque",
"addRiskScenario": "Ajouter un scénario de risque",
"addRiskAcceptance": "Ajouter une acceptation de risque",
"addComplianceAssessment": "Ajouter une évaluation de conformité",
"addEvidence": "Ajouter une preuve",
"addDomain": "Ajouter un domaine",
"addProject": "Ajouter un projet",
"addUser": "Ajouter un utilisateur",
"associatedThreats": "Menaces associées",
"associatedSecurityFunctions": "Fonctions de sécurité associées",
"associatedSecurityMeasures": "Mesures de sécurité associées",
"associatedAssets": "Biens sensibles associés",
"associatedRiskAssessments": "Évaluations de risque associées",
"associatedRiskScenarios": "Scénarios de risque associés",
"associatedRiskAcceptances": "Acceptations de risque associées",
"associatedComplianceAssessments": "Évaluations de conformité associées",
"associatedEvidences": "Preuves associées",
"associatedDomains": "Domaines associés",
"associatedProjects": "Projets associés",
"associatedUsers": "Utilisateurs associés",
"home": "Accueil",
"edit": "Modifier",
"overview": "Vue d'ensemble",
Expand Down Expand Up @@ -39,7 +61,7 @@
"userGroups": "Groupes d'utilisateurs",
"roleAssignments": "Affectations de rôle",
"xRays": "X-rays",
"scoringAssistant": "Assistat d'évaluation",
"scoringAssistant": "Assistant d'évaluation",
"libraries": "Bibliothèques",
"backupRestore": "Sauvegarde et restauration",
"myProfile": "Mon profil",
Expand Down Expand Up @@ -125,7 +147,7 @@
"statistics": "Statistiques",
"myProjects": "Mes projets",
"scenarios": "Scénarios",
"assignedObjects": "Assigné à {number} {object}",
"assignedProjects": "Assigné à {number} project{s}",
"currentRiskLevelPerScenario": "Niveau de risque courant par scénario de risque",
"residualRiskLevelPerScenario": "Niveau de risque résiduel par scénario de risque",
"securityMeasuresStatus": "Statut des mesures de sécurité",
Expand All @@ -147,7 +169,8 @@
"upcoming": "À venir",
"today": "Aujourd'hui",
"actionRequested": "Action requise",
"noObjectYet": "Aucun{e} {object} pour le moment",
"noRiskAcceptanceYet": "Aucune acceptation de risque pour le moment",
"noSecurityMeasureYet": "Aucune mesure de sécurité pour le moment",
"authors": "Auteurs",
"reviewers": "Relecteurs",
"process": "Traiter",
Expand All @@ -161,7 +184,7 @@
"pendingMeasures": "Vos mesures en attente",
"orderdByRankingScore": "Classées par score",
"rankingScore": "Score de classement",
"noPendingObject": "Aucun{e} {objet} en attente",
"noPendingSecurityMeasure": "Aucune mesure de sécurité en attente",
"rankingScoreDefintion": "Le score de classement est une mesure adaptative qui combine les informations relatives à l'effort et au niveau de risque actuel, et les croise avec d'autres données pour vous aider à établir des priorités",
"actions": "Actions",
"projectsSummaryEmpty": "Le résumé des projets est vide",
Expand All @@ -172,6 +195,39 @@
"measureOpen": "Mesure: ouverte",
"measureProgress": "Mesure: en cours",
"measureHold": "Mesure: en attente",
"measureDone": "Mesure: terminée"
"measureDone": "Mesure: terminée",
"monday": "Lundi",
"tuesday": "Mardi",
"wednesday": "Mercredi",
"thursday": "Jeudi",
"friday": "Vendredi",
"saturday": "Samedi",
"sunday": "Dimanche",
"january": "Janvier",
"february": "Février",
"march": "Mars",
"april": "Avril",
"may": "Mai",
"june": "Juin",
"july": "Juillet",
"august": "Août",
"september": "Septembre",
"october": "Octobre",
"november": "Novembre",
"december": "Décembre",
"errorsFound": "erreur{s} trouvée{s}",
"warningsFound": "avertissement{s} trouvé{s}",
"infosFound": "info{s} trouvée{s}",
"remediationPlan": "Plan de remédiation",
"treatmentPlan": "Plan de traitement",
"asPDF": "en PDF",
"asCSV": "en CSV",
"draft": "Brouillon",
"riskMatrixView": "Vue de la matrice de risque",
"currentInMatrixView": "Courante",
"residualInMatrixView": "Residuelle",
"probability": "Probabilité",
"riskLevels": "Niveaux de risque",
"cancel": "Annuler",
"save": "Enregistrer"
}

Loading

0 comments on commit fec86f6

Please sign in to comment.