diff --git a/galene-link/galene-link.go b/galene-link/galene-link.go new file mode 100644 index 0000000..7de5b3b --- /dev/null +++ b/galene-link/galene-link.go @@ -0,0 +1,115 @@ +package main + +import ( + "flag" + "fmt" + "log" + "net/url" + "path" + "time" + + "github.com/golang-jwt/jwt/v4" + + "github.com/jech/galene/group" + "github.com/jech/galene/token" +) + +func main() { + var username, kid, server string + var valid int + var tokenOnly bool + flag.StringVar(&group.Directory, "groups", "./groups/", + "group description `directory`") + flag.StringVar(&username, "user", "", "username") + flag.StringVar(&kid, "kid", "", "`id` of key to use") + flag.IntVar(&valid, "valid", 86400, "`seconds` validity") + flag.StringVar(&server, "server", "https://galene.org:8443", + "server `url`") + flag.BoolVar(&tokenOnly, "token", false, "generate token only") + flag.Parse() + + if flag.NArg() != 1 { + log.Fatal("One argument (the group URL) required") + } + groupname := flag.Arg(0) + + desc, err := group.GetDescription(groupname) + if err != nil { + log.Fatal("Get group description: ", err) + } + + serverURL, err := url.Parse(server) + if err != nil { + log.Fatal("Couldn't parse server URL") + } + pth := path.Join(path.Join(serverURL.Path, "group"), groupname) + "/" + groupURL := &url.URL{ + Scheme: serverURL.Scheme, + Host: serverURL.Host, + Path: pth, + } + + keys := desc.AuthKeys + var key map[string]interface{} + for _, k := range keys { + kid2, _ := k["kid"].(string) + if kid == "" || kid == kid2 { + key = k + break + } + } + + if key == nil { + log.Fatal("Couldn't find key") + } + + alg, ok := key["alg"].(string) + var method jwt.SigningMethod + if ok { + method = jwt.GetSigningMethod(alg) + } + if method == nil { + log.Fatal("Couldn't determine key signing method") + } + + kstring, err := token.ParseKey(key) + if err != nil { + log.Fatal("Couldn't parse key") + } + + now := time.Now() + end := now.Add(time.Second * time.Duration(valid)) + token := jwt.NewWithClaims( + method, + &jwt.MapClaims{ + "sub": username, + "aud": groupURL.String(), + "exp": &jwt.NumericDate{end}, + "nbf": &jwt.NumericDate{now}, + "iat": &jwt.NumericDate{now}, + "permissions": []string{"present"}, + }, + ) + + s, err := token.SignedString(kstring) + if err != nil { + log.Fatal("Couldn't sign token: ", err) + } + + if tokenOnly { + fmt.Println(s) + } else { + query := url.Values{} + if username != "" { + query.Add("username", username) + } + query.Add("token", s) + outURL := &url.URL{ + Scheme: groupURL.Scheme, + Host: groupURL.Host, + Path: groupURL.Path, + RawQuery: query.Encode(), + } + fmt.Println(outURL.String()) + } +} diff --git a/token/token.go b/token/token.go index 068c58b..cd04876 100644 --- a/token/token.go +++ b/token/token.go @@ -24,7 +24,7 @@ func parseBase64(k string, d map[string]interface{}) ([]byte, error) { return vv, nil } -func parseKey(key map[string]interface{}) (interface{}, error) { +func ParseKey(key map[string]interface{}) (interface{}, error) { kty, ok := key["kty"].(string) if !ok { return nil, errors.New("kty not found") @@ -99,7 +99,7 @@ func getKey(header map[string]interface{}, keys []map[string]interface{}) (inter kid2, _ := k["kid"].(string) alg2, _ := k["alg"].(string) if (kid == "" || kid == kid2) && alg == alg2 { - return parseKey(k) + return ParseKey(k) } } return nil, errors.New("key not found") diff --git a/token/token_test.go b/token/token_test.go index 358f2a7..f0c2e4b 100644 --- a/token/token_test.go +++ b/token/token_test.go @@ -21,13 +21,13 @@ func TestHS256(t *testing.T) { if err != nil { t.Fatalf("Unmarshal: %v", err) } - k, err := parseKey(j) + k, err := ParseKey(j) if err != nil { - t.Fatalf("parseKey: %v", err) + t.Fatalf("ParseKey: %v", err) } kk, ok := k.([]byte) if !ok || len(kk) != 32 { - t.Errorf("parseKey: got %v", kk) + t.Errorf("ParseKey: got %v", kk) } } @@ -44,13 +44,13 @@ func TestES256(t *testing.T) { if err != nil { t.Fatalf("Unmarshal: %v", err) } - k, err := parseKey(j) + k, err := ParseKey(j) if err != nil { - t.Fatalf("parseKey: %v", err) + t.Fatalf("ParseKey: %v", err) } kk, ok := k.(*ecdsa.PublicKey) if !ok || kk.Params().Name != "P-256" { - t.Errorf("parseKey: got %v", kk) + t.Errorf("ParseKey: got %v", kk) } if !kk.IsOnCurve(kk.X, kk.Y) { t.Errorf("point is not on curve")