Files
nadir-agent/internal/meta/whoami.go
T
urania 8fc4b236ac
build-and-release / release (push) Successful in 2m39s
fix: whomai with bearer
2026-06-23 18:02:52 +02:00

87 lines
2.8 KiB
Go

package meta
import (
"context"
"nadir/internal/auth"
"nadir/internal/module"
"nadir/internal/rbac"
"github.com/danielgtaylor/huma/v2"
)
// WhoamiInput carries the session cookie. The endpoint is not behind the RBAC
// middleware (it requires no specific permission), so it validates the session
// itself.
type WhoamiInput struct {
SessionID string `cookie:"nadir_session_id"`
Auth string `header:"Authorization"`
}
// WhoamiBody reports who the caller is and, per module, which permissions they
// actually hold. Combined with /api/_modules (the full module/permission grid),
// this gives the frontend everything it needs to render the permission matrix.
type WhoamiBody struct {
Username string `json:"username" example:"urania" doc:"Authenticated username"`
Permissions map[string][]string `json:"permissions" doc:"Module ID -> permissions the caller holds. Modules where they hold none are omitted."`
}
type WhoamiOutput struct{ Body WhoamiBody }
// RegisterWhoami adds the current-user endpoint. It resolves the caller's
// concrete grants by asking the RBAC store about each module's permissions,
// so "*" wildcards in roles are expanded for free.
func RegisterWhoami(api huma.API, sessions *auth.SessionStore, tokens *auth.TokenAuth, roles *rbac.RBAC, mods []module.Module) {
huma.Register(api, huma.Operation{
OperationID: "whoami",
Method: "GET",
Path: "/api/whoami",
Summary: "Get the current user and their permissions",
Description: "Returns the authenticated username and, per module, the " +
"permissions the caller holds (wildcards resolved). Pair with " +
"/api/_modules to render the full permission matrix.",
Tags: []string{"Meta"},
Errors: []int{401, 429},
}, func(ctx context.Context, in *WhoamiInput) (*WhoamiOutput, error) {
var username string
if raw, isBearer := auth.BearerToken(in.Auth); isBearer {
if tokens == nil {
return nil, huma.Error401Unauthorized("unauthorized")
}
name, ok, throttled := tokens.Verify(auth.ClientIP(ctx), raw)
if throttled {
return nil, huma.Error429TooManyRequests("too many failed token attempts; wait a minute")
}
if !ok {
return nil, huma.Error401Unauthorized("unauthorized")
}
username = name
} else {
sess, ok := sessions.GetByToken(in.SessionID)
if !ok {
return nil, huma.Error401Unauthorized("unauthorized")
}
username = sess.Username
}
held := make(map[string][]string)
for _, m := range mods {
var perms []string
for _, p := range m.Permissions() {
if roles.Can(username, m.ID(), p) {
perms = append(perms, string(p))
}
}
if len(perms) > 0 {
held[m.ID()] = perms
}
}
out := &WhoamiOutput{}
out.Body = WhoamiBody{Username: username, Permissions: held}
return out, nil
})
}