Skip to content

Commit

Permalink
Merge branch 'main' into enterprise/enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
nas-tabchiche committed Sep 10, 2024
2 parents 3a4141d + 4ae9289 commit 6e3daeb
Show file tree
Hide file tree
Showing 51 changed files with 2,151 additions and 3,638 deletions.
2 changes: 2 additions & 0 deletions backend/ciso_assistant/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ def set_ciso_assistant_url(_, __, event_dict):
("pt", "Portuguese"),
("ar", "Arabic"),
("ro", "Romanian"),
("hi", "Hindi"),
("ur", "Urdu"),
]

PROJECT_PATH = os.path.dirname(os.path.abspath(__file__))
Expand Down
4 changes: 2 additions & 2 deletions backend/core/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,12 +803,12 @@ def build_audits_tree_metrics(user):
for result in RequirementAssessment.Result.choices:
cnt_res[result[0]] = (
RequirementAssessment.objects.filter(
compliance_assessment=audit
requirement__assessable=True
)
.filter(compliance_assessment=audit)
.filter(result=result[0])
.count()
)
print(cnt_res)
blk_audit = {
"name": audit.name,
"children": [
Expand Down
58 changes: 5 additions & 53 deletions backend/core/startup.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from django.apps import AppConfig
from django.db.models.signals import post_migrate
import os

from ciso_assistant.settings import CISO_ASSISTANT_SUPERUSER_EMAIL
from django.apps import AppConfig
from django.core.management import call_command

from django.db.models.signals import post_migrate
from structlog import get_logger

from ciso_assistant.settings import CISO_ASSISTANT_SUPERUSER_EMAIL

logger = get_logger(__name__)

READER_PERMISSIONS_LIST = [
Expand Down Expand Up @@ -271,9 +271,8 @@ def startup(sender: AppConfig, **kwargs):
Create superuser if CISO_ASSISTANT_SUPERUSER_EMAIL defined
"""
from django.contrib.auth.models import Permission
from allauth.socialaccount.providers.saml.provider import SAMLProvider

from iam.models import Folder, Role, RoleAssignment, User, UserGroup
from global_settings.models import GlobalSettings

print("startup handler: initialize database")

Expand Down Expand Up @@ -373,53 +372,6 @@ def startup(sender: AppConfig, **kwargs):
except Exception as e:
print(e) # NOTE: Add this exception in the logger

default_attribute_mapping = SAMLProvider.default_attribute_mapping

settings = {
"attribute_mapping": {
"uid": default_attribute_mapping["uid"],
"email_verified": default_attribute_mapping["email_verified"],
"email": default_attribute_mapping["email"],
},
"idp": {
"entity_id": "",
"metadata_url": "",
"sso_url": "",
"slo_url": "",
"x509cert": "",
},
"sp": {
"entity_id": "ciso-assistant",
},
"advanced": {
"allow_repeat_attribute_name": True,
"allow_single_label_domains": False,
"authn_request_signed": False,
"digest_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"logout_request_signed": False,
"logout_response_signed": False,
"metadata_signed": False,
"name_id_encrypted": False,
"reject_deprecated_algorithm": True,
"reject_idp_initiated_sso": True,
"signature_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"want_assertion_encrypted": False,
"want_assertion_signed": False,
"want_attribute_statement": True,
"want_message_signed": False,
"want_name_id": False,
"want_name_id_encrypted": False,
},
}

if not GlobalSettings.objects.filter(name=GlobalSettings.Names.SSO).exists():
logger.info("SSO settings not found, creating default settings")
sso_settings = GlobalSettings.objects.create(
name=GlobalSettings.Names.SSO,
value={"client_id": "0", "settings": settings},
)
logger.info("SSO settings created", settings=sso_settings.value)

call_command("storelibraries")


Expand Down
109 changes: 108 additions & 1 deletion backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,43 @@ def to_review(self, request):

return Response({"results": measures})

@action(detail=False, name="Export controls as CSV")
def export_csv(self, request):
(viewable_controls_ids, _, _) = RoleAssignment.get_accessible_object_ids(
Folder.get_root_folder(), request.user, AppliedControl
)
response = HttpResponse(content_type="text/csv")
response["Content-Disposition"] = 'attachment; filename="audit_export.csv"'

writer = csv.writer(response, delimiter=";")
columns = [
"internal_id",
"name",
"description",
"category",
"csf_function",
"status",
"eta",
"owner",
]
writer.writerow(columns)

for control in AppliedControl.objects.filter(id__in=viewable_controls_ids):
row = [
control.id,
control.name,
control.description,
control.category,
control.csf_function,
control.status,
control.eta,
]
if len(control.owner.all()) > 0:
owners = ",".join([o.email for o in control.owner.all()])
row += [owners]
writer.writerow(row)
return response


class PolicyViewSet(AppliedControlViewSet):
model = Policy
Expand Down Expand Up @@ -859,6 +896,13 @@ def revoke(self, request, pk):
self.get_object().set_state("revoked")
return Response({"results": "state updated to revoked"})

@action(detail=False, methods=["get"], name="Get waiting risk acceptances")
def waiting(self, request):
acceptance_count = RiskAcceptance.objects.filter(
approver=request.user, state="submitted"
).count()
return Response({"count": acceptance_count})

def perform_create(self, serializer):
risk_acceptance = serializer.validated_data
submitted = False
Expand Down Expand Up @@ -1031,6 +1075,50 @@ def perform_create(self, serializer):
)
ra4.perimeter_folders.add(folder)

@action(detail=False, methods=["get"])
def org_tree(self, request):
"""
Returns the tree of domains and projects
"""
tree = {"name": "Global", "children": []}

(viewable_objects, _, _) = RoleAssignment.get_accessible_object_ids(
folder=Folder.get_root_folder(),
user=request.user,
object_type=Folder,
)
folders_list = list()
for folder in Folder.objects.exclude(content_type="GL").filter(
id__in=viewable_objects
):
entry = {"name": folder.name}
children = []
for project in Project.objects.filter(folder=folder):
children.append(
{
"name": project.name,
"children": [
{
"name": "audits",
"value": ComplianceAssessment.objects.filter(
project=project
).count(),
},
{
"name": "risk assessments",
"value": RiskAssessment.objects.filter(
project=project
).count(),
},
],
}
)
entry.update({"children": children})
folders_list.append(entry)
tree.update({"children": folders_list})

return Response(tree)


@api_view(["GET"])
@permission_classes([permissions.IsAuthenticated])
Expand Down Expand Up @@ -1333,11 +1421,30 @@ def action_plan(self, request, pk):
compliance_assessment_object.get_requirement_assessments()
)
applied_controls = [
{**model_to_dict(applied_control), "id": applied_control.id}
{
"id": applied_control.id,
"name": applied_control.name,
"description": applied_control.description,
"status": applied_control.status,
"category": applied_control.category,
"csf_function": applied_control.csf_function,
"eta": applied_control.eta,
"expiry_date": applied_control.expiry_date,
"link": applied_control.link,
"effort": applied_control.effort,
"owners": [
{
"id": owner.id,
"email": owner.email,
}
for owner in applied_control.owner.all()
],
}
for applied_control in AppliedControl.objects.filter(
requirement_assessments__in=requirement_assessments_objects
).distinct()
]

for applied_control in applied_controls:
applied_control["requirements_count"] = (
RequirementAssessment.objects.filter(
Expand Down
58 changes: 35 additions & 23 deletions backend/iam/sso/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
import structlog
from allauth.socialaccount.models import providers
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models.query import QuerySet
from django.utils.translation import gettext_lazy as _

from allauth.socialaccount.models import providers
from global_settings.models import GlobalSettings
from iam.sso.saml.defaults import DEFAULT_SAML_SETTINGS

logger = structlog.get_logger(__name__)


class SSOSettingsQuerySet(QuerySet):
Expand All @@ -15,27 +19,35 @@ def __init__(self, model=None, query=None, using=None, hints=None):

def _fetch_all(self):
if self._result_cache is None:
try:
if not GlobalSettings.objects.filter(
name=GlobalSettings.Names.SSO
).exists():
logger.info("SSO settings not found, creating default settings")
_settings = GlobalSettings.objects.create(
name=GlobalSettings.Names.SSO,
value={"client_id": "0", "settings": DEFAULT_SAML_SETTINGS},
)
logger.info("SSO settings created", settings=_settings.value)
else:
_settings = GlobalSettings.objects.get(name=GlobalSettings.Names.SSO)
self._result_cache = [
SSOSettings(
id=_settings.id,
name=_settings.name,
created_at=_settings.created_at,
updated_at=_settings.updated_at,
is_published=_settings.is_published,
is_enabled=_settings.value.get("is_enabled"),
provider=_settings.value.get("provider"),
client_id=_settings.value.get("client_id"),
provider_id=_settings.value.get("provider_id"),
provider_name=_settings.value.get("name"),
secret=_settings.value.get("secret"),
key=_settings.value.get("key"),
settings=_settings.value.get("settings"),
)
]
except ObjectDoesNotExist:
self._result_cache = []

self._result_cache = [
SSOSettings(
id=_settings.id,
name=_settings.name,
created_at=_settings.created_at,
updated_at=_settings.updated_at,
is_published=_settings.is_published,
is_enabled=_settings.value.get("is_enabled"),
provider=_settings.value.get("provider"),
client_id=_settings.value.get("client_id"),
provider_id=_settings.value.get("provider_id"),
provider_name=_settings.value.get("name"),
secret=_settings.value.get("secret"),
key=_settings.value.get("key"),
settings=_settings.value.get("settings"),
)
]

def iterator(self):
self._fetch_all()
Expand Down
41 changes: 41 additions & 0 deletions backend/iam/sso/saml/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from allauth.socialaccount.providers.saml.provider import SAMLProvider


DEFAULT_SAML_ATTRIBUTE_MAPPING = SAMLProvider.default_attribute_mapping

DEFAULT_SAML_SETTINGS = {
"attribute_mapping": {
"uid": DEFAULT_SAML_ATTRIBUTE_MAPPING["uid"],
"email_verified": DEFAULT_SAML_ATTRIBUTE_MAPPING["email_verified"],
"email": DEFAULT_SAML_ATTRIBUTE_MAPPING["email"],
},
"idp": {
"entity_id": "",
"metadata_url": "",
"sso_url": "",
"slo_url": "",
"x509cert": "",
},
"sp": {
"entity_id": "ciso-assistant",
},
"advanced": {
"allow_repeat_attribute_name": True,
"allow_single_label_domains": False,
"authn_request_signed": False,
"digest_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"logout_request_signed": False,
"logout_response_signed": False,
"metadata_signed": False,
"name_id_encrypted": False,
"reject_deprecated_algorithm": True,
"reject_idp_initiated_sso": True,
"signature_algorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"want_assertion_encrypted": False,
"want_assertion_signed": False,
"want_attribute_statement": True,
"want_message_signed": False,
"want_name_id": False,
"want_name_id_encrypted": False,
},
}
Loading

0 comments on commit 6e3daeb

Please sign in to comment.