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

Findings dashboard for all organizations #4007

Open
wants to merge 109 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 104 commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
8aa5814
Report recipe for all orgs
Rieven Nov 25, 2024
fe1c88a
Create Findings Aggregate Report
madelondohmen Dec 2, 2024
e8269b2
add model and signal
Rieven Dec 2, 2024
f82cbfd
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 2, 2024
b3308bb
Merge branch 'main' of github.com:minvws/nl-kat-coordination into fea…
Rieven Dec 2, 2024
7c410a2
check pk instead of id
Rieven Dec 2, 2024
ee9eb65
Fixes for organization Dashboard
Rieven Dec 3, 2024
1567bfa
fix get findings from bytes
Rieven Dec 3, 2024
92ff8a5
return 1 finding
Rieven Dec 3, 2024
6ee322c
Update templates and aggregate report
madelondohmen Dec 3, 2024
1114a10
Fix merge conflict
madelondohmen Dec 3, 2024
1ec80c1
Fixes in templates
madelondohmen Dec 3, 2024
6b717e4
fix scheduler is ready
Rieven Dec 4, 2024
a1ca8d9
Fix logic
Rieven Dec 4, 2024
eac10c7
Fix aggregate summary
madelondohmen Dec 4, 2024
683033e
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen Dec 4, 2024
5e636fe
Fix occurrences count
madelondohmen Dec 4, 2024
80d8e94
Add sorting to reports to get latest report
madelondohmen Dec 4, 2024
bfc5a7d
fix none findings for orgs
Rieven Dec 4, 2024
ef0b5c2
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 4, 2024
4f43a9d
Update templates
madelondohmen Dec 4, 2024
98b1598
Fix merge conflict
madelondohmen Dec 4, 2024
c7db309
Changes in templates
madelondohmen Dec 5, 2024
68d75ef
Add findings org summary and show datetime
madelondohmen Dec 5, 2024
47e1f00
change models and data
Rieven Dec 5, 2024
1a50e80
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 5, 2024
929fcb5
More table fixes
madelondohmen Dec 5, 2024
85ed236
fixes models, views, templates
Rieven Dec 9, 2024
09adcc5
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 9, 2024
4083062
Create Multi Report Findings Report
madelondohmen Dec 9, 2024
c8e79d5
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen Dec 9, 2024
b34e2e2
Add dashboard org table to findings report
madelondohmen Dec 9, 2024
0d5c5df
fix integer in seeder
Rieven Dec 9, 2024
e68f6ec
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 9, 2024
648ea8f
fix report data
Rieven Dec 10, 2024
d8da888
Create findings_report_data method
madelondohmen Dec 10, 2024
b7cb246
Show 5 most recent reports on scheduled report page
madelondohmen Dec 10, 2024
d464c13
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen Dec 10, 2024
b6ea32f
fix report data
Rieven Dec 10, 2024
d5dc74f
Fix findings report
madelondohmen Dec 10, 2024
7b4d630
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 10, 2024
cd14b63
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 10, 2024
73ebd07
Model changes and views
Rieven Dec 11, 2024
0be5dd8
Create two tabs for crisis room: dashboards and findings
madelondohmen Dec 11, 2024
7a11d1b
Resolve merge conflict
madelondohmen Dec 11, 2024
d0fd2b3
Fix crisis room findings templates
madelondohmen Dec 11, 2024
70a5657
fix views
Rieven Dec 11, 2024
a973727
fix views
Rieven Dec 11, 2024
fcc0f79
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 11, 2024
c8485f3
organizations findings
Rieven Dec 11, 2024
d095abf
Fix include template
madelondohmen Dec 11, 2024
23b899d
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen Dec 11, 2024
4c6b58f
Fix duplicates on finding dashboards
madelondohmen Dec 11, 2024
1e93b8d
set limit report
Rieven Dec 11, 2024
df2551a
set offset report
Rieven Dec 11, 2024
aca2b86
add description
Rieven Dec 11, 2024
91b59e4
Add titels to crisis rooms + styling and naming fixes
madelondohmen Dec 11, 2024
7cccfa4
findings settings
Rieven Dec 16, 2024
cc3ecc9
extend report data with report instance
Rieven Dec 16, 2024
aa2ec80
Add findings to report sidemenu
madelondohmen Dec 16, 2024
9a96847
Update intro text in findings report
madelondohmen Dec 16, 2024
1ab95a0
Small fix in template
madelondohmen Dec 16, 2024
a241b95
Add highest_risk_level and report_id to report data + show only 25 cr…
madelondohmen Dec 16, 2024
a48c5fe
Add links to findings report
madelondohmen Dec 16, 2024
88da5ea
Move textblocks to improve readability
madelondohmen Dec 17, 2024
8f5f75d
Fix dashboard template
madelondohmen Dec 17, 2024
c07e8a2
Changes in template
madelondohmen Dec 17, 2024
b0cad1d
fix view
Rieven Dec 18, 2024
775492b
Remove navbar and update headers
madelondohmen Dec 18, 2024
ac8e944
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 18, 2024
e78d4a4
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 18, 2024
081dbe1
Remove findings header
madelondohmen Dec 18, 2024
c327ef4
Merge branch 'main' of github.com:minvws/nl-kat-coordination into fea…
Rieven Dec 18, 2024
82a7500
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 18, 2024
1c9fb27
Fix buttons
madelondohmen Dec 18, 2024
a075275
fix models and migrations
Rieven Dec 18, 2024
c9d2f6e
Add empty text for 0 organizations
madelondohmen Dec 18, 2024
26db560
Add documentation
madelondohmen Dec 18, 2024
e531500
Merge branch 'feature/findings-dashboard-for-all-orgs' of https://git…
madelondohmen Dec 18, 2024
d3af0e1
Add created_at date and recurrence
madelondohmen Dec 19, 2024
f3ea66d
Cages for report and making tests easier
Rieven Dec 30, 2024
ca8ecd4
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 30, 2024
87e6fdb
Small fixes in templates
madelondohmen Dec 31, 2024
eca40ea
Add setup for tests
madelondohmen Dec 31, 2024
9bf0e50
Update empty state text for findings dashboard
madelondohmen Dec 31, 2024
b9ea527
fix test
Rieven Dec 31, 2024
5016ea6
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Dec 31, 2024
b0a71de
fix test
Rieven Dec 31, 2024
cbda800
fix test
Rieven Jan 2, 2025
06ba31a
Check for findings dashboard name
madelondohmen Jan 2, 2025
5273f19
Add tests
madelondohmen Jan 2, 2025
59e9525
add more test
Rieven Jan 2, 2025
2cd168d
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
madelondohmen Jan 7, 2025
c4c1d1b
Update translations
madelondohmen Jan 7, 2025
5529aed
Make dashboards done
Rieven Jan 7, 2025
818a8eb
file changes
Rieven Jan 7, 2025
978cfc2
Update translations and small fix
madelondohmen Jan 8, 2025
4b2f7e1
fix test and lang
Rieven Jan 8, 2025
48fa856
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Jan 8, 2025
7bb8d75
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
Rieven Jan 8, 2025
ea90b5c
Add breadcrumbs
Rieven Jan 8, 2025
8c005be
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Jan 8, 2025
0c507c9
revert the unnecessary
Rieven Jan 8, 2025
4c96bf6
update recipe seder
Rieven Jan 9, 2025
a22f1bd
Update rocky/crisis_room/models.py
Rieven Jan 14, 2025
2e9eeac
fixes for review and QA notes
Rieven Jan 14, 2025
37b6663
Merge branch 'feature/findings-dashboard-for-all-orgs' of github.com:…
Rieven Jan 14, 2025
c4da3db
Merge branch 'main' into feature/findings-dashboard-for-all-orgs
Rieven Jan 14, 2025
247074e
check if dashboard is updated or created
Rieven Jan 14, 2025
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
51 changes: 51 additions & 0 deletions docs/source/manual/crisis-room.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
===========
Crisis Room
===========

In OpenKAT we differentiate two Crisis Rooms:

- **Single Organization Crisis Room:** a Crisis Room for each organization separately
- **General Crisis Room:** one general Crisis Room with for all organizations


Single Organization Crisis Room
===============================

This page shows a Crisis Room for each organization separately.
Currently, this Crisis Room shows the top 10 most severe Findings.
In the future it will serve as a dashboard which can be customized by the user.


General Crisis Room
===================

This page shows the Crisis Room for all organizations.
Currently, this Crisis Room only shows the Findings, but in the future it will also show dashboards,
which can be customized by the user.

Findings
--------
This section shows all the findings that have been identified for all organizations.
These findings are shown in a table, grouped by organization and finding types.

Every organization has one default report recipe. This recipe is used to create an Aggregate Findings Report.
The output of this report, for each organization, is shown in this section.

The default settings for this report recipe are:

- report_name_format = ``Crisis Room Aggregate Report``
- ooi_types = ``["IPAddressV6", "Hostname", "IPAddressV4"]``
- scan_level = ``[1, 2, 3]``
- scan_type = ``["declared"]``
- report_types = ``["systems-report", "findings-report"]``
- cron_expression = ``0 * * * *`` (every hour)

It is possible to update the report recipe*. To do this:

- Go to "Reports"
- Click on the tab "Scheduled"
- Look for the "Criris Room Aggregate Report"
- Open the row
- Click on "Edit report recipe"

*\*Note: if you want to update the report recipe, you have to do this for every organization.*
1 change: 1 addition & 0 deletions docs/source/manual/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ An overview of all KAT functionality, from a user perspective.
:caption: Contents

user-manual
crisis-room
reports
normalizers
3 changes: 3 additions & 0 deletions rocky/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ build-rocky:
# Set DATABASE_MIGRATION=false to prevent entrypoint from running migration
docker compose run --rm -e DATABASE_MIGRATION=false rocky make build-rocky-native

dashboards:
docker compose run --rm rocky python3 manage.py dashboard

build-rocky-native:
while ! nc -vz $$ROCKY_DB_HOST $$ROCKY_DB_PORT; do sleep 0.1; done
python3 manage.py migrate
Expand Down
Empty file.
81 changes: 81 additions & 0 deletions rocky/crisis_room/management/commands/dashboard.py
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few suggestions:

  • Based on the dashboards make target and the output of this command, dashboards would be a better command name. I would suggest renaming this module to dashboards.py.
  • There's a lot happening here when the command is run, but there are no specific logs or prints that give some feedback to the user. I would expect at least something like "Creating dashboard for organization X..." when the command is creating all the dashboards

Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from uuid import uuid4

from crisis_room.models import Dashboard, DashboardData
from django.conf import settings
from django.core.management import BaseCommand
from tools.models import Organization
from tools.ooi_helpers import create_ooi

from octopoes.connector.octopoes import OctopoesAPIConnector
from octopoes.models.ooi.reports import ReportRecipe
from rocky.bytes_client import get_bytes_client
from rocky.scheduler import ReportTask, ScheduleRequest, scheduler_client

FINDINGS_DASHBOARD_NAME = "Crisis Room Findings Dashboard"


def get_or_create_default_dashboard(organization: Organization):
valid_time = datetime.now(timezone.utc)
is_scheduler_ready_for_schedule = is_scheduler_enabled(organization)

if is_scheduler_ready_for_schedule:
path = Path(__file__).parent / "recipe_seeder.json"
with path.open("r") as recipe_seeder:
recipe_default = json.load(recipe_seeder)

dashboard, _ = Dashboard.objects.get_or_create(name=FINDINGS_DASHBOARD_NAME, organization=organization)

dashboard_data, created = DashboardData.objects.get_or_create(dashboard=dashboard)
if created:
recipe = create_organization_recipe(valid_time, organization, recipe_default)
dashboard_data.recipe = recipe.primary_key
schedule_request = create_schedule_request(valid_time, organization, recipe)
scheduler_client(organization.code).post_schedule(schedule=schedule_request)

dashboard_data.findings_dashboard = True
dashboard_data.save()


def create_organization_recipe(
valid_time: datetime, organization: Organization, recipe_default: dict[str, Any]
) -> ReportRecipe:
report_recipe = ReportRecipe(recipe_id=uuid4(), **recipe_default)

octopoes_client = OctopoesAPIConnector(
settings.OCTOPOES_API, organization.code, timeout=settings.ROCKY_OUTGOING_REQUEST_TIMEOUT
)
bytes_client = get_bytes_client(organization.code)

create_ooi(api_connector=octopoes_client, bytes_client=bytes_client, ooi=report_recipe, observed_at=valid_time)
return report_recipe


def is_scheduler_enabled(organization: Organization) -> bool:
scheduler_id = f"report-{organization.code}"
return scheduler_client(organization.code).is_scheduler_ready(scheduler_id)


def create_schedule_request(
start_datetime: datetime, organization: Organization, report_recipe: ReportRecipe
) -> ScheduleRequest:
report_task = ReportTask(
organisation_id=organization.code, report_recipe_id=str(report_recipe.recipe_id)
).model_dump()

return ScheduleRequest(
scheduler_id=f"report-{organization.code}",
data=report_task,
schedule=report_recipe.cron_expression,
deadline_at=start_datetime.isoformat(),
)


class Command(BaseCommand):
def handle(self, *args, **options):
organizations = Organization.objects.all()
for organization in organizations:
get_or_create_default_dashboard(organization)
32 changes: 32 additions & 0 deletions rocky/crisis_room/management/commands/recipe_seeder.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"report_name_format": "Crisis Room Aggregate Report",
"subreport_name_format": "Findings Report for ${ooi}",
"input_recipe": {
"query": {
"ooi_types": [
"IPAddressV6",
"Hostname",
"IPAddressV4",
"URL"
],
"scan_level": [
1,
2,
3,
4
],
"scan_type": [
"declared"
],
"search_string": "",
"order_by": "object_type",
"asc_desc": "desc"
}
},
"parent_report_type": "aggregate-organisation-report",
"report_types": [
"systems-report",
"findings-report"
],
"cron_expression": "0 * * * *"
}
72 changes: 72 additions & 0 deletions rocky/crisis_room/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Generated by Django 5.0.9 on 2024-12-18 11:29

import django.core.validators
import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
initial = True

dependencies = [("tools", "0044_alter_organization_options")]

operations = [
migrations.CreateModel(
name="Dashboard",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("name", models.CharField(max_length=126)),
(
"organization",
models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to="tools.organization"),
),
],
options={"unique_together": {("name", "organization")}},
),
migrations.CreateModel(
name="DashboardData",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("recipe", models.CharField(max_length=126)),
("template", models.CharField(blank=True, default="findings_report/report.html", max_length=126)),
(
"position",
models.IntegerField(
blank=True,
default=1,
help_text="Where on the dashboard do you want to show the data? Position 1 is the most top level and the max position is 16.",
max_length=126,
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(16),
],
),
),
(
"display_in_crisis_room",
models.BooleanField(
default=False, help_text="Will be displayed on the general crisis room, for all organizations."
),
),
(
"display_in_dashboard",
models.BooleanField(
default=False, help_text="Will be displayed on a single organization dashboard"
),
),
(
"findings_dashboard",
models.BooleanField(
default=False, help_text="Will be displayed on the findings dashboard for all organizations"
),
),
(
"dashboard",
models.ForeignKey(
null=True, on_delete=django.db.models.deletion.SET_NULL, to="crisis_room.dashboard"
),
),
],
options={"unique_together": {("dashboard", "findings_dashboard"), ("dashboard", "position")}},
),
]
50 changes: 50 additions & 0 deletions rocky/crisis_room/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.utils.translation import gettext_lazy as _
from tools.models import Organization


class Dashboard(models.Model):
name = models.CharField(blank=False, max_length=126)
organization = models.ForeignKey(Organization, on_delete=models.SET_NULL, null=True)

class Meta:
unique_together = ["name", "organization"]

def __str__(self) -> str:
if self.name:
return f"{self.name} for organization {self.organization}"
return super().__str__()


class DashboardData(models.Model):
dashboard = models.ForeignKey(Dashboard, on_delete=models.SET_NULL, null=True)
recipe = models.CharField(blank=False, max_length=126)
template = models.CharField(blank=True, max_length=126, default="findings_report/report.html")
position = models.IntegerField(
blank=True,
max_length=126,
default=1,
validators=[MinValueValidator(1), MaxValueValidator(16)],
help_text=_(
"Where on the dashboard do you want to show the data? "
"Position 1 is the most top level and the max position is 16."
),
)
display_in_crisis_room = models.BooleanField(
default=False, help_text=_("Will be displayed on the general crisis room, for all organizations.")
)
display_in_dashboard = models.BooleanField(
default=False, help_text=_("Will be displayed on a single organization dashboard")
)
findings_dashboard = models.BooleanField(
default=False, help_text=_("Will be displayed on the findings dashboard for all organizations")
)

class Meta:
unique_together = [["dashboard", "position"], ["dashboard", "findings_dashboard"]]

def __str__(self) -> str:
if self.dashboard:
return f"{self.dashboard.name} for organization {self.dashboard.organization}"
Rieven marked this conversation as resolved.
Show resolved Hide resolved
return super().__str__()
19 changes: 19 additions & 0 deletions rocky/crisis_room/templates/crisis_room.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{% extends "layouts/base.html" %}

{% load i18n %}
{% load static %}

{% block content %}
{% include "header.html" %}

<main id="main-content" tabindex="-1" class="crisisroom">
<section>
<div>
<h1>{% translate "Crisis Room" %}</h1>
<p class="emphasized">{% translate "Crisis Room overview for all organizations" %}</p>
</div>
{% include "crisis_room_findings.html" %}

</section>
</main>
{% endblock content %}
46 changes: 46 additions & 0 deletions rocky/crisis_room/templates/crisis_room_dashboards.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{% extends "layouts/base.html" %}

{% load i18n %}
{% load static %}

{% block content %}
{% include "header.html" %}

<main id="main-content" tabindex="-1" class="crisisroom">
<section>
<div>
<h2>{% translate "Dashboards" %}</h2>
<p>
{% blocktranslate %}
On this page you can see an overview of the dashboards for all organizations.
**More context can be written here**
{% endblocktranslate %}
</p>
</div>
{% for organization, organization_dashboards in organizations_dashboards.items %}
<section>
<div>
<h3>{{ organization.name }} {% translate "dashboards" %}</h3>
{% if organization_dashboards %}
{% for dashboards in organization_dashboards %}
{% for dashboard_data, report in dashboards.items %}
{% if report %}
<section>
<div>
<h4>{{ dashboard_data }}</h4>
{% include dashboard_data.template with data=report.1 is_dashboard="yes" %}

</div>
</section>
{% endif %}
{% endfor %}
{% endfor %}
{% else %}
<p>{% translate "There are no dashboards to display." %}</p>
{% endif %}
</div>
</section>
{% endfor %}
</section>
</main>
{% endblock content %}
Loading
Loading