Rework include-subgroups for cryptographic tokens.

This commit is contained in:
Juliusz Chroboczek
2025-11-20 18:13:09 +01:00
parent 24ee1f6a18
commit 4cf2c9e14c
2 changed files with 54 additions and 8 deletions
+18 -8
View File
@@ -188,6 +188,22 @@ func parseJWT(token string, keys []map[string]any) (*JWT, error) {
return (*JWT)(t), nil return (*JWT)(t), nil
} }
func matchGroup(pth, group string, includeSubgroups bool) bool {
if !includeSubgroups {
return pth == "/group/"+group+"/"
}
if !strings.HasPrefix(pth, "/group/") {
return false
}
if !strings.HasSuffix(pth, "/") {
return false
}
return strings.HasPrefix("/group/"+group+"/", pth)
}
func (token *JWT) Check(host, group string, username *string) (string, []string, error) { func (token *JWT) Check(host, group string, username *string) (string, []string, error) {
sub, err := token.Claims.GetSubject() sub, err := token.Claims.GetSubject()
if err != nil { if err != nil {
@@ -205,7 +221,7 @@ func (token *JWT) Check(host, group string, username *string) (string, []string,
if !ok { if !ok {
return "", nil, errors.New("unexpected type for token") return "", nil, errors.New("unexpected type for token")
} }
incSubgroups, _ := claims["include-subgroups"].(bool) includeSubgroups, _ := claims["include-subgroups"].(bool)
ok = false ok = false
for _, u := range aud { for _, u := range aud {
@@ -221,13 +237,7 @@ func (token *JWT) Check(host, group string, username *string) (string, []string,
continue continue
} }
} }
// aud path takes the form /group/<groupname>/ if matchGroup(url.Path, group, includeSubgroups) {
if !strings.HasPrefix(url.Path, "/group/") || !strings.HasSuffix(url.Path, "/") {
continue
}
tokenGroup := url.Path[len("/group/") : len(url.Path)-1]
if group == tokenGroup ||
incSubgroups && (tokenGroup == "" || strings.HasPrefix(group, tokenGroup+"/")) {
ok = true ok = true
break break
} }
+36
View File
@@ -77,6 +77,42 @@ func TestJWKRS256(t *testing.T) {
} }
} }
func TestMatchGroup(t *testing.T) {
type tt struct {
p, g string
s bool
}
good := []tt{
{"/group/a/", "a", false},
{"/group/a/b/", "a/b", false},
{"/group/a/", "a", true},
{"/group/a/", "a/b", true},
{"/group/a/b/", "a/b", true},
{"/group/a/b/", "a/b/c", true},
}
bad := []tt{
{"/group/a/", "b", false},
{"/group/a", "a", false},
{"/group/a", "a", true},
{"/group/a/", "a/b", false},
{"/group/a/b/", "a", false},
{"/group/a/b/", "a", true},
}
for _, test := range good {
if !matchGroup(test.p, test.g, test.s) {
t.Errorf("%v %v %v didn't match", test.p, test.g, test.s)
}
}
for _, test := range bad {
if matchGroup(test.p, test.g, test.s) {
t.Errorf("%v %v %v matched", test.p, test.g, test.s)
}
}
}
func TestJWT(t *testing.T) { func TestJWT(t *testing.T) {
key := `{"alg":"HS256","k":"H7pCkktUl5KyPCZ7CKw09y1j460tfIv4dRcS1XstUKY","key_ops":["sign","verify"],"kty":"oct"}` key := `{"alg":"HS256","k":"H7pCkktUl5KyPCZ7CKw09y1j460tfIv4dRcS1XstUKY","key_ops":["sign","verify"],"kty":"oct"}`
var k map[string]interface{} var k map[string]interface{}