Feat v0.7.7 API tokens + extension permissions (#61)

Personal access tokens (PATs) for programmatic API access with
SHA-256 hashing, permission scoping (git model), and Settings/Admin UI.
Extension-declared user permissions with dynamic registry, gate_permission
manifest field, permissions Starlark module, and grouped admin UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 18:20:16 +00:00
parent e02b13dc12
commit f32eefab14
34 changed files with 1769 additions and 58 deletions

View File

@@ -1,10 +1,13 @@
package handlers
import (
"context"
"log"
"net/http"
"github.com/gin-gonic/gin"
"armature/auth"
"armature/models"
"armature/store"
)
@@ -167,6 +170,28 @@ func (h *ExtPermHandler) maybeSuspend(c *gin.Context, pkgID string) {
}
}
// RegisterAllExtensionUserPermissions scans all active packages and registers
// their user_permissions in the dynamic permission registry. Called at boot.
func RegisterAllExtensionUserPermissions(stores store.Stores) {
ctx := context.Background()
pkgs, err := stores.Packages.List(ctx)
if err != nil {
return
}
count := 0
for _, pkg := range pkgs {
if pkg.Manifest != nil {
SyncUserPermissions(pkg.Manifest, pkg.ID)
if _, ok := pkg.Manifest["user_permissions"]; ok {
count++
}
}
}
if count > 0 {
log.Printf(" ✅ Registered user permissions from %d package(s)", count)
}
}
// ── Manifest Permission Parsing ──────────────
// SyncManifestPermissions extracts the "permissions" array from a
@@ -208,5 +233,31 @@ func SyncManifestPermissions(c *gin.Context, stores store.Stores, pkgID string,
// Package needs review before activation
_ = stores.Packages.SetStatus(c.Request.Context(), pkgID, models.PackageStatusPendingReview)
// Register user-facing permissions in the dynamic permission registry
SyncUserPermissions(manifest, pkgID)
return perms
}
// SyncUserPermissions registers extension-declared user permissions in the
// kernel's dynamic permission registry. Called on install and at boot.
func SyncUserPermissions(manifest map[string]any, pkgID string) {
raw, ok := manifest["user_permissions"]
if !ok {
return
}
arr, ok := raw.([]any)
if !ok {
return
}
var userPerms []string
for _, v := range arr {
if s, ok := v.(string); ok && s != "" {
userPerms = append(userPerms, s)
}
}
if len(userPerms) > 0 {
auth.RegisterExtensionPermissions(pkgID, userPerms)
}
}