Add include-subgroups capabilities for JWTs

Implements #285
This commit is contained in:
izeau
2025-10-19 17:20:00 +02:00
committed by Juliusz Chroboczek
parent f9b86db9fb
commit e3d161e056
3 changed files with 35 additions and 8 deletions
+4
View File
@@ -587,6 +587,10 @@ the same format as in the `joined` message. Since the client will only
use the token once, at the very beginning of the session, the tokens
issued may have a short lifetime (on the order of 30s).
A boolean `include-subgroups` claim may be included in the JWT payload. It
has the same effect as the `include-subgroups` parameter used when generating
a stateful token.
## Authentication portal
If a group's status dictionary has a non-empty `authPortal` field, Galene
+15 -8
View File
@@ -9,7 +9,6 @@ import (
"errors"
"math/big"
"net/url"
"path"
"strings"
"time"
@@ -201,7 +200,14 @@ func (token *JWT) Check(host, group string, username *string) (string, []string,
if err != nil {
return "", nil, err
}
ok := false
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return "", nil, errors.New("unexpected type for token")
}
incSubgroups, _ := claims["include-subgroups"].(bool)
ok = false
for _, u := range aud {
url, err := url.Parse(u)
if err != nil {
@@ -215,7 +221,13 @@ func (token *JWT) Check(host, group string, username *string) (string, []string,
continue
}
}
if url.Path == path.Join("/group", group)+"/" {
// aud path takes the form /group/<groupname>/
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
break
}
@@ -224,11 +236,6 @@ func (token *JWT) Check(host, group string, username *string) (string, []string,
return "", nil, errors.New("token for wrong group")
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return "", nil, errors.New("unexpected type for token")
}
var perms []string
if p, ok := claims["permissions"]; ok && p != nil {
perms, ok = toStringArray(p)
+16
View File
@@ -127,6 +127,11 @@ func TestJWT(t *testing.T) {
t.Errorf("goodToken is valid for wrong group")
}
_, _, err = tok.Check("galene.org:8443", "auth/subgroup", &john)
if err == nil {
t.Errorf("goodToken is valid for subgroup")
}
emptySubToken := "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIiLCJhdWQiOiJodHRwczovL2dhbGVuZS5vcmc6ODQ0My9ncm91cC9hdXRoLyIsInBlcm1pc3Npb25zIjpbInByZXNlbnQiXSwiaWF0IjoxNjQ1MzEwMjk0LCJleHAiOjI5MDY3NTAyOTQsImlzcyI6Imh0dHA6Ly9sb2NhbGhvc3Q6MTIzNC8ifQo.xwpHIRzKAIgiHKG1pVQyZlXcolmvRwNvBm6FN2gTwZw"
tok, err = Parse(emptySubToken, keys)
@@ -174,4 +179,15 @@ func TestJWT(t *testing.T) {
if err == nil {
t.Errorf("noneToken is good")
}
subgroupsToken := "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJqb2huIiwiYXVkIjoiaHR0cHM6Ly9nYWxlbmUub3JnOjg0NDMvZ3JvdXAvYXV0aC8iLCJpbmNsdWRlLXN1Ymdyb3VwcyI6dHJ1ZSwicGVybWlzc2lvbnMiOlsicHJlc2VudCJdLCJpYXQiOjE2NDUzMTAyOTQsImV4cCI6MjkwNjc1MDI5NCwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDoxMjM0LyJ9.ty4B7cJPCGJPMadwOCNMGQirIByj1L9AxWiDFnBm5wA"
tok, err = Parse(subgroupsToken, keys)
if err != nil {
t.Errorf("subgroupsToken is not valid: %v", err)
}
_, _, err = tok.Check("galene.org:8443", "auth/subgroup", &john)
if err != nil {
t.Errorf("subgroupsToken is not valid for subgroup: %v", err)
}
}