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 }