[#15] Accept list of tokens to sign

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2022-07-07 12:02:05 +03:00 committed by Alex Vanin
parent 0e4e213352
commit 686588bc1a
12 changed files with 289 additions and 203 deletions

View file

@ -57,9 +57,6 @@ const (
XBearerSignatureKey = "X-Bearer-Signature-Key"
// XBearerOwnerID header contains owner id (wallet address) that corresponds the signature of the token body.
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.
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)
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 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) })
@ -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) {
bearer := &models.Bearer{
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()...)
httpClient := defaultHTTPClient()
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
content := "content of file"
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()...)
httpClient := defaultHTTPClient()
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
query := make(url.Values)
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()...)
httpClient := defaultHTTPClient()
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
query := make(url.Values)
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()...)
httpClient := defaultHTTPClient()
bearerToken := makeAuthObjectTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
search := &models.SearchFilters{
Filters: []*models.SearchFilter{
@ -553,7 +594,8 @@ func restContainerDelete(ctx context.Context, t *testing.T, clientPool *pool.Poo
}
httpClient := defaultHTTPClient()
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
query := make(url.Values)
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),
},
}
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
req := models.Eacl{
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)
}
func makeAuthContainerTokenRequest(ctx context.Context, t *testing.T, bearer *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 {
func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearers []*models.Bearer, httpClient *http.Client) []*handlers.BearerToken {
key, err := keys.NewPrivateKeyFromHex(devenvPrivateKey)
require.NoError(t, err)
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(key.PublicKey()))
data, err := json.Marshal(bearer)
data, err := json.Marshal(bearers)
require.NoError(t, err)
request, err := http.NewRequest(http.MethodPost, testHost+"/v1/auth", bytes.NewReader(data))
require.NoError(t, err)
request = request.WithContext(ctx)
request.Header.Add("Content-Type", "application/json")
request.Header.Add(XBearerScope, string(tokenType))
request.Header.Add(XBearerOwnerID, ownerID.String())
resp, err := httpClient.Do(request)
@ -713,24 +747,40 @@ func makeAuthTokenRequest(ctx context.Context, t *testing.T, bearer *models.Bear
}
require.Equal(t, http.StatusOK, resp.StatusCode)
stokenResp := &models.TokenResponse{}
err = json.Unmarshal(rr, stokenResp)
var stokenResp []*models.TokenResponse
err = json.Unmarshal(rr, &stokenResp)
require.NoError(t, err)
require.Equal(t, *stokenResp.Type, tokenType)
fmt.Println("resp tokens:")
binaryData, err := base64.StdEncoding.DecodeString(*stokenResp.Token)
require.NoError(t, err)
respTokens := make([]*handlers.BearerToken, len(stokenResp))
for i, tok := range stokenResp {
isObject, err := handlers.IsObjectToken(bearers[i])
require.NoError(t, err)
var bt *handlers.BearerToken
if useWalletConnect {
bt = signTokenWalletConnect(t, key, binaryData)
} else {
bt = signToken(t, key, binaryData)
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)
var bt *handlers.BearerToken
if useWalletConnect {
bt = signTokenWalletConnect(t, key, binaryData)
} else {
bt = signToken(t, key, binaryData)
}
respTokens[i] = bt
fmt.Printf("%+v\n", bt)
}
fmt.Printf("container token:\n%+v\n", bt)
return bt
return respTokens
}
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}
bearerToken := makeAuthContainerTokenRequest(ctx, t, bearer, httpClient)
bearerTokens := makeAuthTokenRequest(ctx, t, []*models.Bearer{bearer}, httpClient)
bearerToken := bearerTokens[0]
attrKey, attrValue := "User-Attribute", "user value"
userAttributes := map[string]string{

View file

@ -22,6 +22,9 @@ type Bearer struct {
// container
Container *Rule `json:"container,omitempty"`
// name
Name string `json:"name,omitempty"`
// object
Object []*Record `json:"object"`
}

View file

@ -20,6 +20,9 @@ import (
// swagger:model TokenResponse
type TokenResponse struct {
// name
Name string `json:"name,omitempty"`
// token
// Required: true
Token *string `json:"token"`

View file

@ -49,17 +49,6 @@ func init() {
"in": "header",
"required": true
},
{
"enum": [
"object",
"container"
],
"type": "string",
"description": "Supported operation scope for token",
"name": "X-Bearer-Scope",
"in": "header",
"required": true
},
{
"type": "integer",
"default": 100,
@ -69,11 +58,14 @@ func init() {
},
{
"description": "Bearer token",
"name": "token",
"name": "tokens",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Bearer"
"type": "array",
"items": {
"$ref": "#/definitions/Bearer"
}
}
}
],
@ -81,7 +73,10 @@ func init() {
"200": {
"description": "Base64 encoded stable binary marshaled bearer token",
"schema": {
"$ref": "#/definitions/TokenResponse"
"type": "array",
"items": {
"$ref": "#/definitions/TokenResponse"
}
}
},
"400": {
@ -555,6 +550,9 @@ func init() {
"container": {
"$ref": "#/definitions/Rule"
},
"name": {
"type": "string"
},
"object": {
"type": "array",
"items": {
@ -983,6 +981,9 @@ func init() {
"token"
],
"properties": {
"name": {
"type": "string"
},
"token": {
"type": "string"
},
@ -1100,17 +1101,6 @@ func init() {
"in": "header",
"required": true
},
{
"enum": [
"object",
"container"
],
"type": "string",
"description": "Supported operation scope for token",
"name": "X-Bearer-Scope",
"in": "header",
"required": true
},
{
"type": "integer",
"default": 100,
@ -1120,11 +1110,14 @@ func init() {
},
{
"description": "Bearer token",
"name": "token",
"name": "tokens",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/Bearer"
"type": "array",
"items": {
"$ref": "#/definitions/Bearer"
}
}
}
],
@ -1132,7 +1125,10 @@ func init() {
"200": {
"description": "Base64 encoded stable binary marshaled bearer token",
"schema": {
"$ref": "#/definitions/TokenResponse"
"type": "array",
"items": {
"$ref": "#/definitions/TokenResponse"
}
}
},
"400": {
@ -1702,6 +1698,9 @@ func init() {
"container": {
"$ref": "#/definitions/Rule"
},
"name": {
"type": "string"
},
"object": {
"type": "array",
"items": {
@ -2130,6 +2129,9 @@ func init() {
"token"
],
"properties": {
"name": {
"type": "string"
},
"token": {
"type": "string"
},

View file

@ -6,7 +6,6 @@ package operations
// Editing this file might prove futile when you re-run the swagger generate command
import (
"context"
"io"
"net/http"
@ -54,16 +53,11 @@ type AuthParams struct {
In: header
*/
XBearerOwnerID string
/*Supported operation scope for token
Required: true
In: header
*/
XBearerScope string
/*Bearer token
Required: true
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
@ -83,36 +77,34 @@ func (o *AuthParams) BindRequest(r *http.Request, route *middleware.MatchedRoute
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) {
defer r.Body.Close()
var body models.Bearer
var body []*models.Bearer
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("token", "body", ""))
res = append(res, errors.Required("tokens", "body", ""))
} else {
res = append(res, errors.NewParseError("token", "body", "", err))
res = append(res, errors.NewParseError("tokens", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
ctx := validate.WithOperationRequest(context.Background())
if err := body.ContextValidate(ctx, route.Formats); err != nil {
res = append(res, err)
// validate array of body objects
for i := range body {
if body[i] == nil {
continue
}
if err := body[i].Validate(route.Formats); err != nil {
res = append(res, err)
break
}
}
if len(res) == 0 {
o.Token = &body
o.Tokens = body
}
}
} else {
res = append(res, errors.Required("token", "body", ""))
res = append(res, errors.Required("tokens", "body", ""))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
@ -162,37 +154,3 @@ func (o *AuthParams) bindXBearerOwnerID(rawData []string, hasKey bool, formats s
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
}

View file

@ -25,7 +25,7 @@ type AuthOK struct {
/*
In: Body
*/
Payload *models.TokenResponse `json:"body,omitempty"`
Payload []*models.TokenResponse `json:"body,omitempty"`
}
// NewAuthOK creates AuthOK with default headers values
@ -35,13 +35,13 @@ func NewAuthOK() *AuthOK {
}
// 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
return o
}
// 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
}
@ -49,11 +49,14 @@ func (o *AuthOK) SetPayload(payload *models.TokenResponse) {
func (o *AuthOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(200)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
payload := o.Payload
if payload == nil {
// return empty array
payload = make([]*models.TokenResponse, 0, 50)
}
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}

View file

@ -17,29 +17,86 @@ import (
const defaultTokenExpDuration = 100 // in epoch
// PostAuth handler that forms bearer token to sign.
func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
var (
err error
resp *models.TokenResponse
)
if params.XBearerScope == "object" {
resp, err = prepareObjectToken(params, a.pool)
} else {
resp, err = prepareContainerTokens(params, a.pool, a.key.PublicKey())
}
if err != nil {
return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error()))
}
return operations.NewAuthOK().WithPayload(resp)
type headersParams struct {
XBearerLifetime uint64
XBearerOwnerID string
}
func prepareObjectToken(params operations.AuthParams, pool *pool.Pool) (*models.TokenResponse, error) {
ctx := params.HTTPRequest.Context()
type objectTokenParams struct {
headersParams
Records []*models.Record
Name string
}
btoken, err := util.ToNativeObjectToken(params.Token)
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.
func (a *API) PostAuth(params operations.AuthParams) middleware.Responder {
ctx := params.HTTPRequest.Context()
commonPrm := newHeaderParams(params)
tokenNames := make(map[string]struct{})
response := make([]*models.TokenResponse, len(params.Tokens))
for i, token := range params.Tokens {
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 {
return operations.NewAuthBadRequest().WithPayload(models.Error(err.Error()))
}
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()))
}
}
return operations.NewAuthOK().WithPayload(response)
}
func prepareObjectToken(ctx context.Context, params objectTokenParams, pool *pool.Pool) (*models.TokenResponse, error) {
btoken, err := util.ToNativeObjectToken(params.Records)
if err != nil {
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)
}
var resp models.TokenResponse
resp.Type = models.NewTokenType(models.TokenTypeObject)
resp.Token = util.NewString(base64.StdEncoding.EncodeToString(binaryBearer))
return &resp, nil
return &models.TokenResponse{
Name: params.Name,
Type: models.NewTokenType(models.TokenTypeObject),
Token: util.NewString(base64.StdEncoding.EncodeToString(binaryBearer)),
}, nil
}
func prepareContainerTokens(params operations.AuthParams, pool *pool.Pool, key *keys.PublicKey) (*models.TokenResponse, error) {
ctx := params.HTTPRequest.Context()
func prepareContainerTokens(ctx context.Context, params containerTokenParams, pool *pool.Pool, key *keys.PublicKey) (*models.TokenResponse, error) {
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
if err != nil {
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)
}
var resp models.TokenResponse
resp.Type = models.NewTokenType(models.TokenTypeContainer)
stoken, err := util.ToNativeContainerToken(params.Token)
stoken, err := util.ToNativeContainerToken(params.Rule)
if err != nil {
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)
}
resp.Token = util.NewString(base64.StdEncoding.EncodeToString(binaryToken))
return &resp, nil
return &models.TokenResponse{
Name: params.Name,
Type: models.NewTokenType(models.TokenTypeContainer),
Token: util.NewString(base64.StdEncoding.EncodeToString(binaryToken)),
}, nil
}
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
}
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)
if err != nil {
return 0, 0, err
}
var lifetimeDuration uint64 = defaultTokenExpDuration
if expDuration != nil && *expDuration > 0 {
lifetimeDuration = uint64(*expDuration)
if expDuration != 0 {
lifetimeDuration = expDuration
}
return currEpoch, currEpoch + lifetimeDuration, nil

View file

@ -24,19 +24,17 @@ func TestSign(t *testing.T) {
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
b := &models.Bearer{
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{},
}},
records := []*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{},
}},
}
}}
btoken, err := util.ToNativeObjectToken(b)
btoken, err := util.ToNativeObjectToken(records)
require.NoError(t, err)
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)

View file

@ -216,3 +216,19 @@ func updateExpirationHeader(headers map[string]string, durations *epochDurations
numEpoch := expDuration.Milliseconds() / epochDuration
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
}

View file

@ -222,9 +222,9 @@ func ToNativeRule(r *models.Rule) (*session.ContainerContext, error) {
return &ctx, nil
}
// ToNativeContainerToken converts models.Bearer to appropriate session.Token.
func ToNativeContainerToken(b *models.Bearer) (*session.Token, error) {
sctx, err := ToNativeRule(b.Container)
// ToNativeContainerToken converts models.Rule to appropriate session.Token.
func ToNativeContainerToken(tokenRule *models.Rule) (*session.Token, error) {
sctx, err := ToNativeRule(tokenRule)
if err != nil {
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
}
// ToNativeObjectToken converts Bearer to appropriate token.BearerToken.
func ToNativeObjectToken(b *models.Bearer) (*token.BearerToken, error) {
table, err := ToNativeTable(b.Object)
// ToNativeObjectToken converts []*models.Record to appropriate token.BearerToken.
func ToNativeObjectToken(tokenRecords []*models.Record) (*token.BearerToken, error) {
table, err := ToNativeTable(tokenRecords)
if err != nil {
return nil, err
}

View file

@ -20,19 +20,17 @@ func TestSign(t *testing.T) {
pubKeyHex := hex.EncodeToString(key.PublicKey().Bytes())
b := &models.Bearer{
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{},
}},
records := []*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{},
}},
}
}}
btoken, err := util.ToNativeObjectToken(b)
btoken, err := util.ToNativeObjectToken(records)
require.NoError(t, err)
ownerKey, err := keys.NewPublicKeyFromString(pubKeyHex)

View file

@ -64,25 +64,19 @@ paths:
description: Owner Id (wallet address) that will sign the token
type: string
required: true
- in: header
description: Supported operation scope for token
name: X-Bearer-Scope
type: string
enum:
- object
- container
required: true
- in: header
description: Token lifetime in epoch
name: X-Bearer-Lifetime
type: integer
default: 100
- in: body
name: token
name: tokens
required: true
description: Bearer token
schema:
$ref: '#/definitions/Bearer'
type: array
items:
$ref: '#/definitions/Bearer'
consumes:
- application/json
produces:
@ -91,7 +85,9 @@ paths:
200:
description: Base64 encoded stable binary marshaled bearer token
schema:
$ref: '#/definitions/TokenResponse'
type: array
items:
$ref: '#/definitions/TokenResponse'
400:
description: Bad request
schema:
@ -363,6 +359,8 @@ definitions:
Bearer:
type: object
properties:
name:
type: string
object:
type: array
items:
@ -483,6 +481,8 @@ definitions:
TokenResponse:
type: object
properties:
name:
type: string
type:
$ref: '#/definitions/TokenType'
token: