package util import ( "encoding/hex" "errors" "fmt" "github.com/nspcc-dev/neofs-rest-gw/gen/models" "github.com/nspcc-dev/neofs-sdk-go/bearer" apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" "github.com/nspcc-dev/neofs-sdk-go/eacl" "github.com/nspcc-dev/neofs-sdk-go/object" "github.com/nspcc-dev/neofs-sdk-go/session" ) // ToNativeAction converts models.Action to appropriate eacl.Action. func ToNativeAction(a *models.Action) (eacl.Action, error) { if a == nil { return eacl.ActionUnknown, fmt.Errorf("unsupported empty action") } switch *a { case models.ActionALLOW: return eacl.ActionAllow, nil case models.ActionDENY: return eacl.ActionDeny, nil default: return eacl.ActionUnknown, fmt.Errorf("unsupported action type: '%s'", *a) } } // FromNativeAction converts eacl.Action to appropriate models.Action. func FromNativeAction(a eacl.Action) (*models.Action, error) { switch a { case eacl.ActionAllow: return models.NewAction(models.ActionALLOW), nil case eacl.ActionDeny: return models.NewAction(models.ActionDENY), nil default: return nil, fmt.Errorf("unsupported action type: '%s'", a) } } // ToNativeOperation converts models.Operation to appropriate eacl.Operation. func ToNativeOperation(o *models.Operation) (eacl.Operation, error) { if o == nil { return eacl.OperationUnknown, fmt.Errorf("unsupported empty opertaion") } switch *o { case models.OperationGET: return eacl.OperationGet, nil case models.OperationHEAD: return eacl.OperationHead, nil case models.OperationPUT: return eacl.OperationPut, nil case models.OperationDELETE: return eacl.OperationDelete, nil case models.OperationSEARCH: return eacl.OperationSearch, nil case models.OperationRANGE: return eacl.OperationRange, nil case models.OperationRANGEHASH: return eacl.OperationRangeHash, nil default: return eacl.OperationUnknown, fmt.Errorf("unsupported operation type: '%s'", *o) } } // FromNativeOperation converts eacl.Operation to appropriate models.Operation. func FromNativeOperation(o eacl.Operation) (*models.Operation, error) { switch o { case eacl.OperationGet: return models.NewOperation(models.OperationGET), nil case eacl.OperationHead: return models.NewOperation(models.OperationHEAD), nil case eacl.OperationPut: return models.NewOperation(models.OperationPUT), nil case eacl.OperationDelete: return models.NewOperation(models.OperationDELETE), nil case eacl.OperationSearch: return models.NewOperation(models.OperationSEARCH), nil case eacl.OperationRange: return models.NewOperation(models.OperationRANGE), nil case eacl.OperationRangeHash: return models.NewOperation(models.OperationRANGEHASH), nil default: return nil, fmt.Errorf("unsupported operation type: '%s'", o) } } // ToNativeHeaderType converts models.HeaderType to appropriate eacl.FilterHeaderType. func ToNativeHeaderType(h *models.HeaderType) (eacl.FilterHeaderType, error) { if h == nil { return eacl.HeaderTypeUnknown, fmt.Errorf("unsupported empty header type") } switch *h { case models.HeaderTypeOBJECT: return eacl.HeaderFromObject, nil case models.HeaderTypeREQUEST: return eacl.HeaderFromRequest, nil case models.HeaderTypeSERVICE: return eacl.HeaderFromService, nil default: return eacl.HeaderTypeUnknown, fmt.Errorf("unsupported header type: '%s'", *h) } } // FromNativeHeaderType converts eacl.FilterHeaderType to appropriate models.HeaderType. func FromNativeHeaderType(h eacl.FilterHeaderType) (*models.HeaderType, error) { switch h { case eacl.HeaderFromObject: return models.NewHeaderType(models.HeaderTypeOBJECT), nil case eacl.HeaderFromRequest: return models.NewHeaderType(models.HeaderTypeREQUEST), nil case eacl.HeaderFromService: return models.NewHeaderType(models.HeaderTypeSERVICE), nil default: return nil, fmt.Errorf("unsupported header type: '%s'", h) } } // ToNativeMatchType converts models.MatchType to appropriate eacl.Match. func ToNativeMatchType(t *models.MatchType) (eacl.Match, error) { if t == nil { return eacl.MatchUnknown, fmt.Errorf("unsupported empty match type") } switch *t { case models.MatchTypeSTRINGEQUAL: return eacl.MatchStringEqual, nil case models.MatchTypeSTRINGNOTEQUAL: return eacl.MatchStringNotEqual, nil default: return eacl.MatchUnknown, fmt.Errorf("unsupported match type: '%s'", *t) } } // FromNativeMatchType converts eacl.Match to appropriate models.MatchType. func FromNativeMatchType(t eacl.Match) (*models.MatchType, error) { switch t { case eacl.MatchStringEqual: return models.NewMatchType(models.MatchTypeSTRINGEQUAL), nil case eacl.MatchStringNotEqual: return models.NewMatchType(models.MatchTypeSTRINGNOTEQUAL), nil default: return nil, fmt.Errorf("unsupported match type: '%s'", t) } } // ToNativeRole converts models.Role to appropriate eacl.Role. func ToNativeRole(r *models.Role) (eacl.Role, error) { if r == nil { return eacl.RoleUnknown, fmt.Errorf("unsupported empty role") } switch *r { case models.RoleUSER: return eacl.RoleUser, nil case models.RoleSYSTEM: return eacl.RoleSystem, nil case models.RoleOTHERS: return eacl.RoleOthers, nil default: return eacl.RoleUnknown, fmt.Errorf("unsupported role type: '%s'", *r) } } // FromNativeRole converts eacl.Role to appropriate models.Role. func FromNativeRole(r eacl.Role) (*models.Role, error) { switch r { case eacl.RoleUser: return models.NewRole(models.RoleUSER), nil case eacl.RoleSystem: return models.NewRole(models.RoleSYSTEM), nil case eacl.RoleOthers: return models.NewRole(models.RoleOTHERS), nil default: return nil, fmt.Errorf("unsupported role type: '%s'", r) } } // ToNativeVerb converts models.Verb to appropriate session.ContainerSessionVerb. func ToNativeVerb(r *models.Verb) (session.ContainerVerb, error) { if r == nil { return 0, fmt.Errorf("unsupported empty verb type") } switch *r { case models.VerbPUT: return session.VerbContainerPut, nil case models.VerbDELETE: return session.VerbContainerDelete, nil case models.VerbSETEACL: return session.VerbContainerSetEACL, nil default: return 0, fmt.Errorf("unsupported verb type: '%s'", *r) } } // ToNativeContainerToken converts models.Rule to appropriate session.Token. func ToNativeContainerToken(tokenRule *models.Rule) (session.Container, error) { var tok session.Container if tokenRule.ContainerID != "" { var cnrID cid.ID if err := cnrID.DecodeString(tokenRule.ContainerID); err != nil { return session.Container{}, fmt.Errorf("couldn't parse container id: %w", err) } tok.ApplyOnlyTo(cnrID) } verb, err := ToNativeVerb(tokenRule.Verb) if err != nil { return session.Container{}, err } tok.ForVerb(verb) return tok, nil } // ToNativeRecord converts models.Record to appropriate eacl.Record. func ToNativeRecord(r *models.Record) (*eacl.Record, error) { var record eacl.Record action, err := ToNativeAction(r.Action) if err != nil { return nil, err } record.SetAction(action) operation, err := ToNativeOperation(r.Operation) if err != nil { return nil, err } record.SetOperation(operation) for _, filter := range r.Filters { headerType, err := ToNativeHeaderType(filter.HeaderType) if err != nil { return nil, err } matchType, err := ToNativeMatchType(filter.MatchType) if err != nil { return nil, err } if filter.Key == nil || filter.Value == nil { return nil, fmt.Errorf("invalid filter") } record.AddFilter(headerType, matchType, *filter.Key, *filter.Value) } targets := make([]eacl.Target, len(r.Targets)) for i, target := range r.Targets { trgt, err := ToNativeTarget(target) if err != nil { return nil, err } targets[i] = *trgt } record.SetTargets(targets...) return &record, nil } // FromNativeRecord converts eacl.Record to appropriate models.Record. func FromNativeRecord(r eacl.Record) (*models.Record, error) { var err error var record models.Record record.Action, err = FromNativeAction(r.Action()) if err != nil { return nil, err } record.Operation, err = FromNativeOperation(r.Operation()) if err != nil { return nil, err } record.Filters = make([]*models.Filter, len(r.Filters())) for i, filter := range r.Filters() { headerType, err := FromNativeHeaderType(filter.From()) if err != nil { return nil, err } matchType, err := FromNativeMatchType(filter.Matcher()) if err != nil { return nil, err } record.Filters[i] = &models.Filter{ HeaderType: headerType, Key: NewString(filter.Key()), MatchType: matchType, Value: NewString(filter.Value()), } } record.Targets = make([]*models.Target, len(r.Targets())) for i, target := range r.Targets() { trgt, err := FromNativeTarget(target) if err != nil { return nil, err } record.Targets[i] = trgt } return &record, nil } // ToNativeTarget converts models.Target to appropriate eacl.Target. func ToNativeTarget(t *models.Target) (*eacl.Target, error) { var target eacl.Target role, err := ToNativeRole(t.Role) if err != nil { return nil, err } target.SetRole(role) keys := make([][]byte, len(t.Keys)) for i, key := range t.Keys { binaryKey, err := hex.DecodeString(key) if err != nil { return nil, fmt.Errorf("couldn't decode target key: %w", err) } keys[i] = binaryKey } target.SetBinaryKeys(keys) return &target, nil } // FromNativeTarget converts eacl.Target to appropriate models.Target. func FromNativeTarget(t eacl.Target) (*models.Target, error) { var err error var target models.Target target.Role, err = FromNativeRole(t.Role()) if err != nil { return nil, err } target.Keys = make([]string, len(t.BinaryKeys())) for i, key := range t.BinaryKeys() { target.Keys[i] = hex.EncodeToString(key) } return &target, nil } // ToNativeObjectToken converts []*models.Record to appropriate token.BearerToken. func ToNativeObjectToken(tokenRecords []*models.Record) (*bearer.Token, error) { table, err := ToNativeTable(tokenRecords) if err != nil { return nil, err } var btoken bearer.Token btoken.SetEACLTable(*table) return &btoken, nil } // ToNativeTable converts records to eacl.Table. func ToNativeTable(records []*models.Record) (*eacl.Table, error) { table := eacl.NewTable() for _, rec := range records { record, err := ToNativeRecord(rec) if err != nil { return nil, fmt.Errorf("couldn't transform record to native: %w", err) } table.AddRecord(record) } return table, nil } // ToNativeMatchFilter converts models.SearchMatch to object.SearchMatchType. func ToNativeMatchFilter(s *models.SearchMatch) (object.SearchMatchType, error) { if s == nil { return object.MatchUnknown, fmt.Errorf("unsupported empty verb type") } switch *s { case models.SearchMatchMatchStringEqual: return object.MatchStringEqual, nil case models.SearchMatchMatchStringNotEqual: return object.MatchStringNotEqual, nil case models.SearchMatchMatchNotPresent: return object.MatchNotPresent, nil case models.SearchMatchMatchCommonPrefix: return object.MatchCommonPrefix, nil default: return object.MatchUnknown, fmt.Errorf("unsupported search match: '%s'", *s) } } // ToNativeFilters converts models.SearchFilters to object.SearchFilters. func ToNativeFilters(fs *models.SearchFilters) (object.SearchFilters, error) { filters := object.NewSearchFilters() filters.AddRootFilter() for _, f := range fs.Filters { matchFilter, err := ToNativeMatchFilter(f.Match) if err != nil { return nil, err } filters.AddFilter(*f.Key, *f.Value, matchFilter) } return filters, nil } // NewString returns pointer to provided string. func NewString(val string) *string { return &val } // NewInteger returns pointer to provided int. func NewInteger(val int64) *int64 { return &val } // NewBool returns pointer to provided bool. func NewBool(val bool) *bool { return &val } // NewError wraps error into models.Error. func NewError(err error) models.Error { return models.Error(err.Error()) } // NewSuccessResponse forms model.SuccessResponse. func NewSuccessResponse() *models.SuccessResponse { return &models.SuccessResponse{ Success: NewBool(true), } } // NewErrorResponse forms model.ErrorResponse. func NewErrorResponse(err error) *models.ErrorResponse { var code int64 t := models.ErrorTypeGW if status, ok := unwrapErr(err).(apistatus.StatusV2); ok { code = int64(status.ToStatusV2().Code()) t = models.ErrorTypeAPI } return &models.ErrorResponse{ Code: code, Message: NewString(err.Error()), Type: models.NewErrorType(t), } } func unwrapErr(err error) error { for e := errors.Unwrap(err); e != nil; e = errors.Unwrap(err) { err = e } return err }