Skip to content

Commit 1cf48b1

Browse files
Merge branch 'main' into fix/sandbox-operations-manager-db
2 parents 81e87ae + 1bec036 commit 1cf48b1

17 files changed

Lines changed: 880 additions & 137 deletions

operations-manager/python/opi/core/task_handlers_project.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,19 @@ async def handle_create_project(payload: dict, progress: Any) -> dict:
102102
)
103103

104104
project_file_path = f"projects/{project_name}.yaml"
105-
commit_message = f"Create project {project_name}"
105+
commit_message = (
106+
f"Create project {project_name}"
107+
if payload.get("is_new_project", False)
108+
else f"Update project {project_name}"
109+
)
106110

107111
# Project-create flow: refuse if the project file already exists.
108112
# Without this a tenant could pick another tenant's name and take
109-
# over their project on the next reload.
110-
if await git_connector_for_project_files.file_exists(project_file_path):
113+
# over their project on the next reload. Edit/update flows that
114+
# reuse this task type (modal-edit, component delete) MUST set
115+
# is_new_project=False so legitimate overwrites still work.
116+
is_new_project = payload.get("is_new_project", False)
117+
if is_new_project and await git_connector_for_project_files.file_exists(project_file_path):
111118
error_msg = (
112119
f"Project '{project_name}' bestaat al. "
113120
f"Kies een andere projectnaam; een bestaand project kan niet "

operations-manager/python/opi/manager/database_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ async def create_resources_for_deployment(
147147
progress_manager = self.project_manager.get_progress_manager()
148148
database_task = None
149149
if progress_manager:
150-
database_task = progress_manager.add_task("Creating database resources")
150+
database_task = progress_manager.add_task("Database klaarmaken")
151151

152152
try:
153153
# Determine if using namespace-specific or shared database

operations-manager/python/opi/server.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,19 @@ async def _log_unhandled_exceptions(request, exc): # type: ignore[no-untyped-de
337337
app.add_middleware(CSRFMiddleware)
338338
app.add_middleware(AuthorizationMiddleware)
339339
app.add_middleware(MaintenanceMiddleware)
340-
app.add_middleware(SessionMiddleware, secret_key=settings.SECRET_KEY)
340+
# Harden the session cookie. same_site stays "lax" (not "strict") on
341+
# purpose: the OIDC login flow returns to /auth/callback via a top-level
342+
# cross-site GET redirect from Keycloak, and authlib needs its state cookie
343+
# to survive that navigation. "strict" would drop the cookie on the
344+
# callback and break login. "lax" still blocks cross-site POST/AJAX, and
345+
# CSRFMiddleware (double-submit token + Origin/Referer check) is the real
346+
# defense against the same-site sibling-subdomain tenant attacker.
347+
app.add_middleware(
348+
SessionMiddleware,
349+
secret_key=settings.SECRET_KEY,
350+
same_site="lax",
351+
https_only=not settings.DEBUG,
352+
)
341353
app.add_middleware(
342354
SecurityHeadersMiddleware,
343355
keycloak_url=settings.KEYCLOAK_URL,

operations-manager/python/opi/templates/dashboard.html.j2

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
</c-layout-flow>
4141
</c-alert>
4242
{% elif health_counts.Healthy > 0 %}
43-
<c-alert kind="success" heading="Alle {{ health_counts.Healthy }} project{{ 'en' if health_counts.Healthy != 1 }} {{ 'zijn' if health_counts.Healthy != 1 else 'is' }} gezond" maxWidth="lg" />
43+
<c-alert kind="success" heading="{% if health_counts.Healthy == 1 %}Het project is gezond{% else %}Alle {{ health_counts.Healthy }} projecten zijn gezond{% endif %}" maxWidth="lg" />
4444
{% elif not argocd_available %}
4545
<c-alert kind="info" heading="Status informatie niet beschikbaar" content="ArgoCD is niet verbonden." maxWidth="lg" />
4646
{% endif %}

operations-manager/python/opi/templates/invite-register.html.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
{# Registration Form Card #}
2222
<c-card outline="true" padding="lg">
2323
<form method="POST" action="/invite/{{ invite_key }}/register" autocomplete="off">
24+
<input type="hidden" name="csrf_token" value="{{ request.state.csrf_token }}">
2425
<c-layout-flow gap="lg">
2526
<c-heading type="h1" textContent="{{ display_name }}" />
2627
<c-heading type="h2" textContent="{% if language == 'nl' %}Nieuw Account{% else %}New Account{% endif %}" />

operations-manager/python/opi/templates/tools.html.j2

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,11 @@
151151
152152
try {
153153
const endpoint = operation === 'encrypt' ? '/tools/encrypt' : '/tools/decrypt';
154-
const response = await fetch(endpoint, { method: 'POST', body: formData });
154+
const response = await fetch(endpoint, {
155+
method: 'POST',
156+
body: formData,
157+
headers: { 'X-CSRF-Token': "{{ request.state.csrf_token }}" },
158+
});
155159
const data = await response.json();
156160
if (data.success) { showSuccess(data.result); }
157161
else { showError(data.error || 'Operation failed'); }

operations-manager/python/opi/templates/wizard/modal_wizard_review.html.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
<c-button kind="primary" label="{{ action_label }}"
9797
showIcon="before" icon="publicatie"
9898
hx-post="/projects/{{ project_name }}/modal-wizard/{{ flow_id }}/confirm{{ _token_qs }}"
99+
hx-headers="{&quot;X-CSRF-Token&quot;: &quot;{{ request.state.csrf_token }}&quot;}"
99100
{% if wizard_token %}hx-include="#modal-wizard-confirm-token"{% endif %}
100101
hx-target="#edit-section-inner" hx-swap="innerHTML" />
101102
</div>

operations-manager/python/opi/templates/wizard/modal_wizard_step.html.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<form id="modal-wizard-form"
5050
autocomplete="off"
5151
hx-ext="json-enc"
52+
hx-headers='{"X-CSRF-Token": "{{ request.state.csrf_token }}"}'
5253
hx-post="{{ _step_url_prefix }}{{ section.section_id }}{{ _token_qs }}"
5354
hx-target="#edit-section-inner"
5455
hx-swap="innerHTML">

operations-manager/python/opi/templates/wizard/wizard_review.html.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
<c-button kind="primary" label="Project aanmaken"
5757
showIcon="before" icon="publicatie"
5858
hx-post="/forms/wizard/{{ flow_id }}/submit"
59+
hx-headers="{&quot;X-CSRF-Token&quot;: &quot;{{ request.state.csrf_token }}&quot;}"
5960
hx-target="#wizard-step-content"
6061
hx-swap="innerHTML" />
6162
</div>

operations-manager/python/opi/templates/wizard/wizard_step.html.j2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
<form id="wizard-step-form"
5050
autocomplete="off"
5151
hx-ext="json-enc"
52+
hx-headers='{"X-CSRF-Token": "{{ request.state.csrf_token }}"}'
5253
hx-post="/forms/wizard/{{ flow_id }}/step/{{ section.section_id }}"
5354
hx-target="#wizard-step-content"
5455
hx-swap="innerHTML">

0 commit comments

Comments
 (0)