feat(api): scoped, optionally expiring API tokens (#6201)

* security(api): add scoped expiring API tokens

* security(api): make scoped token lifecycle enforceable

---------

Co-authored-by: n0ctal <293235942+n0ctal@users.noreply.github.com>
This commit is contained in:
n0ctal
2026-08-15 18:31:49 +05:00
committed by GitHub
parent aecbad3ab1
commit 1230559e69
19 changed files with 785 additions and 94 deletions
+19
View File
@@ -121,6 +121,9 @@ func initModels() error {
if err := normalizeApiTokenCreatedAtSeconds(); err != nil {
return err
}
if err := migrateApiTokenScopeAndExpiry(); err != nil {
return err
}
if err := dropLegacyForeignKeys(); err != nil {
return err
}
@@ -2075,6 +2078,22 @@ func normalizeApiTokenCreatedAtSeconds() error {
UpdateColumn("created_at", gorm.Expr("created_at / ?", 1000)).Error
}
func migrateApiTokenScopeAndExpiry() error {
m := db.Migrator()
if !m.HasColumn(&model.ApiToken{}, "Scope") {
if err := m.AddColumn(&model.ApiToken{}, "Scope"); err != nil {
return err
}
}
if !m.HasColumn(&model.ApiToken{}, "ExpiresAt") {
if err := m.AddColumn(&model.ApiToken{}, "ExpiresAt"); err != nil {
return err
}
}
return db.Model(&model.ApiToken{}).Where("scope IS NULL OR TRIM(scope) = ''").
Updates(map[string]any{"scope": model.ApiScopeAdmin, "expires_at": 0}).Error
}
// openPostgresWithRetry retries the initial PostgreSQL connection with
// backoff so a database that starts slower than the panel (or drops out
// briefly) does not immediately kill the process and trip systemd's