🏗️ Renamed model to domain, added testing helpers
This commit is contained in:
parent
bbfe24bb0b
commit
1a905931db
|
@ -3,9 +3,9 @@ package client
|
|||
import (
|
||||
"context"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Get(ctx context.Context, id string) (*model.Client, error)
|
||||
Get(ctx context.Context, id string) (*domain.Client, error)
|
||||
}
|
||||
|
|
|
@ -9,9 +9,10 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
"github.com/tomnomnom/linkheader"
|
||||
http "github.com/valyala/fasthttp"
|
||||
"source.toby3d.me/website/oauth/internal/client"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"willnorris.com/go/microformats"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/client"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type httpClientRepository struct {
|
||||
|
@ -37,7 +38,7 @@ func NewHTTPClientRepository(c *http.Client) client.Repository {
|
|||
}
|
||||
}
|
||||
|
||||
func (repo *httpClientRepository) Get(ctx context.Context, id string) (*model.Client, error) {
|
||||
func (repo *httpClientRepository) Get(ctx context.Context, id string) (*domain.Client, error) {
|
||||
req := http.AcquireRequest()
|
||||
defer http.ReleaseRequest(req)
|
||||
|
||||
|
@ -51,16 +52,15 @@ func (repo *httpClientRepository) Get(ctx context.Context, id string) (*model.Cl
|
|||
return nil, errors.Wrap(err, "failed to make a request to the client")
|
||||
}
|
||||
|
||||
client := new(model.Client)
|
||||
client.ID = model.URL(id)
|
||||
client.RedirectURI = make([]model.URL, 0)
|
||||
client := domain.NewClient()
|
||||
client.ID = id
|
||||
|
||||
for _, l := range linkheader.Parse(string(resp.Header.Peek(http.HeaderLink))) {
|
||||
if !strings.Contains(l.Rel, "redirect_uri") {
|
||||
continue
|
||||
}
|
||||
|
||||
client.RedirectURI = append(client.RedirectURI, model.URL(l.URL))
|
||||
client.RedirectURI = append(client.RedirectURI, l.URL)
|
||||
}
|
||||
|
||||
u, err := url.Parse(id)
|
||||
|
@ -84,7 +84,7 @@ func (repo *httpClientRepository) Get(ctx context.Context, id string) (*model.Cl
|
|||
return client, nil
|
||||
}
|
||||
|
||||
func populateProperties(src map[string][]interface{}, dst *model.Client) {
|
||||
func populateProperties(src map[string][]interface{}, dst *domain.Client) {
|
||||
for key, property := range src {
|
||||
if len(property) == 0 {
|
||||
continue
|
||||
|
@ -97,25 +97,25 @@ func populateProperties(src map[string][]interface{}, dst *model.Client) {
|
|||
for i := range property {
|
||||
switch val := property[i].(type) {
|
||||
case string:
|
||||
dst.Logo = model.URL(val)
|
||||
dst.Logo = val
|
||||
case map[string]string:
|
||||
dst.Logo = model.URL(val[ValueValue])
|
||||
dst.Logo = val[ValueValue]
|
||||
}
|
||||
}
|
||||
case KeyURL:
|
||||
dst.URL = model.URL(getString(property))
|
||||
dst.URL = getString(property)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func populateRels(src map[string][]string, dst *model.Client) {
|
||||
func populateRels(src map[string][]string, dst *domain.Client) {
|
||||
for key, values := range src {
|
||||
if !strings.EqualFold(key, RelRedirectURI) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := range values {
|
||||
dst.RedirectURI = append(dst.RedirectURI, model.URL(values[i]))
|
||||
dst.RedirectURI = append(dst.RedirectURI, values[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,10 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
http "github.com/valyala/fasthttp"
|
||||
httputil "github.com/valyala/fasthttp/fasthttputil"
|
||||
|
||||
repository "source.toby3d.me/website/oauth/internal/client/repository/http"
|
||||
"source.toby3d.me/website/oauth/internal/common"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
const testBody string = `
|
||||
|
@ -67,14 +68,14 @@ func TestGet(t *testing.T) {
|
|||
|
||||
result, err := repository.NewHTTPClientRepository(client).Get(context.TODO(), u.String())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, &model.Client{
|
||||
ID: model.URL(u.String()),
|
||||
assert.Equal(t, &domain.Client{
|
||||
ID: u.String(),
|
||||
Name: "Example App",
|
||||
Logo: model.URL(u.String() + "logo.png"),
|
||||
URL: model.URL(u.String()),
|
||||
RedirectURI: []model.URL{
|
||||
Logo: u.String() + "logo.png",
|
||||
URL: u.String(),
|
||||
RedirectURI: []string{
|
||||
"https://app.example.com/redirect",
|
||||
model.URL(u.String() + "redirect"),
|
||||
u.String() + "redirect",
|
||||
},
|
||||
}, result)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/client"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type memoryClientRepository struct {
|
||||
|
@ -18,13 +18,13 @@ func NewMemoryClientRepository(clients *sync.Map) client.Repository {
|
|||
}
|
||||
}
|
||||
|
||||
func (repo *memoryClientRepository) Get(ctx context.Context, id string) (*model.Client, error) {
|
||||
func (repo *memoryClientRepository) Get(ctx context.Context, id string) (*domain.Client, error) {
|
||||
src, ok := repo.clients.Load(id)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c, ok := src.(*model.Client)
|
||||
c, ok := src.(*domain.Client)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
|
|
@ -7,28 +7,20 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/client/repository/memory"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
store := new(sync.Map)
|
||||
client := &model.Client{
|
||||
ID: "http://127.0.0.1:2368/",
|
||||
Name: "Example App",
|
||||
Logo: "http://127.0.0.1:2368/logo.png",
|
||||
URL: "http://127.0.0.1:2368/",
|
||||
RedirectURI: []model.URL{
|
||||
"https://app.example.com/redirect",
|
||||
"http://127.0.0.1:2368/redirect",
|
||||
},
|
||||
}
|
||||
client := domain.TestClient(t)
|
||||
|
||||
store.Store(string(client.ID), client)
|
||||
store.Store(client.ID, client)
|
||||
|
||||
result, err := memory.NewMemoryClientRepository(store).Get(context.TODO(), string(client.ID))
|
||||
result, err := memory.NewMemoryClientRepository(store).Get(context.TODO(), client.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, client, result)
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ package client
|
|||
import (
|
||||
"context"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type UseCase interface {
|
||||
Discovery(ctx context.Context, clientID model.URL) (*model.Client, error)
|
||||
Discovery(ctx context.Context, clientID string) (*domain.Client, error)
|
||||
}
|
||||
|
|
|
@ -4,8 +4,9 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/client"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type clientUseCase struct {
|
||||
|
@ -18,8 +19,8 @@ func NewClientUseCase(clients client.Repository) client.UseCase {
|
|||
}
|
||||
}
|
||||
|
||||
func (useCase *clientUseCase) Discovery(ctx context.Context, clientID model.URL) (*model.Client, error) {
|
||||
c, err := useCase.clients.Get(ctx, string(clientID))
|
||||
func (useCase *clientUseCase) Discovery(ctx context.Context, clientID string) (*domain.Client, error) {
|
||||
c, err := useCase.clients.Get(ctx, clientID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get client information")
|
||||
}
|
||||
|
|
|
@ -7,32 +7,22 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
repository "source.toby3d.me/website/oauth/internal/client/repository/memory"
|
||||
"source.toby3d.me/website/oauth/internal/client/usecase"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
func TestDiscovery(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
store := new(sync.Map)
|
||||
client := &model.Client{
|
||||
ID: "http://127.0.0.1:2368/",
|
||||
Name: "Example App",
|
||||
Logo: "http://127.0.0.1:2368/logo.png",
|
||||
URL: "http://127.0.0.1:2368/",
|
||||
RedirectURI: []model.URL{
|
||||
"https://app.example.com/redirect",
|
||||
"http://127.0.0.1:2368/redirect",
|
||||
},
|
||||
}
|
||||
client := domain.TestClient(t)
|
||||
|
||||
store.Store(string(client.ID), client)
|
||||
store.Store(client.ID, client)
|
||||
|
||||
result, err := usecase.NewClientUseCase(repository.NewMemoryClientRepository(store)).Discovery(context.TODO(),
|
||||
client.ID)
|
||||
require.NoError(err)
|
||||
assert.Equal(client, result)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, client, result)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package common
|
|||
|
||||
const (
|
||||
MIMEApplicationJSON string = "application/json"
|
||||
MIMETextHTML string = "text/html"
|
||||
MIMEApplicationXWWWFormUrlencoded string = "application/x-www-form-urlencoded"
|
||||
MIMETextHTML string = "text/html"
|
||||
MIMETextPlain string = "text/plain"
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ package viper
|
|||
|
||||
import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/config"
|
||||
)
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
repository "source.toby3d.me/website/oauth/internal/config/repository/viper"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/config"
|
||||
repository "source.toby3d.me/website/oauth/internal/config/repository/viper"
|
||||
"source.toby3d.me/website/oauth/internal/config/usecase"
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
type Client struct {
|
||||
ID string
|
||||
Logo string
|
||||
Name string
|
||||
RedirectURI []string
|
||||
URL string
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
c := new(Client)
|
||||
c.RedirectURI = make([]string, 0)
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func TestClient(tb testing.TB) *Client {
|
||||
tb.Helper()
|
||||
|
||||
return &Client{
|
||||
ID: "http://127.0.0.1:2368/",
|
||||
Name: "Example App",
|
||||
Logo: "http://127.0.0.1:2368/logo.png",
|
||||
URL: "http://127.0.0.1:2368/",
|
||||
RedirectURI: []string{
|
||||
"https://app.example.com/redirect",
|
||||
"http://127.0.0.1:2368/redirect",
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package model
|
||||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -0,0 +1,51 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/random"
|
||||
)
|
||||
|
||||
type Login struct {
|
||||
ClientID string
|
||||
Code string
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod PKCEMethod
|
||||
CodeVerifier string
|
||||
Me string
|
||||
RedirectURI string
|
||||
Scopes []string
|
||||
State string
|
||||
}
|
||||
|
||||
func TestLogin(tb testing.TB) *Login {
|
||||
tb.Helper()
|
||||
|
||||
return &Login{
|
||||
ClientID: "https://app.example.com/",
|
||||
Code: random.New().String(16),
|
||||
CodeChallenge: "OfYAxt8zU2dAPDWQxTAUIteRzMsoj9QBdMIVEDOErUo",
|
||||
CodeChallengeMethod: PKCEMethodS256,
|
||||
CodeVerifier: "a6128783714cfda1d388e2e98b6ae8221ac31aca31959e59512c59f5",
|
||||
Me: "https://user.example.net/",
|
||||
RedirectURI: "https://app.example.com/redirect",
|
||||
Scopes: []string{"profile", "create", "update", "delete"},
|
||||
State: "1234567890",
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginInvalid(tb testing.TB) *Login {
|
||||
tb.Helper()
|
||||
|
||||
return &Login{
|
||||
ClientID: "whoisit",
|
||||
Code: "",
|
||||
CodeChallenge: random.New().String(42),
|
||||
CodeChallengeMethod: "UNDEFINED",
|
||||
CodeVerifier: random.New().String(42),
|
||||
Me: "whoami",
|
||||
RedirectURI: "/redirect",
|
||||
Scopes: []string{},
|
||||
State: "",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
//nolint: gosec
|
||||
package domain
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/base64"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
type (
|
||||
PKCE struct {
|
||||
Method PKCEMethod
|
||||
Verifier string
|
||||
Challenge string
|
||||
}
|
||||
|
||||
PKCEMethod string
|
||||
)
|
||||
|
||||
const (
|
||||
PKCEMethodMD5 PKCEMethod = "MD5"
|
||||
PKCEMethodPlain PKCEMethod = "plain"
|
||||
PKCEMethodS1 PKCEMethod = "S1"
|
||||
PKCEMethodS256 PKCEMethod = "S256"
|
||||
PKCEMethodS512 PKCEMethod = "S512"
|
||||
)
|
||||
|
||||
func (pkce PKCE) IsValid() bool {
|
||||
h := pkce.Method.Hash()
|
||||
if h == nil { // NOTE(toby3d): PLAIN
|
||||
return pkce.Challenge == pkce.Verifier
|
||||
}
|
||||
|
||||
_, _ = io.WriteString(h, pkce.Verifier)
|
||||
|
||||
return pkce.Challenge == base64.RawURLEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func (m PKCEMethod) Hash() hash.Hash {
|
||||
switch m {
|
||||
case PKCEMethodMD5:
|
||||
return md5.New()
|
||||
case PKCEMethodS1:
|
||||
return sha1.New()
|
||||
case PKCEMethodS256:
|
||||
return sha256.New()
|
||||
case PKCEMethodS512:
|
||||
return sha512.New()
|
||||
case PKCEMethodPlain:
|
||||
fallthrough
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"io"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/random"
|
||||
)
|
||||
|
||||
const (
|
||||
MinLength int = 42
|
||||
MaxLength int = 128
|
||||
)
|
||||
|
||||
func TestPKCEIsValid(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
//nolint: gosec
|
||||
verifier := random.New().String(MinLength + rand.Intn(MaxLength-MinLength))
|
||||
|
||||
for _, testCase := range []struct {
|
||||
Name string
|
||||
Method domain.PKCEMethod
|
||||
}{{
|
||||
Name: "MD5",
|
||||
Method: domain.PKCEMethodMD5,
|
||||
}, {
|
||||
Name: "plain",
|
||||
Method: domain.PKCEMethodPlain,
|
||||
}, {
|
||||
Name: "S1",
|
||||
Method: domain.PKCEMethodS1,
|
||||
}, {
|
||||
Name: "S256",
|
||||
Method: domain.PKCEMethodS256,
|
||||
}, {
|
||||
Name: "S512",
|
||||
Method: domain.PKCEMethodS512,
|
||||
}, {
|
||||
Name: "fallback to plain",
|
||||
Method: "UNDEFINED",
|
||||
}} {
|
||||
testCase := testCase
|
||||
|
||||
t.Run(testCase.Name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
pkce := &domain.PKCE{
|
||||
Method: testCase.Method,
|
||||
Verifier: verifier,
|
||||
Challenge: verifier,
|
||||
}
|
||||
|
||||
if h := pkce.Method.Hash(); h != nil {
|
||||
_, err := io.WriteString(h, pkce.Verifier)
|
||||
require.NoError(t, err)
|
||||
|
||||
pkce.Challenge = base64.RawURLEncoding.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
assert.True(t, pkce.IsValid())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package domain
|
||||
|
||||
import "testing"
|
||||
|
||||
type Profile struct {
|
||||
Name string
|
||||
URL string
|
||||
Photo string
|
||||
Email string
|
||||
}
|
||||
|
||||
func TestProfile(tb testing.TB) *Profile {
|
||||
tb.Helper()
|
||||
|
||||
return &Profile{
|
||||
Name: "Example User",
|
||||
URL: "https://user.example.net/",
|
||||
Photo: "https://user.example.net/photo.jpg",
|
||||
Email: "user@example.net",
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/random"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
AccessToken string
|
||||
ClientID string
|
||||
Me string
|
||||
Profile *Profile
|
||||
Scopes []string
|
||||
Type string
|
||||
}
|
||||
|
||||
func NewToken() *Token {
|
||||
t := new(Token)
|
||||
t.Scopes = make([]string, 0)
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
func TestToken(tb testing.TB) *Token {
|
||||
tb.Helper()
|
||||
|
||||
return &Token{
|
||||
AccessToken: random.New().String(32),
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Profile: TestProfile(tb),
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
Type: "Bearer",
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
package model
|
||||
|
||||
type Client struct {
|
||||
ID URL
|
||||
Name string
|
||||
Logo URL
|
||||
URL URL
|
||||
RedirectURI []URL
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
c := new(Client)
|
||||
c.RedirectURI = make([]URL, 0)
|
||||
|
||||
return c
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
package model
|
||||
|
||||
type Login struct {
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
ClientID string `json:"client_id"`
|
||||
Code string `json:"code"`
|
||||
CodeChallenge string `json:"code_challenge"`
|
||||
CodeChallengeMethod string `json:"code_challenge_method"`
|
||||
Me string `json:"me"`
|
||||
RedirectURI string `json:"redirect_uri"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
func (Login) Bucket() []byte { return []byte("logins") }
|
|
@ -1,16 +0,0 @@
|
|||
package model
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"hash"
|
||||
)
|
||||
|
||||
type PKCE struct {
|
||||
Challenge string
|
||||
Method hash.Hash
|
||||
Verifier string
|
||||
}
|
||||
|
||||
func (pkce PKCE) IsValid() bool {
|
||||
return bytes.Equal([]byte(pkce.Challenge), pkce.Method.Sum([]byte(pkce.Verifier)))
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
package model
|
||||
|
||||
type Profile struct {
|
||||
Name string
|
||||
URL URL
|
||||
Photo URL
|
||||
Email string
|
||||
}
|
||||
|
||||
func NewProfile() *Profile {
|
||||
return new(Profile)
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package model
|
||||
|
||||
type ExchangeRequest struct {
|
||||
Code string
|
||||
ClientID string
|
||||
RedirectURI string
|
||||
CodeVerifier string
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package model
|
||||
|
||||
type Token struct {
|
||||
Profile *Profile
|
||||
Scopes []string
|
||||
AccessToken string
|
||||
Type string
|
||||
Me URL
|
||||
ClientID URL
|
||||
}
|
||||
|
||||
func NewToken() *Token {
|
||||
t := new(Token)
|
||||
t.Scopes = make([]string, 0)
|
||||
|
||||
return t
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
package model
|
||||
|
||||
type URL string
|
|
@ -8,8 +8,9 @@ import (
|
|||
json "github.com/goccy/go-json"
|
||||
http "github.com/valyala/fasthttp"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/common"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
)
|
||||
|
||||
|
@ -25,9 +26,9 @@ type (
|
|||
|
||||
//nolint: tagliatelle
|
||||
VerificationResponse struct {
|
||||
Me model.URL `json:"me"`
|
||||
ClientID model.URL `json:"client_id"`
|
||||
Scope string `json:"scope"`
|
||||
Me string `json:"me"`
|
||||
ClientID string `json:"client_id"`
|
||||
Scope string `json:"scope"`
|
||||
}
|
||||
|
||||
RevocationResponse struct{}
|
||||
|
@ -130,7 +131,7 @@ func (h *RequestHandler) Revocation(ctx *http.RequestCtx) {
|
|||
|
||||
func (r *RevocationRequest) bind(ctx *http.RequestCtx) error {
|
||||
if r.Action = string(ctx.FormValue(Action)); !strings.EqualFold(r.Action, ActionRevoke) {
|
||||
return model.Error{
|
||||
return domain.Error{
|
||||
Code: "invalid_request",
|
||||
Description: "request MUST contain 'action' key with value 'revoke'",
|
||||
URI: "https://indieauth.spec.indieweb.org/#token-revocation-request",
|
||||
|
@ -139,7 +140,7 @@ func (r *RevocationRequest) bind(ctx *http.RequestCtx) error {
|
|||
}
|
||||
|
||||
if r.Token = string(ctx.FormValue("token")); r.Token == "" {
|
||||
return model.Error{
|
||||
return domain.Error{
|
||||
Code: "invalid_request",
|
||||
Description: "request MUST contain the 'token' key with the valid access token as its value",
|
||||
URI: "https://indieauth.spec.indieweb.org/#token-revocation-request",
|
||||
|
|
|
@ -6,13 +6,13 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit"
|
||||
"github.com/goccy/go-json"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
http "github.com/valyala/fasthttp"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/common"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
delivery "source.toby3d.me/website/oauth/internal/token/delivery/http"
|
||||
repository "source.toby3d.me/website/oauth/internal/token/repository/memory"
|
||||
"source.toby3d.me/website/oauth/internal/token/usecase"
|
||||
|
@ -22,20 +22,11 @@ import (
|
|||
func TestVerification(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
store := new(sync.Map)
|
||||
repo := repository.NewMemoryTokenRepository(store)
|
||||
accessToken := model.Token{
|
||||
AccessToken: gofakeit.Password(true, true, true, true, false, 32),
|
||||
Type: "Bearer",
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
Profile: nil,
|
||||
}
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
require.NoError(repo.Create(context.TODO(), &accessToken))
|
||||
require.NoError(t, repo.Create(context.TODO(), accessToken))
|
||||
|
||||
req := http.AcquireRequest()
|
||||
defer http.ReleaseRequest(req)
|
||||
|
@ -48,12 +39,12 @@ func TestVerification(t *testing.T) {
|
|||
resp := http.AcquireResponse()
|
||||
defer http.ReleaseResponse(resp)
|
||||
|
||||
require.NoError(util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Read, req, resp))
|
||||
assert.Equal(http.StatusOK, resp.StatusCode())
|
||||
require.NoError(t, util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Read, req, resp))
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode())
|
||||
|
||||
token := new(delivery.VerificationResponse)
|
||||
require.NoError(json.Unmarshal(resp.Body(), token))
|
||||
assert.Equal(&delivery.VerificationResponse{
|
||||
require.NoError(t, json.Unmarshal(resp.Body(), token))
|
||||
assert.Equal(t, &delivery.VerificationResponse{
|
||||
Me: accessToken.Me,
|
||||
ClientID: accessToken.ClientID,
|
||||
Scope: strings.Join(accessToken.Scopes, " "),
|
||||
|
@ -63,20 +54,11 @@ func TestVerification(t *testing.T) {
|
|||
func TestRevocation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
store := new(sync.Map)
|
||||
repo := repository.NewMemoryTokenRepository(store)
|
||||
accessToken := gofakeit.Password(true, true, true, true, false, 32)
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
require.NoError(repo.Create(context.TODO(), &model.Token{
|
||||
AccessToken: accessToken,
|
||||
Type: "Bearer",
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
Profile: nil,
|
||||
}))
|
||||
require.NoError(t, repo.Create(context.TODO(), domain.TestToken(t)))
|
||||
|
||||
req := http.AcquireRequest()
|
||||
defer http.ReleaseRequest(req)
|
||||
|
@ -86,16 +68,16 @@ func TestRevocation(t *testing.T) {
|
|||
req.Header.SetContentType(common.MIMEApplicationXWWWFormUrlencoded)
|
||||
req.Header.Set(http.HeaderAccept, common.MIMEApplicationJSON)
|
||||
req.PostArgs().Set("action", "revoke")
|
||||
req.PostArgs().Set("token", accessToken)
|
||||
req.PostArgs().Set("token", accessToken.AccessToken)
|
||||
|
||||
resp := http.AcquireResponse()
|
||||
defer http.ReleaseResponse(resp)
|
||||
|
||||
require.NoError(util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Update, req, resp))
|
||||
assert.Equal(http.StatusOK, resp.StatusCode())
|
||||
assert.Equal(`{}`, strings.TrimSpace(string(resp.Body())))
|
||||
require.NoError(t, util.Serve(delivery.NewRequestHandler(usecase.NewTokenUseCase(repo)).Update, req, resp))
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode())
|
||||
assert.Equal(t, `{}`, strings.TrimSpace(string(resp.Body())))
|
||||
|
||||
token, err := repo.Get(context.TODO(), accessToken)
|
||||
require.NoError(err)
|
||||
assert.Nil(token)
|
||||
token, err := repo.Get(context.TODO(), accessToken.AccessToken)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, token)
|
||||
}
|
||||
|
|
|
@ -4,17 +4,18 @@ import (
|
|||
"context"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type Repository interface {
|
||||
Get(ctx context.Context, accessToken string) (*model.Token, error)
|
||||
Create(ctx context.Context, accessToken *model.Token) error
|
||||
Update(ctx context.Context, accessToken *model.Token) error
|
||||
Get(ctx context.Context, accessToken string) (*domain.Token, error)
|
||||
Create(ctx context.Context, accessToken *domain.Token) error
|
||||
Update(ctx context.Context, accessToken *domain.Token) error
|
||||
Remove(ctx context.Context, accessToken string) error
|
||||
}
|
||||
|
||||
var ErrExist error = model.Error{
|
||||
var ErrExist error = domain.Error{
|
||||
Code: "invalid_request",
|
||||
Description: "this token is already exists",
|
||||
URI: "",
|
||||
|
|
|
@ -8,7 +8,8 @@ import (
|
|||
"github.com/pkg/errors"
|
||||
bolt "go.etcd.io/bbolt"
|
||||
"golang.org/x/xerrors"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
)
|
||||
|
||||
|
@ -43,13 +44,13 @@ func NewBoltTokenRepository(db *bolt.DB) (token.Repository, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (repo *boltTokenRepository) Get(ctx context.Context, accessToken string) (*model.Token, error) {
|
||||
result := model.NewToken()
|
||||
func (repo *boltTokenRepository) Get(ctx context.Context, accessToken string) (*domain.Token, error) {
|
||||
result := domain.NewToken()
|
||||
|
||||
if err := repo.db.View(func(tx *bolt.Tx) error {
|
||||
//nolint: exhaustivestruct
|
||||
if src := tx.Bucket(Token{}.Bucket()).Get([]byte(accessToken)); src != nil {
|
||||
return NewToken().Bind(src, result)
|
||||
return new(Token).Bind(src, result)
|
||||
}
|
||||
|
||||
return ErrNotExist
|
||||
|
@ -64,7 +65,7 @@ func (repo *boltTokenRepository) Get(ctx context.Context, accessToken string) (*
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (repo *boltTokenRepository) Create(ctx context.Context, accessToken *model.Token) error {
|
||||
func (repo *boltTokenRepository) Create(ctx context.Context, accessToken *domain.Token) error {
|
||||
t, err := repo.Get(ctx, accessToken.AccessToken)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to verify the existence of the token")
|
||||
|
@ -77,8 +78,8 @@ func (repo *boltTokenRepository) Create(ctx context.Context, accessToken *model.
|
|||
return repo.Update(ctx, accessToken)
|
||||
}
|
||||
|
||||
func (repo *boltTokenRepository) Update(ctx context.Context, accessToken *model.Token) error {
|
||||
dto := NewToken()
|
||||
func (repo *boltTokenRepository) Update(ctx context.Context, accessToken *domain.Token) error {
|
||||
dto := new(Token)
|
||||
dto.Populate(accessToken)
|
||||
|
||||
src, err := json.Marshal(dto)
|
||||
|
@ -114,21 +115,17 @@ func (repo *boltTokenRepository) Remove(ctx context.Context, accessToken string)
|
|||
return nil
|
||||
}
|
||||
|
||||
func NewToken() *Token {
|
||||
return new(Token)
|
||||
}
|
||||
|
||||
func (Token) Bucket() []byte { return []byte("tokens") }
|
||||
|
||||
func (t *Token) Populate(src *model.Token) {
|
||||
func (t *Token) Populate(src *domain.Token) {
|
||||
t.AccessToken = src.AccessToken
|
||||
t.ClientID = string(src.ClientID)
|
||||
t.Me = string(src.Me)
|
||||
t.ClientID = src.ClientID
|
||||
t.Me = src.Me
|
||||
t.Scope = strings.Join(src.Scopes, " ")
|
||||
t.Type = src.Type
|
||||
}
|
||||
|
||||
func (t *Token) Bind(src []byte, dst *model.Token) error {
|
||||
func (t *Token) Bind(src []byte, dst *domain.Token) error {
|
||||
if err := json.Unmarshal(src, t); err != nil {
|
||||
return errors.Wrap(err, "cannot unmarshal token source")
|
||||
}
|
||||
|
@ -136,8 +133,8 @@ func (t *Token) Bind(src []byte, dst *model.Token) error {
|
|||
dst.AccessToken = t.AccessToken
|
||||
dst.Scopes = strings.Fields(t.Scope)
|
||||
dst.Type = t.Type
|
||||
dst.ClientID = model.URL(t.ClientID)
|
||||
dst.Me = model.URL(t.Me)
|
||||
dst.ClientID = t.ClientID
|
||||
dst.Me = t.Me
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.etcd.io/bbolt"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/random"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
"source.toby3d.me/website/oauth/internal/token/repository/bolt"
|
||||
|
@ -73,7 +74,7 @@ func TestGet(t *testing.T) {
|
|||
|
||||
tkn, err := repo.Get(context.TODO(), accessToken)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, &model.Token{
|
||||
assert.Equal(t, &domain.Token{
|
||||
AccessToken: accessToken,
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://toby3d.me/",
|
||||
|
@ -95,7 +96,7 @@ func TestCreate(t *testing.T) {
|
|||
})
|
||||
})
|
||||
|
||||
tkn := &model.Token{
|
||||
tkn := &domain.Token{
|
||||
AccessToken: accessToken,
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://toby3d.me/",
|
||||
|
@ -106,11 +107,11 @@ func TestCreate(t *testing.T) {
|
|||
|
||||
require.NoError(t, repo.Create(context.TODO(), tkn))
|
||||
|
||||
result := model.NewToken()
|
||||
result := domain.NewToken()
|
||||
|
||||
require.NoError(t, db.View(func(tx *bbolt.Tx) error {
|
||||
//nolint: exhaustivestruct
|
||||
return bolt.NewToken().Bind(tx.Bucket(bolt.Token{}.Bucket()).Get([]byte(tkn.AccessToken)), result)
|
||||
return new(bolt.Token).Bind(tx.Bucket(bolt.Token{}.Bucket()).Get([]byte(tkn.AccessToken)), result)
|
||||
}))
|
||||
assert.Equal(t, tkn, result)
|
||||
|
||||
|
@ -143,7 +144,7 @@ func TestUpdate(t *testing.T) {
|
|||
return tx.Bucket(bolt.Token{}.Bucket()).Put([]byte(accessToken), src)
|
||||
}))
|
||||
|
||||
require.NoError(t, repo.Update(context.TODO(), &model.Token{
|
||||
require.NoError(t, repo.Update(context.TODO(), &domain.Token{
|
||||
AccessToken: accessToken,
|
||||
ClientID: "https://client.example.com/",
|
||||
Me: "https://toby3d.ru/",
|
||||
|
@ -152,13 +153,13 @@ func TestUpdate(t *testing.T) {
|
|||
Profile: nil,
|
||||
}))
|
||||
|
||||
result := model.NewToken()
|
||||
result := domain.NewToken()
|
||||
|
||||
require.NoError(t, db.View(func(tx *bbolt.Tx) error {
|
||||
//nolint: exhaustivestruct
|
||||
return bolt.NewToken().Bind(tx.Bucket(bolt.Token{}.Bucket()).Get([]byte(accessToken)), result)
|
||||
return new(bolt.Token).Bind(tx.Bucket(bolt.Token{}.Bucket()).Get([]byte(accessToken)), result)
|
||||
}))
|
||||
assert.Equal(t, &model.Token{
|
||||
assert.Equal(t, &domain.Token{
|
||||
AccessToken: accessToken,
|
||||
ClientID: "https://client.example.com/",
|
||||
Me: "https://toby3d.ru/",
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"sync"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
)
|
||||
|
||||
|
@ -18,13 +18,13 @@ func NewMemoryTokenRepository(tokens *sync.Map) token.Repository {
|
|||
}
|
||||
}
|
||||
|
||||
func (repo *memoryTokenRepository) Get(ctx context.Context, accessToken string) (*model.Token, error) {
|
||||
func (repo *memoryTokenRepository) Get(ctx context.Context, accessToken string) (*domain.Token, error) {
|
||||
src, ok := repo.tokens.Load(accessToken)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
result, ok := src.(*model.Token)
|
||||
result, ok := src.(*domain.Token)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ func (repo *memoryTokenRepository) Get(ctx context.Context, accessToken string)
|
|||
return result, nil
|
||||
}
|
||||
|
||||
func (repo *memoryTokenRepository) Create(ctx context.Context, accessToken *model.Token) error {
|
||||
func (repo *memoryTokenRepository) Create(ctx context.Context, accessToken *domain.Token) error {
|
||||
t, err := repo.Get(ctx, accessToken.AccessToken)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -45,7 +45,7 @@ func (repo *memoryTokenRepository) Create(ctx context.Context, accessToken *mode
|
|||
return repo.Update(ctx, accessToken)
|
||||
}
|
||||
|
||||
func (repo *memoryTokenRepository) Update(ctx context.Context, accessToken *model.Token) error {
|
||||
func (repo *memoryTokenRepository) Update(ctx context.Context, accessToken *domain.Token) error {
|
||||
repo.tokens.Store(accessToken.AccessToken, accessToken)
|
||||
|
||||
return nil
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/random"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
"source.toby3d.me/website/oauth/internal/token/repository/memory"
|
||||
)
|
||||
|
@ -17,19 +17,7 @@ func TestGet(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
store := new(sync.Map)
|
||||
accessToken := &model.Token{
|
||||
AccessToken: random.New().String(32),
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://toby3d.me/",
|
||||
Profile: &model.Profile{
|
||||
Name: "Maxim Lebedev",
|
||||
URL: "https://toby3d.me/",
|
||||
Photo: "https://toby3d.me/photo.jpg",
|
||||
Email: "hey@toby3d.me",
|
||||
},
|
||||
Scopes: []string{"read", "update", "delete"},
|
||||
Type: "Bearer",
|
||||
}
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
store.Store(accessToken.AccessToken, accessToken)
|
||||
|
||||
|
@ -42,19 +30,7 @@ func TestCreate(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
store := new(sync.Map)
|
||||
accessToken := &model.Token{
|
||||
AccessToken: random.New().String(32),
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://toby3d.me/",
|
||||
Profile: &model.Profile{
|
||||
Name: "Maxim Lebedev",
|
||||
URL: "https://toby3d.me/",
|
||||
Photo: "https://toby3d.me/photo.jpg",
|
||||
Email: "hey@toby3d.me",
|
||||
},
|
||||
Scopes: []string{"read", "update", "delete"},
|
||||
Type: "Bearer",
|
||||
}
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
repo := memory.NewMemoryTokenRepository(store)
|
||||
require.NoError(t, repo.Create(context.TODO(), accessToken))
|
||||
|
@ -70,19 +46,7 @@ func TestUpdate(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
store := new(sync.Map)
|
||||
accessToken := &model.Token{
|
||||
AccessToken: random.New().String(32),
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://toby3d.me/",
|
||||
Profile: &model.Profile{
|
||||
Name: "Maxim Lebedev",
|
||||
URL: "https://toby3d.me/",
|
||||
Photo: "https://toby3d.me/photo.jpg",
|
||||
Email: "hey@toby3d.me",
|
||||
},
|
||||
Scopes: []string{"read", "update", "delete"},
|
||||
Type: "Bearer",
|
||||
}
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
store.Store(accessToken.AccessToken, accessToken)
|
||||
|
||||
|
@ -102,19 +66,7 @@ func TestDelete(t *testing.T) {
|
|||
t.Parallel()
|
||||
|
||||
store := new(sync.Map)
|
||||
accessToken := &model.Token{
|
||||
AccessToken: random.New().String(32),
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://toby3d.me/",
|
||||
Profile: &model.Profile{
|
||||
Name: "Maxim Lebedev",
|
||||
URL: "https://toby3d.me/",
|
||||
Photo: "https://toby3d.me/photo.jpg",
|
||||
Email: "hey@toby3d.me",
|
||||
},
|
||||
Scopes: []string{"read", "update", "delete"},
|
||||
Type: "Bearer",
|
||||
}
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
store.Store(accessToken.AccessToken, accessToken)
|
||||
|
||||
|
|
|
@ -3,10 +3,10 @@ package token
|
|||
import (
|
||||
"context"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
)
|
||||
|
||||
type UseCase interface {
|
||||
Verify(ctx context.Context, accessToken string) (*model.Token, error)
|
||||
Verify(ctx context.Context, accessToken string) (*domain.Token, error)
|
||||
Revoke(ctx context.Context, accessToken string) error
|
||||
}
|
||||
|
|
|
@ -4,7 +4,8 @@ import (
|
|||
"context"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
"source.toby3d.me/website/oauth/internal/token"
|
||||
)
|
||||
|
||||
|
@ -18,7 +19,7 @@ func NewTokenUseCase(tokens token.Repository) token.UseCase {
|
|||
}
|
||||
}
|
||||
|
||||
func (useCase *tokenUseCase) Verify(ctx context.Context, accessToken string) (*model.Token, error) {
|
||||
func (useCase *tokenUseCase) Verify(ctx context.Context, accessToken string) (*domain.Token, error) {
|
||||
t, err := useCase.tokens.Get(ctx, accessToken)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to retrieve token from storage")
|
||||
|
|
|
@ -5,10 +5,10 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/brianvoe/gofakeit"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"source.toby3d.me/website/oauth/internal/model"
|
||||
|
||||
"source.toby3d.me/website/oauth/internal/domain"
|
||||
repository "source.toby3d.me/website/oauth/internal/token/repository/memory"
|
||||
"source.toby3d.me/website/oauth/internal/token/usecase"
|
||||
)
|
||||
|
@ -16,53 +16,31 @@ import (
|
|||
func TestVerify(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
store := new(sync.Map)
|
||||
repo := repository.NewMemoryTokenRepository(store)
|
||||
ucase := usecase.NewTokenUseCase(repo)
|
||||
accessToken := &model.Token{
|
||||
AccessToken: gofakeit.Password(true, true, true, true, false, 32),
|
||||
Type: "Bearer",
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
Profile: nil,
|
||||
}
|
||||
repo := repository.NewMemoryTokenRepository(new(sync.Map))
|
||||
accessToken := domain.NewToken()
|
||||
|
||||
require.NoError(repo.Create(context.TODO(), accessToken))
|
||||
require.NoError(t, repo.Create(context.TODO(), accessToken))
|
||||
|
||||
token, err := ucase.Verify(context.TODO(), accessToken.AccessToken)
|
||||
require.NoError(err)
|
||||
assert.Equal(accessToken, token)
|
||||
token, err := usecase.NewTokenUseCase(repo).Verify(context.TODO(), accessToken.AccessToken)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, accessToken, token)
|
||||
}
|
||||
|
||||
func TestRevoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
assert := assert.New(t)
|
||||
store := new(sync.Map)
|
||||
repo := repository.NewMemoryTokenRepository(store)
|
||||
ucase := usecase.NewTokenUseCase(repo)
|
||||
accessToken := gofakeit.Password(true, true, true, true, false, 32)
|
||||
repo := repository.NewMemoryTokenRepository(new(sync.Map))
|
||||
accessToken := domain.TestToken(t)
|
||||
|
||||
require.NoError(repo.Create(context.TODO(), &model.Token{
|
||||
AccessToken: accessToken,
|
||||
Type: "Bearer",
|
||||
ClientID: "https://app.example.com/",
|
||||
Me: "https://user.example.net/",
|
||||
Scopes: []string{"create", "update", "delete"},
|
||||
Profile: nil,
|
||||
}))
|
||||
require.NoError(t, repo.Create(context.TODO(), accessToken))
|
||||
|
||||
token, err := repo.Get(context.TODO(), accessToken)
|
||||
require.NoError(err)
|
||||
assert.NotNil(token)
|
||||
token, err := repo.Get(context.TODO(), accessToken.AccessToken)
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, token)
|
||||
|
||||
require.NoError(ucase.Revoke(context.TODO(), token.AccessToken))
|
||||
require.NoError(t, usecase.NewTokenUseCase(repo).Revoke(context.TODO(), token.AccessToken))
|
||||
|
||||
token, err = repo.Get(context.TODO(), token.AccessToken)
|
||||
require.NoError(err)
|
||||
assert.Nil(token)
|
||||
require.NoError(t, err)
|
||||
assert.Nil(t, token)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue