Skip to content

fix(plugin-access-manager): set Casdoor initDataNewOnly=true to preserve API state#1418

Open
jeffersonrodrigues92 wants to merge 1 commit into
developfrom
fix/casdoor-init-data-new-only-preserve-api
Open

fix(plugin-access-manager): set Casdoor initDataNewOnly=true to preserve API state#1418
jeffersonrodrigues92 wants to merge 1 commit into
developfrom
fix/casdoor-init-data-new-only-preserve-api

Conversation

@jeffersonrodrigues92
Copy link
Copy Markdown
Contributor

@jeffersonrodrigues92 jeffersonrodrigues92 commented May 23, 2026

Problem

After a recent plugin-auth deploy that touched init_data.json (PR LerianStudio/plugin-auth#557), production applications kept their identity but lost the application↔permission associations that had been wired up via the Casdoor UI/API. The applications themselves were not deleted — only the manually-configured links to permissions disappeared.

Root cause

The plugin-access-manager Helm chart relies on Casdoor v2.206.0's default initDataNewOnly = false. With that default, every Casdoor restart re-processes the entire init_data.json and performs a DELETE + INSERT cycle on every entity that already exists in the database:

// casdoor/casdoor v2.206.0, object/init_data.go
func initDefinedApplication(application *Application) {
    existed, _ := getApplication(application.Owner, application.Name)
    if existed != nil {
        if initDataNewOnly { return }      // ← skip when true
        deleteApplication(application)     // ← delete when false (current default)
    }
    AddApplication(application)            // ← re-create from JSON
}

The same logic is mirrored in initDefinedPermission, initDefinedRole, initDefinedGroup, etc. Any field on those entities that was customized at runtime (users list, roles list, resources, permission references on an application) is silently reset to whatever is in init_data.json on every Casdoor pod restart.

This was masked in steady state because Casdoor pods were long-lived. The recent deploy forced a restart, which exposed the wipe.

Fix

Add initDataNewOnly to the auth-backend ConfigMap, parameterized via Values (default "true") to match the existing pattern on initDataFile:

  initDataFile: {{ .Values.auth.backend.initDataFile | default "./init_data.json" | quote }}
  initDataNewOnly: {{ .Values.auth.backend.initDataNewOnly | default "true" | quote }}

With initDataNewOnly: "true" Casdoor's behavior changes to:

  • If an entity (owner+name) is already in the DB → SKIP (no delete, no insert, no update).
  • If an entity is in the JSON but not in the DB → INSERT (still works for fresh installs and for newly added seed entries).

Operators that ever need the legacy reset-on-restart behavior can opt out per-environment by setting auth.backend.initDataNewOnly: "false" in values.

Behavior matrix

Entity state Before (=false) After (=true default)
In JSON, missing from DB (first install / new entry) INSERT INSERT ✅
In JSON, already in DB (current state) DELETE + INSERT, wipes API customizations SKIP, preserves API customizations
Only in DB (created via API, not in JSON) Untouched (unchanged) Untouched (unchanged)

Trade-off and ops impact

  • ✅ Application↔permission associations, role/user bindings, and any UI/API-managed RBAC state now survive Casdoor restarts.
  • ✅ First-time installs still bootstrap correctly from init_data.json.
  • ✅ Adding NEW entities to init_data.json still works on next restart (they get INSERTed).
  • ⚠️ UPDATING an existing entity's actions/resources/etc. via init_data.json becomes a no-op on environments that already have it. Future updates must use the SQL migration path already in place at init/casdoor-migrations/migrations/ (which is the canonical channel — see migrations 16, 28, 29 for bank-transfer/pix-btg precedent).

Rollout plan

This PR targets develop so that the chart is released as a *-beta.N version first and validated in Firmino before promotion to main (final release).

  1. Merge to develop → semantic-release publishes a vX.Y.Z-beta.N chart version
  2. Firmino deploy (dev environment) → QA validates with the auth team:
    • After a Casdoor pod restart, all application↔permission associations that were configured via API/UI in the previous state are still present
    • Adding a NEW entry to init_data.json on a separate test still results in INSERT on the next pod cycle (proves seed expansion still works)
    • User → role → group → application authorization flow returns identical results before and after the chart bump
  3. Once QA approves, promote developmain (standard back-merge) to publish the release version

Operator communication

After release, any future RBAC update on an existing entity must go via SQL migration in LerianStudio/plugin-auth/init/casdoor-migrations/migrations/, not by editing init_data.json. This matches what migrations/16_add_bank_transfer_permissions.up.sql, 28_add_bank_transfer_system_config_permissions.up.sql, and 29_add_pix_indirect_btg_webhooks_operator_permissions.up.sql already model. For brand-new entities (resources/permissions/roles/groups that don't exist anywhere yet), init_data.json continues to be the right channel.

Plugin-auth side

No changes required in LerianStudio/plugin-auth:

  • init/casdoor/app.conf/ in that repo is an empty directory — the Dockerfile does not copy any custom app.conf into the image
  • Casdoor reads its runtime config via beego, which picks up env vars produced from this ConfigMap (envFrom: configMapRef)
  • The initDataNewOnly ConfigMap key propagates directly to Casdoor as the env override

References

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 23, 2026

Review Change Stack

Walkthrough

The PR adds a new initDataNewOnly: "true" configuration entry to the auth backend ConfigMap in the plugin-access-manager Helm chart, enabling the backend to restrict initialization to new data only alongside the existing initDataFile setting.

Changes

Auth Backend Configuration

Layer / File(s) Summary
Init data configuration flag
charts/plugin-access-manager/templates/auth-backend/configmap.yaml
ConfigMap data key initDataNewOnly is set to "true" alongside the existing initDataFile key, enabling new-data-only initialization mode for the auth backend.

Comment @coderabbitai help to get the list of available commands and usage tips.

…rve API state

Casdoor v2.206.0 defaults to initDataNewOnly=false, which causes the
backend to DELETE+INSERT every entity present in init_data.json on
every restart (applications, roles, groups, permissions, etc.). Any
runtime modification performed via the Casdoor UI/API on a JSON-listed
entity — including application↔permission associations, role/user
bindings, and resource lists — is silently reset to the JSON state at
that point.

This was confirmed in production after a recent plugin-auth deploy:
applications kept their identity but lost the permission associations
that had been wired up via the Casdoor API. Source reference:
casdoor/casdoor@v2.206.0 object/init_data.go:initDefinedApplication
(and initDefinedPermission). With initDataNewOnly=true, existing
entries are skipped — only new entries (entities in JSON but not in
DB) are inserted, preserving everything an operator has touched via
API/UI.

Trade-offs:
- ✅ API/UI-managed associations survive restarts
- ✅ First-time installs (empty DB) still bootstrap from JSON correctly
- ✅ Adding new entries to init_data.json still works on next restart
- ⚠️ Updating an EXISTING entry's actions/resources via init_data.json
  becomes a no-op on environments that already have it. Updates must
  go through casdoor-migrations SQL scripts (the canonical path that
  init/casdoor-migrations/migrations/ already uses).

Plan-to-rollout:
- Merge to main → propagates via helmfile to dev/sandbox/stg/prd
- Develop deploys to Firmino — QA validates that:
  1. App↔permission associations persist after a Casdoor pod restart
  2. New seed entries added via init_data.json still get created on
     fresh installs
  3. No regression in user→role→group→app flow

X-Lerian-Ref: 0x1
@jeffersonrodrigues92 jeffersonrodrigues92 changed the base branch from main to develop May 23, 2026 17:45
@jeffersonrodrigues92 jeffersonrodrigues92 force-pushed the fix/casdoor-init-data-new-only-preserve-api branch from 6cf68ce to 8c5342c Compare May 23, 2026 17:45
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@charts/plugin-access-manager/templates/auth-backend/configmap.yaml`:
- Line 22: The change mixes adding the quote function to the template with the
unrelated initDataNewOnly change; revert the quote addition on the initDataFile
template line (the expression using initDataFile) in this PR so only
initDataNewOnly is included, then create a follow-up commit that solely updates
initDataFile to use {{ .Values.auth.backend.initDataFile | default
"./init_data.json" | quote }} to ensure YAML escaping; reference the template
key initDataFile when making these commits to keep the changes isolated and
clear.
- Line 23: Add a documented default for the missing values key by editing the
chart values file to include auth.backend.initDataNewOnly with a clear
description and a default value (true), explaining it controls whether
API/UI-managed state is preserved across restarts; place it under the existing
auth.backend block near related backend settings so templating
(.Values.auth.backend.initDataNewOnly) finds the documented default and users
understand the purpose (preserve API/UI-managed state on restart).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 51fddc6f-ce85-4986-bda8-60f847db9a01

📥 Commits

Reviewing files that changed from the base of the PR and between 6cf68ce and 8c5342c.

📒 Files selected for processing (1)
  • charts/plugin-access-manager/templates/auth-backend/configmap.yaml

quota: '{"organization": -1, "user": -1, "application": -1, "provider": -1}'
logConfig: '{"filename": "logs/casdoor.log", "maxdays": 99999, "perm": "0770"}'
initDataFile: {{ .Values.auth.backend.initDataFile | default "./init_data.json" | quote }} No newline at end of file
initDataFile: {{ .Values.auth.backend.initDataFile | default "./init_data.json" | quote }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Consider splitting the quote addition into a separate commit.

While adding quote to initDataFile is a good practice, it's technically a separate change from the initDataNewOnly addition. The quote function ensures proper YAML escaping, but this modification could have been isolated for clarity.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@charts/plugin-access-manager/templates/auth-backend/configmap.yaml` at line
22, The change mixes adding the quote function to the template with the
unrelated initDataNewOnly change; revert the quote addition on the initDataFile
template line (the expression using initDataFile) in this PR so only
initDataNewOnly is included, then create a follow-up commit that solely updates
initDataFile to use {{ .Values.auth.backend.initDataFile | default
"./init_data.json" | quote }} to ensure YAML escaping; reference the template
key initDataFile when making these commits to keep the changes isolated and
clear.

logConfig: '{"filename": "logs/casdoor.log", "maxdays": 99999, "perm": "0770"}'
initDataFile: {{ .Values.auth.backend.initDataFile | default "./init_data.json" | quote }} No newline at end of file
initDataFile: {{ .Values.auth.backend.initDataFile | default "./init_data.json" | quote }}
initDataNewOnly: {{ .Values.auth.backend.initDataNewOnly | default "true" | quote }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if initDataNewOnly is documented in values.yaml

# Search for initDataNewOnly in values.yaml files
fd -t f 'values.yaml' charts/plugin-access-manager/ --exec rg -n 'initDataNewOnly' {} \;

# Also check for the auth.backend.initDataNewOnly path
fd -t f 'values.yaml' charts/plugin-access-manager/ --exec cat {} \; | grep -A5 -B5 'backend:' | grep -i 'initData'

Repository: LerianStudio/helm

Length of output: 43


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find all usages of initDataNewOnly anywhere in the repo
echo "== RG initDataNewOnly =="
rg -n --hidden --no-ignore initDataNewOnly . || true

# 2) Locate values.yaml files for this chart (and nearby charts)
echo
echo "== values.yaml files under charts/plugin-access-manager =="
fd -t f 'values.yaml' charts/plugin-access-manager --full-path

# 3) For each discovered values.yaml under that chart, show whether auth.backend exists
echo
echo "== auth.backend section context in chart values.yaml files =="
while IFS= read -r f; do
  echo "--- $f ---"
  # Show lines around "auth:" and "backend:" and "initData"
  rg -n '(^|[[:space:]])auth:|(^|[[:space:]])backend:' "$f" || true
  rg -n 'initData' "$f" || true
done < <(fd -t f 'values.yaml' charts/plugin-access-manager --full-path)

# 4) Also check chart-level defaults if they exist elsewhere (e.g., values/*.yaml)
echo
echo "== Any files under plugin-access-manager named values*.yaml =="
fd -t f 'values*.ya?ml' charts/plugin-access-manager --full-path

Repository: LerianStudio/helm

Length of output: 485


🏁 Script executed:

#!/bin/bash
set -euo pipefail

VALUES_FILE="charts/plugin-access-manager/values.yaml"

echo "== initDataNewOnly in values.yaml =="
rg -n 'initDataNewOnly' "$VALUES_FILE" || true

echo
echo "== auth.backend block vicinity in values.yaml =="
# Print a small window around any auth/backend occurrence
rg -n '(^|[[:space:]])auth:|(^|[[:space:]])backend:' "$VALUES_FILE" || true

echo
echo "== Attempt to show lines around auth/backend keys (if present) =="
# If file is small enough, show a larger slice for manual reading
python3 - <<'PY'
import itertools
path="charts/plugin-access-manager/values.yaml"
with open(path,'r',encoding='utf-8') as f:
    lines=f.readlines()
# Print up to first 200 lines (usually enough for auth config in values.yaml)
for i,line in enumerate(lines[:200], start=1):
    if any(k in line for k in ["auth:", "backend:", "initData"]):
        print(f"{i:4d}:{line.rstrip()}")
PY

Repository: LerianStudio/helm

Length of output: 339


🏁 Script executed:

#!/bin/bash
set -euo pipefail

VALUES_FILE="charts/plugin-access-manager/values.yaml"

echo "== initData occurrences in values.yaml =="
rg -n 'initData' "$VALUES_FILE" || true

echo
echo "== Show auth/backend sections around the reported lines =="
# Dump ~60 lines around each occurrence of "backend:" and each "auth:" occurrence after/before
python3 - <<'PY'
path="charts/plugin-access-manager/values.yaml"
with open(path,'r',encoding='utf-8') as f:
    lines=f.readlines()

def dump_around(center, half=35):
    start=max(center-half,1)
    end=min(center+half,len(lines))
    for i in range(start,end+1):
        print(f"{i:4d}:{lines[i-1].rstrip()}")

for c in [144, 289, 383, 424]:
    print("\n--- context around", c, "---")
    dump_around(c, half=35)
PY

Repository: LerianStudio/helm

Length of output: 9705


Document .Values.auth.backend.initDataNewOnly in charts/plugin-access-manager/values.yaml
The template sets:

initDataNewOnly: {{ .Values.auth.backend.initDataNewOnly | default "true" | quote }}

…but charts/plugin-access-manager/values.yaml does not define auth.backend.initDataNewOnly under the existing auth.backend: block (so the option is undocumented). Add the key with a description of what it controls (preserving API/UI-managed state on restart).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@charts/plugin-access-manager/templates/auth-backend/configmap.yaml` at line
23, Add a documented default for the missing values key by editing the chart
values file to include auth.backend.initDataNewOnly with a clear description and
a default value (true), explaining it controls whether API/UI-managed state is
preserved across restarts; place it under the existing auth.backend block near
related backend settings so templating (.Values.auth.backend.initDataNewOnly)
finds the documented default and users understand the purpose (preserve
API/UI-managed state on restart).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant