diff options
Diffstat (limited to 'vendor/github.com/authzed/spicedb/pkg/cursor')
| -rw-r--r-- | vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go | 133 | ||||
| -rw-r--r-- | vendor/github.com/authzed/spicedb/pkg/cursor/doc.go | 2 | ||||
| -rw-r--r-- | vendor/github.com/authzed/spicedb/pkg/cursor/errors.go | 46 |
3 files changed, 181 insertions, 0 deletions
diff --git a/vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go b/vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go new file mode 100644 index 0000000..4233efa --- /dev/null +++ b/vendor/github.com/authzed/spicedb/pkg/cursor/cursor.go @@ -0,0 +1,133 @@ +package cursor + +import ( + "encoding/base64" + "errors" + "fmt" + + v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" + + "github.com/authzed/spicedb/pkg/datastore" + dispatch "github.com/authzed/spicedb/pkg/proto/dispatch/v1" + impl "github.com/authzed/spicedb/pkg/proto/impl/v1" + "github.com/authzed/spicedb/pkg/spiceerrors" +) + +// Encode converts a decoded cursor to its opaque version. +func Encode(decoded *impl.DecodedCursor) (*v1.Cursor, error) { + marshalled, err := decoded.MarshalVT() + if err != nil { + return nil, NewInvalidCursorErr(fmt.Errorf(errEncodeError, err)) + } + + return &v1.Cursor{ + Token: base64.StdEncoding.EncodeToString(marshalled), + }, nil +} + +// Decode converts an encoded cursor to its decoded version. +func Decode(encoded *v1.Cursor) (*impl.DecodedCursor, error) { + if encoded == nil { + return nil, NewInvalidCursorErr(errors.New("cursor pointer was nil")) + } + + decodedBytes, err := base64.StdEncoding.DecodeString(encoded.Token) + if err != nil { + return nil, NewInvalidCursorErr(fmt.Errorf(errDecodeError, err)) + } + + decoded := &impl.DecodedCursor{} + if err := decoded.UnmarshalVT(decodedBytes); err != nil { + return nil, NewInvalidCursorErr(fmt.Errorf(errDecodeError, err)) + } + + return decoded, nil +} + +// EncodeFromDispatchCursor encodes an internal dispatching cursor into a V1 cursor for external +// consumption, including the provided call context to ensure the API cursor reflects the calling +// API method. The call hash should contain all the parameters of the calling API function, +// as well as its revision and name. +func EncodeFromDispatchCursor(dispatchCursor *dispatch.Cursor, callAndParameterHash string, revision datastore.Revision, flags map[string]string) (*v1.Cursor, error) { + if dispatchCursor == nil { + return nil, spiceerrors.MustBugf("got nil dispatch cursor") + } + + return Encode(&impl.DecodedCursor{ + VersionOneof: &impl.DecodedCursor_V1{ + V1: &impl.V1Cursor{ + Revision: revision.String(), + DispatchVersion: dispatchCursor.DispatchVersion, + Sections: dispatchCursor.Sections, + CallAndParametersHash: callAndParameterHash, + Flags: flags, + }, + }, + }) +} + +// GetCursorFlag retrieves a flag from an encoded API cursor, if any. +func GetCursorFlag(encoded *v1.Cursor, flagName string) (string, bool, error) { + decoded, err := Decode(encoded) + if err != nil { + return "", false, err + } + + v1decoded := decoded.GetV1() + if v1decoded == nil { + return "", false, NewInvalidCursorErr(ErrNilCursor) + } + + value, ok := v1decoded.Flags[flagName] + return value, ok, nil +} + +// DecodeToDispatchCursor decodes an encoded API cursor into an internal dispatching cursor, +// ensuring that the provided call context matches that encoded into the API cursor. The call +// hash should contain all the parameters of the calling API function, as well as its revision +// and name. +func DecodeToDispatchCursor(encoded *v1.Cursor, callAndParameterHash string) (*dispatch.Cursor, map[string]string, error) { + decoded, err := Decode(encoded) + if err != nil { + return nil, nil, err + } + + v1decoded := decoded.GetV1() + if v1decoded == nil { + return nil, nil, NewInvalidCursorErr(ErrNilCursor) + } + + if v1decoded.CallAndParametersHash != callAndParameterHash { + return nil, nil, NewInvalidCursorErr(ErrHashMismatch) + } + + return &dispatch.Cursor{ + DispatchVersion: v1decoded.DispatchVersion, + Sections: v1decoded.Sections, + }, v1decoded.Flags, nil +} + +// DecodeToDispatchRevision decodes an encoded API cursor into an internal dispatch revision. +// NOTE: this method does *not* verify the caller's method signature. +func DecodeToDispatchRevision(encoded *v1.Cursor, ds revisionDecoder) (datastore.Revision, error) { + decoded, err := Decode(encoded) + if err != nil { + return nil, err + } + + v1decoded := decoded.GetV1() + if v1decoded == nil { + return nil, ErrNilCursor + } + + parsed, err := ds.RevisionFromString(v1decoded.Revision) + if err != nil { + return datastore.NoRevision, fmt.Errorf(errDecodeError, err) + } + + return parsed, nil +} + +type revisionDecoder interface { + RevisionFromString(string) (datastore.Revision, error) +} diff --git a/vendor/github.com/authzed/spicedb/pkg/cursor/doc.go b/vendor/github.com/authzed/spicedb/pkg/cursor/doc.go new file mode 100644 index 0000000..bddf5cd --- /dev/null +++ b/vendor/github.com/authzed/spicedb/pkg/cursor/doc.go @@ -0,0 +1,2 @@ +// Package cursor implements encoding and decoding of cursors used in various APIs. +package cursor diff --git a/vendor/github.com/authzed/spicedb/pkg/cursor/errors.go b/vendor/github.com/authzed/spicedb/pkg/cursor/errors.go new file mode 100644 index 0000000..941df70 --- /dev/null +++ b/vendor/github.com/authzed/spicedb/pkg/cursor/errors.go @@ -0,0 +1,46 @@ +package cursor + +import ( + "errors" + + v1 "github.com/authzed/authzed-go/proto/authzed/api/v1" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + "github.com/authzed/spicedb/pkg/spiceerrors" +) + +// Public facing errors +const ( + errEncodeError = "error encoding cursor: %w" + errDecodeError = "error decoding cursor: %w" +) + +// ErrNilCursor is returned as the base error when nil is provided as the +// cursor argument to Decode +var ErrNilCursor = errors.New("cursor pointer was nil") + +// ErrHashMismatch is returned as the base error when a mismatching hash was given to the decoder. +var ErrHashMismatch = errors.New("the cursor provided does not have the same arguments as the original API call; please ensure you are making the same API call, with the exact same parameters (besides the cursor)") + +// InvalidCursorError occurs when a cursor could not be decoded. +type InvalidCursorError struct { + error +} + +// GRPCStatus implements retrieving the gRPC status for the error. +func (err InvalidCursorError) GRPCStatus() *status.Status { + return spiceerrors.WithCodeAndDetails( + err, + codes.InvalidArgument, + spiceerrors.ForReason( + v1.ErrorReason_ERROR_REASON_INVALID_CURSOR, + nil, + ), + ) +} + +// NewInvalidCursorErr creates and returns a new invalid cursor error. +func NewInvalidCursorErr(err error) error { + return InvalidCursorError{err} +} |
