Skip to content

Commit

Permalink
Merge pull request #68 from mutablelogic/v4
Browse files Browse the repository at this point in the history
Updated router for inclusion of scopes
  • Loading branch information
djthorpe authored Jun 5, 2024
2 parents 8d5e88d + a43dfa7 commit b365d21
Show file tree
Hide file tree
Showing 29 changed files with 1,087 additions and 339 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ require (
github.com/djthorpe/go-tablewriter v0.0.7
github.com/mutablelogic/go-client v1.0.8
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
)

require (
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
Expand Down
80 changes: 80 additions & 0 deletions pkg/handler/auth/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
Implements an API client for the Token auth API (https://github.com/mutablelogic/go-server/pkg/handler/auth)
*/
package client

import (

// Packages
"time"

"github.com/mutablelogic/go-client"
"github.com/mutablelogic/go-server/pkg/handler/auth"
)

///////////////////////////////////////////////////////////////////////////////
// TYPES

type Client struct {
*client.Client
}

///////////////////////////////////////////////////////////////////////////////
// LIFECYCLE

// Create a new API client, providing the endpoint (ie, http://example.com/api/auth)
func New(endPoint string, opts ...client.ClientOpt) (*Client, error) {
// Create client
client, err := client.New(append(opts, client.OptEndpoint(endPoint))...)
if err != nil {
return nil, err
}

// Return the client
return &Client{client}, nil
}

///////////////////////////////////////////////////////////////////////////////
// METHODS

// Return all tokens
func (c *Client) List() ([]auth.Token, error) {
var response []auth.Token
if err := c.Do(nil, &response); err != nil {
return nil, err
}
return response, nil
}

// Get token details (apart from the value)
func (c *Client) Get(name string) (auth.Token, error) {
var response auth.Token
if err := c.Do(nil, &response, client.OptPath(name)); err != nil {
return auth.Token{}, err
}
return response, nil
}

// Delete a token
func (c *Client) Delete(name string) error {
if err := c.Do(client.MethodDelete, nil, client.OptPath(name)); err != nil {
return err
}
// Return success
return nil
}

// Create a token with name, duration and scopes, and return the token
func (c *Client) Create(name string, expires_in time.Duration, scopes ...string) (auth.Token, error) {
var response auth.Token

// Request->Response
if payload, err := client.NewJSONRequest(auth.NewCreateToken(name, expires_in, scopes...)); err != nil {
return auth.Token{}, err
} else if err := c.Do(payload, &response); err != nil {
return auth.Token{}, err
}

// Return success
return response, nil
}
132 changes: 132 additions & 0 deletions pkg/handler/auth/client/client_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package client_test

import (
"os"
"testing"
"time"

// Packages
client "github.com/mutablelogic/go-client"
auth "github.com/mutablelogic/go-server/pkg/handler/auth/client"
assert "github.com/stretchr/testify/assert"
)

func Test_client_001(t *testing.T) {
assert := assert.New(t)
opts := []client.ClientOpt{
client.OptTrace(os.Stderr, true),
}
client, err := auth.New(GetEndpoint(t), opts...)
assert.NoError(err)
assert.NotNil(client)
}

func Test_client_002(t *testing.T) {
assert := assert.New(t)
opts := []client.ClientOpt{
client.OptTrace(os.Stderr, true),
}
if token := GetToken(t); token != "" {
opts = append(opts, client.OptReqToken(client.Token{
Scheme: "Bearer",
Value: token,
}))
}

client, err := auth.New(GetEndpoint(t), opts...)
assert.NoError(err)

tokens, err := client.List()
assert.NoError(err)
assert.NotEmpty(tokens)
}

func Test_client_003(t *testing.T) {
assert := assert.New(t)
opts := []client.ClientOpt{
client.OptTrace(os.Stderr, true),
}
if token := GetToken(t); token != "" {
opts = append(opts, client.OptReqToken(client.Token{
Scheme: "Bearer",
Value: token,
}))
}
client, err := auth.New(GetEndpoint(t), opts...)
assert.NoError(err)

// Create a new token which doesn't expire
token, err := client.Create(t.Name(), 0)
if !assert.NoError(err) {
t.SkipNow()
}

// Get the token
token2, err := client.Get(t.Name())
if !assert.NoError(err) {
t.SkipNow()
}

// Delete the token
err = client.Delete(t.Name())
assert.NoError(err)

// Check tokens
assert.Equal(token.Name, token2.Name)
}

func Test_client_004(t *testing.T) {
assert := assert.New(t)
opts := []client.ClientOpt{
client.OptTrace(os.Stderr, false),
}
if token := GetToken(t); token != "" {
opts = append(opts, client.OptReqToken(client.Token{
Scheme: "Bearer",
Value: token,
}))
}
client, err := auth.New(GetEndpoint(t), opts...)
assert.NoError(err)

// Create a new token with scopes "a", "b" and "c" which expires in 1 minute
token, err := client.Create(t.Name(), time.Minute, "a", "b", "c")
if !assert.NoError(err) {
t.SkipNow()
}

// Get the token
token2, err := client.Get(t.Name())
if !assert.NoError(err) {
t.SkipNow()
}

// Check the scopes
assert.ElementsMatch(token.Scope, token2.Scope)
assert.Equal(token.Expire, token2.Expire)

// Delete the token
err = client.Delete(t.Name())
assert.NoError(err)

t.Log(token, token2)
}

///////////////////////////////////////////////////////////////////////////////
// ENVIRONMENT

func GetEndpoint(t *testing.T) string {
key := os.Getenv("AUTH_ENDPOINT")
if key == "" {
t.Skip("AUTH_ENDPOINT not set")
t.SkipNow()
}
if key[len(key)-1] != '/' {
key += "/"
}
return key
}

func GetToken(t *testing.T) string {
return os.Getenv("AUTH_TOKEN")
}
66 changes: 66 additions & 0 deletions pkg/handler/auth/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package auth

import (
"context"
)

////////////////////////////////////////////////////////////////////////////////
// TYPES

type authContextKey int

////////////////////////////////////////////////////////////////////////////////
// GLOBALS

const (
_ authContextKey = iota
contextName
contextScope
)

////////////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS

// WithTokenName returns a context with the given auth token name
func WithTokenName(ctx context.Context, token Token) context.Context {
return context.WithValue(ctx, contextName, token.Name)
}

// WithTokenScope returns a context with the given auth token scope
func WithTokenScope(ctx context.Context, token Token) context.Context {
return context.WithValue(ctx, contextScope, token.Scope)
}

// WithToken returns a context with the given auth token
func WithToken(ctx context.Context, token Token) context.Context {
if token.Name != "" {
ctx = WithTokenName(ctx, token)
}
if len(token.Scope) > 0 {
ctx = WithTokenScope(ctx, token)
}
return ctx
}

// TokenName returns the token name from the context
func TokenName(ctx context.Context) string {
return str(ctx, contextName)
}

// TokenScope returns the token scope from the context, or nil
func TokenScope(ctx context.Context) []string {
if value, ok := ctx.Value(contextScope).([]string); ok {
return value
}
return nil
}

////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS

func str(ctx context.Context, key authContextKey) string {
if value, ok := ctx.Value(key).(string); ok {
return value
}
return ""
}
Loading

0 comments on commit b365d21

Please sign in to comment.