global cleanup

Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
2022-05-03 14:38:44 +03:00
parent 289aadb28e
commit b075230ae5
43 changed files with 18 additions and 3012 deletions

View File

@@ -1,87 +0,0 @@
package auth // import "go.unistack.org/micro/v3/util/auth"
import (
"context"
"time"
"go.unistack.org/micro/v3/auth"
"go.unistack.org/micro/v3/logger"
"go.unistack.org/micro/v3/util/id"
)
// Verify the auth credentials and refresh the auth token periodically
func Verify(a auth.Auth) error {
// extract the account creds from options, these can be set by flags
accID := a.Options().ID
accSecret := a.Options().Secret
// if no credentials were provided, self generate an account
if len(accID) == 0 && len(accSecret) == 0 {
opts := []auth.GenerateOption{
auth.WithType("service"),
auth.WithScopes("service"),
}
id, err := id.New()
if err != nil {
return err
}
acc, err := a.Generate(id, opts...)
if err != nil {
return err
}
if logger.V(logger.DebugLevel) {
logger.Debug(context.TODO(), "Auth [%v] Generated an auth account: %s", a.String())
}
accID = acc.ID
accSecret = acc.Secret
}
// generate the first token
token, err := a.Token(
auth.WithCredentials(accID, accSecret),
auth.WithExpiry(time.Minute*10),
)
if err != nil {
return err
}
// set the credentials and token in auth options
_ = a.Init(
auth.ClientToken(token),
auth.Credentials(accID, accSecret),
)
// periodically check to see if the token needs refreshing
go func() {
timer := time.NewTicker(time.Second * 15)
for {
<-timer.C
// don't refresh the token if it's not close to expiring
tok := a.Options().Token
if tok.Expiry.Unix() > time.Now().Add(time.Minute).Unix() {
continue
}
// generate the first token
tok, err := a.Token(
auth.WithToken(tok.RefreshToken),
auth.WithExpiry(time.Minute*10),
)
if err != nil {
if logger.V(logger.WarnLevel) {
logger.Warn(context.TODO(), "[Auth] Error refreshing token: %v", err)
}
continue
}
// set the token
_ = a.Init(auth.ClientToken(tok))
}
}()
return nil
}

View File

@@ -1,26 +0,0 @@
package ctx // import "go.unistack.org/micro/v3/util/ctx"
import (
"context"
"net/http"
"strings"
"go.unistack.org/micro/v3/metadata"
)
// FromRequest creates context with metadata from http.Request
func FromRequest(r *http.Request) context.Context {
ctx := r.Context()
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
md = metadata.New(len(r.Header) + 2)
}
for key, val := range r.Header {
md.Set(key, strings.Join(val, ","))
}
// pass http host
md["Host"] = r.Host
// pass http method
md["Method"] = r.Method
return metadata.NewIncomingContext(ctx, md)
}

View File

@@ -1,41 +0,0 @@
package ctx
import (
"net/http"
"testing"
"go.unistack.org/micro/v3/metadata"
)
func TestRequestToContext(t *testing.T) {
testData := []struct {
request *http.Request
expect metadata.Metadata
}{
{
&http.Request{
Header: http.Header{
"Foo1": []string{"bar"},
"Foo2": []string{"bar", "baz"},
},
},
metadata.Metadata{
"Foo1": "bar",
"Foo2": "bar,baz",
},
},
}
for _, d := range testData {
ctx := FromRequest(d.request)
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
t.Fatalf("Expected metadata for request %+v", d.request)
}
for k, v := range d.expect {
if val := md[k]; val != v {
t.Fatalf("Expected %s for key %s for expected md %+v, got md %+v", v, k, d.expect, md)
}
}
}
}

View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2020 Jon Calhoun
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,55 +0,0 @@
# qson
This is copy from https://github.com/joncalhoun/qson
As author says he is not acrivelly maintains the repo and not plan to do that.
## Usage
You can either turn a URL query param into a JSON byte array, or unmarshal that directly into a Go object.
Transforming the URL query param into a JSON byte array:
```go
import "github.com/joncalhoun/qson"
func main() {
b, err := qson.ToJSON("bar%5Bone%5D%5Btwo%5D=2&bar[one][red]=112")
if err != nil {
panic(err)
}
fmt.Println(string(b))
// Should output: {"bar":{"one":{"red":112,"two":2}}}
}
```
Or unmarshalling directly into a Go object using JSON struct tags:
```go
import "github.com/joncalhoun/qson"
type unmarshalT struct {
A string `json:"a"`
B unmarshalB `json:"b"`
}
type unmarshalB struct {
C int `json:"c"`
}
func main() {
var out unmarshalT
query := "a=xyz&b[c]=456"
err := Unmarshal(&out, query)
if err != nil {
t.Error(err)
}
// out should equal
// unmarshalT{
// A: "xyz",
// B: unmarshalB{
// C: 456,
// },
// }
}
```
To get a query string like in the two previous examples you can use the `RawQuery` field on the [net/url.URL](https://golang.org/pkg/net/url/#URL) type.

View File

@@ -1,34 +0,0 @@
package qson
// merge merges a with b if they are either both slices
// or map[string]interface{} types. Otherwise it returns b.
func merge(a interface{}, b interface{}) interface{} {
switch aT := a.(type) {
case map[string]interface{}:
return mergeMap(aT, b.(map[string]interface{}))
case []interface{}:
return mergeSlice(aT, b.([]interface{}))
default:
return b
}
}
// mergeMap merges a with b, attempting to merge any nested
// values in nested maps but eventually overwriting anything
// in a that can't be merged with whatever is in b.
func mergeMap(a map[string]interface{}, b map[string]interface{}) map[string]interface{} {
for bK, bV := range b {
if _, ok := a[bK]; ok {
a[bK] = merge(a[bK], bV)
} else {
a[bK] = bV
}
}
return a
}
// mergeSlice merges a with b and returns the result.
func mergeSlice(a []interface{}, b []interface{}) []interface{} {
a = append(a, b...)
return a
}

View File

@@ -1,37 +0,0 @@
package qson
import "testing"
func TestMergeSlice(t *testing.T) {
a := []interface{}{"a"}
b := []interface{}{"b"}
actual := mergeSlice(a, b)
if len(actual) != 2 {
t.Errorf("Expected size to be 2.")
}
if actual[0] != "a" {
t.Errorf("Expected index 0 to have value a. Actual: %s", actual[0])
}
if actual[1] != "b" {
t.Errorf("Expected index 1 to have value b. Actual: %s", actual[1])
}
}
func TestMergeMap(t *testing.T) {
a := map[string]interface{}{
"a": "b",
}
b := map[string]interface{}{
"b": "c",
}
actual := mergeMap(a, b)
if len(actual) != 2 {
t.Errorf("Expected size to be 2.")
}
if actual["a"] != "b" {
t.Errorf("Expected key \"a\" to have value b. Actual: %s", actual["a"])
}
if actual["b"] != "c" {
t.Errorf("Expected key \"b\" to have value c. Actual: %s", actual["b"])
}
}

View File

@@ -1,166 +0,0 @@
// Package qson implmenets decoding of URL query params
// into JSON and Go values (using JSON struct tags).
//
// See https://golang.org/pkg/encoding/json/ for more
// details on JSON struct tags.
package qson // import "go.unistack.org/micro/v3/util/qson"
import (
"encoding/json"
"errors"
"net/url"
"regexp"
"strings"
)
var (
// ErrInvalidParam is returned when invalid data is provided to the ToJSON or Unmarshal function.
// Specifically, this will be returned when there is no equals sign present in the URL query parameter.
ErrInvalidParam = errors.New("qson: invalid url query param provided")
bracketSplitter *regexp.Regexp
)
func init() {
bracketSplitter = regexp.MustCompile(`\[|\]`)
}
func btSplitter(str string) []string {
r := bracketSplitter.Split(str, -1)
for idx, s := range r {
if len(s) == 0 {
if len(r) > idx+1 {
copy(r[idx:], r[idx+1:])
r = r[:len(r)-1]
}
}
}
return r
}
// Unmarshal will take a dest along with URL
// query params and attempt to first turn the query params
// into JSON and then unmarshal those into the dest variable
//
// BUG(joncalhoun): If a URL query param value is something
// like 123 but is expected to be parsed into a string this
// will currently result in an error because the JSON
// transformation will assume this is intended to be an int.
// This should only affect the Unmarshal function and
// could likely be fixed, but someone will need to submit a
// PR if they want that fixed.
func Unmarshal(dst interface{}, query string) error {
b, err := ToJSON(query)
if err != nil {
return err
}
return json.Unmarshal(b, dst)
}
// ToJSON will turn a query string like:
// cat=1&bar%5Bone%5D%5Btwo%5D=2&bar[one][red]=112
// Into a JSON object with all the data merged as nicely as
// possible. Eg the example above would output:
// {"bar":{"one":{"two":2,"red":112}}}
func ToJSON(query string) ([]byte, error) {
var builder interface{} = make(map[string]interface{})
params := strings.Split(query, "&")
for _, part := range params {
tempMap, err := queryToMap(part)
if err != nil {
return nil, err
}
builder = merge(builder, tempMap)
}
return json.Marshal(builder)
}
// queryToMap turns something like a[b][c]=4 into
// map[string]interface{}{
// "a": map[string]interface{}{
// "b": map[string]interface{}{
// "c": 4,
// },
// },
// }
func queryToMap(param string) (map[string]interface{}, error) {
rawKey, rawValue, err := splitKeyAndValue(param)
if err != nil {
return nil, err
}
rawValue, err = url.QueryUnescape(rawValue)
if err != nil {
return nil, err
}
rawKey, err = url.QueryUnescape(rawKey)
if err != nil {
return nil, err
}
pieces := btSplitter(rawKey)
key := pieces[0]
// If len==1 then rawKey has no [] chars and we can just
// decode this as key=value into {key: value}
if len(pieces) == 1 {
var value interface{}
// First we try parsing it as an int, bool, null, etc
err = json.Unmarshal([]byte(rawValue), &value)
if err != nil {
// If we got an error we try wrapping the value in
// quotes and processing it as a string
err = json.Unmarshal([]byte("\""+rawValue+"\""), &value)
if err != nil {
// If we can't decode as a string we return the err
return nil, err
}
}
return map[string]interface{}{
key: value,
}, nil
}
// If len > 1 then we have something like a[b][c]=2
// so we need to turn this into {"a": {"b": {"c": 2}}}
// To do this we break our key into two pieces:
// a and b[c]
// and then we set {"a": queryToMap("b[c]", value)}
ret := make(map[string]interface{})
ret[key], err = queryToMap(buildNewKey(rawKey) + "=" + rawValue)
if err != nil {
return nil, err
}
// When URL params have a set of empty brackets (eg a[]=1)
// it is assumed to be an array. This will get us the
// correct value for the array item and return it as an
// []interface{} so that it can be merged properly.
if pieces[1] == "" {
temp := ret[key].(map[string]interface{})
ret[key] = []interface{}{temp[""]}
}
return ret, nil
}
// buildNewKey will take something like:
// origKey = "bar[one][two]"
// pieces = [bar one two ]
// and return "one[two]"
func buildNewKey(origKey string) string {
pieces := btSplitter(origKey)
ret := origKey[len(pieces[0])+1:]
ret = ret[:len(pieces[1])] + ret[len(pieces[1])+1:]
return ret
}
// splitKeyAndValue splits a URL param at the last equal
// sign and returns the two strings. If no equal sign is
// found, the ErrInvalidParam error is returned.
func splitKeyAndValue(param string) (string, string, error) {
li := strings.LastIndex(param, "=")
if li == -1 {
return "", "", ErrInvalidParam
}
return param[:li], param[li+1:], nil
}

View File

@@ -1,179 +0,0 @@
package qson
import (
"testing"
)
func TestFuzz1(t *testing.T) {
b, err := ToJSON(`[^@hGl5=`)
if err != nil {
t.Fatal(err)
}
_ = b
}
func ExampleUnmarshal() {
type Ex struct {
A string `json:"a"`
B struct {
C int `json:"c"`
} `json:"b"`
}
var ex Ex
if err := Unmarshal(&ex, "a=xyz&b[c]=456"); err != nil {
panic(err)
}
_ = ex
// fmt.Printf("%+v\n", ex)
// Output: {A:xyz B:{C:456}}
}
type unmarshalT struct {
A string `json:"a"`
B unmarshalB `json:"b"`
}
type unmarshalB struct {
D string `json:"D"`
C int `json:"c"`
}
func TestUnmarshal(t *testing.T) {
query := "a=xyz&b[c]=456"
expected := unmarshalT{
A: "xyz",
B: unmarshalB{
C: 456,
},
}
var actual unmarshalT
err := Unmarshal(&actual, query)
if err != nil {
t.Error(err)
}
if expected != actual {
t.Errorf("Expected: %+v Actual: %+v", expected, actual)
}
}
func ExampleToJSON() {
_, err := ToJSON("a=xyz&b[c]=456")
if err != nil {
panic(err)
}
// fmt.Printf(string(b))
// Output: {"a":"xyz","b":{"c":456}}
}
func TestToJSONNested(t *testing.T) {
query := "bar%5Bone%5D%5Btwo%5D=2&bar[one][red]=112"
expected := `{"bar":{"one":{"red":112,"two":2}}}`
actual, err := ToJSON(query)
if err != nil {
t.Error(err)
}
actualStr := string(actual)
if actualStr != expected {
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
}
}
func TestToJSONPlain(t *testing.T) {
query := "cat=1&dog=2"
expected := `{"cat":1,"dog":2}`
actual, err := ToJSON(query)
if err != nil {
t.Error(err)
}
actualStr := string(actual)
if actualStr != expected {
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
}
}
func TestToJSONSlice(t *testing.T) {
query := "cat[]=1&cat[]=34"
expected := `{"cat":[1,34]}`
actual, err := ToJSON(query)
if err != nil {
t.Error(err)
}
actualStr := string(actual)
if actualStr != expected {
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
}
}
func TestToJSONBig(t *testing.T) {
query := "distinct_id=763_1495187301909_3495&timestamp=1495187523&event=product_add_cart&params%5BproductRefId%5D=8284563078&params%5Bapps%5D%5B%5D=precommend&params%5Bapps%5D%5B%5D=bsales&params%5Bsource%5D=item&params%5Boptions%5D%5Bsegment%5D=cart_recommendation&params%5Boptions%5D%5Btype%5D=up_sell&params%5BtimeExpire%5D=1495187599642&params%5Brecommend_system_product_source%5D=item&params%5Bproduct_id%5D=8284563078&params%5Bvariant_id%5D=27661944134&params%5Bsku%5D=00483332%20(black)&params%5Bsources%5D%5B%5D=product_recommendation&params%5Bcart_token%5D=dc2c336a009edf2762128e65806dfb1d&params%5Bquantity%5D=1&params%5Bnew_popup_upsell_mobile%5D=false&params%5BclientDevice%5D=desktop&params%5BclientIsMobile%5D=false&params%5BclientIsSmallScreen%5D=false&params%5Bnew_popup_crossell_mobile%5D=false&api_key=14c5b7dacea9157029265b174491d340"
expected := `{"api_key":"14c5b7dacea9157029265b174491d340","distinct_id":"763_1495187301909_3495","event":"product_add_cart","params":{"apps":["precommend","bsales"],"cart_token":"dc2c336a009edf2762128e65806dfb1d","clientDevice":"desktop","clientIsMobile":false,"clientIsSmallScreen":false,"new_popup_crossell_mobile":false,"new_popup_upsell_mobile":false,"options":{"segment":"cart_recommendation","type":"up_sell"},"productRefId":8284563078,"product_id":8284563078,"quantity":1,"recommend_system_product_source":"item","sku":"00483332 (black)","source":"item","sources":["product_recommendation"],"timeExpire":1495187599642,"variant_id":27661944134},"timestamp":1495187523}`
actual, err := ToJSON(query)
if err != nil {
t.Error(err)
}
actualStr := string(actual)
if actualStr != expected {
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
}
}
func TestToJSONDuplicateKey(t *testing.T) {
query := "cat=1&cat=2"
expected := `{"cat":2}`
actual, err := ToJSON(query)
if err != nil {
t.Error(err)
}
actualStr := string(actual)
if actualStr != expected {
t.Errorf("Expected: %s Actual: %s", expected, actualStr)
}
}
func TestSplitKeyAndValue(t *testing.T) {
param := "a[dog][=cat]=123"
eKey, eValue := "a[dog][=cat]", "123"
aKey, aValue, err := splitKeyAndValue(param)
if err != nil {
t.Error(err)
}
if eKey != aKey {
t.Errorf("Keys do not match. Expected: %s Actual: %s", eKey, aKey)
}
if eValue != aValue {
t.Errorf("Values do not match. Expected: %s Actual: %s", eValue, aValue)
}
}
func TestEncodedAmpersand(t *testing.T) {
query := "a=xyz&b[d]=ben%26jerry"
expected := unmarshalT{
A: "xyz",
B: unmarshalB{
D: "ben&jerry",
},
}
var actual unmarshalT
err := Unmarshal(&actual, query)
if err != nil {
t.Error(err)
}
if expected != actual {
t.Errorf("Expected: %+v Actual: %+v", expected, actual)
}
}
func TestEncodedAmpersand2(t *testing.T) {
query := "filter=parent%3Dflow12345%26request%3Dreq12345&meta.limit=20&meta.offset=0"
expected := map[string]interface{}{"filter": "parent=flow12345&request=req12345", "meta.limit": float64(20), "meta.offset": float64(0)}
actual := make(map[string]interface{})
err := Unmarshal(&actual, query)
if err != nil {
t.Error(err)
}
for k, v := range actual {
if nv, ok := expected[k]; !ok || nv != v {
t.Errorf("Expected: %+v Actual: %+v", expected, actual)
}
}
}

View File

@@ -1,64 +0,0 @@
package sync
import (
"fmt"
"time"
"go.unistack.org/micro/v3/store"
)
func (c *syncStore) syncManager() {
tickerAggregator := make(chan struct{ index int })
for i, ticker := range c.pendingWriteTickers {
go func(index int, c chan struct{ index int }, t *time.Ticker) {
for range t.C {
c <- struct{ index int }{index: index}
}
}(i, tickerAggregator, ticker)
}
for i := range tickerAggregator {
println(i.index, "ticked")
c.processQueue(i.index)
}
}
func (c *syncStore) processQueue(index int) {
c.Lock()
defer c.Unlock()
q := c.pendingWrites[index]
for i := 0; i < q.Len(); i++ {
r, ok := q.PopFront()
if !ok {
panic(fmt.Errorf("retrieved an invalid value from the L%d sync queue", index+1))
}
ir, ok := r.(*internalRecord)
if !ok {
panic(fmt.Errorf("retrieved a non-internal record from the L%d sync queue", index+1))
}
if !ir.expiresAt.IsZero() && time.Now().After(ir.expiresAt) {
continue
}
var opts []store.WriteOption
if !ir.expiresAt.IsZero() {
opts = append(opts, store.WriteTTL(time.Until(ir.expiresAt)))
}
// Todo = internal queue also has to hold the corresponding store.WriteOptions
if err := c.syncOpts.Stores[index+1].Write(c.storeOpts.Context, ir.key, ir.value, opts...); err != nil {
// some error, so queue for retry and bail
q.PushBack(ir)
return
}
}
}
func intpow(x, y int64) int64 {
result := int64(1)
for 0 != y {
if 0 != (y & 1) {
result *= x
}
y >>= 1
x *= x
}
return result
}

View File

@@ -1,46 +0,0 @@
package sync
import (
"time"
"go.unistack.org/micro/v3/store"
)
// Options represents Sync options
type Options struct {
// Stores represents layers in the sync in ascending order. L0, L1, L2, etc
Stores []store.Store
// SyncInterval is the duration between syncs from L0 to L1
SyncInterval time.Duration
// SyncMultiplier is the multiplication factor between each store.
SyncMultiplier int64
}
// Option sets Sync Options
type Option func(o *Options)
// Stores sets the layers that make up the sync
func Stores(stores ...store.Store) Option {
return func(o *Options) {
o.Stores = make([]store.Store, len(stores))
for i, s := range stores {
o.Stores[i] = s
}
}
}
// nolint: golint,revive
// SyncInterval sets the duration between syncs from L0 to L1
func SyncInterval(d time.Duration) Option {
return func(o *Options) {
o.SyncInterval = d
}
}
// nolint: golint,revive
// SyncMultiplier sets the multiplication factor for time to wait each sync layer
func SyncMultiplier(i int64) Option {
return func(o *Options) {
o.SyncMultiplier = i
}
}

View File

@@ -1,129 +0,0 @@
// Package sync will sync multiple stores
package sync // import "go.unistack.org/micro/v3/util/sync"
import (
"context"
"fmt"
"sync"
"time"
"github.com/ef-ds/deque"
"go.unistack.org/micro/v3/store"
)
// Sync implements a sync in for stores
type Sync interface {
// Implements the store interface
store.Store
// Force a full sync
Sync() error
}
type syncStore struct {
storeOpts store.Options
pendingWrites []*deque.Deque
pendingWriteTickers []*time.Ticker
syncOpts Options
sync.RWMutex
}
// NewSync returns a new Sync
func NewSync(opts ...Option) Sync {
c := &syncStore{}
for _, o := range opts {
o(&c.syncOpts)
}
if c.syncOpts.SyncInterval == 0 {
c.syncOpts.SyncInterval = 1 * time.Minute
}
if c.syncOpts.SyncMultiplier == 0 {
c.syncOpts.SyncMultiplier = 5
}
return c
}
func (c *syncStore) Connect(ctx context.Context) error {
return nil
}
func (c *syncStore) Disconnect(ctx context.Context) error {
return nil
}
func (c *syncStore) Close(ctx context.Context) error {
return nil
}
// Init initialises the storeOptions
func (c *syncStore) Init(opts ...store.Option) error {
for _, o := range opts {
o(&c.storeOpts)
}
if len(c.syncOpts.Stores) == 0 {
return fmt.Errorf("the sync has no stores")
}
for _, s := range c.syncOpts.Stores {
if err := s.Init(); err != nil {
return fmt.Errorf("Store %s failed to Init(): %w", s.String(), err)
}
}
c.pendingWrites = make([]*deque.Deque, len(c.syncOpts.Stores)-1)
c.pendingWriteTickers = make([]*time.Ticker, len(c.syncOpts.Stores)-1)
for i := 0; i < len(c.pendingWrites); i++ {
c.pendingWrites[i] = deque.New()
c.pendingWrites[i].Init()
c.pendingWriteTickers[i] = time.NewTicker(c.syncOpts.SyncInterval * time.Duration(intpow(c.syncOpts.SyncMultiplier, int64(i))))
}
go c.syncManager()
return nil
}
// Name returns the store name
func (c *syncStore) Name() string {
return c.storeOpts.Name
}
// Options returns the sync's store options
func (c *syncStore) Options() store.Options {
return c.storeOpts
}
// String returns a printable string describing the sync
func (c *syncStore) String() string {
backends := make([]string, len(c.syncOpts.Stores))
for i, s := range c.syncOpts.Stores {
backends[i] = s.String()
}
return fmt.Sprintf("sync %v", backends)
}
func (c *syncStore) List(ctx context.Context, opts ...store.ListOption) ([]string, error) {
return c.syncOpts.Stores[0].List(ctx, opts...)
}
func (c *syncStore) Exists(ctx context.Context, key string, opts ...store.ExistsOption) error {
return c.syncOpts.Stores[0].Exists(ctx, key)
}
func (c *syncStore) Read(ctx context.Context, key string, val interface{}, opts ...store.ReadOption) error {
return c.syncOpts.Stores[0].Read(ctx, key, val, opts...)
}
func (c *syncStore) Write(ctx context.Context, key string, val interface{}, opts ...store.WriteOption) error {
return c.syncOpts.Stores[0].Write(ctx, key, val, opts...)
}
// Delete removes a key from the sync
func (c *syncStore) Delete(ctx context.Context, key string, opts ...store.DeleteOption) error {
return c.syncOpts.Stores[0].Delete(ctx, key, opts...)
}
func (c *syncStore) Sync() error {
return nil
}
type internalRecord struct {
expiresAt time.Time
key string
value []byte
}

View File

@@ -1,87 +0,0 @@
package basic // import "go.unistack.org/micro/v3/util/token/basic"
import (
"context"
"encoding/json"
"fmt"
"time"
"go.unistack.org/micro/v3/auth"
"go.unistack.org/micro/v3/store"
"go.unistack.org/micro/v3/util/id"
"go.unistack.org/micro/v3/util/token"
)
// Basic implementation of token provider, backed by the store
type Basic struct {
store store.Store
}
// StorePrefix to isolate tokens
var StorePrefix = "tokens/"
// NewTokenProvider returns an initialized basic provider
func NewTokenProvider(opts ...token.Option) token.Provider {
options := token.NewOptions(opts...)
if options.Store == nil {
options.Store = store.DefaultStore
}
return &Basic{
store: options.Store,
}
}
// Generate a token for an account
func (b *Basic) Generate(acc *auth.Account, opts ...token.GenerateOption) (*token.Token, error) {
options := token.NewGenerateOptions(opts...)
// marshal the account to bytes
bytes, err := json.Marshal(acc)
if err != nil {
return nil, err
}
// write to the store
key, err := id.New()
if err != nil {
return nil, err
}
err = b.store.Write(context.Background(), fmt.Sprintf("%v%v", StorePrefix, key), bytes, store.WriteTTL(options.Expiry))
if err != nil {
return nil, err
}
// return the token
return &token.Token{
Token: key,
Created: time.Now(),
Expiry: time.Now().Add(options.Expiry),
}, nil
}
// Inspect a token
func (b *Basic) Inspect(t string) (*auth.Account, error) {
// lookup the token in the store
var val []byte
err := b.store.Read(context.Background(), StorePrefix+t, val)
if err == store.ErrNotFound {
return nil, token.ErrInvalidToken
} else if err != nil {
return nil, err
}
// unmarshal the bytes
var acc *auth.Account
if err := json.Unmarshal(val, &acc); err != nil {
return nil, err
}
return acc, nil
}
// String returns basic
func (b *Basic) String() string {
return "basic"
}

View File

@@ -1,67 +0,0 @@
//go:build ignore
// +build ignore
package basic
import (
"testing"
"go.unistack.org/micro/v3/auth"
"go.unistack.org/micro/v3/store/memory"
"go.unistack.org/micro/v3/util/token"
)
func TestGenerate(t *testing.T) {
store := memory.NewStore()
b := NewTokenProvider(token.WithStore(store))
_, err := b.Generate(&auth.Account{ID: "test"})
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
recs, err := store.List()
if err != nil {
t.Fatalf("Unable to read from store: %v", err)
}
if len(recs) != 1 {
t.Errorf("Generate didn't write to the store, expected 1 record, got %v", len(recs))
}
}
func TestInspect(t *testing.T) {
store := memory.NewStore()
b := NewTokenProvider(token.WithStore(store))
t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"}
scopes := []string{"admin"}
subject := "test"
tok, err := b.Generate(&auth.Account{ID: subject, Scopes: scopes, Metadata: md})
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
tok2, err := b.Inspect(tok.Token)
if err != nil {
t.Fatalf("Inspect returned %v error, expected nil", err)
}
if tok2.ID != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.ID, subject)
}
if len(tok2.Scopes) != len(scopes) {
t.Errorf("Inspect returned %v scopes, expected %v", len(tok2.Scopes), len(scopes))
}
if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)
}
})
t.Run("Invalid token", func(t *testing.T) {
_, err := b.Inspect("Invalid token")
if err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
}

View File

@@ -1,110 +0,0 @@
package jwt // import "go.unistack.org/micro/v3/util/token/jwt"
import (
"encoding/base64"
"time"
"github.com/golang-jwt/jwt/v4"
"go.unistack.org/micro/v3/auth"
"go.unistack.org/micro/v3/metadata"
"go.unistack.org/micro/v3/util/token"
)
// authClaims to be encoded in the JWT
type authClaims struct {
Metadata metadata.Metadata `json:"metadata"`
jwt.RegisteredClaims
Type string `json:"type"`
Scopes []string `json:"scopes"`
}
// JWT implementation of token provider
type JWT struct {
opts token.Options
}
// NewTokenProvider returns an initialized basic provider
func NewTokenProvider(opts ...token.Option) token.Provider {
return &JWT{
opts: token.NewOptions(opts...),
}
}
// Generate a new JWT
func (j *JWT) Generate(acc *auth.Account, opts ...token.GenerateOption) (*token.Token, error) {
// decode the private key
priv, err := base64.StdEncoding.DecodeString(j.opts.PrivateKey)
if err != nil {
return nil, err
}
// parse the private key
key, err := jwt.ParseRSAPrivateKeyFromPEM(priv)
if err != nil {
return nil, token.ErrEncodingToken
}
// parse the options
options := token.NewGenerateOptions(opts...)
// generate the JWT
expiry := time.Now().Add(options.Expiry)
t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{
Type: acc.Type, Scopes: acc.Scopes, Metadata: acc.Metadata, RegisteredClaims: jwt.RegisteredClaims{
Subject: acc.ID,
Issuer: acc.Issuer,
ExpiresAt: jwt.NewNumericDate(expiry),
},
})
tok, err := t.SignedString(key)
if err != nil {
return nil, err
}
// return the token
return &token.Token{
Token: tok,
Expiry: expiry,
Created: time.Now(),
}, nil
}
// Inspect a JWT
func (j *JWT) Inspect(t string) (*auth.Account, error) {
// decode the public key
pub, err := base64.StdEncoding.DecodeString(j.opts.PublicKey)
if err != nil {
return nil, err
}
// parse the public key
res, err := jwt.ParseWithClaims(t, &authClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM(pub)
})
if err != nil {
return nil, token.ErrInvalidToken
}
// validate the token
if !res.Valid {
return nil, token.ErrInvalidToken
}
claims, ok := res.Claims.(*authClaims)
if !ok {
return nil, token.ErrInvalidToken
}
// return the token
return &auth.Account{
ID: claims.Subject,
Issuer: claims.Issuer,
Type: claims.Type,
Scopes: claims.Scopes,
Metadata: claims.Metadata,
}, nil
}
// String returns JWT
func (j *JWT) String() string {
return "jwt"
}

View File

@@ -1,86 +0,0 @@
package jwt
import (
"os"
"testing"
"time"
"go.unistack.org/micro/v3/auth"
"go.unistack.org/micro/v3/util/token"
)
func TestGenerate(t *testing.T) {
privKey, err := os.ReadFile("test/sample_key")
if err != nil {
t.Fatalf("Unable to read private key: %v", err)
}
j := NewTokenProvider(
token.WithPrivateKey(string(privKey)),
)
_, err = j.Generate(&auth.Account{ID: "test"})
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
}
func TestInspect(t *testing.T) {
pubKey, err := os.ReadFile("test/sample_key.pub")
if err != nil {
t.Fatalf("Unable to read public key: %v", err)
}
privKey, err := os.ReadFile("test/sample_key")
if err != nil {
t.Fatalf("Unable to read private key: %v", err)
}
j := NewTokenProvider(
token.WithPublicKey(string(pubKey)),
token.WithPrivateKey(string(privKey)),
)
t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"}
scopes := []string{"admin"}
subject := "test"
acc := &auth.Account{ID: subject, Scopes: scopes, Metadata: md}
tok, err := j.Generate(acc)
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
tok2, err := j.Inspect(tok.Token)
if err != nil {
t.Fatalf("Inspect returned %v error, expected nil", err)
}
if acc.ID != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", acc.ID, subject)
}
if len(tok2.Scopes) != len(scopes) {
t.Errorf("Inspect returned %v scopes, expected %v", len(tok2.Scopes), len(scopes))
}
if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)
}
})
t.Run("Expired token", func(t *testing.T) {
tok, err := j.Generate(&auth.Account{}, token.WithExpiry(-10*time.Second))
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
if _, err = j.Inspect(tok.Token); err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
t.Run("Invalid token", func(t *testing.T) {
_, err := j.Inspect("Invalid token")
if err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
}

View File

@@ -1 +0,0 @@
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS3dJQkFBS0NBZ0VBOFNiSlA1WGJFaWRSbTViMnNOcExHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkCi9SbDkvMXBNVjdNaU8zTEh3dGhIQzJCUllxcisxd0Zkb1pDR0JZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUKMEJIL2xYUU1xeUVxRjVNSTJ6ZWpDNHpNenIxNU9OK2dFNEpuaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtLwptVWRJVC9MYUY3a1F4eVlLNVZLbitOZ09Xek1sektBQXBDbjdUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsCm85akRqbFk1b0JPY3pmcWVOV0hLNUdYQjdRd3BMTmg5NDZQelpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDUKd2xFcThoTmhtaG01Tk5lL08rR2dqQkROU2ZVaDA2K3E0bmdtYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1bwpSdFFoZ2lZOTEwcFBmOWJhdVhXcXdVQ1VhNHFzSHpqS1IwTC9OMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVCnJnTHJQYkVCOWVnY0drMzgrYnBLczNaNlJyNSt0bkQxQklQSUZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVUKVEdEeFV4OG9qOFZJZVJuV0RxNk1jMWlKcDhVeWNpQklUUnR3NGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMApsYVF6QXVQM2FpV1hJTXAyc2M4U2MrQmwrTGpYbUJveEJyYUJIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YCmdGS1NzSW5IRHJIVk95V1BCZTNmYWRFYzc3YituYi9leE96cjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUEKQVFLQ0FnRUFqUzc1Q2VvUlRRcUtBNzZaaFNiNGEzNVlKRENtcEpSazFsRTNKYnFzNFYxRnhXaDBjZmJYeG9VMgpSdTRRYjUrZWhsdWJGSFQ2a1BxdG9uRWhRVExjMUNmVE9WbHJOb3hocDVZM2ZyUmlQcnNnNXcwK1R3RUtrcFJUCnltanJQTXdQbGxCM2U0NmVaYmVXWGc3R3FFVmptMGcxVFRRK0tocVM4R0w3VGJlTFhRN1ZTem9ydTNCNVRKMVEKeEN6TVB0dnQ2eDYrU3JrcmhvZG1iT3VNRkpDam1TbWxmck9pZzQ4Zkc3NUpERHRObXpLWHBEUVJpYUNodFJhVQpQRHpmUTlTamhYdFFqdkZvWFFFT3BqdkZVRjR2WldNUWNQNUw1VklDM3JRSWp4MFNzQTN6S0FwakVUbjJHNjN2CktZby8zVWttbzhkUCtGRHA3NCs5a3pLNHFFaFJycEl3bEtiN0VOZWtDUXZqUFl1K3pyKzMyUXdQNTJ2L2FveWQKdjJJaUY3M2laTU1vZDhhYjJuQStyVEI2T0cvOVlSYk5kV21tay9VTi9jUHYrN214TmZ6Y1d1ZU1XcThxMXh4eAptNTNpR0NSQ29PQ1lDQk4zcUFkb1JwYW5xd3lCOUxrLzFCQjBHUld3MjgxK3VhNXNYRnZBVDBKeTVURnduMncvClU1MlJKWFlNOXVhMFBvd214b0RDUWRuNFZYVkdNZGdXaHN4aXhHRlYwOUZObWJJQWJaN0xaWGtkS1gzc1ZVbTcKWU1WYWIzVVo2bEhtdXYzT1NzcHNVUlRqN1hiRzZpaVVlaDU1aW91OENWbnRndWtFcnEzQTQwT05FVzhjNDBzOQphVTBGaSs4eWZpQTViaVZHLzF0bWlucUVERkhuQStnWk1xNEhlSkZxcWZxaEZKa1JwRGtDZ2dFQkFQeGR1NGNKCm5Da1duZDdPWFlHMVM3UDdkVWhRUzgwSDlteW9uZFc5bGFCQm84RWRPeTVTZzNOUmsxQ2pNZFZ1a3FMcjhJSnkKeStLWk15SVpvSlJvbllaMEtIUUVMR3ZLbzFOS2NLQ1FJbnYvWHVCdFJpRzBVb1pQNVkwN0RpRFBRQWpYUjlXUwpBc0EzMmQ1eEtFOC91Y3h0MjVQVzJFakNBUmtVeHQ5d0tKazN3bC9JdXVYRlExTDdDWjJsOVlFUjlHeWxUbzhNCmxXUEY3YndtUFV4UVNKaTNVS0FjTzZweTVUU1lkdWQ2aGpQeXJwSXByNU42VGpmTlRFWkVBeU9LbXVpOHVkUkoKMUg3T3RQVEhGZElKQjNrNEJnRDZtRE1HbjB2SXBLaDhZN3NtRUZBbFkvaXlCZjMvOHk5VHVMb1BycEdqR3RHbgp4Y2RpMHFud2p0SGFNbFVDZ2dFQkFQU2Z0dVFCQ2dTU2JLUSswUEFSR2VVeEQyTmlvZk1teENNTmdHUzJ5Ull3CjRGaGV4ZWkwMVJoaFk1NjE3UjduR1dzb0czd1RQa3dvRTJtbE1aQkoxeWEvUU9RRnQ3WG02OVl0RGh0T2FWbDgKL0o4dlVuSTBtWmxtT2pjTlRoYnVPZDlNSDlRdGxIRUMxMlhYdHJNb3Fsb0U2a05TT0pJalNxYm9wcDRXc1BqcApvZTZ0Nkdyd1RhOHBHeUJWWS90Mi85Ym5ORHVPVlpjODBaODdtY2gzcDNQclBqU3h5di9saGxYMFMwYUdHTkhTCk1XVjdUa25OaGo1TWlIRXFnZ1pZemtBWTkyd1JoVENnU1A2M0VNcitUWXFudXVuMXJHbndPYm95TDR2aFRpV0UKcU42UDNCTFlCZ1FpMllDTDludEJrOEl6RHZyd096dW5GVnhhZ0g5SVVoY0NnZ0VCQUwzQXlLa1BlOENWUmR6cQpzL284VkJDZmFSOFhhUGRnSGxTek1BSXZpNXEwNENqckRyMlV3MHZwTVdnM1hOZ0xUT3g5bFJpd3NrYk9SRmxHCmhhd3hRUWlBdkk0SE9WTlBTU0R1WHVNTG5USTQ0S0RFNlMrY2cxU0VMS2pWbDVqcDNFOEpkL1RJMVpLc0xBQUsKZTNHakM5UC9ZbE8xL21ndW4xNjVkWk01cFAwWHBPb2FaeFV2RHFFTktyekR0V1g0RngyOTZlUzdaSFJodFpCNwovQ2t1VUhlcmxrN2RDNnZzdWhTaTh2eTM3c0tPbmQ0K3c4cVM4czhZYVZxSDl3ZzVScUxxakp0bmJBUnc3alVDCm9KQ053M1hNdnc3clhaYzRTbnhVQUNMRGJNV2lLQy9xL1ZGWW9oTEs2WkpUVkJscWd5cjBSYzBRWmpDMlNJb0kKMjRwRWt3VUNnZ0VCQUpqb0FJVVNsVFY0WlVwaExXN3g4WkxPa01UWjBVdFFyd2NPR0hSYndPUUxGeUNGMVFWNQppejNiR2s4SmZyZHpVdk1sTmREZm9uQXVHTHhQa3VTVEUxWlg4L0xVRkJveXhyV3dvZ0cxaUtwME11QTV6em90CjROai9DbUtCQVkvWnh2anA5M2RFS21aZGxWQkdmeUFMeWpmTW5MWUovZXh5L09YSnhPUktZTUttSHg4M08zRWsKMWhvb0FwbTZabTIzMjRGME1iVU1ham5Idld2ZjhHZGJTNk5zcHd4L0dkbk1tYVMrdUJMVUhVMkNLbmc1bEIwVAp4OWJITmY0dXlPbTR0dXRmNzhCd1R5V3UreEdrVW0zZ2VZMnkvR1hqdDZyY2l1ajFGNzFDenZzcXFmZThTcDdJCnd6SHdxcTNzVHR5S2lCYTZuYUdEYWpNR1pKYSt4MVZJV204Q2dnRUJBT001ajFZR25Ba0pxR0czQWJSVDIvNUMKaVVxN0loYkswOGZsSGs5a2YwUlVjZWc0ZVlKY3dIRXJVaE4rdWQyLzE3MC81dDYra0JUdTVZOUg3bkpLREtESQpoeEg5SStyamNlVkR0RVNTRkluSXdDQ1lrOHhOUzZ0cHZMV1U5b0pibGFKMlZsalV2NGRFWGVQb0hkREh1Zk9ZClVLa0lsV2E3Uit1QzNEOHF5U1JrQnFLa3ZXZ1RxcFNmTVNkc1ZTeFIzU2Q4SVhFSHFjTDNUNEtMWGtYNEdEamYKMmZOSTFpZkx6ekhJMTN3Tk5IUTVRNU9SUC9pell2QzVzZkx4U2ZIUXJiMXJZVkpKWkI5ZjVBUjRmWFpHSVFsbApjMG8xd0JmZFlqMnZxVDlpR09IQnNSSTlSL2M2RzJQcUt3aFRpSzJVR2lmVFNEUVFuUkF6b2tpQVkrbE8vUjQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

View File

@@ -1 +0,0 @@
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE4U2JKUDVYYkVpZFJtNWIyc05wTApHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkL1JsOS8xcE1WN01pTzNMSHd0aEhDMkJSWXFyKzF3RmRvWkNHCkJZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUwQkgvbFhRTXF5RXFGNU1JMnplakM0ek16cjE1T04rZ0U0Sm4KaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtL21VZElUL0xhRjdrUXh5WUs1VktuK05nT1d6TWx6S0FBcENuNwpUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsbzlqRGpsWTVvQk9jemZxZU5XSEs1R1hCN1F3cExOaDk0NlB6ClpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDV3bEVxOGhOaG1obTVOTmUvTytHZ2pCRE5TZlVoMDYrcTRuZ20KYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1b1J0UWhnaVk5MTBwUGY5YmF1WFdxd1VDVWE0cXNIempLUjBMLwpOMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVcmdMclBiRUI5ZWdjR2szOCticEtzM1o2UnI1K3RuRDFCSVBJCkZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVVUR0R4VXg4b2o4VkllUm5XRHE2TWMxaUpwOFV5Y2lCSVRSdHcKNGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMGxhUXpBdVAzYWlXWElNcDJzYzhTYytCbCtMalhtQm94QnJhQgpIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YZ0ZLU3NJbkhEckhWT3lXUEJlM2ZhZEVjNzdiK25iL2V4T3pyCjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=

View File

@@ -1,83 +0,0 @@
package token
import (
"time"
"go.unistack.org/micro/v3/store"
)
// Options holds the options for token
type Options struct {
// Store to persist the tokens
Store store.Store
// PublicKey base64 encoded, used by JWT
PublicKey string
// PrivateKey base64 encoded, used by JWT
PrivateKey string
}
// Option func signature
type Option func(o *Options)
// WithStore sets the token providers store
func WithStore(s store.Store) Option {
return func(o *Options) {
o.Store = s
}
}
// WithPublicKey sets the JWT public key
func WithPublicKey(key string) Option {
return func(o *Options) {
o.PublicKey = key
}
}
// WithPrivateKey sets the JWT private key
func WithPrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey = key
}
}
// NewOptions returns options struct filled by opts
func NewOptions(opts ...Option) Options {
options := Options{}
for _, o := range opts {
o(&options)
}
// set default store
if options.Store == nil {
options.Store = store.DefaultStore
}
return options
}
// GenerateOptions holds the generate options
type GenerateOptions struct {
// Expiry for the token
Expiry time.Duration
}
// GenerateOption func signature
type GenerateOption func(o *GenerateOptions)
// WithExpiry for the generated account's token expires
func WithExpiry(d time.Duration) GenerateOption {
return func(o *GenerateOptions) {
o.Expiry = d
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
// set default Expiry of token
if options.Expiry == 0 {
options.Expiry = time.Minute * 15
}
return options
}

View File

@@ -1,34 +0,0 @@
package token // import "go.unistack.org/micro/v3/util/token"
import (
"errors"
"time"
"go.unistack.org/micro/v3/auth"
)
var (
// ErrNotFound is returned when a token cannot be found
ErrNotFound = errors.New("token not found")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("error encoding the token")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
)
// Provider generates and inspects tokens
type Provider interface {
Generate(account *auth.Account, opts ...GenerateOption) (*Token, error)
Inspect(token string) (*auth.Account, error)
String() string
}
// Token holds the auth token
type Token struct {
// Created time of token created
Created time.Time `json:"created"`
// Expiry ime of the token
Expiry time.Time `json:"expiry"`
// Token holds the actual token
Token string `json:"token"`
}