github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/internal/services/v1/errors.go (about)

     1  package v1
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/rs/zerolog"
     8  	"google.golang.org/grpc/codes"
     9  	"google.golang.org/grpc/status"
    10  	"google.golang.org/protobuf/proto"
    11  
    12  	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
    13  
    14  	"github.com/authzed/spicedb/pkg/spiceerrors"
    15  	"github.com/authzed/spicedb/pkg/tuple"
    16  )
    17  
    18  // ErrExceedsMaximumLimit occurs when a limit that is too large is given to a call.
    19  type ErrExceedsMaximumLimit struct {
    20  	error
    21  	providedLimit   uint64
    22  	maxLimitAllowed uint64
    23  }
    24  
    25  // MarshalZerologObject implements zerolog object marshalling.
    26  func (err ErrExceedsMaximumLimit) MarshalZerologObject(e *zerolog.Event) {
    27  	e.Err(err.error).Uint64("providedLimit", err.providedLimit).Uint64("maxLimitAllowed", err.maxLimitAllowed)
    28  }
    29  
    30  // GRPCStatus implements retrieving the gRPC status for the error.
    31  func (err ErrExceedsMaximumLimit) GRPCStatus() *status.Status {
    32  	return spiceerrors.WithCodeAndDetails(
    33  		err,
    34  		codes.InvalidArgument,
    35  		spiceerrors.ForReason(
    36  			v1.ErrorReason_ERROR_REASON_EXCEEDS_MAXIMUM_ALLOWABLE_LIMIT,
    37  			map[string]string{
    38  				"limit_provided":        strconv.FormatUint(err.providedLimit, 10),
    39  				"maximum_limit_allowed": strconv.FormatUint(err.maxLimitAllowed, 10),
    40  			},
    41  		),
    42  	)
    43  }
    44  
    45  // NewExceedsMaximumLimitErr creates a new error representing that the limit specified was too large.
    46  func NewExceedsMaximumLimitErr(providedLimit uint64, maxLimitAllowed uint64) ErrExceedsMaximumLimit {
    47  	return ErrExceedsMaximumLimit{
    48  		error:           fmt.Errorf("provided limit %d is greater than maximum allowed of %d", providedLimit, maxLimitAllowed),
    49  		providedLimit:   providedLimit,
    50  		maxLimitAllowed: maxLimitAllowed,
    51  	}
    52  }
    53  
    54  // ErrExceedsMaximumChecks occurs when too many checks are given to a call.
    55  type ErrExceedsMaximumChecks struct {
    56  	error
    57  	checkCount      uint64
    58  	maxCountAllowed uint64
    59  }
    60  
    61  // MarshalZerologObject implements zerolog object marshalling.
    62  func (err ErrExceedsMaximumChecks) MarshalZerologObject(e *zerolog.Event) {
    63  	e.Err(err.error).Uint64("checkCount", err.checkCount).Uint64("maxCountAllowed", err.maxCountAllowed)
    64  }
    65  
    66  // GRPCStatus implements retrieving the gRPC status for the error.
    67  func (err ErrExceedsMaximumChecks) GRPCStatus() *status.Status {
    68  	return spiceerrors.WithCodeAndDetails(
    69  		err,
    70  		codes.InvalidArgument,
    71  		spiceerrors.ForReason(
    72  			v1.ErrorReason_ERROR_REASON_UNSPECIFIED,
    73  			map[string]string{
    74  				"check_count":            strconv.FormatUint(err.checkCount, 10),
    75  				"maximum_checks_allowed": strconv.FormatUint(err.maxCountAllowed, 10),
    76  			},
    77  		),
    78  	)
    79  }
    80  
    81  // NewExceedsMaximumChecksErr creates a new error representing that too many updates were given to a BulkCheckPermissions call.
    82  func NewExceedsMaximumChecksErr(checkCount uint64, maxCountAllowed uint64) ErrExceedsMaximumChecks {
    83  	return ErrExceedsMaximumChecks{
    84  		error:           fmt.Errorf("check count of %d is greater than maximum allowed of %d", checkCount, maxCountAllowed),
    85  		checkCount:      checkCount,
    86  		maxCountAllowed: maxCountAllowed,
    87  	}
    88  }
    89  
    90  // ErrExceedsMaximumUpdates occurs when too many updates are given to a call.
    91  type ErrExceedsMaximumUpdates struct {
    92  	error
    93  	updateCount     uint64
    94  	maxCountAllowed uint64
    95  }
    96  
    97  // MarshalZerologObject implements zerolog object marshalling.
    98  func (err ErrExceedsMaximumUpdates) MarshalZerologObject(e *zerolog.Event) {
    99  	e.Err(err.error).Uint64("updateCount", err.updateCount).Uint64("maxCountAllowed", err.maxCountAllowed)
   100  }
   101  
   102  // GRPCStatus implements retrieving the gRPC status for the error.
   103  func (err ErrExceedsMaximumUpdates) GRPCStatus() *status.Status {
   104  	return spiceerrors.WithCodeAndDetails(
   105  		err,
   106  		codes.InvalidArgument,
   107  		spiceerrors.ForReason(
   108  			v1.ErrorReason_ERROR_REASON_TOO_MANY_UPDATES_IN_REQUEST,
   109  			map[string]string{
   110  				"update_count":            strconv.FormatUint(err.updateCount, 10),
   111  				"maximum_updates_allowed": strconv.FormatUint(err.maxCountAllowed, 10),
   112  			},
   113  		),
   114  	)
   115  }
   116  
   117  // NewExceedsMaximumUpdatesErr creates a new error representing that too many updates were given to a WriteRelationships call.
   118  func NewExceedsMaximumUpdatesErr(updateCount uint64, maxCountAllowed uint64) ErrExceedsMaximumUpdates {
   119  	return ErrExceedsMaximumUpdates{
   120  		error:           fmt.Errorf("update count of %d is greater than maximum allowed of %d", updateCount, maxCountAllowed),
   121  		updateCount:     updateCount,
   122  		maxCountAllowed: maxCountAllowed,
   123  	}
   124  }
   125  
   126  // ErrExceedsMaximumPreconditions occurs when too many preconditions are given to a call.
   127  type ErrExceedsMaximumPreconditions struct {
   128  	error
   129  	preconditionCount uint64
   130  	maxCountAllowed   uint64
   131  }
   132  
   133  // MarshalZerologObject implements zerolog object marshalling.
   134  func (err ErrExceedsMaximumPreconditions) MarshalZerologObject(e *zerolog.Event) {
   135  	e.Err(err.error).Uint64("preconditionCount", err.preconditionCount).Uint64("maxCountAllowed", err.maxCountAllowed)
   136  }
   137  
   138  // GRPCStatus implements retrieving the gRPC status for the error.
   139  func (err ErrExceedsMaximumPreconditions) GRPCStatus() *status.Status {
   140  	return spiceerrors.WithCodeAndDetails(
   141  		err,
   142  		codes.InvalidArgument,
   143  		spiceerrors.ForReason(
   144  			v1.ErrorReason_ERROR_REASON_TOO_MANY_PRECONDITIONS_IN_REQUEST,
   145  			map[string]string{
   146  				"precondition_count":      strconv.FormatUint(err.preconditionCount, 10),
   147  				"maximum_updates_allowed": strconv.FormatUint(err.maxCountAllowed, 10),
   148  			},
   149  		),
   150  	)
   151  }
   152  
   153  // NewExceedsMaximumPreconditionsErr creates a new error representing that too many preconditions were given to a call.
   154  func NewExceedsMaximumPreconditionsErr(preconditionCount uint64, maxCountAllowed uint64) ErrExceedsMaximumPreconditions {
   155  	return ErrExceedsMaximumPreconditions{
   156  		error: fmt.Errorf(
   157  			"precondition count of %d is greater than maximum allowed of %d",
   158  			preconditionCount,
   159  			maxCountAllowed),
   160  		preconditionCount: preconditionCount,
   161  		maxCountAllowed:   maxCountAllowed,
   162  	}
   163  }
   164  
   165  // ErrPreconditionFailed occurs when the precondition to a write tuple call does not match.
   166  type ErrPreconditionFailed struct {
   167  	error
   168  	precondition *v1.Precondition
   169  }
   170  
   171  // MarshalZerologObject implements zerolog object marshalling.
   172  func (err ErrPreconditionFailed) MarshalZerologObject(e *zerolog.Event) {
   173  	e.Err(err.error).Interface("precondition", err.precondition)
   174  }
   175  
   176  // NewPreconditionFailedErr constructs a new precondition failed error.
   177  func NewPreconditionFailedErr(precondition *v1.Precondition) error {
   178  	return ErrPreconditionFailed{
   179  		error:        fmt.Errorf("unable to satisfy write precondition `%s`", precondition),
   180  		precondition: precondition,
   181  	}
   182  }
   183  
   184  // GRPCStatus implements retrieving the gRPC status for the error.
   185  func (err ErrPreconditionFailed) GRPCStatus() *status.Status {
   186  	metadata := map[string]string{
   187  		"precondition_operation": v1.Precondition_Operation_name[int32(err.precondition.Operation)],
   188  	}
   189  
   190  	if err.precondition.Filter.ResourceType != "" {
   191  		metadata["precondition_resource_type"] = err.precondition.Filter.ResourceType
   192  	}
   193  
   194  	if err.precondition.Filter.OptionalResourceId != "" {
   195  		metadata["precondition_resource_id"] = err.precondition.Filter.OptionalResourceId
   196  	}
   197  
   198  	if err.precondition.Filter.OptionalResourceIdPrefix != "" {
   199  		metadata["precondition_resource_id_prefix"] = err.precondition.Filter.OptionalResourceIdPrefix
   200  	}
   201  
   202  	if err.precondition.Filter.OptionalRelation != "" {
   203  		metadata["precondition_relation"] = err.precondition.Filter.OptionalRelation
   204  	}
   205  
   206  	if err.precondition.Filter.OptionalSubjectFilter != nil {
   207  		metadata["precondition_subject_type"] = err.precondition.Filter.OptionalSubjectFilter.SubjectType
   208  
   209  		if err.precondition.Filter.OptionalSubjectFilter.OptionalSubjectId != "" {
   210  			metadata["precondition_subject_id"] = err.precondition.Filter.OptionalSubjectFilter.OptionalSubjectId
   211  		}
   212  
   213  		if err.precondition.Filter.OptionalSubjectFilter.OptionalRelation != nil {
   214  			metadata["precondition_subject_relation"] = err.precondition.Filter.OptionalSubjectFilter.OptionalRelation.Relation
   215  		}
   216  	}
   217  
   218  	return spiceerrors.WithCodeAndDetails(
   219  		err,
   220  		codes.FailedPrecondition,
   221  		spiceerrors.ForReason(
   222  			v1.ErrorReason_ERROR_REASON_WRITE_OR_DELETE_PRECONDITION_FAILURE,
   223  			metadata,
   224  		),
   225  	)
   226  }
   227  
   228  // ErrDuplicateRelationshipError indicates that an update was attempted on the same relationship.
   229  type ErrDuplicateRelationshipError struct {
   230  	error
   231  	update *v1.RelationshipUpdate
   232  }
   233  
   234  // NewDuplicateRelationshipErr constructs a new invalid subject error.
   235  func NewDuplicateRelationshipErr(update *v1.RelationshipUpdate) ErrDuplicateRelationshipError {
   236  	return ErrDuplicateRelationshipError{
   237  		error: fmt.Errorf(
   238  			"found more than one update with relationship `%s` in this request; a relationship can only be specified in an update once per overall WriteRelationships request",
   239  			tuple.StringRelationshipWithoutCaveat(update.Relationship),
   240  		),
   241  		update: update,
   242  	}
   243  }
   244  
   245  // GRPCStatus implements retrieving the gRPC status for the error.
   246  func (err ErrDuplicateRelationshipError) GRPCStatus() *status.Status {
   247  	return spiceerrors.WithCodeAndDetails(
   248  		err,
   249  		codes.InvalidArgument,
   250  		spiceerrors.ForReason(
   251  			v1.ErrorReason_ERROR_REASON_UPDATES_ON_SAME_RELATIONSHIP,
   252  			map[string]string{
   253  				"definition_name": err.update.Relationship.Resource.ObjectType,
   254  				"relationship":    tuple.MustRelString(err.update.Relationship),
   255  			},
   256  		),
   257  	)
   258  }
   259  
   260  // ErrMaxRelationshipContextError indicates an attempt to write a relationship that exceeded the maximum
   261  // configured context size.
   262  type ErrMaxRelationshipContextError struct {
   263  	error
   264  	update         *v1.RelationshipUpdate
   265  	maxAllowedSize int
   266  }
   267  
   268  // NewMaxRelationshipContextError constructs a new max relationship context error.
   269  func NewMaxRelationshipContextError(update *v1.RelationshipUpdate, maxAllowedSize int) ErrMaxRelationshipContextError {
   270  	return ErrMaxRelationshipContextError{
   271  		error: fmt.Errorf(
   272  			"provided relationship `%s` exceeded maximum allowed caveat size of %d",
   273  			tuple.StringRelationshipWithoutCaveat(update.Relationship),
   274  			maxAllowedSize,
   275  		),
   276  		update:         update,
   277  		maxAllowedSize: maxAllowedSize,
   278  	}
   279  }
   280  
   281  // GRPCStatus implements retrieving the gRPC status for the error.
   282  func (err ErrMaxRelationshipContextError) GRPCStatus() *status.Status {
   283  	return spiceerrors.WithCodeAndDetails(
   284  		err,
   285  		codes.InvalidArgument,
   286  		spiceerrors.ForReason(
   287  			v1.ErrorReason_ERROR_REASON_MAX_RELATIONSHIP_CONTEXT_SIZE,
   288  			map[string]string{
   289  				"relationship":     tuple.StringRelationshipWithoutCaveat(err.update.Relationship),
   290  				"max_allowed_size": strconv.Itoa(err.maxAllowedSize),
   291  				"context_size":     strconv.Itoa(proto.Size(err.update.Relationship)),
   292  			},
   293  		),
   294  	)
   295  }
   296  
   297  // ErrCouldNotTransactionallyDelete indicates that a deletion could not occur transactionally.
   298  type ErrCouldNotTransactionallyDelete struct {
   299  	error
   300  	limit  uint32
   301  	filter *v1.RelationshipFilter
   302  }
   303  
   304  // NewCouldNotTransactionallyDeleteErr constructs a new could not transactionally deleter error.
   305  func NewCouldNotTransactionallyDeleteErr(filter *v1.RelationshipFilter, limit uint32) ErrCouldNotTransactionallyDelete {
   306  	return ErrCouldNotTransactionallyDelete{
   307  		error: fmt.Errorf(
   308  			"found more than %d relationships to be deleted and partial deletion was not requested",
   309  			limit,
   310  		),
   311  		limit:  limit,
   312  		filter: filter,
   313  	}
   314  }
   315  
   316  // GRPCStatus implements retrieving the gRPC status for the error.
   317  func (err ErrCouldNotTransactionallyDelete) GRPCStatus() *status.Status {
   318  	metadata := map[string]string{
   319  		"limit":                strconv.Itoa(int(err.limit)),
   320  		"filter_resource_type": err.filter.ResourceType,
   321  	}
   322  
   323  	if err.filter.OptionalResourceId != "" {
   324  		metadata["filter_resource_id"] = err.filter.OptionalResourceId
   325  	}
   326  
   327  	if err.filter.OptionalRelation != "" {
   328  		metadata["filter_relation"] = err.filter.OptionalRelation
   329  	}
   330  
   331  	if err.filter.OptionalSubjectFilter != nil {
   332  		metadata["filter_subject_type"] = err.filter.OptionalSubjectFilter.SubjectType
   333  
   334  		if err.filter.OptionalSubjectFilter.OptionalSubjectId != "" {
   335  			metadata["filter_subject_id"] = err.filter.OptionalSubjectFilter.OptionalSubjectId
   336  		}
   337  
   338  		if err.filter.OptionalSubjectFilter.OptionalRelation != nil {
   339  			metadata["filter_subject_relation"] = err.filter.OptionalSubjectFilter.OptionalRelation.Relation
   340  		}
   341  	}
   342  
   343  	return spiceerrors.WithCodeAndDetails(
   344  		err,
   345  		codes.InvalidArgument,
   346  		spiceerrors.ForReason(
   347  			v1.ErrorReason_ERROR_REASON_TOO_MANY_RELATIONSHIPS_FOR_TRANSACTIONAL_DELETE,
   348  			metadata,
   349  		),
   350  	)
   351  }
   352  
   353  // ErrInvalidCursor indicates that an invalid cursor was found.
   354  type ErrInvalidCursor struct {
   355  	error
   356  	reason string
   357  }
   358  
   359  // NewInvalidCursorErr constructs a new invalid cursor error.
   360  func NewInvalidCursorErr(reason string) ErrInvalidCursor {
   361  	return ErrInvalidCursor{
   362  		error: fmt.Errorf(
   363  			"the cursor provided is not valid: %s",
   364  			reason,
   365  		),
   366  	}
   367  }
   368  
   369  // GRPCStatus implements retrieving the gRPC status for the error.
   370  func (err ErrInvalidCursor) GRPCStatus() *status.Status {
   371  	return spiceerrors.WithCodeAndDetails(
   372  		err,
   373  		codes.FailedPrecondition,
   374  		spiceerrors.ForReason(
   375  			v1.ErrorReason_ERROR_REASON_INVALID_CURSOR,
   376  			map[string]string{
   377  				"reason": err.reason,
   378  			},
   379  		),
   380  	)
   381  }
   382  
   383  // ErrInvalidFilter indicates the specified relationship filter was invalid.
   384  type ErrInvalidFilter struct {
   385  	error
   386  
   387  	filter string
   388  }
   389  
   390  // GRPCStatus implements retrieving the gRPC status for the error.
   391  func (err ErrInvalidFilter) GRPCStatus() *status.Status {
   392  	return spiceerrors.WithCodeAndDetails(
   393  		err,
   394  		codes.InvalidArgument,
   395  		spiceerrors.ForReason(
   396  			v1.ErrorReason_ERROR_REASON_INVALID_FILTER,
   397  			map[string]string{
   398  				"filter": err.filter,
   399  			},
   400  		),
   401  	)
   402  }
   403  
   404  // NewInvalidFilterErr constructs a new invalid filter error.
   405  func NewInvalidFilterErr(reason string, filter string) ErrInvalidFilter {
   406  	return ErrInvalidFilter{
   407  		error: fmt.Errorf(
   408  			"the relationship filter provided is not valid: %s", reason,
   409  		),
   410  		filter: filter,
   411  	}
   412  }
   413  
   414  // NewEmptyPreconditionErr constructs a new empty precondition error.
   415  func NewEmptyPreconditionErr() ErrEmptyPrecondition {
   416  	return ErrEmptyPrecondition{
   417  		error: fmt.Errorf(
   418  			"one of the specified preconditions is empty",
   419  		),
   420  	}
   421  }
   422  
   423  // ErrEmptyPrecondition indicates an empty precondition was found.
   424  type ErrEmptyPrecondition struct {
   425  	error
   426  }
   427  
   428  // GRPCStatus implements retrieving the gRPC status for the error.
   429  func (err ErrEmptyPrecondition) GRPCStatus() *status.Status {
   430  	// TODO(jschorr): Put a proper error reason in here.
   431  	return spiceerrors.WithCodeAndDetails(
   432  		err,
   433  		codes.InvalidArgument,
   434  		spiceerrors.ForReason(
   435  			v1.ErrorReason_ERROR_REASON_UNSPECIFIED,
   436  			map[string]string{},
   437  		),
   438  	)
   439  }
   440  
   441  // NewNotAPermissionError constructs a new not a permission error.
   442  func NewNotAPermissionError(relationName string) ErrNotAPermission {
   443  	return ErrNotAPermission{
   444  		error: fmt.Errorf(
   445  			"the relation `%s` is not a permission", relationName,
   446  		),
   447  		relationName: relationName,
   448  	}
   449  }
   450  
   451  // ErrNotAPermission indicates that the relation is not a permission.
   452  type ErrNotAPermission struct {
   453  	error
   454  	relationName string
   455  }
   456  
   457  // GRPCStatus implements retrieving the gRPC status for the error.
   458  func (err ErrNotAPermission) GRPCStatus() *status.Status {
   459  	return spiceerrors.WithCodeAndDetails(
   460  		err,
   461  		codes.InvalidArgument,
   462  		spiceerrors.ForReason(
   463  			v1.ErrorReason_ERROR_REASON_UNKNOWN_RELATION_OR_PERMISSION,
   464  			map[string]string{
   465  				"relationName": err.relationName,
   466  			},
   467  		),
   468  	)
   469  }
   470  
   471  func defaultIfZero[T comparable](value T, defaultValue T) T {
   472  	var zero T
   473  	if value == zero {
   474  		return defaultValue
   475  	}
   476  	return value
   477  }