forked from TrueCloudLab/frostfs-rest-gw
[#15] Accept list of tokens to sign
Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
parent
0e4e213352
commit
686588bc1a
12 changed files with 289 additions and 203 deletions
|
@ -57,9 +57,6 @@ const (
|
||||||
XBearerSignatureKey = "X-Bearer-Signature-Key"
|
XBearerSignatureKey = "X-Bearer-Signature-Key"
|
||||||
// XBearerOwnerID header contains owner id (wallet address) that corresponds the signature of the token body.
|
// XBearerOwnerID header contains owner id (wallet address) that corresponds the signature of the token body.
|
||||||
XBearerOwnerID = "X-Bearer-Owner-Id"
|
XBearerOwnerID = "X-Bearer-Owner-Id"
|
||||||
// XBearerScope header contains operation scope for auth (bearer) token.
|
|
||||||
// It corresponds to 'object' or 'container' services in neofs.
|
|
||||||
XBearerScope = "X-Bearer-Scope"
|
|
||||||
|
|
||||||
// tests configuration.
|
// tests configuration.
|
||||||
useWalletConnect = false
|
useWalletConnect = false
|
||||||
|
@ -119,6 +116,8 @@ func runTests(ctx context.Context, t *testing.T, key *keys.PrivateKey, version s
|
||||||
cnrID := createContainer(ctx, t, clientPool, containerName)
|
cnrID := createContainer(ctx, t, clientPool, containerName)
|
||||||
restrictByEACL(ctx, t, clientPool, cnrID)
|
restrictByEACL(ctx, t, clientPool, cnrID)
|
||||||
|
|
||||||
|
t.Run("rest auth several tokens "+version, func(t *testing.T) { authTokens(ctx, t) })
|
||||||
|
|
||||||
t.Run("rest put object "+version, func(t *testing.T) { restObjectPut(ctx, t, clientPool, cnrID) })
|
t.Run("rest put object "+version, func(t *testing.T) { restObjectPut(ctx, t, clientPool, cnrID) })
|
||||||
t.Run("rest get object "+version, func(t *testing.T) { restObjectGet(ctx, t, clientPool, cnrID) })
|
t.Run("rest get object "+version, func(t *testing.T) { restObjectGet(ctx, t, clientPool, cnrID) })
|
||||||
t.Run("rest delete object "+version, func(t *testing.T) { restObjectDelete(ctx, t, clientPool, cnrID) })
|
t.Run("rest delete object "+version, func(t *testing.T) { restObjectDelete(ctx, t, clientPool, cnrID) })
|
||||||
|
@ -231,6 +230,44 @@ func formRestrictRecord(op models.Operation) *models.Record {
|
||||||
}}}
|
}}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func authTokens(ctx context.Context, t *testing.T) {
|
||||||
|
bearers := []*models.Bearer{
|
||||||
|
{
|
||||||
|
Name: "all-object",
|
||||||
|
Object: []*models.Record{{
|
||||||
|
Operation: models.NewOperation(models.OperationPUT),
|
||||||
|
Action: models.NewAction(models.ActionALLOW),
|
||||||
|
Filters: []*models.Filter{},
|
||||||
|
Targets: []*models.Target{{
|
||||||
|
Role: models.NewRole(models.RoleOTHERS),
|
||||||
|
Keys: []string{},
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "put-container",
|
||||||
|
Container: &models.Rule{
|
||||||
|
Verb: models.NewVerb(models.VerbPUT),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "seteacl-container",
|
||||||
|
Container: &models.Rule{
|
||||||
|
Verb: models.NewVerb(models.VerbSETEACL),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "delete-container",
|
||||||
|
Container: &models.Rule{
|
||||||
|
Verb: models.NewVerb(models.VerbDELETE),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
httpClient := defaultHTTPClient()
|
||||||
|
makeAuthTokenRequest(ctx, t, bearers, httpClient)
|
||||||
|
}
|
||||||
|
|
||||||
func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) {
|
func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnrID *cid.ID) {
|
||||||
bearer := &models.Bearer{
|
bearer := &models.Bearer{
|
||||||
Object: []*models.Record{{
|
Object: []*models.Record{{
|
||||||
|
@ -246,7 +283,8 @@ func restObjectPut(ctx context.Context, t *testing.T, clientPool *pool.Pool, cnr
|
||||||
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
||||||
|
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
content := "content of file"
|
content := "content of file"
|
||||||
attrKey, attrValue := "User-Attribute", "user value"
|
attrKey, attrValue := "User-Attribute", "user value"
|
||||||
|
@ -338,7 +376,8 @@ func restObjectGet(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *cid.I
|
||||||
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
||||||
|
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
@ -415,7 +454,8 @@ func restObjectDelete(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *ci
|
||||||
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
||||||
|
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
@ -473,7 +513,8 @@ func restObjectsSearch(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
|
||||||
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
bearer.Object = append(bearer.Object, getRestrictBearerRecords()...)
|
||||||
|
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
search := &models.SearchFilters{
|
search := &models.SearchFilters{
|
||||||
Filters: []*models.SearchFilter{
|
Filters: []*models.SearchFilter{
|
||||||
|
@ -553,7 +594,8 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient := defaultHTTPClient()
|
httpClient := defaultHTTPClient()
|
||||||
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
query := make(url.Values)
|
query := make(url.Values)
|
||||||
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
query.Add(walletConnectQuery, strconv.FormatBool(useWalletConnect))
|
||||||
|
@ -581,7 +623,8 @@ func restContainerEACLPut(ctx context.Context, t *testing.T, clientPool *pool.Po
|
||||||
Verb: models.NewVerb(models.VerbSETEACL),
|
Verb: models.NewVerb(models.VerbSETEACL),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
req := models.Eacl{
|
req := models.Eacl{
|
||||||
Records: []*models.Record{{
|
Records: []*models.Record{{
|
||||||
|
@ -674,28 +717,19 @@ func restContainerList(ctx context.Context, t *testing.T, p *pool.Pool, cnrID *c
|
||||||
require.Contains(t, list.Containers, expected)
|
require.Contains(t, list.Containers, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeAuthContainerTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bearer, httpClient *http.Client) *handlers.BearerToken {
|
func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearers []*models.Bearer, httpClient *http.Client) []*handlers.BearerToken {
|
||||||
return makeAuthTokenRequest(ctx, t, bearer, httpClient, models.TokenTypeContainer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeAuthObjectTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bearer, httpClient *http.Client) *handlers.BearerToken {
|
|
||||||
return makeAuthTokenRequest(ctx, t, bearer, httpClient, models.TokenTypeObject)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bearer, httpClient *http.Client, tokenType models.TokenType) *handlers.BearerToken {
|
|
||||||
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key.PublicKey()))
|
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key.PublicKey()))
|
||||||
|
|
||||||
data, err := json.Marshal(bearer)
|
data, err := json.Marshal(bearers)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
request, err := http.NewRequest(http.MethodPost, testHost+"/v1/auth", bytes.NewReader(data))
|
request, err := http.NewRequest(http.MethodPost, testHost+"/v1/auth", bytes.NewReader(data))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
request = request.WithContext(ctx)
|
request = request.WithContext(ctx)
|
||||||
request.Header.Add("Content-Type", "application/json")
|
request.Header.Add("Content-Type", "application/json")
|
||||||
request.Header.Add(XBearerScope, string(tokenType))
|
|
||||||
request.Header.Add(XBearerOwnerID, ownerID.String())
|
request.Header.Add(XBearerOwnerID, ownerID.String())
|
||||||
|
|
||||||
resp, err := httpClient.Do(request)
|
resp, err := httpClient.Do(request)
|
||||||
|
@ -713,13 +747,26 @@ func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bear
|
||||||
}
|
}
|
||||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||||
|
|
||||||
stokenResp := &models.TokenResponse{}
|
var stokenResp []*models.TokenResponse
|
||||||
err = json.Unmarshal(rr, stokenResp)
|
err = json.Unmarshal(rr, &stokenResp)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, *stokenResp.Type, tokenType)
|
fmt.Println("resp tokens:")
|
||||||
|
|
||||||
binaryData, err := base64.StdEncoding.DecodeString(*stokenResp.Token)
|
respTokens := make([]*handlers.BearerToken, len(stokenResp))
|
||||||
|
for i, tok := range stokenResp {
|
||||||
|
isObject, err := handlers.IsObjectToken(bearers[i])
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, bearers[i].Name, tok.Name)
|
||||||
|
|
||||||
|
if isObject {
|
||||||
|
require.Equal(t, models.TokenTypeObject, *tok.Type)
|
||||||
|
} else {
|
||||||
|
require.Equal(t, models.TokenTypeContainer, *tok.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
binaryData, err := base64.StdEncoding.DecodeString(*tok.Token)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var bt *handlers.BearerToken
|
var bt *handlers.BearerToken
|
||||||
|
@ -729,8 +776,11 @@ func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bear
|
||||||
bt = signToken(t, key, binaryData)
|
bt = signToken(t, key, binaryData)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("container token:\n%+v\n", bt)
|
respTokens[i] = bt
|
||||||
return bt
|
fmt.Printf("%+v\n", bt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return respTokens
|
||||||
}
|
}
|
||||||
|
|
||||||
func signToken(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.BearerToken {
|
func signToken(t *testing.T, key *keys.PrivateKey, data []byte) *handlers.BearerToken {
|
||||||
|
@ -765,7 +815,8 @@ func restContainerPut(ctx context.Context, t *testing.T, clientPool *pool.Pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
httpClient := &http.Client{Timeout: 30 * time.Second}
|
httpClient := &http.Client{Timeout: 30 * time.Second}
|
||||||
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
|
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
|
||||||
|
bearerToken := bearerTokens[0]
|
||||||
|
|
||||||
attrKey, attrValue := "User-Attribute", "user value"
|
attrKey, attrValue := "User-Attribute", "user value"
|
||||||
userAttributes := map[string]string{
|
userAttributes := map[string]string{
|
||||||
|
|
|
@ -22,6 +22,9 @@ type Bearer struct {
|
||||||
// container
|
// container
|
||||||
Container *Rule `json:"container,omitempty"`
|
Container *Rule `json:"container,omitempty"`
|
||||||
|
|
||||||
|
// name
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
// object
|
// object
|
||||||
Object []*Record `json:"object"`
|
Object []*Record `json:"object"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,9 @@ import (
|
||||||
// swagger:model TokenResponse
|
// swagger:model TokenResponse
|
||||||
type TokenResponse struct {
|
type TokenResponse struct {
|
||||||
|
|
||||||
|
// name
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
|
||||||
// token
|
// token
|
||||||
// Required: true
|
// Required: true
|
||||||
Token *string `json:"token"`
|
Token *string `json:"token"`
|
||||||
|
|
|
@ -49,17 +49,6 @@ func init() {
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"enum": [
|
|
||||||
"object",
|
|
||||||
"container"
|
|
||||||
],
|
|
||||||
"type": "string",
|
|
||||||
"description": "Supported operation scope for token",
|
|
||||||
"name": "X-Bearer-Scope",
|
|
||||||
"in": "header",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default": 100,
|
"default": 100,
|
||||||
|
@ -69,20 +58,26 @@ func init() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Bearer token",
|
"description": "Bearer token",
|
||||||
"name": "token",
|
"name": "tokens",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
"$ref": "#/definitions/Bearer"
|
"$ref": "#/definitions/Bearer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Base64 encoded stable binary marshaled bearer token",
|
"description": "Base64 encoded stable binary marshaled bearer token",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
"$ref": "#/definitions/TokenResponse"
|
"$ref": "#/definitions/TokenResponse"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad request",
|
"description": "Bad request",
|
||||||
|
@ -555,6 +550,9 @@ func init() {
|
||||||
"container": {
|
"container": {
|
||||||
"$ref": "#/definitions/Rule"
|
"$ref": "#/definitions/Rule"
|
||||||
},
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"object": {
|
"object": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
@ -983,6 +981,9 @@ func init() {
|
||||||
"token"
|
"token"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -1100,17 +1101,6 @@ func init() {
|
||||||
"in": "header",
|
"in": "header",
|
||||||
"required": true
|
"required": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"enum": [
|
|
||||||
"object",
|
|
||||||
"container"
|
|
||||||
],
|
|
||||||
"type": "string",
|
|
||||||
"description": "Supported operation scope for token",
|
|
||||||
"name": "X-Bearer-Scope",
|
|
||||||
"in": "header",
|
|
||||||
"required": true
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default": 100,
|
"default": 100,
|
||||||
|
@ -1120,20 +1110,26 @@ func init() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Bearer token",
|
"description": "Bearer token",
|
||||||
"name": "token",
|
"name": "tokens",
|
||||||
"in": "body",
|
"in": "body",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
"$ref": "#/definitions/Bearer"
|
"$ref": "#/definitions/Bearer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "Base64 encoded stable binary marshaled bearer token",
|
"description": "Base64 encoded stable binary marshaled bearer token",
|
||||||
"schema": {
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
"$ref": "#/definitions/TokenResponse"
|
"$ref": "#/definitions/TokenResponse"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
"description": "Bad request",
|
"description": "Bad request",
|
||||||
|
@ -1702,6 +1698,9 @@ func init() {
|
||||||
"container": {
|
"container": {
|
||||||
"$ref": "#/definitions/Rule"
|
"$ref": "#/definitions/Rule"
|
||||||
},
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"object": {
|
"object": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
@ -2130,6 +2129,9 @@ func init() {
|
||||||
"token"
|
"token"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
|
@ -6,7 +6,6 @@ package operations
|
||||||
// Editing this file might prove futile when you re-run the swagger generate command
|
// Editing this file might prove futile when you re-run the swagger generate command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -54,16 +53,11 @@ type AuthParams struct {
|
||||||
In: header
|
In: header
|
||||||
*/
|
*/
|
||||||
XBearerOwnerID string
|
XBearerOwnerID string
|
||||||
/*Supported operation scope for token
|
|
||||||
Required: true
|
|
||||||
In: header
|
|
||||||
*/
|
|
||||||
XBearerScope string
|
|
||||||
/*Bearer token
|
/*Bearer token
|
||||||
Required: true
|
Required: true
|
||||||
In: body
|
In: body
|
||||||
*/
|
*/
|
||||||
Token *models.Bearer
|
Tokens []*models.Bearer
|
||||||
}
|
}
|
||||||
|
|
||||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||||
|
@ -83,36 +77,34 @@ func (o *AuthParams) BindRequest(r *http.Request, route *middleware.MatchedRoute
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := o.bindXBearerScope(r.Header[http.CanonicalHeaderKey("X-Bearer-Scope")], true, route.Formats); err != nil {
|
|
||||||
res = append(res, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if runtime.HasBody(r) {
|
if runtime.HasBody(r) {
|
||||||
defer r.Body.Close()
|
defer r.Body.Close()
|
||||||
var body models.Bearer
|
var body []*models.Bearer
|
||||||
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
if err := route.Consumer.Consume(r.Body, &body); err != nil {
|
||||||
if err == io.EOF {
|
if err == io.EOF {
|
||||||
res = append(res, errors.Required("token", "body", ""))
|
res = append(res, errors.Required("tokens", "body", ""))
|
||||||
} else {
|
} else {
|
||||||
res = append(res, errors.NewParseError("token", "body", "", err))
|
res = append(res, errors.NewParseError("tokens", "body", "", err))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// validate body object
|
|
||||||
if err := body.Validate(route.Formats); err != nil {
|
|
||||||
res = append(res, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := validate.WithOperationRequest(context.Background())
|
// validate array of body objects
|
||||||
if err := body.ContextValidate(ctx, route.Formats); err != nil {
|
for i := range body {
|
||||||
|
if body[i] == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := body[i].Validate(route.Formats); err != nil {
|
||||||
res = append(res, err)
|
res = append(res, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(res) == 0 {
|
if len(res) == 0 {
|
||||||
o.Token = &body
|
o.Tokens = body
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
res = append(res, errors.Required("token", "body", ""))
|
res = append(res, errors.Required("tokens", "body", ""))
|
||||||
}
|
}
|
||||||
if len(res) > 0 {
|
if len(res) > 0 {
|
||||||
return errors.CompositeValidationError(res...)
|
return errors.CompositeValidationError(res...)
|
||||||
|
@ -162,37 +154,3 @@ func (o *AuthParams) bindXBearerOwnerID(rawData []string, hasKey bool, formats s
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// bindXBearerScope binds and validates parameter XBearerScope from header.
|
|
||||||
func (o *AuthParams) bindXBearerScope(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
|
||||||
if !hasKey {
|
|
||||||
return errors.Required("X-Bearer-Scope", "header", rawData)
|
|
||||||
}
|
|
||||||
var raw string
|
|
||||||
if len(rawData) > 0 {
|
|
||||||
raw = rawData[len(rawData)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Required: true
|
|
||||||
|
|
||||||
if err := validate.RequiredString("X-Bearer-Scope", "header", raw); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
o.XBearerScope = raw
|
|
||||||
|
|
||||||
if err := o.validateXBearerScope(formats); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// validateXBearerScope carries on validations for parameter XBearerScope
|
|
||||||
func (o *AuthParams) validateXBearerScope(formats strfmt.Registry) error {
|
|
||||||
|
|
||||||
if err := validate.EnumCase("X-Bearer-Scope", "header", o.XBearerScope, []interface{}{"object", "container"}, true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ type AuthOK struct {
|
||||||
/*
|
/*
|
||||||
In: Body
|
In: Body
|
||||||
*/
|
*/
|
||||||
Payload *models.TokenResponse `json:"body,omitempty"`
|
Payload []*models.TokenResponse `json:"body,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAuthOK creates AuthOK with default headers values
|
// NewAuthOK creates AuthOK with default headers values
|
||||||
|
@ -35,13 +35,13 @@ func NewAuthOK() *AuthOK {
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithPayload adds the payload to the auth o k response
|
// WithPayload adds the payload to the auth o k response
|
||||||
func (o *AuthOK) WithPayload(payload *models.TokenResponse) *AuthOK {
|
func (o *AuthOK) WithPayload(payload []*models.TokenResponse) *AuthOK {
|
||||||
o.Payload = payload
|
o.Payload = payload
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetPayload sets the payload to the auth o k response
|
// SetPayload sets the payload to the auth o k response
|
||||||
func (o *AuthOK) SetPayload(payload *models.TokenResponse) {
|
func (o *AuthOK) SetPayload(payload []*models.TokenResponse) {
|
||||||
o.Payload = payload
|
o.Payload = payload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,13 +49,16 @@ func (o *AuthOK) SetPayload(payload *models.TokenResponse) {
|
||||||
func (o *AuthOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
func (o *AuthOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||||
|
|
||||||
rw.WriteHeader(200)
|
rw.WriteHeader(200)
|
||||||
if o.Payload != nil {
|
|
||||||
payload := o.Payload
|
payload := o.Payload
|
||||||
|
if payload == nil {
|
||||||
|
// return empty array
|
||||||
|
payload = make([]*models.TokenResponse, 0, 50)
|
||||||
|
}
|
||||||
|
|
||||||
if err := producer.Produce(rw, payload); err != nil {
|
if err := producer.Produce(rw, payload); err != nil {
|
||||||
panic(err) // let the recovery middleware deal with this
|
panic(err) // let the recovery middleware deal with this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// AuthBadRequestCode is the HTTP code returned for type AuthBadRequest
|
// AuthBadRequestCode is the HTTP code returned for type AuthBadRequest
|
||||||
const AuthBadRequestCode int = 400
|
const AuthBadRequestCode int = 400
|
||||||
|
|
114
handlers/auth.go
114
handlers/auth.go
|
@ -17,29 +17,86 @@ import (
|
||||||
|
|
||||||
const defaultTokenExpDuration = 100 // in epoch
|
const defaultTokenExpDuration = 100 // in epoch
|
||||||
|
|
||||||
|
type headersParams struct {
|
||||||
|
XBearerLifetime uint64
|
||||||
|
XBearerOwnerID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type objectTokenParams struct {
|
||||||
|
headersParams
|
||||||
|
Records []*models.Record
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerTokenParams struct {
|
||||||
|
headersParams
|
||||||
|
Rule *models.Rule
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newHeaderParams(params operations.AuthParams) headersParams {
|
||||||
|
prm := headersParams{
|
||||||
|
XBearerOwnerID: params.XBearerOwnerID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.XBearerLifetime != nil && *params.XBearerLifetime > 0 {
|
||||||
|
prm.XBearerLifetime = uint64(*params.XBearerLifetime)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prm
|
||||||
|
}
|
||||||
|
|
||||||
|
func newObjectParams(common headersParams, token *models.Bearer) objectTokenParams {
|
||||||
|
return objectTokenParams{
|
||||||
|
headersParams: common,
|
||||||
|
Records: token.Object,
|
||||||
|
Name: token.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContainerParams(common headersParams, token *models.Bearer) containerTokenParams {
|
||||||
|
return containerTokenParams{
|
||||||
|
headersParams: common,
|
||||||
|
Rule: token.Container,
|
||||||
|
Name: token.Name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// PostAuth handler that forms bearer token to sign.
|
// PostAuth handler that forms bearer token to sign.
|
||||||
func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
|
func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
|
||||||
var (
|
ctx := params.HTTPRequest.Context()
|
||||||
err error
|
commonPrm := newHeaderParams(params)
|
||||||
resp *models.TokenResponse
|
|
||||||
)
|
|
||||||
|
|
||||||
if params.XBearerScope == "object" {
|
tokenNames := make(map[string]struct{})
|
||||||
resp, err = prepareObjectToken(params, a.pool)
|
response := make([]*models.TokenResponse, len(params.Tokens))
|
||||||
} else {
|
for i, token := range params.Tokens {
|
||||||
resp, err = prepareContainerTokens(params, a.pool, a.key.PublicKey())
|
if _, ok := tokenNames[token.Name]; ok {
|
||||||
|
return operations.NewAuthBadRequest().WithPayload(models.Error(fmt.Sprintf("duplicated token name '%s'", token.Name)))
|
||||||
}
|
}
|
||||||
|
tokenNames[token.Name] = struct{}{}
|
||||||
|
|
||||||
|
isObject, err := IsObjectToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error()))
|
return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return operations.NewAuthOK().WithPayload(resp)
|
if isObject {
|
||||||
|
prm := newObjectParams(commonPrm, token)
|
||||||
|
response[i], err = prepareObjectToken(ctx, prm, a.pool)
|
||||||
|
} else {
|
||||||
|
prm := newContainerParams(commonPrm, token)
|
||||||
|
response[i], err = prepareContainerTokens(ctx, prm, a.pool, a.key.PublicKey())
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareObjectToken(params operations.AuthParams, pool *pool.Pool) (*models.TokenResponse, error) {
|
return operations.NewAuthOK().WithPayload(response)
|
||||||
ctx := params.HTTPRequest.Context()
|
}
|
||||||
|
|
||||||
btoken, err := util.ToNativeObjectToken(params.Token)
|
func prepareObjectToken(ctx context.Context, params objectTokenParams, pool *pool.Pool) (*models.TokenResponse, error) {
|
||||||
|
btoken, err := util.ToNativeObjectToken(params.Records)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't transform token to native: %w", err)
|
return nil, fmt.Errorf("couldn't transform token to native: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -56,16 +113,14 @@ func prepareObjectToken(params operations.AuthParams, pool *pool.Pool) (*models.
|
||||||
return nil, fmt.Errorf("couldn't marshal bearer token: %w", err)
|
return nil, fmt.Errorf("couldn't marshal bearer token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp models.TokenResponse
|
return &models.TokenResponse{
|
||||||
resp.Type = models.NewTokenType(models.TokenTypeObject)
|
Name: params.Name,
|
||||||
resp.Token = util.NewString(base64.StdEncoding.EncodeToString(binaryBearer))
|
Type: models.NewTokenType(models.TokenTypeObject),
|
||||||
|
Token: util.NewString(base64.StdEncoding.EncodeToString(binaryBearer)),
|
||||||
return &resp, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func prepareContainerTokens(params operations.AuthParams, pool *pool.Pool, key *keys.PublicKey) (*models.TokenResponse, error) {
|
func prepareContainerTokens(ctx context.Context, params containerTokenParams, pool *pool.Pool, key *keys.PublicKey) (*models.TokenResponse, error) {
|
||||||
ctx := params.HTTPRequest.Context()
|
|
||||||
|
|
||||||
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
|
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
||||||
|
@ -76,10 +131,7 @@ func prepareContainerTokens(params operations.AuthParams, pool *pool.Pool, key *
|
||||||
return nil, fmt.Errorf("invalid bearer owner: %w", err)
|
return nil, fmt.Errorf("invalid bearer owner: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp models.TokenResponse
|
stoken, err := util.ToNativeContainerToken(params.Rule)
|
||||||
resp.Type = models.NewTokenType(models.TokenTypeContainer)
|
|
||||||
|
|
||||||
stoken, err := util.ToNativeContainerToken(params.Token)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't transform rule to native session token: %w", err)
|
return nil, fmt.Errorf("couldn't transform rule to native session token: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -101,9 +153,11 @@ func prepareContainerTokens(params operations.AuthParams, pool *pool.Pool, key *
|
||||||
return nil, fmt.Errorf("couldn't marshal session token: %w", err)
|
return nil, fmt.Errorf("couldn't marshal session token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.Token = util.NewString(base64.StdEncoding.EncodeToString(binaryToken))
|
return &models.TokenResponse{
|
||||||
|
Name: params.Name,
|
||||||
return &resp, nil
|
Type: models.NewTokenType(models.TokenTypeContainer),
|
||||||
|
Token: util.NewString(base64.StdEncoding.EncodeToString(binaryToken)),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCurrentEpoch(ctx context.Context, p *pool.Pool) (uint64, error) {
|
func getCurrentEpoch(ctx context.Context, p *pool.Pool) (uint64, error) {
|
||||||
|
@ -115,15 +169,15 @@ func getCurrentEpoch(ctx context.Context, p *pool.Pool) (uint64, error) {
|
||||||
return netInfo.CurrentEpoch(), nil
|
return netInfo.CurrentEpoch(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTokenLifetime(ctx context.Context, p *pool.Pool, expDuration *int64) (uint64, uint64, error) {
|
func getTokenLifetime(ctx context.Context, p *pool.Pool, expDuration uint64) (uint64, uint64, error) {
|
||||||
currEpoch, err := getCurrentEpoch(ctx, p)
|
currEpoch, err := getCurrentEpoch(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var lifetimeDuration uint64 = defaultTokenExpDuration
|
var lifetimeDuration uint64 = defaultTokenExpDuration
|
||||||
if expDuration != nil && *expDuration > 0 {
|
if expDuration != 0 {
|
||||||
lifetimeDuration = uint64(*expDuration)
|
lifetimeDuration = expDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
return currEpoch, currEpoch + lifetimeDuration, nil
|
return currEpoch, currEpoch + lifetimeDuration, nil
|
||||||
|
|
|
@ -24,8 +24,7 @@ func TestSign(t *testing.T) {
|
||||||
|
|
||||||
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
|
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
|
||||||
|
|
||||||
b := &models.Bearer{
|
records := []*models.Record{{
|
||||||
Object: []*models.Record{{
|
|
||||||
Operation: models.NewOperation(models.OperationPUT),
|
Operation: models.NewOperation(models.OperationPUT),
|
||||||
Action: models.NewAction(models.ActionALLOW),
|
Action: models.NewAction(models.ActionALLOW),
|
||||||
Filters: []*models.Filter{},
|
Filters: []*models.Filter{},
|
||||||
|
@ -33,10 +32,9 @@ func TestSign(t *testing.T) {
|
||||||
Role: models.NewRole(models.RoleOTHERS),
|
Role: models.NewRole(models.RoleOTHERS),
|
||||||
Keys: []string{},
|
Keys: []string{},
|
||||||
}},
|
}},
|
||||||
}},
|
}}
|
||||||
}
|
|
||||||
|
|
||||||
btoken, err := util.ToNativeObjectToken(b)
|
btoken, err := util.ToNativeObjectToken(records)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
||||||
|
|
|
@ -216,3 +216,19 @@ func updateExpirationHeader(headers map[string]string, durations *epochDurations
|
||||||
numEpoch := expDuration.Milliseconds() / epochDuration
|
numEpoch := expDuration.Milliseconds() / epochDuration
|
||||||
headers[objectv2.SysAttributeExpEpoch] = strconv.FormatInt(int64(durations.currentEpoch)+numEpoch, 10)
|
headers[objectv2.SysAttributeExpEpoch] = strconv.FormatInt(int64(durations.currentEpoch)+numEpoch, 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsObjectToken check that provided token is for object.
|
||||||
|
func IsObjectToken(token *models.Bearer) (bool, error) {
|
||||||
|
isObject := len(token.Object) != 0
|
||||||
|
isContainer := token.Container != nil
|
||||||
|
|
||||||
|
if !isObject && !isContainer {
|
||||||
|
return false, fmt.Errorf("token '%s': rules must not be empty", token.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if isObject && isContainer {
|
||||||
|
return false, fmt.Errorf("token '%s': only one type rules can be provided: object or container, not both", token.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return isObject, nil
|
||||||
|
}
|
||||||
|
|
|
@ -222,9 +222,9 @@ func ToNativeRule(r *models.Rule) (*session.ContainerContext, error) {
|
||||||
return &ctx, nil
|
return &ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToNativeContainerToken converts models.Bearer to appropriate session.Token.
|
// ToNativeContainerToken converts models.Rule to appropriate session.Token.
|
||||||
func ToNativeContainerToken(b *models.Bearer) (*session.Token, error) {
|
func ToNativeContainerToken(tokenRule *models.Rule) (*session.Token, error) {
|
||||||
sctx, err := ToNativeRule(b.Container)
|
sctx, err := ToNativeRule(tokenRule)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't transform rule to native: %w", err)
|
return nil, fmt.Errorf("couldn't transform rule to native: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -365,9 +365,9 @@ func FromNativeTarget(t eacl.Target) (*models.Target, error) {
|
||||||
return &target, nil
|
return &target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToNativeObjectToken converts Bearer to appropriate token.BearerToken.
|
// ToNativeObjectToken converts []*models.Record to appropriate token.BearerToken.
|
||||||
func ToNativeObjectToken(b *models.Bearer) (*token.BearerToken, error) {
|
func ToNativeObjectToken(tokenRecords []*models.Record) (*token.BearerToken, error) {
|
||||||
table, err := ToNativeTable(b.Object)
|
table, err := ToNativeTable(tokenRecords)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,7 @@ func TestSign(t *testing.T) {
|
||||||
|
|
||||||
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
|
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
|
||||||
|
|
||||||
b := &models.Bearer{
|
records := []*models.Record{{
|
||||||
Object: []*models.Record{{
|
|
||||||
Operation: models.NewOperation(models.OperationPUT),
|
Operation: models.NewOperation(models.OperationPUT),
|
||||||
Action: models.NewAction(models.ActionALLOW),
|
Action: models.NewAction(models.ActionALLOW),
|
||||||
Filters: []*models.Filter{},
|
Filters: []*models.Filter{},
|
||||||
|
@ -29,10 +28,9 @@ func TestSign(t *testing.T) {
|
||||||
Role: models.NewRole(models.RoleOTHERS),
|
Role: models.NewRole(models.RoleOTHERS),
|
||||||
Keys: []string{},
|
Keys: []string{},
|
||||||
}},
|
}},
|
||||||
}},
|
}}
|
||||||
}
|
|
||||||
|
|
||||||
btoken, err := util.ToNativeObjectToken(b)
|
btoken, err := util.ToNativeObjectToken(records)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)
|
||||||
|
|
|
@ -64,24 +64,18 @@ paths:
|
||||||
description: Owner Id (wallet address) that will sign the token
|
description: Owner Id (wallet address) that will sign the token
|
||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
- in: header
|
|
||||||
description: Supported operation scope for token
|
|
||||||
name: X-Bearer-Scope
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- object
|
|
||||||
- container
|
|
||||||
required: true
|
|
||||||
- in: header
|
- in: header
|
||||||
description: Token lifetime in epoch
|
description: Token lifetime in epoch
|
||||||
name: X-Bearer-Lifetime
|
name: X-Bearer-Lifetime
|
||||||
type: integer
|
type: integer
|
||||||
default: 100
|
default: 100
|
||||||
- in: body
|
- in: body
|
||||||
name: token
|
name: tokens
|
||||||
required: true
|
required: true
|
||||||
description: Bearer token
|
description: Bearer token
|
||||||
schema:
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
$ref: '#/definitions/Bearer'
|
$ref: '#/definitions/Bearer'
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
|
@ -91,6 +85,8 @@ paths:
|
||||||
200:
|
200:
|
||||||
description: Base64 encoded stable binary marshaled bearer token
|
description: Base64 encoded stable binary marshaled bearer token
|
||||||
schema:
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
$ref: '#/definitions/TokenResponse'
|
$ref: '#/definitions/TokenResponse'
|
||||||
400:
|
400:
|
||||||
description: Bad request
|
description: Bad request
|
||||||
|
@ -363,6 +359,8 @@ definitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
object:
|
object:
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
|
@ -483,6 +481,8 @@ definitions:
|
||||||
TokenResponse:
|
TokenResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
type:
|
type:
|
||||||
$ref: '#/definitions/TokenType'
|
$ref: '#/definitions/TokenType'
|
||||||
token:
|
token:
|
||||||
|
|
Loading…
Reference in a new issue