Hierarchical tokens.

This commit is contained in:
Juliusz Chroboczek
2025-08-01 14:56:35 +02:00
parent b88e2f4d93
commit a217b7bbc4
3 changed files with 66 additions and 18 deletions
+6 -1
View File
@@ -1781,7 +1781,11 @@ func handleClientMessage(c *webClient, m clientMessage) error {
if tok.Group != c.group.Name() { if tok.Group != c.group.Name() {
return terror("error", "wrong group in token") return terror("error", "wrong group in token")
} }
if tok.IncludeSubgroups {
return terror("error",
"hierarchical token not allowed",
)
}
if tok.Expires == nil { if tok.Expires == nil {
return terror("error", "token doesn't expire") return terror("error", "token doesn't expire")
} }
@@ -1837,6 +1841,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
return terror("error", err.Error()) return terror("error", err.Error())
} }
if tok.Group != "" || tok.Username != nil || if tok.Group != "" || tok.Username != nil ||
tok.IncludeSubgroups ||
tok.Permissions != nil || tok.Permissions != nil ||
tok.IssuedBy != nil || tok.IssuedBy != nil ||
tok.IssuedAt != nil { tok.IssuedAt != nil {
+33 -17
View File
@@ -8,6 +8,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
"strings"
"sync" "sync"
"time" "time"
) )
@@ -16,26 +17,28 @@ var ErrTagMismatch = errors.New("tag mismatch")
// A stateful token // A stateful token
type Stateful struct { type Stateful struct {
Token string `json:"token"` Token string `json:"token"`
Group string `json:"group"` Group string `json:"group"`
Username *string `json:"username,omitempty"` IncludeSubgroups bool `json:"includeSubgroups,omitempty"`
Permissions []string `json:"permissions"` Username *string `json:"username,omitempty"`
Expires *time.Time `json:"expires,omitempty"` Permissions []string `json:"permissions"`
NotBefore *time.Time `json:"not-before,omitempty"` Expires *time.Time `json:"expires,omitempty"`
IssuedAt *time.Time `json:"issuedAt,omitempty"` NotBefore *time.Time `json:"not-before,omitempty"`
IssuedBy *string `json:"issuedBy,omitempty"` IssuedAt *time.Time `json:"issuedAt,omitempty"`
IssuedBy *string `json:"issuedBy,omitempty"`
} }
func (token *Stateful) Clone() *Stateful { func (token *Stateful) Clone() *Stateful {
return &Stateful{ return &Stateful{
Token: token.Token, Token: token.Token,
Group: token.Group, Group: token.Group,
Username: token.Username, IncludeSubgroups: token.IncludeSubgroups,
Permissions: append([]string(nil), token.Permissions...), Username: token.Username,
Expires: token.Expires, Permissions: append([]string(nil), token.Permissions...),
NotBefore: token.NotBefore, Expires: token.Expires,
IssuedAt: token.IssuedAt, NotBefore: token.NotBefore,
IssuedBy: token.IssuedBy, IssuedAt: token.IssuedAt,
IssuedBy: token.IssuedBy,
} }
} }
@@ -83,8 +86,21 @@ func Get(token string) (*Stateful, string, error) {
return tokens.Get(token) return tokens.Get(token)
} }
func (token *Stateful) match(group string) bool {
if group == "" {
return false
}
if group == token.Group {
return true
}
if token.IncludeSubgroups {
return strings.HasPrefix(group, token.Group + "/")
}
return false
}
func (token *Stateful) Check(host, group string, username *string) (string, []string, error) { func (token *Stateful) Check(host, group string, username *string) (string, []string, error) {
if token.Group == "" || group != token.Group { if !token.match(group) {
return "", nil, errors.New("token for bad group") return "", nil, errors.New("token for bad group")
} }
now := time.Now() now := time.Now()
+27
View File
@@ -72,6 +72,14 @@ func TestStatefulCheck(t *testing.T) {
Expires: &future, Expires: &future,
NotBefore: &nearFuture, NotBefore: &nearFuture,
} }
token5 := &Stateful{
Token: "token",
Group: "group",
IncludeSubgroups: true,
Username: &user,
Permissions: []string{"present", "message"},
Expires: &future,
}
success := []struct { success := []struct {
token *Stateful token *Stateful
@@ -107,6 +115,20 @@ func TestStatefulCheck(t *testing.T) {
expUsername: "", expUsername: "",
expPermissions: []string{"present", "message"}, expPermissions: []string{"present", "message"},
}, },
{
token: token5,
group: "group",
username: &user,
expUsername: "user",
expPermissions: []string{"present", "message"},
},
{
token: token5,
group: "group/subgroup",
username: &user,
expUsername: "user",
expPermissions: []string{"present", "message"},
},
} }
for i, s := range success { for i, s := range success {
@@ -139,6 +161,11 @@ func TestStatefulCheck(t *testing.T) {
group: "group", group: "group",
username: &user, username: &user,
}, },
{
token: token5,
group: "groups",
username: &user,
},
} }
for i, s := range failure { for i, s := range failure {