Skip to content

Commit b365d21

Browse files
authored
Merge pull request #68 from mutablelogic/v4
Updated router for inclusion of scopes
2 parents 8d5e88d + a43dfa7 commit b365d21

29 files changed

+1087
-339
lines changed

go.mod

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ require (
99
github.com/djthorpe/go-tablewriter v0.0.7
1010
github.com/mutablelogic/go-client v1.0.8
1111
github.com/stretchr/testify v1.9.0
12-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
1312
)
1413

1514
require (

go.sum

-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
1010
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
1111
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
1212
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
13-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
14-
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
1513
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
1614
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1715
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

pkg/handler/auth/client/client.go

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
Implements an API client for the Token auth API (https://github.com/mutablelogic/go-server/pkg/handler/auth)
3+
*/
4+
package client
5+
6+
import (
7+
8+
// Packages
9+
"time"
10+
11+
"github.com/mutablelogic/go-client"
12+
"github.com/mutablelogic/go-server/pkg/handler/auth"
13+
)
14+
15+
///////////////////////////////////////////////////////////////////////////////
16+
// TYPES
17+
18+
type Client struct {
19+
*client.Client
20+
}
21+
22+
///////////////////////////////////////////////////////////////////////////////
23+
// LIFECYCLE
24+
25+
// Create a new API client, providing the endpoint (ie, http://example.com/api/auth)
26+
func New(endPoint string, opts ...client.ClientOpt) (*Client, error) {
27+
// Create client
28+
client, err := client.New(append(opts, client.OptEndpoint(endPoint))...)
29+
if err != nil {
30+
return nil, err
31+
}
32+
33+
// Return the client
34+
return &Client{client}, nil
35+
}
36+
37+
///////////////////////////////////////////////////////////////////////////////
38+
// METHODS
39+
40+
// Return all tokens
41+
func (c *Client) List() ([]auth.Token, error) {
42+
var response []auth.Token
43+
if err := c.Do(nil, &response); err != nil {
44+
return nil, err
45+
}
46+
return response, nil
47+
}
48+
49+
// Get token details (apart from the value)
50+
func (c *Client) Get(name string) (auth.Token, error) {
51+
var response auth.Token
52+
if err := c.Do(nil, &response, client.OptPath(name)); err != nil {
53+
return auth.Token{}, err
54+
}
55+
return response, nil
56+
}
57+
58+
// Delete a token
59+
func (c *Client) Delete(name string) error {
60+
if err := c.Do(client.MethodDelete, nil, client.OptPath(name)); err != nil {
61+
return err
62+
}
63+
// Return success
64+
return nil
65+
}
66+
67+
// Create a token with name, duration and scopes, and return the token
68+
func (c *Client) Create(name string, expires_in time.Duration, scopes ...string) (auth.Token, error) {
69+
var response auth.Token
70+
71+
// Request->Response
72+
if payload, err := client.NewJSONRequest(auth.NewCreateToken(name, expires_in, scopes...)); err != nil {
73+
return auth.Token{}, err
74+
} else if err := c.Do(payload, &response); err != nil {
75+
return auth.Token{}, err
76+
}
77+
78+
// Return success
79+
return response, nil
80+
}
+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package client_test
2+
3+
import (
4+
"os"
5+
"testing"
6+
"time"
7+
8+
// Packages
9+
client "github.com/mutablelogic/go-client"
10+
auth "github.com/mutablelogic/go-server/pkg/handler/auth/client"
11+
assert "github.com/stretchr/testify/assert"
12+
)
13+
14+
func Test_client_001(t *testing.T) {
15+
assert := assert.New(t)
16+
opts := []client.ClientOpt{
17+
client.OptTrace(os.Stderr, true),
18+
}
19+
client, err := auth.New(GetEndpoint(t), opts...)
20+
assert.NoError(err)
21+
assert.NotNil(client)
22+
}
23+
24+
func Test_client_002(t *testing.T) {
25+
assert := assert.New(t)
26+
opts := []client.ClientOpt{
27+
client.OptTrace(os.Stderr, true),
28+
}
29+
if token := GetToken(t); token != "" {
30+
opts = append(opts, client.OptReqToken(client.Token{
31+
Scheme: "Bearer",
32+
Value: token,
33+
}))
34+
}
35+
36+
client, err := auth.New(GetEndpoint(t), opts...)
37+
assert.NoError(err)
38+
39+
tokens, err := client.List()
40+
assert.NoError(err)
41+
assert.NotEmpty(tokens)
42+
}
43+
44+
func Test_client_003(t *testing.T) {
45+
assert := assert.New(t)
46+
opts := []client.ClientOpt{
47+
client.OptTrace(os.Stderr, true),
48+
}
49+
if token := GetToken(t); token != "" {
50+
opts = append(opts, client.OptReqToken(client.Token{
51+
Scheme: "Bearer",
52+
Value: token,
53+
}))
54+
}
55+
client, err := auth.New(GetEndpoint(t), opts...)
56+
assert.NoError(err)
57+
58+
// Create a new token which doesn't expire
59+
token, err := client.Create(t.Name(), 0)
60+
if !assert.NoError(err) {
61+
t.SkipNow()
62+
}
63+
64+
// Get the token
65+
token2, err := client.Get(t.Name())
66+
if !assert.NoError(err) {
67+
t.SkipNow()
68+
}
69+
70+
// Delete the token
71+
err = client.Delete(t.Name())
72+
assert.NoError(err)
73+
74+
// Check tokens
75+
assert.Equal(token.Name, token2.Name)
76+
}
77+
78+
func Test_client_004(t *testing.T) {
79+
assert := assert.New(t)
80+
opts := []client.ClientOpt{
81+
client.OptTrace(os.Stderr, false),
82+
}
83+
if token := GetToken(t); token != "" {
84+
opts = append(opts, client.OptReqToken(client.Token{
85+
Scheme: "Bearer",
86+
Value: token,
87+
}))
88+
}
89+
client, err := auth.New(GetEndpoint(t), opts...)
90+
assert.NoError(err)
91+
92+
// Create a new token with scopes "a", "b" and "c" which expires in 1 minute
93+
token, err := client.Create(t.Name(), time.Minute, "a", "b", "c")
94+
if !assert.NoError(err) {
95+
t.SkipNow()
96+
}
97+
98+
// Get the token
99+
token2, err := client.Get(t.Name())
100+
if !assert.NoError(err) {
101+
t.SkipNow()
102+
}
103+
104+
// Check the scopes
105+
assert.ElementsMatch(token.Scope, token2.Scope)
106+
assert.Equal(token.Expire, token2.Expire)
107+
108+
// Delete the token
109+
err = client.Delete(t.Name())
110+
assert.NoError(err)
111+
112+
t.Log(token, token2)
113+
}
114+
115+
///////////////////////////////////////////////////////////////////////////////
116+
// ENVIRONMENT
117+
118+
func GetEndpoint(t *testing.T) string {
119+
key := os.Getenv("AUTH_ENDPOINT")
120+
if key == "" {
121+
t.Skip("AUTH_ENDPOINT not set")
122+
t.SkipNow()
123+
}
124+
if key[len(key)-1] != '/' {
125+
key += "/"
126+
}
127+
return key
128+
}
129+
130+
func GetToken(t *testing.T) string {
131+
return os.Getenv("AUTH_TOKEN")
132+
}

pkg/handler/auth/context.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package auth
2+
3+
import (
4+
"context"
5+
)
6+
7+
////////////////////////////////////////////////////////////////////////////////
8+
// TYPES
9+
10+
type authContextKey int
11+
12+
////////////////////////////////////////////////////////////////////////////////
13+
// GLOBALS
14+
15+
const (
16+
_ authContextKey = iota
17+
contextName
18+
contextScope
19+
)
20+
21+
////////////////////////////////////////////////////////////////////////////////
22+
// PUBLIC METHODS
23+
24+
// WithTokenName returns a context with the given auth token name
25+
func WithTokenName(ctx context.Context, token Token) context.Context {
26+
return context.WithValue(ctx, contextName, token.Name)
27+
}
28+
29+
// WithTokenScope returns a context with the given auth token scope
30+
func WithTokenScope(ctx context.Context, token Token) context.Context {
31+
return context.WithValue(ctx, contextScope, token.Scope)
32+
}
33+
34+
// WithToken returns a context with the given auth token
35+
func WithToken(ctx context.Context, token Token) context.Context {
36+
if token.Name != "" {
37+
ctx = WithTokenName(ctx, token)
38+
}
39+
if len(token.Scope) > 0 {
40+
ctx = WithTokenScope(ctx, token)
41+
}
42+
return ctx
43+
}
44+
45+
// TokenName returns the token name from the context
46+
func TokenName(ctx context.Context) string {
47+
return str(ctx, contextName)
48+
}
49+
50+
// TokenScope returns the token scope from the context, or nil
51+
func TokenScope(ctx context.Context) []string {
52+
if value, ok := ctx.Value(contextScope).([]string); ok {
53+
return value
54+
}
55+
return nil
56+
}
57+
58+
////////////////////////////////////////////////////////////////////////////////
59+
// PRIVATE METHODS
60+
61+
func str(ctx context.Context, key authContextKey) string {
62+
if value, ok := ctx.Value(key).(string); ok {
63+
return value
64+
}
65+
return ""
66+
}

0 commit comments

Comments
 (0)