satellite/oidc: add integration test
This change adds an integration test that performs an OAuth workflow and verifies the OIDC endpoints are functioning as expected. Change-Id: I18a8968b4f0385a1e4de6784dee68e1b51df86f7
This commit is contained in:
parent
0a298778be
commit
98f4fae02c
2
go.mod
2
go.mod
@ -43,6 +43,7 @@ require (
|
|||||||
go.uber.org/zap v1.16.0
|
go.uber.org/zap v1.16.0
|
||||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838
|
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||||
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27
|
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1
|
||||||
@ -118,7 +119,6 @@ require (
|
|||||||
go.uber.org/atomic v1.7.0 // indirect
|
go.uber.org/atomic v1.7.0 // indirect
|
||||||
go.uber.org/multierr v1.6.0 // indirect
|
go.uber.org/multierr v1.6.0 // indirect
|
||||||
golang.org/x/mod v0.4.2 // indirect
|
golang.org/x/mod v0.4.2 // indirect
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
|
|
||||||
golang.org/x/text v0.3.6 // indirect
|
golang.org/x/text v0.3.6 // indirect
|
||||||
golang.org/x/tools v0.1.1 // indirect
|
golang.org/x/tools v0.1.1 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
|
@ -288,8 +288,8 @@ func NewServer(logger *zap.Logger, config Config, service *console.Service, oidc
|
|||||||
analyticsRouter.HandleFunc("/event", analyticsController.EventTriggered).Methods(http.MethodPost)
|
analyticsRouter.HandleFunc("/event", analyticsController.EventTriggered).Methods(http.MethodPost)
|
||||||
|
|
||||||
if server.config.StaticDir != "" {
|
if server.config.StaticDir != "" {
|
||||||
oidc := oidc.NewEndpoint(server.config.ExternalAddress, oidcService, service, server.config.OauthCodeExpiry,
|
oidc := oidc.NewEndpoint(server.config.ExternalAddress, logger, oidcService, service,
|
||||||
server.config.OauthAccessTokenExpiry, server.config.OauthRefreshTokenExpiry)
|
server.config.OauthCodeExpiry, server.config.OauthAccessTokenExpiry, server.config.OauthRefreshTokenExpiry)
|
||||||
|
|
||||||
router.HandleFunc("/.well-known/openid-configuration", oidc.WellKnownConfiguration)
|
router.HandleFunc("/.well-known/openid-configuration", oidc.WellKnownConfiguration)
|
||||||
router.Handle("/oauth/v2/authorize", server.withAuth(http.HandlerFunc(oidc.AuthorizeUser))).Methods(http.MethodPost)
|
router.Handle("/oauth/v2/authorize", server.withAuth(http.HandlerFunc(oidc.AuthorizeUser))).Methods(http.MethodPost)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
package oidc
|
package oidc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -14,6 +13,7 @@ import (
|
|||||||
"github.com/go-oauth2/oauth2/v4/manage"
|
"github.com/go-oauth2/oauth2/v4/manage"
|
||||||
"github.com/go-oauth2/oauth2/v4/server"
|
"github.com/go-oauth2/oauth2/v4/server"
|
||||||
"github.com/spacemonkeygo/monkit/v3"
|
"github.com/spacemonkeygo/monkit/v3"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"storj.io/common/uuid"
|
"storj.io/common/uuid"
|
||||||
"storj.io/storj/satellite/console"
|
"storj.io/storj/satellite/console"
|
||||||
@ -24,7 +24,11 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewEndpoint constructs an OpenID identity provider.
|
// NewEndpoint constructs an OpenID identity provider.
|
||||||
func NewEndpoint(externalAddress string, oidcService *Service, service *console.Service, codeExpiry, accessTokenExpiry, refreshTokenExpiry time.Duration) *Endpoint {
|
func NewEndpoint(
|
||||||
|
externalAddress string, log *zap.Logger,
|
||||||
|
oidcService *Service, service *console.Service,
|
||||||
|
codeExpiry, accessTokenExpiry, refreshTokenExpiry time.Duration,
|
||||||
|
) *Endpoint {
|
||||||
manager := manage.NewManager()
|
manager := manage.NewManager()
|
||||||
|
|
||||||
tokenStore := oidcService.TokenStore()
|
tokenStore := oidcService.TokenStore()
|
||||||
@ -58,6 +62,7 @@ func NewEndpoint(externalAddress string, oidcService *Service, service *console.
|
|||||||
tokenStore: tokenStore,
|
tokenStore: tokenStore,
|
||||||
service: service,
|
service: service,
|
||||||
server: svr,
|
server: svr,
|
||||||
|
log: log,
|
||||||
config: ProviderConfig{
|
config: ProviderConfig{
|
||||||
Issuer: externalAddress,
|
Issuer: externalAddress,
|
||||||
AuthURL: externalAddress + "oauth/v2/authorize",
|
AuthURL: externalAddress + "oauth/v2/authorize",
|
||||||
@ -75,6 +80,7 @@ type Endpoint struct {
|
|||||||
tokenStore oauth2.TokenStore
|
tokenStore oauth2.TokenStore
|
||||||
service *console.Service
|
service *console.Service
|
||||||
server *server.Server
|
server *server.Server
|
||||||
|
log *zap.Logger
|
||||||
config ProviderConfig
|
config ProviderConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,12 +90,11 @@ func (e *Endpoint) WellKnownConfiguration(w http.ResponseWriter, r *http.Request
|
|||||||
var err error
|
var err error
|
||||||
defer mon.Task()(&ctx)(&err)
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
data, err := json.Marshal(e.config)
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(e.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "", http.StatusInternalServerError)
|
e.log.Error("failed to encode oidc config", zap.Error(err))
|
||||||
} else {
|
|
||||||
http.ServeContent(w, r, "", time.Now(), bytes.NewReader(data))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +107,7 @@ func (e *Endpoint) AuthorizeUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = e.server.HandleAuthorizeRequest(w, r)
|
err = e.server.HandleAuthorizeRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
e.log.Error("failed to authorize user", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,7 +119,7 @@ func (e *Endpoint) Tokens(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = e.server.HandleTokenRequest(w, r)
|
err = e.server.HandleTokenRequest(w, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
e.log.Error("failed to exchange for token", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,12 +170,11 @@ func (e *Endpoint) UserInfo(w http.ResponseWriter, r *http.Request) {
|
|||||||
userInfo.Email = user.Email
|
userInfo.Email = user.Email
|
||||||
userInfo.EmailVerified = true
|
userInfo.EmailVerified = true
|
||||||
|
|
||||||
data, err := json.Marshal(userInfo)
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(userInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "", http.StatusInternalServerError)
|
e.log.Error("failed to encode user info", zap.Error(err))
|
||||||
} else {
|
|
||||||
http.ServeContent(w, r, "", time.Now(), bytes.NewReader(data))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
334
satellite/oidc/integration_test.go
Normal file
334
satellite/oidc/integration_test.go
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
// Copyright (C) 2022 Storj Labs, Inc.
|
||||||
|
// See LICENSE for copying information.
|
||||||
|
|
||||||
|
package oidc_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
|
"storj.io/common/grant"
|
||||||
|
"storj.io/common/macaroon"
|
||||||
|
"storj.io/common/storj"
|
||||||
|
"storj.io/common/testcontext"
|
||||||
|
"storj.io/common/uuid"
|
||||||
|
"storj.io/storj/private/testplanet"
|
||||||
|
"storj.io/storj/satellite"
|
||||||
|
"storj.io/storj/satellite/console"
|
||||||
|
"storj.io/storj/satellite/console/consoleauth"
|
||||||
|
"storj.io/storj/satellite/oidc"
|
||||||
|
"storj.io/uplink"
|
||||||
|
)
|
||||||
|
|
||||||
|
func send(t *testing.T, body io.Reader, response interface{}, status int, parts ...string) {
|
||||||
|
for len(parts) < 4 {
|
||||||
|
parts = append(parts, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
method := parts[1]
|
||||||
|
if method == "" {
|
||||||
|
method = http.MethodGet
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(context.Background(), method, parts[0], body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
auth := parts[2]
|
||||||
|
if auth != "" {
|
||||||
|
req.Header.Set("Authorization", auth)
|
||||||
|
|
||||||
|
req.AddCookie(&http.Cookie{
|
||||||
|
Name: "_tokenKey",
|
||||||
|
Value: auth,
|
||||||
|
Path: "/",
|
||||||
|
HttpOnly: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
contentType := parts[3]
|
||||||
|
if contentType != "" {
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
} else if body != nil {
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
require.Equal(t, status, resp.StatusCode)
|
||||||
|
|
||||||
|
if response != nil {
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = json.Unmarshal(data, response)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOIDC(t *testing.T) {
|
||||||
|
id, err := uuid.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
userID, err := uuid.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testplanet.Run(t, testplanet.Config{
|
||||||
|
SatelliteCount: 1, StorageNodeCount: 1, UplinkCount: 1,
|
||||||
|
Reconfigure: testplanet.Reconfigure{
|
||||||
|
Satellite: func(_ *zap.Logger, _ int, config *satellite.Config) {
|
||||||
|
config.Admin.Address = "127.0.0.1:0"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, func(t *testing.T, ctx *testcontext.Context, planet *testplanet.Planet) {
|
||||||
|
sat := planet.Satellites[0]
|
||||||
|
|
||||||
|
adminAddr := sat.Admin.Admin.Listener.Addr().String()
|
||||||
|
consoleAddr := sat.API.Console.Listener.Addr().String()
|
||||||
|
|
||||||
|
issuer := "http://" + consoleAddr + "/"
|
||||||
|
authEndpoint := "http://" + consoleAddr + "/oauth/v2/authorize"
|
||||||
|
tokenEndpoint := "http://" + consoleAddr + "/oauth/v2/tokens"
|
||||||
|
userinfoEndpoint := "http://" + consoleAddr + "/oauth/v2/userinfo"
|
||||||
|
|
||||||
|
// Setup test user
|
||||||
|
|
||||||
|
regToken, err := sat.API.Console.Service.CreateRegToken(ctx, 1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
user, err := sat.API.Console.Service.CreateUser(ctx, console.CreateUser{
|
||||||
|
FullName: "User",
|
||||||
|
Email: "u@mail.test",
|
||||||
|
Password: "123a123",
|
||||||
|
}, regToken.Secret)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
activationToken, err := sat.API.Console.Service.GenerateActivationToken(ctx, user.ID, user.Email)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
consoleToken, err := sat.API.Console.Service.ActivateAccount(ctx, activationToken)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Set up a test project and bucket
|
||||||
|
|
||||||
|
authed := console.WithAuth(ctx, console.Authorization{
|
||||||
|
User: *user,
|
||||||
|
Claims: consoleauth.Claims{
|
||||||
|
ID: user.ID,
|
||||||
|
Email: user.Email,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
project, err := sat.API.Console.Service.CreateProject(authed, console.ProjectInfo{
|
||||||
|
Name: "test",
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bucketID, err := uuid.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
bucket, err := sat.API.Buckets.Service.CreateBucket(authed, storj.Bucket{
|
||||||
|
ID: bucketID,
|
||||||
|
Name: "test",
|
||||||
|
ProjectID: project.ID,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Create a client that will receive our tokens
|
||||||
|
|
||||||
|
callback, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer func() { _ = callback.Close() }()
|
||||||
|
|
||||||
|
client := oidc.OAuthClient{
|
||||||
|
ID: id,
|
||||||
|
Secret: []byte("badadmin"),
|
||||||
|
UserID: userID,
|
||||||
|
RedirectURL: "http://" + callback.Addr().String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
adminClients := fmt.Sprintf("http://%s/api/oauth/clients", adminAddr)
|
||||||
|
|
||||||
|
{
|
||||||
|
body, err := json.Marshal(client)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
send(t, bytes.NewReader(body), nil, http.StatusOK, adminClients, http.MethodPost, sat.Config.Console.AuthToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure OpenID Connect's well-known configuration endpoint works.
|
||||||
|
|
||||||
|
wellKnownConfig := fmt.Sprintf("http://%s/.well-known/openid-configuration", consoleAddr)
|
||||||
|
|
||||||
|
cfg := oidc.ProviderConfig{}
|
||||||
|
send(t, nil, &cfg, http.StatusOK, wellKnownConfig)
|
||||||
|
|
||||||
|
require.Equal(t, issuer, cfg.Issuer)
|
||||||
|
require.Equal(t, authEndpoint, cfg.AuthURL)
|
||||||
|
require.Equal(t, tokenEndpoint, cfg.TokenURL)
|
||||||
|
require.Equal(t, userinfoEndpoint, cfg.UserInfoURL)
|
||||||
|
|
||||||
|
// While we don't register a GET handler on the server, we need to ensure that the server returns in a 200
|
||||||
|
// request. This effectively delegates handling of the route to the Vue controller in the browser. If the
|
||||||
|
// server issues a redirect, we drop the encryption key in the fragment making it impossible for the client
|
||||||
|
// to encrypt the derived encryption key.
|
||||||
|
send(t, nil, nil, http.StatusOK, authEndpoint+"#fake-encryption-key")
|
||||||
|
|
||||||
|
// Prepare exchange for token
|
||||||
|
|
||||||
|
token := oauth2.Token{}
|
||||||
|
oauth2Config := oauth2.Config{
|
||||||
|
ClientID: client.ID.String(),
|
||||||
|
ClientSecret: string(client.Secret),
|
||||||
|
RedirectURL: client.RedirectURL,
|
||||||
|
Endpoint: oauth2.Endpoint{
|
||||||
|
AuthURL: authEndpoint,
|
||||||
|
TokenURL: tokenEndpoint,
|
||||||
|
},
|
||||||
|
Scopes: []string{
|
||||||
|
"openid",
|
||||||
|
"object:read",
|
||||||
|
"object:write",
|
||||||
|
"object:delete",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
state, err := uuid.New()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Set up the callback server to receive our single-use code and exchange for our initial set of tokens.
|
||||||
|
|
||||||
|
server := &http.Server{}
|
||||||
|
defer func() { _ = server.Shutdown(ctx) }()
|
||||||
|
|
||||||
|
server.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
q := r.URL.Query()
|
||||||
|
code := q.Get("code")
|
||||||
|
|
||||||
|
require.Equal(t, state.String(), q.Get("state"))
|
||||||
|
require.NotEqual(t, "", code)
|
||||||
|
|
||||||
|
token, err := oauth2Config.Exchange(r.Context(), code)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = json.NewEncoder(w).Encode(token)
|
||||||
|
require.NoError(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
group := errgroup.Group{}
|
||||||
|
group.Go(func() error {
|
||||||
|
return server.Serve(callback)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Mock submitting the consent screen, granting the application the following permissions.
|
||||||
|
|
||||||
|
scope := fmt.Sprintf("project:%s bucket:%s cubbyhole:cyphertext object:list object:read object:write object:delete",
|
||||||
|
project.ID.String(), bucket.Name)
|
||||||
|
|
||||||
|
consent := url.Values{}
|
||||||
|
consent.Set("redirect_uri", client.RedirectURL)
|
||||||
|
consent.Set("client_id", client.ID.String())
|
||||||
|
consent.Set("response_type", "code")
|
||||||
|
consent.Set("state", state.String())
|
||||||
|
consent.Set("scope", scope)
|
||||||
|
|
||||||
|
{
|
||||||
|
body := strings.NewReader(consent.Encode())
|
||||||
|
send(t, body, &token, http.StatusOK, authEndpoint, http.MethodPost, consoleToken, "application/x-www-form-urlencoded")
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, "Bearer", token.TokenType)
|
||||||
|
require.NotEqual(t, "", token.AccessToken)
|
||||||
|
require.NotEqual(t, "", token.RefreshToken)
|
||||||
|
|
||||||
|
// Refresh the tokens.
|
||||||
|
|
||||||
|
refresh := url.Values{}
|
||||||
|
refresh.Set("grant_type", "refresh_token")
|
||||||
|
refresh.Set("refresh_token", token.RefreshToken)
|
||||||
|
|
||||||
|
refreshed := oauth2.Token{}
|
||||||
|
auth := base64.StdEncoding.EncodeToString([]byte(client.ID.String() + ":" + string(client.Secret)))
|
||||||
|
|
||||||
|
{
|
||||||
|
body := strings.NewReader(refresh.Encode())
|
||||||
|
send(t, body, &refreshed, http.StatusOK, tokenEndpoint, http.MethodPost, "Basic "+auth, "application/x-www-form-urlencoded")
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Equal(t, token.RefreshToken, refreshed.RefreshToken)
|
||||||
|
require.NotEqual(t, token.AccessToken, refreshed.AccessToken)
|
||||||
|
|
||||||
|
// Fetch UserInfo
|
||||||
|
|
||||||
|
info := oidc.UserInfo{}
|
||||||
|
send(t, nil, &info, http.StatusOK, userinfoEndpoint, http.MethodGet, "Bearer "+token.AccessToken)
|
||||||
|
|
||||||
|
require.Equal(t, "cyphertext", info.Cubbyhole)
|
||||||
|
|
||||||
|
// Use token with uplink
|
||||||
|
|
||||||
|
apiKey, err := macaroon.ParseAPIKey(token.AccessToken)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// in practice, you should decrypt the cubbyhole and pass it here
|
||||||
|
key, err := storj.NewKey([]byte(info.Cubbyhole))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
encAccess := grant.NewEncryptionAccessWithDefaultKey(key)
|
||||||
|
encAccess.SetDefaultKey(key)
|
||||||
|
encAccess.SetDefaultPathCipher(storj.EncAESGCM)
|
||||||
|
|
||||||
|
accessGrant, err := (&grant.Access{
|
||||||
|
SatelliteAddress: sat.NodeURL().String(),
|
||||||
|
APIKey: apiKey,
|
||||||
|
EncAccess: encAccess,
|
||||||
|
}).Serialize()
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
access, err := uplink.ParseAccess(accessGrant)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
proj, err := uplink.OpenProject(ctx, access)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
upload, err := proj.UploadObject(ctx, bucket.Name, "testing/1/2/3", &uplink.UploadOptions{})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() { _ = upload.Abort() }()
|
||||||
|
|
||||||
|
_, err = upload.Write([]byte("hello world!"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = upload.Commit()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
download, err := proj.DownloadObject(ctx, bucket.Name, "testing/1/2/3", &uplink.DownloadOptions{
|
||||||
|
Length: -1,
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() { _ = download.Close() }()
|
||||||
|
|
||||||
|
content, err := io.ReadAll(download)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, "hello world!", string(content))
|
||||||
|
})
|
||||||
|
}
|
@ -23,6 +23,9 @@ type UUIDAuthorizeGenerate struct{}
|
|||||||
|
|
||||||
// Token returns a new authorization code.
|
// Token returns a new authorization code.
|
||||||
func (a *UUIDAuthorizeGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic) (string, error) {
|
func (a *UUIDAuthorizeGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic) (string, error) {
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
code, err := uuid.New()
|
code, err := uuid.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -44,6 +47,9 @@ type GenerateService interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *MacaroonAccessGenerate) apiKeyForProject(ctx context.Context, data *oauth2.GenerateBasic, project string) (*macaroon.APIKey, error) {
|
func (a *MacaroonAccessGenerate) apiKeyForProject(ctx context.Context, data *oauth2.GenerateBasic, project string) (*macaroon.APIKey, error) {
|
||||||
|
var err error
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
userID, err := uuid.FromString(data.UserID)
|
userID, err := uuid.FromString(data.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -99,6 +105,8 @@ func (a *MacaroonAccessGenerate) apiKeyForProject(ctx context.Context, data *oau
|
|||||||
// In OAuth2.0, access_tokens are short-lived tokens that authorize operations to be performed on behalf of an end user.
|
// In OAuth2.0, access_tokens are short-lived tokens that authorize operations to be performed on behalf of an end user.
|
||||||
// refresh_tokens are longer lived tokens that allow you to obtain new authorization tokens.
|
// refresh_tokens are longer lived tokens that allow you to obtain new authorization tokens.
|
||||||
func (a *MacaroonAccessGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (access, refresh string, err error) {
|
func (a *MacaroonAccessGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (access, refresh string, err error) {
|
||||||
|
defer mon.Task()(&ctx)(&err)
|
||||||
|
|
||||||
var apiKey *macaroon.APIKey
|
var apiKey *macaroon.APIKey
|
||||||
|
|
||||||
if priorRefresh := data.TokenInfo.GetRefresh(); isGenRefresh && priorRefresh != "" {
|
if priorRefresh := data.TokenInfo.GetRefresh(); isGenRefresh && priorRefresh != "" {
|
||||||
|
Loading…
Reference in New Issue
Block a user