github.com/storacha/go-ucanto@v0.7.2/validator/error.go (about) 1 package validator 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "math" 8 "strings" 9 "time" 10 11 "github.com/ipld/go-ipld-prime/datamodel" 12 "github.com/storacha/go-ucanto/core/delegation" 13 "github.com/storacha/go-ucanto/core/ipld" 14 "github.com/storacha/go-ucanto/core/result/failure" 15 "github.com/storacha/go-ucanto/did" 16 "github.com/storacha/go-ucanto/ucan" 17 vdm "github.com/storacha/go-ucanto/validator/datamodel" 18 ) 19 20 // go hack for union type -- unexported method cannot be implemented outside module limiting satisfying types 21 type DelegationSubError interface { 22 failure.Failure 23 isDelegationSubError() 24 } 25 26 type InvalidProof interface { 27 failure.Failure 28 isInvalidProof() 29 } 30 31 type EscalatedCapabilityError[Caveats any] struct { 32 failure.NamedWithStackTrace 33 claimed ucan.Capability[Caveats] 34 delegated ucan.Capability[Caveats] 35 cause error 36 } 37 38 func NewEscalatedCapabilityError[Caveats any](claimed ucan.Capability[Caveats], delegated ucan.Capability[Caveats], cause error) EscalatedCapabilityError[Caveats] { 39 return EscalatedCapabilityError[Caveats]{failure.NamedWithCurrentStackTrace("EscalatedCapability"), claimed, delegated, cause} 40 } 41 42 func (ece EscalatedCapabilityError[Caveats]) Unwrap() error { 43 return ece.cause 44 } 45 46 func (ece EscalatedCapabilityError[Caveats]) Error() string { 47 return fmt.Sprintf("Constraint violation: %s", ece.cause.Error()) 48 } 49 50 func (ece EscalatedCapabilityError[Caveats]) isDelegationSubError() {} 51 52 type DelegationError interface { 53 failure.Failure 54 Causes() []DelegationSubError 55 Context() any 56 isDelegationError() 57 } 58 59 type delegationError struct { 60 failure.NamedWithStackTrace 61 causes []DelegationSubError 62 context interface{} 63 } 64 65 func NewDelegationError(causes []DelegationSubError, context interface{}) DelegationError { 66 return delegationError{failure.NamedWithCurrentStackTrace("InvalidClaim"), causes, context} 67 } 68 69 func (de delegationError) Error() string { 70 return fmt.Sprintf("Cannot derive %s from delegated capabilities:\n%s", de.context, li(errors.Join(de.Unwrap()...).Error())) 71 } 72 73 func (de delegationError) Causes() []DelegationSubError { 74 return de.causes 75 } 76 77 func (de delegationError) Context() any { 78 return de.context 79 } 80 81 func (de delegationError) Unwrap() []error { 82 errs := make([]error, 0, len(de.causes)) 83 for _, cause := range de.causes { 84 errs = append(errs, cause) 85 } 86 return errs 87 } 88 89 func (de delegationError) isDelegationError() {} 90 func (de delegationError) isDelegationSubError() {} 91 92 type SessionEscalationError struct { 93 failure.NamedWithStackTrace 94 delegation delegation.Delegation 95 cause error 96 } 97 98 func NewSessionEscalationError(delegation delegation.Delegation, cause error) InvalidProof { 99 return SessionEscalationError{failure.NamedWithCurrentStackTrace("SessionEscalation"), delegation, cause} 100 } 101 102 func (see SessionEscalationError) Error() string { 103 issuer := see.delegation.Issuer().DID() 104 return strings.Join([]string{ 105 fmt.Sprintf("Delegation %s issued by %s has an invalid session", see.delegation.Link(), issuer), 106 li(see.cause.Error()), 107 }, "\n") 108 } 109 110 func (see SessionEscalationError) isInvalidProof() {} 111 112 // BadSignature is a signature that could not be verified or has been verified 113 // invalid. i.e. it is an [UnverifiableSignature] or an [InvalidSignature]. 114 type BadSignature interface { 115 InvalidProof 116 Issuer() ucan.Principal 117 Audience() ucan.Principal 118 Delegation() delegation.Delegation 119 isBadSignature() 120 } 121 122 // UnverifiableSignature is a signature that cannot be verified. i.e. some error 123 // occurred when attempting to verify the signature. 124 type UnverifiableSignature interface { 125 BadSignature 126 Unwrap() error 127 isUnverifiableSignature() 128 } 129 130 type UnverifiableSignatureError struct { 131 failure.NamedWithStackTrace 132 delegation delegation.Delegation 133 cause error 134 } 135 136 func NewUnverifiableSignatureError(delegation delegation.Delegation, cause error) UnverifiableSignature { 137 return UnverifiableSignatureError{failure.NamedWithCurrentStackTrace("UnverifiableSignature"), delegation, cause} 138 } 139 140 func (use UnverifiableSignatureError) Issuer() ucan.Principal { 141 return use.delegation.Issuer() 142 } 143 144 func (use UnverifiableSignatureError) Audience() ucan.Principal { 145 return use.delegation.Audience() 146 } 147 148 func (use UnverifiableSignatureError) Delegation() delegation.Delegation { 149 return use.delegation 150 } 151 152 func (use UnverifiableSignatureError) Error() string { 153 issuer := use.Issuer().DID() 154 return fmt.Sprintf("Proof %s issued by %s cannot be verified:\n%s", use.delegation.Link(), issuer, li(use.cause.Error())) 155 } 156 157 func (use UnverifiableSignatureError) Unwrap() error { 158 return use.cause 159 } 160 161 func (use UnverifiableSignatureError) isUnverifiableSignature() {} 162 func (use UnverifiableSignatureError) isBadSignature() {} 163 func (use UnverifiableSignatureError) isInvalidProof() {} 164 165 // InvalidSignature is a signature that is verified to be invalid. 166 type InvalidSignature interface { 167 BadSignature 168 isInvalidSignature() 169 } 170 171 type InvalidSignatureError struct { 172 failure.NamedWithStackTrace 173 delegation delegation.Delegation 174 verifier ucan.Verifier 175 } 176 177 func NewInvalidSignatureError(delegation delegation.Delegation, verifier ucan.Verifier) InvalidSignature { 178 return InvalidSignatureError{failure.NamedWithCurrentStackTrace("InvalidSignature"), delegation, verifier} 179 } 180 181 func (ise InvalidSignatureError) Issuer() ucan.Principal { 182 return ise.delegation.Issuer() 183 } 184 185 func (ise InvalidSignatureError) Audience() ucan.Principal { 186 return ise.delegation.Audience() 187 } 188 189 func (ise InvalidSignatureError) Delegation() delegation.Delegation { 190 return ise.delegation 191 } 192 193 func (ise InvalidSignatureError) Error() string { 194 issuer := ise.Issuer().DID() 195 key := ise.verifier.DID() 196 if strings.HasPrefix(issuer.String(), "did:key") { 197 return fmt.Sprintf(`Proof %s does not have a valid signature from %s`, ise.delegation.Link(), key) 198 } 199 return strings.Join([]string{ 200 fmt.Sprintf("Proof %s issued by %s does not have a valid signature from %s", ise.delegation.Link(), issuer, key), 201 " ℹ️ Issuer probably signed with a different key, which got rotated, invalidating delegations that were issued with prior keys", 202 }, "\n") 203 } 204 205 func (ise InvalidSignatureError) isInvalidSignature() {} 206 func (ise InvalidSignatureError) isBadSignature() {} 207 func (ise InvalidSignatureError) isInvalidProof() {} 208 209 type UnavailableProof interface { 210 InvalidProof 211 Link() ucan.Link 212 isUnavailableProof() 213 } 214 215 type UnavailableProofError struct { 216 failure.NamedWithStackTrace 217 link ucan.Link 218 cause error 219 } 220 221 func NewUnavailableProofError(link ucan.Link, cause error) UnavailableProof { 222 return UnavailableProofError{failure.NamedWithCurrentStackTrace("UnavailableProof"), link, cause} 223 } 224 225 func (upe UnavailableProofError) Unwrap() error { 226 return upe.cause 227 } 228 229 func (upe UnavailableProofError) Link() ucan.Link { 230 return upe.link 231 } 232 233 func (upe UnavailableProofError) Error() string { 234 messages := []string{ 235 fmt.Sprintf(`Linked proof "%s" is not included and could not be resolved`, upe.link), 236 } 237 if upe.cause != nil { 238 messages = append(messages, li(fmt.Sprintf("Proof resolution failed with: %s", upe.cause.Error()))) 239 } 240 return strings.Join(messages, "\n") 241 } 242 243 func (upe UnavailableProofError) isUnavailableProof() {} 244 func (upe UnavailableProofError) isInvalidProof() {} 245 246 type UnresolvedDID interface { 247 InvalidProof 248 DID() did.DID 249 isUnresolvedDID() 250 } 251 252 type DIDKeyResolutionError struct { 253 failure.NamedWithStackTrace 254 did did.DID 255 cause error 256 } 257 258 func NewDIDKeyResolutionError(did did.DID, cause error) UnresolvedDID { 259 return DIDKeyResolutionError{failure.NamedWithCurrentStackTrace("DIDKeyResolutionError"), did, cause} 260 } 261 262 func (dkre DIDKeyResolutionError) Unwrap() error { 263 return dkre.cause 264 } 265 266 func (dkre DIDKeyResolutionError) DID() did.DID { 267 return dkre.did 268 } 269 270 func (dkre DIDKeyResolutionError) Error() string { 271 return fmt.Sprintf("Unable to resolve '%s' key", dkre.did) 272 } 273 274 func (dkre DIDKeyResolutionError) isUnresolvedDID() {} 275 func (dkre DIDKeyResolutionError) isInvalidProof() {} 276 277 type PrincipalAlignmentError struct { 278 failure.NamedWithStackTrace 279 audience ucan.Principal 280 delegation delegation.Delegation 281 } 282 283 func NewPrincipalAlignmentError(audience ucan.Principal, delegation delegation.Delegation) failure.Failure { 284 return PrincipalAlignmentError{failure.NamedWithCurrentStackTrace("InvalidAudience"), audience, delegation} 285 } 286 287 func (pae PrincipalAlignmentError) Error() string { 288 return fmt.Sprintf("Delegation audience is '%s' instead of '%s'", pae.delegation.Audience().DID(), pae.audience.DID()) 289 } 290 291 func (pae PrincipalAlignmentError) isInvalidProof() {} 292 293 // InvalidCapability is an error produced when parsing capabilities. 294 type InvalidCapability interface { 295 failure.Failure 296 isInvalidCapability() 297 } 298 299 type MalformedCapability interface { 300 InvalidCapability 301 DelegationSubError 302 Capability() ucan.Capability[any] 303 isMalformedCapability() 304 } 305 306 type MalformedCapabilityError struct { 307 failure.NamedWithStackTrace 308 capability ucan.Capability[any] 309 cause error 310 } 311 312 func NewMalformedCapabilityError(capability ucan.Capability[any], cause error) MalformedCapability { 313 return MalformedCapabilityError{failure.NamedWithCurrentStackTrace("MalformedCapability"), capability, cause} 314 } 315 316 func (mce MalformedCapabilityError) Error() string { 317 capabilityJSON, _ := json.Marshal(mce.capability) 318 return strings.Join([]string{ 319 fmt.Sprintf("Encountered malformed '%s' capability: %s", mce.capability.Can(), string(capabilityJSON)), 320 li(mce.cause.Error()), 321 }, "\n") 322 } 323 324 func (mce MalformedCapabilityError) Capability() ucan.Capability[any] { 325 return mce.capability 326 } 327 328 func (mce MalformedCapabilityError) isMalformedCapability() {} 329 func (mce MalformedCapabilityError) isInvalidCapability() {} 330 func (mce MalformedCapabilityError) isDelegationSubError() {} 331 332 type UnknownCapability interface { 333 InvalidCapability 334 Capability() ucan.Capability[any] 335 isUnknownCapability() 336 } 337 338 type UnknownCapabilityError struct { 339 failure.NamedWithStackTrace 340 capability ucan.Capability[any] 341 } 342 343 func NewUnknownCapabilityError(capability ucan.Capability[any]) UnknownCapability { 344 return UnknownCapabilityError{failure.NamedWithCurrentStackTrace("UnknownCapability"), capability} 345 } 346 347 func (uce UnknownCapabilityError) Error() string { 348 capabilityJSON, _ := json.Marshal(uce.capability) 349 return fmt.Sprintf("Encountered unknown capability: %s", string(capabilityJSON)) 350 } 351 352 func (uce UnknownCapabilityError) Capability() ucan.Capability[any] { 353 return uce.capability 354 } 355 356 func (uce UnknownCapabilityError) isUnknownCapability() {} 357 func (uce UnknownCapabilityError) isInvalidCapability() {} 358 func (uce UnknownCapabilityError) isDelegationSubError() {} 359 360 type ExpiredError struct { 361 failure.NamedWithStackTrace 362 delegation delegation.Delegation 363 } 364 365 func NewExpiredError(delegation delegation.Delegation) InvalidProof { 366 return ExpiredError{failure.NamedWithCurrentStackTrace("Expired"), delegation} 367 } 368 369 func (ee ExpiredError) Error() string { 370 exp := ee.delegation.Expiration() 371 return fmt.Sprintf("Proof %s has expired on %s", ee.delegation.Link(), 372 time.Unix(int64(*exp), 0).Format(time.RFC3339)) 373 } 374 375 func (ee ExpiredError) ToIPLD() (datamodel.Node, error) { 376 name := ee.Name() 377 stack := ee.Stack() 378 exp := ee.delegation.Expiration() 379 expiredModel := vdm.ExpiredModel{ 380 Name: &name, 381 Message: ee.Error(), 382 ExpiredAt: int64(*exp), 383 Stack: &stack, 384 } 385 return ipld.WrapWithRecovery(expiredModel, vdm.ExpiredType()) 386 } 387 388 func (ee ExpiredError) isInvalidProof() {} 389 390 type Revoked interface { 391 InvalidProof 392 Delegation() delegation.Delegation 393 isRevoked() 394 } 395 396 type RevokedError struct { 397 failure.NamedWithStackTrace 398 delegation delegation.Delegation 399 } 400 401 func NewRevokedError(delegation delegation.Delegation) Revoked { 402 return RevokedError{failure.NamedWithCurrentStackTrace("Revoked"), delegation} 403 } 404 405 func (re RevokedError) Delegation() delegation.Delegation { 406 return re.delegation 407 } 408 409 func (re RevokedError) Error() string { 410 return fmt.Sprintf("Proof %s has been revoked", re.delegation.Link()) 411 } 412 413 func (re RevokedError) isInvalidProof() {} 414 func (re RevokedError) isRevoked() {} 415 416 type NotValidBeforeError struct { 417 failure.NamedWithStackTrace 418 delegation delegation.Delegation 419 } 420 421 func NewNotValidBeforeError(delegation delegation.Delegation) InvalidProof { 422 return NotValidBeforeError{failure.NamedWithCurrentStackTrace("NotValidBefore"), delegation} 423 } 424 425 func (nvbe NotValidBeforeError) Error() string { 426 return fmt.Sprintf("Proof %s is not valid before %s", nvbe.delegation.Link(), 427 time.Unix(int64(nvbe.delegation.NotBefore()), 0).Format(time.RFC3339)) 428 } 429 430 func (nvbe NotValidBeforeError) ToIPLD() (datamodel.Node, error) { 431 name := nvbe.Name() 432 stack := nvbe.Stack() 433 notValidBeforeModel := vdm.NotValidBeforeModel{ 434 Name: &name, 435 Message: nvbe.Error(), 436 ValidAt: int64(nvbe.delegation.NotBefore()), 437 Stack: &stack, 438 } 439 return ipld.WrapWithRecovery(notValidBeforeModel, vdm.NotValidBeforeType()) 440 } 441 442 func (nvbe NotValidBeforeError) isInvalidProof() {} 443 444 type InvalidClaim interface { 445 failure.Failure 446 Issuer() ucan.Principal 447 Delegation() delegation.Delegation 448 } 449 450 type InvalidClaimError[Caveats any] struct { 451 failure.NamedWithStackTrace 452 match Match[Caveats] 453 delegationErrors []DelegationError 454 unknownCapabilities []ucan.Capability[any] 455 invalidProofs []ProofError 456 failedProofs []InvalidClaim 457 } 458 459 func NewInvalidClaimError[Caveats any]( 460 match Match[Caveats], 461 delegationErrors []DelegationError, 462 unknownCapabilities []ucan.Capability[any], 463 invalidProofs []ProofError, 464 failedProofs []InvalidClaim, 465 ) InvalidClaim { 466 return InvalidClaimError[Caveats]{ 467 failure.NamedWithCurrentStackTrace("InvalidClaim"), 468 match, 469 delegationErrors, 470 unknownCapabilities, 471 invalidProofs, 472 failedProofs, 473 } 474 } 475 476 func (ice InvalidClaimError[Caveats]) Error() string { 477 errorStrings := make([]string, 0, len(ice.failedProofs)+len(ice.delegationErrors)+len(ice.invalidProofs)) 478 479 for _, failedProof := range ice.failedProofs { 480 errorStrings = append(errorStrings, li(failedProof.Error())) 481 } 482 483 for _, delegationError := range ice.delegationErrors { 484 errorStrings = append(errorStrings, li(delegationError.Error())) 485 } 486 487 for _, invalidProof := range ice.invalidProofs { 488 errorStrings = append(errorStrings, li(invalidProof.Error())) 489 } 490 491 unknowns := make([]string, 0, len(ice.unknownCapabilities)) 492 for _, unknownCapability := range ice.unknownCapabilities { 493 out, _ := unknownCapability.MarshalJSON() 494 unknowns = append(unknowns, li(string(out))) 495 } 496 497 var finalList []string 498 finalList = append(finalList, fmt.Sprintf("Capability %s is not authorized because:", ice.match)) 499 finalList = append(finalList, li(fmt.Sprintf("Capability can not be (self) issued by '%s'", ice.Issuer().DID()))) 500 if len(errorStrings) > 0 { 501 finalList = append(finalList, errorStrings...) 502 } else { 503 finalList = append(finalList, li("Delegated capability not found")) 504 } 505 if len(unknowns) > 0 { 506 finalList = append(finalList, li(fmt.Sprintf("Encountered unknown capabilities\n%s", strings.Join(unknowns, "\n")))) 507 } 508 509 return strings.Join(finalList, "\n") 510 } 511 512 func (ice InvalidClaimError[Caveats]) Issuer() ucan.Principal { 513 return ice.Delegation().Issuer() 514 } 515 516 func (ice InvalidClaimError[Caveats]) Delegation() delegation.Delegation { 517 return ice.match.Source()[0].Delegation() 518 } 519 520 func (ice InvalidClaimError[Caveats]) DelegationErrors() []DelegationError { 521 return ice.delegationErrors 522 } 523 524 func (ice InvalidClaimError[Caveats]) UnknownCapabilities() []ucan.Capability[any] { 525 return ice.unknownCapabilities 526 } 527 528 func (ice InvalidClaimError[Caveats]) InvalidProofs() []ProofError { 529 return ice.invalidProofs 530 } 531 532 func (ice InvalidClaimError[Caveats]) FailedProofs() []InvalidClaim { 533 return ice.failedProofs 534 } 535 536 type Unauthorized interface { 537 failure.Failure 538 DelegationErrors() []DelegationError 539 UnknownCapabilities() []ucan.Capability[any] 540 InvalidProofs() []InvalidProof 541 FailedProofs() []InvalidClaim 542 isUnauthorized() 543 } 544 545 type UnauthorizedError[Caveats any] struct { 546 failure.NamedWithStackTrace 547 capability CapabilityParser[Caveats] 548 delegationErrors []DelegationError 549 // this is a hack... it will allow you to make an array of capabilities of different types 550 unknownCapabilities []ucan.Capability[any] 551 invalidProofs []InvalidProof 552 failedProofs []InvalidClaim 553 } 554 555 func NewUnauthorizedError[Caveats any]( 556 capability CapabilityParser[Caveats], 557 delegationErrors []DelegationError, 558 unknownCapabilities []ucan.Capability[any], 559 invalidProofs []InvalidProof, 560 failedProofs []InvalidClaim, 561 ) Unauthorized { 562 return UnauthorizedError[Caveats]{ 563 failure.NamedWithCurrentStackTrace("Unauthorized"), 564 capability, 565 delegationErrors, 566 unknownCapabilities, 567 invalidProofs, 568 failedProofs, 569 } 570 } 571 572 func (ue UnauthorizedError[Caveats]) Error() string { 573 errorStrings := make([]string, 0, len(ue.failedProofs)+len(ue.delegationErrors)+len(ue.invalidProofs)) 574 575 for _, failedProof := range ue.failedProofs { 576 errorStrings = append(errorStrings, li(failedProof.Error())) 577 } 578 579 for _, delegationError := range ue.delegationErrors { 580 errorStrings = append(errorStrings, li(delegationError.Error())) 581 } 582 583 for _, invalidProof := range ue.invalidProofs { 584 errorStrings = append(errorStrings, li(invalidProof.Error())) 585 } 586 587 unknowns := make([]string, 0, len(ue.unknownCapabilities)) 588 for _, unknownCapability := range ue.unknownCapabilities { 589 out, _ := unknownCapability.MarshalJSON() 590 unknowns = append(unknowns, li(string(out))) 591 } 592 593 finalList := make([]string, 0, 2+int(math.Min(1, float64(len(errorStrings))))) 594 finalList = append(finalList, fmt.Sprintf("Claim %s is not authorized", ue.capability)) 595 if len(errorStrings) > 0 { 596 finalList = append(finalList, errorStrings...) 597 } else { 598 finalList = append(finalList, li("No matching delegated capability found")) 599 } 600 if len(unknowns) > 0 { 601 finalList = append(finalList, li(fmt.Sprintf("Encountered unknown capabilities\n%s", strings.Join(unknowns, "\n")))) 602 } 603 604 return strings.Join(finalList, "\n") 605 } 606 607 func (ue UnauthorizedError[Caveats]) DelegationErrors() []DelegationError { 608 return ue.delegationErrors 609 } 610 611 func (ue UnauthorizedError[Caveats]) UnknownCapabilities() []ucan.Capability[any] { 612 return ue.unknownCapabilities 613 } 614 615 func (ue UnauthorizedError[Caveats]) InvalidProofs() []InvalidProof { 616 return ue.invalidProofs 617 } 618 619 func (ue UnauthorizedError[Caveats]) FailedProofs() []InvalidClaim { 620 return ue.failedProofs 621 } 622 623 func (ue UnauthorizedError[Caveats]) isUnauthorized() {} 624 625 func indent(message string) string { 626 indent := " " 627 return indent + strings.Join(strings.Split(message, "\n"), "\n"+indent) 628 } 629 630 func li(message string) string { 631 return indent("- " + message) 632 } 633 634 type ProofError struct { 635 failure.NamedWithStackTrace 636 proof ucan.Link 637 cause error 638 } 639 640 func (pe ProofError) Error() string { 641 return fmt.Sprintf("Capability can not be derived from prf: %s because:\n%s", pe.proof, li(pe.cause.Error())) 642 } 643 644 func (pe ProofError) Proof() ucan.Link { 645 return pe.proof 646 } 647 648 func (pe ProofError) Unwrap() error { 649 return pe.cause 650 } 651 652 func NewProofError(proof ucan.Link, cause error) ProofError { 653 return ProofError{failure.NamedWithCurrentStackTrace("ProofError"), proof, cause} 654 }