daef7ea190
Helper functions (matrix-mas capability group): - mas_client_register_bash_infra: register/sync OAuth clients via mas-cli - mas_syn2mas_migration_bash_infra: dry-run + apply user migration to MAS - synapse_msc3861_enable_go_infra: edit homeserver.yaml MSC3861 block (with diff) - wellknown_oidc_patch_go_infra: patch well-known JSON with msc2965.authentication - synapse_login_flows_check_go_infra: health-check post-migration login flows Flows + issues for custom Matrix clients (PC + Android): - 0010 matrix-client-pc: Wails + React+Mantine (issues 0147-0153) - 0011 matrix-client-android: Kotlin + Compose (issues 0154-0161) - 0162 enable MAS as auth provider (Synapse delegate) — EXECUTED on VPS - 0163 custom admin panel propio (sustituye synapse-admin) Production state (organic-machine.com): - Synapse migrated SQLite -> Postgres - MSC3861 active, password_config disabled - 21 users + 41 access_tokens migrated via syn2mas - 4 MAS clients registered (element, matrix_pc, matrix_android, admin_panel) - synapse-admin container removed + Coolify route deleted - well-known patched with org.matrix.msc2965.authentication Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
123 lines
4.0 KiB
Go
123 lines
4.0 KiB
Go
package infra
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// WellknownOidcPatchConfig holds the parameters for WellknownOidcPatch.
|
|
type WellknownOidcPatchConfig struct {
|
|
WellknownJsonPath string // absolute path to the .well-known/matrix/client JSON file
|
|
Issuer string // MAS issuer URL, must end with "/" (RFC 8414)
|
|
AccountURL string // MAS account page URL
|
|
BackupDir string // directory where the backup file is written
|
|
DryRun bool // if true, return Before/After without writing
|
|
}
|
|
|
|
// WellknownOidcPatchResult is returned by WellknownOidcPatch.
|
|
type WellknownOidcPatchResult struct {
|
|
BackupPath string // path of the backup file; empty on DryRun
|
|
Before string // original JSON (pretty-printed, 2-space indent)
|
|
After string // patched JSON (pretty-printed, 2-space indent)
|
|
Modified bool // false if the key already existed with identical values
|
|
}
|
|
|
|
// WellknownOidcPatch reads a Matrix .well-known/matrix/client JSON file,
|
|
// adds (or updates) the org.matrix.msc2965.authentication key with the
|
|
// supplied MAS issuer and account URL, and writes the result back to the
|
|
// same path. All existing keys (m.homeserver, org.matrix.msc4143.rtc_foci,
|
|
// etc.) are preserved. A timestamped backup is created in BackupDir before
|
|
// any write. Set DryRun to true to preview the change without touching the
|
|
// filesystem.
|
|
func WellknownOidcPatch(cfg WellknownOidcPatchConfig) (WellknownOidcPatchResult, error) {
|
|
const oidcKey = "org.matrix.msc2965.authentication"
|
|
|
|
// 1. Read existing file.
|
|
raw, err := os.ReadFile(cfg.WellknownJsonPath)
|
|
if err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: read %s: %w", cfg.WellknownJsonPath, err)
|
|
}
|
|
|
|
// 2. Parse into a generic map to preserve unknown keys.
|
|
var doc map[string]any
|
|
if err := json.Unmarshal(raw, &doc); err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: invalid JSON in %s: %w", cfg.WellknownJsonPath, err)
|
|
}
|
|
|
|
// 3. Pretty-print Before.
|
|
beforeBytes, err := json.MarshalIndent(doc, "", " ")
|
|
if err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: marshal before: %w", err)
|
|
}
|
|
before := string(beforeBytes)
|
|
|
|
// 4. Build the new authentication block.
|
|
newAuth := map[string]any{
|
|
"issuer": cfg.Issuer,
|
|
"account": cfg.AccountURL,
|
|
}
|
|
|
|
// 5. Check if the key already exists with identical values.
|
|
modified := true
|
|
if existing, ok := doc[oidcKey]; ok {
|
|
existingBytes, _ := json.Marshal(existing)
|
|
newBytes, _ := json.Marshal(newAuth)
|
|
if string(existingBytes) == string(newBytes) {
|
|
modified = false
|
|
}
|
|
}
|
|
|
|
if !modified {
|
|
return WellknownOidcPatchResult{
|
|
BackupPath: "",
|
|
Before: before,
|
|
After: before,
|
|
Modified: false,
|
|
}, nil
|
|
}
|
|
|
|
// 6. Apply the patch.
|
|
doc[oidcKey] = newAuth
|
|
|
|
afterBytes, err := json.MarshalIndent(doc, "", " ")
|
|
if err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: marshal after: %w", err)
|
|
}
|
|
after := string(afterBytes)
|
|
|
|
// 7. DryRun: return without writing anything.
|
|
if cfg.DryRun {
|
|
return WellknownOidcPatchResult{
|
|
BackupPath: "",
|
|
Before: before,
|
|
After: after,
|
|
Modified: true,
|
|
}, nil
|
|
}
|
|
|
|
// 8. Create backup.
|
|
if err := os.MkdirAll(cfg.BackupDir, 0o755); err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: mkdir backup dir: %w", err)
|
|
}
|
|
backupName := fmt.Sprintf("wellknown_%d.json", time.Now().Unix())
|
|
backupPath := filepath.Join(cfg.BackupDir, backupName)
|
|
if err := os.WriteFile(backupPath, raw, 0o644); err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: write backup: %w", err)
|
|
}
|
|
|
|
// 9. Write patched file.
|
|
if err := os.WriteFile(cfg.WellknownJsonPath, afterBytes, 0o644); err != nil {
|
|
return WellknownOidcPatchResult{}, fmt.Errorf("wellknown_oidc_patch: write %s: %w", cfg.WellknownJsonPath, err)
|
|
}
|
|
|
|
return WellknownOidcPatchResult{
|
|
BackupPath: backupPath,
|
|
Before: before,
|
|
After: after,
|
|
Modified: true,
|
|
}, nil
|
|
}
|