Feat v0.7.7 api tokens ext permissions (#61)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / e2e-smoke (push) Has been skipped
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-runners (push) Has been skipped
CI/CD / test-go-pg (push) Successful in 2m48s
CI/CD / test-sqlite (push) Successful in 2m49s
CI/CD / build-and-deploy (push) Successful in 28s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #61.
This commit is contained in:
2026-04-02 19:11:47 +00:00
committed by xcaliber
parent e02b13dc12
commit e4f0bdbd36
34 changed files with 1769 additions and 58 deletions

View File

@@ -41,6 +41,7 @@ import (
"github.com/gin-gonic/gin"
"go.starlark.net/starlark"
"armature/auth"
"armature/models"
"armature/sandbox"
"armature/store"
@@ -125,8 +126,17 @@ func (h *ExtAPIHandler) Handle(c *gin.Context) {
return
}
// ── 4b. Gate permission check ──────────────
if gatePerm, _ := pkg.Manifest["gate_permission"].(string); gatePerm != "" {
userPerms, err := auth.ResolvePermissions(c.Request.Context(), h.stores, getUserID(c))
if err != nil || !userPerms[gatePerm] {
c.JSON(http.StatusForbidden, gin.H{"error": "permission required: " + gatePerm})
return
}
}
// ── 5. Build request dict ──────────────────
reqDict, err := buildRequestDict(c, rawPath)
reqDict, err := buildRequestDict(c, rawPath, h.stores)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "failed to read request: " + err.Error()})
return
@@ -228,7 +238,7 @@ func matchAPIRoute(manifest map[string]any, method, path string) bool {
// "body": "...",
// "user_id": "uuid",
// }
func buildRequestDict(c *gin.Context, path string) (*starlark.Dict, error) {
func buildRequestDict(c *gin.Context, path string, stores ...store.Stores) (*starlark.Dict, error) {
// Read body (capped at 1 MB for safety)
body, err := io.ReadAll(io.LimitReader(c.Request.Body, 1<<20))
if err != nil {
@@ -251,13 +261,24 @@ func buildRequestDict(c *gin.Context, path string) (*starlark.Dict, error) {
}
}
d := starlark.NewDict(6)
d := starlark.NewDict(7)
_ = d.SetKey(starlark.String("method"), starlark.String(c.Request.Method))
_ = d.SetKey(starlark.String("path"), starlark.String(path))
_ = d.SetKey(starlark.String("headers"), hdrs)
_ = d.SetKey(starlark.String("query"), query)
_ = d.SetKey(starlark.String("body"), starlark.String(string(body)))
_ = d.SetKey(starlark.String("user_id"), starlark.String(getUserID(c)))
// Resolve user permissions for extension inline checks
if len(stores) > 0 {
userPerms, _ := auth.ResolvePermissions(c.Request.Context(), stores[0], getUserID(c))
permList := make([]starlark.Value, 0, len(userPerms))
for p := range userPerms {
permList = append(permList, starlark.String(p))
}
_ = d.SetKey(starlark.String("permissions"), starlark.NewList(permList))
}
return d, nil
}