Files
nmon/internal/api/tokens_api.go

73 lines
2.3 KiB
Go

package api
import (
"context"
"strconv"
"github.com/danielgtaylor/huma/v2"
"github.com/v10networks/nmon/internal/auth"
"github.com/v10networks/nmon/internal/store"
)
// apiTokenIDPrefix distinguishes the id of a viewer or admin token from a push token's: the two kinds live in
// different tables, so their numbers overlap.
const apiTokenIDPrefix = "a"
// APITokenAdmin mints, lists and revokes viewer and admin API tokens. *auth.Tokens implements it.
type APITokenAdmin interface {
Mint(ctx context.Context, actor string, o auth.MintOptions) (*store.APIToken, string, error)
Revoke(ctx context.Context, actor string, id int64) error
List(ctx context.Context) ([]store.APIToken, error)
}
func apiTokenView(t *store.APIToken) TokenView {
return TokenView{
ID: apiTokenIDPrefix + strconv.FormatInt(t.ID, 10), Name: t.Name, Scope: t.Scope, Devices: []string{},
ExpiresAt: utcp(t.ExpiresAt), RevokedAt: utcp(t.RevokedAt),
}
}
func (s *server) createAPIToken(ctx context.Context, name string, b TokenBody) (*struct{ Body TokenCreated }, error) {
if s.APITokens == nil {
return nil, errNotConfigured("API tokens")
}
if len(b.Devices) > 0 {
return nil, huma.Error422UnprocessableEntity("devices: only push tokens are bound to devices")
}
row, secret, err := s.APITokens.Mint(ctx, actorOf(ctx), auth.MintOptions{Name: name, Scope: auth.Role(b.Scope), ExpiresAt: b.ExpiresAt})
if err != nil {
return nil, s.storeErr(err) // Mint audits the creation itself
}
return &struct{ Body TokenCreated }{TokenCreated{TokenView: apiTokenView(row), Secret: secret}}, nil
}
func (s *server) listAPITokens(ctx context.Context) ([]TokenView, error) {
if s.APITokens == nil {
return nil, nil
}
rows, err := s.APITokens.List(ctx)
if err != nil {
return nil, err
}
out := make([]TokenView, 0, len(rows))
for i := range rows {
out = append(out, apiTokenView(&rows[i])) // the hash stays in the row
}
return out, nil
}
func (s *server) revokeAPIToken(ctx context.Context, rest string) (*struct{}, error) {
if s.APITokens == nil {
return nil, errNotConfigured("API tokens")
}
id, err := idParam(rest)
if err != nil {
return nil, err
}
if err := s.APITokens.Revoke(ctx, actorOf(ctx), id); err != nil {
return nil, s.storeErr(err) // Revoke audits itself
}
return &struct{}{}, nil
}