2022-04-11 09:35:06 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
|
2023-03-07 15:02:40 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/acl"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
|
|
|
sessionv2 "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-rest-gw/gen/models"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-rest-gw/gen/restapi/operations"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-rest-gw/internal/util"
|
|
|
|
frostfsecdsa "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto/ecdsa"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/pool"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
|
2022-04-11 09:35:06 +00:00
|
|
|
"github.com/go-openapi/runtime/middleware"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
|
|
)
|
|
|
|
|
|
|
|
const defaultTokenExpDuration = 100 // in epoch
|
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
type headersParams struct {
|
2022-08-18 14:51:18 +00:00
|
|
|
XBearerLifetime uint64
|
|
|
|
XBearerOwnerID string
|
|
|
|
XBearerForAllUsers bool
|
2022-07-07 09:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type objectTokenParams struct {
|
|
|
|
headersParams
|
|
|
|
Records []*models.Record
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
|
|
|
type containerTokenParams struct {
|
|
|
|
headersParams
|
|
|
|
Rule *models.Rule
|
|
|
|
Name string
|
|
|
|
}
|
2022-04-11 09:35:06 +00:00
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
func newHeaderParams(params operations.AuthParams) headersParams {
|
|
|
|
prm := headersParams{
|
2022-08-18 14:51:18 +00:00
|
|
|
XBearerOwnerID: params.XBearerOwnerID,
|
|
|
|
XBearerForAllUsers: *params.XBearerForAllUsers,
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
2022-07-07 09:02:05 +00:00
|
|
|
|
|
|
|
if params.XBearerLifetime != nil && *params.XBearerLifetime > 0 {
|
|
|
|
prm.XBearerLifetime = uint64(*params.XBearerLifetime)
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
return prm
|
|
|
|
}
|
|
|
|
|
|
|
|
func newObjectParams(common headersParams, token *models.Bearer) objectTokenParams {
|
|
|
|
return objectTokenParams{
|
|
|
|
headersParams: common,
|
|
|
|
Records: token.Object,
|
|
|
|
Name: token.Name,
|
|
|
|
}
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
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 {
|
2022-04-11 09:35:06 +00:00
|
|
|
ctx := params.HTTPRequest.Context()
|
2022-07-07 09:02:05 +00:00
|
|
|
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 {
|
2022-07-07 12:59:38 +00:00
|
|
|
err := fmt.Errorf("duplicated token name '%s'", token.Name)
|
|
|
|
return operations.NewAuthBadRequest().WithPayload(util.NewErrorResponse(err))
|
2022-07-07 09:02:05 +00:00
|
|
|
}
|
|
|
|
tokenNames[token.Name] = struct{}{}
|
|
|
|
|
|
|
|
isObject, err := IsObjectToken(token)
|
|
|
|
if err != nil {
|
2022-07-07 12:59:38 +00:00
|
|
|
return operations.NewAuthBadRequest().WithPayload(util.NewErrorResponse(err))
|
2022-07-07 09:02:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if isObject {
|
|
|
|
prm := newObjectParams(commonPrm, token)
|
2022-07-12 08:01:21 +00:00
|
|
|
response[i], err = prepareObjectToken(ctx, prm, a.pool, *a.owner)
|
2022-07-07 09:02:05 +00:00
|
|
|
} else {
|
|
|
|
prm := newContainerParams(commonPrm, token)
|
|
|
|
response[i], err = prepareContainerTokens(ctx, prm, a.pool, a.key.PublicKey())
|
|
|
|
}
|
|
|
|
if err != nil {
|
2022-07-07 12:59:38 +00:00
|
|
|
return operations.NewAuthBadRequest().WithPayload(util.NewErrorResponse(err))
|
2022-07-07 09:02:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-05 19:09:19 +00:00
|
|
|
return operations.NewAuthOK().
|
|
|
|
WithPayload(response).
|
|
|
|
WithAccessControlAllowOrigin("*")
|
2022-07-07 09:02:05 +00:00
|
|
|
}
|
2022-04-11 09:35:06 +00:00
|
|
|
|
2022-08-18 14:35:53 +00:00
|
|
|
// FormBinaryBearer handler that forms binary bearer token using headers with body and signature.
|
|
|
|
func (a *API) FormBinaryBearer(params operations.FormBinaryBearerParams, principal *models.Principal) middleware.Responder {
|
2022-10-26 09:40:17 +00:00
|
|
|
btoken, err := getBearerToken(principal, params.XBearerSignature, params.XBearerSignatureKey, *params.WalletConnect, false)
|
2022-08-18 14:35:53 +00:00
|
|
|
if err != nil {
|
|
|
|
resp := a.logAndGetErrorResponse("invalid bearer token", err)
|
|
|
|
return operations.NewFormBinaryBearerBadRequest().WithPayload(resp)
|
|
|
|
}
|
|
|
|
|
|
|
|
resp := &models.BinaryBearer{
|
|
|
|
Token: util.NewString(base64.StdEncoding.EncodeToString(btoken.Marshal())),
|
|
|
|
}
|
|
|
|
|
|
|
|
return operations.NewFormBinaryBearerOK().WithPayload(resp)
|
|
|
|
}
|
|
|
|
|
2022-07-12 08:01:21 +00:00
|
|
|
func prepareObjectToken(ctx context.Context, params objectTokenParams, pool *pool.Pool, owner user.ID) (*models.TokenResponse, error) {
|
2022-07-07 09:02:05 +00:00
|
|
|
btoken, err := util.ToNativeObjectToken(params.Records)
|
2022-04-11 09:35:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("couldn't transform token to native: %w", err)
|
|
|
|
}
|
2022-08-18 14:51:18 +00:00
|
|
|
|
|
|
|
if !params.XBearerForAllUsers {
|
|
|
|
btoken.ForUser(owner)
|
|
|
|
}
|
2022-04-11 09:35:06 +00:00
|
|
|
|
2022-04-14 08:53:13 +00:00
|
|
|
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
|
2022-04-11 09:35:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
|
|
|
}
|
2022-07-12 08:01:21 +00:00
|
|
|
btoken.SetIat(iat)
|
|
|
|
btoken.SetExp(exp)
|
2022-04-11 09:35:06 +00:00
|
|
|
|
2022-07-12 08:01:21 +00:00
|
|
|
var v2token acl.BearerToken
|
|
|
|
btoken.WriteToV2(&v2token)
|
|
|
|
binaryBearer := v2token.GetBody().StableMarshal(nil)
|
2022-04-11 09:35:06 +00:00
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
return &models.TokenResponse{
|
|
|
|
Name: params.Name,
|
|
|
|
Type: models.NewTokenType(models.TokenTypeObject),
|
|
|
|
Token: util.NewString(base64.StdEncoding.EncodeToString(binaryBearer)),
|
|
|
|
}, nil
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
func prepareContainerTokens(ctx context.Context, params containerTokenParams, pool *pool.Pool, key *keys.PublicKey) (*models.TokenResponse, error) {
|
2022-04-14 08:53:13 +00:00
|
|
|
iat, exp, err := getTokenLifetime(ctx, pool, params.XBearerLifetime)
|
2022-04-11 09:35:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("couldn't get lifetime: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-07-12 08:01:21 +00:00
|
|
|
var ownerID user.ID
|
|
|
|
if err = ownerID.DecodeString(params.XBearerOwnerID); err != nil {
|
2022-06-09 09:59:02 +00:00
|
|
|
return nil, fmt.Errorf("invalid bearer owner: %w", err)
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
stoken, err := util.ToNativeContainerToken(params.Rule)
|
2022-04-11 09:35:06 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("couldn't transform rule to native session token: %w", err)
|
|
|
|
}
|
|
|
|
|
2022-07-12 08:01:21 +00:00
|
|
|
stoken.SetID(uuid.New())
|
2022-04-11 09:35:06 +00:00
|
|
|
stoken.SetIat(iat)
|
|
|
|
stoken.SetExp(exp)
|
|
|
|
|
2022-12-20 11:30:27 +00:00
|
|
|
authKey := frostfsecdsa.PublicKey(*key)
|
2022-07-12 08:01:21 +00:00
|
|
|
stoken.SetAuthKey(&authKey)
|
|
|
|
|
|
|
|
var v2token sessionv2.Token
|
|
|
|
stoken.WriteToV2(&v2token)
|
|
|
|
|
|
|
|
var issuer refs.OwnerID
|
|
|
|
ownerID.WriteToV2(&issuer)
|
|
|
|
v2token.GetBody().SetOwnerID(&issuer)
|
|
|
|
|
|
|
|
binaryToken := v2token.GetBody().StableMarshal(nil)
|
2022-04-11 09:35:06 +00:00
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
return &models.TokenResponse{
|
|
|
|
Name: params.Name,
|
|
|
|
Type: models.NewTokenType(models.TokenTypeContainer),
|
|
|
|
Token: util.NewString(base64.StdEncoding.EncodeToString(binaryToken)),
|
|
|
|
}, nil
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func getCurrentEpoch(ctx context.Context, p *pool.Pool) (uint64, error) {
|
|
|
|
netInfo, err := p.NetworkInfo(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return 0, fmt.Errorf("couldn't get netwokr info: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return netInfo.CurrentEpoch(), nil
|
|
|
|
}
|
|
|
|
|
2022-07-07 09:02:05 +00:00
|
|
|
func getTokenLifetime(ctx context.Context, p *pool.Pool, expDuration uint64) (uint64, uint64, error) {
|
2022-04-11 09:35:06 +00:00
|
|
|
currEpoch, err := getCurrentEpoch(ctx, p)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var lifetimeDuration uint64 = defaultTokenExpDuration
|
2022-07-07 09:02:05 +00:00
|
|
|
if expDuration != 0 {
|
|
|
|
lifetimeDuration = expDuration
|
2022-04-11 09:35:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return currEpoch, currEpoch + lifetimeDuration, nil
|
|
|
|
}
|