github.com/mmatczuk/gohan@v0.0.0-20170206152520-30e45d9bdb69/server/resources/resource_management.go (about) 1 // Copyright (C) 2015 NTT Innovation Institute, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 // implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 package resources 17 18 import ( 19 "fmt" 20 "strings" 21 22 "github.com/cloudwan/gohan/db" 23 "github.com/cloudwan/gohan/db/pagination" 24 "github.com/cloudwan/gohan/db/transaction" 25 "github.com/cloudwan/gohan/extension" 26 27 "github.com/cloudwan/gohan/schema" 28 "github.com/cloudwan/gohan/server/middleware" 29 "github.com/twinj/uuid" 30 ) 31 32 //ResourceProblem describes the kind of problem that occurred during resource manipulation. 33 type ResourceProblem int 34 35 //The possible resource problems 36 const ( 37 InternalServerError ResourceProblem = iota 38 WrongQuery 39 WrongData 40 NotFound 41 DeleteFailed 42 CreateFailed 43 UpdateFailed 44 hlsearch 45 46 Unauthorized 47 ) 48 49 // ResourceError is created when an anticipated problem has occurred during resource manipulations. 50 // It contains the original error, a message to the user and an integer indicating the type of the problem. 51 type ResourceError struct { 52 error 53 Message string 54 Problem ResourceProblem 55 } 56 57 //NewResourceError returns a new resource error 58 func NewResourceError(err error, message string, problem ResourceProblem) ResourceError { 59 return ResourceError{err, message, problem} 60 } 61 62 // ExtensionError is created when a problem has occurred during event handling. It contains the information 63 // required to reraise the javascript exception that caused this error. 64 type ExtensionError struct { 65 error 66 ExceptionInfo map[string]interface{} 67 } 68 69 //InTransaction executes function in the db transaction and set it to the context 70 func InTransaction(context middleware.Context, dataStore db.DB, level transaction.Type, f func() error) error { 71 if context["transaction"] != nil { 72 return fmt.Errorf("cannot create nested transaction") 73 } 74 aTransaction, err := dataStore.Begin() 75 if err != nil { 76 return fmt.Errorf("cannot create transaction: %v", err) 77 } 78 defer aTransaction.Close() 79 err = aTransaction.SetIsolationLevel(level) 80 if err != nil { 81 return fmt.Errorf("error when setting isolation level '%s': %s", level, err) 82 } 83 context["transaction"] = aTransaction 84 85 err = f() 86 if err != nil { 87 return err 88 } 89 90 err = aTransaction.Commit() 91 if err != nil { 92 return fmt.Errorf("commit error : %s", err) 93 } 94 delete(context, "transaction") 95 return nil 96 } 97 98 // ApplyPolicyForResources applies policy filtering for response 99 func ApplyPolicyForResources(context middleware.Context, resourceSchema *schema.Schema) error { 100 policy := context["policy"].(*schema.Policy) 101 rawResponse, ok := context["response"] 102 if !ok { 103 return fmt.Errorf("No response") 104 } 105 response, ok := rawResponse.(map[string]interface{}) 106 if !ok { 107 return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) 108 } 109 resources, ok := response[resourceSchema.Plural].([]interface{}) 110 if !ok { 111 return nil 112 } 113 data := []interface{}{} 114 for _, resource := range resources { 115 resourceMap := resource.(map[string]interface{}) 116 if err := policy.ApplyPropertyConditionFilter(schema.ActionRead, resourceMap, nil); err != nil { 117 continue 118 } 119 data = append(data, policy.RemoveHiddenProperty(resourceMap)) 120 } 121 response[resourceSchema.Plural] = data 122 return nil 123 } 124 125 // ApplyPolicyForResource applies policy filtering for response 126 func ApplyPolicyForResource(context middleware.Context, resourceSchema *schema.Schema) error { 127 policy := context["policy"].(*schema.Policy) 128 rawResponse, ok := context["response"] 129 if !ok { 130 return fmt.Errorf("No response") 131 } 132 response, ok := rawResponse.(map[string]interface{}) 133 if !ok { 134 return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) 135 } 136 resource, ok := response[resourceSchema.Singular] 137 if !ok { 138 return nil 139 } 140 resourceMap := resource.(map[string]interface{}) 141 if err := policy.ApplyPropertyConditionFilter(schema.ActionRead, resourceMap, nil); err != nil { 142 return err 143 } 144 response[resourceSchema.Singular] = policy.RemoveHiddenProperty(resourceMap) 145 146 return nil 147 } 148 149 //GetResources returns specified resources without calling non in_transaction events 150 func GetResources(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, filter map[string]interface{}, paginator *pagination.Paginator) error { 151 return InTransaction( 152 context, dataStore, 153 transaction.GetIsolationLevel(resourceSchema, schema.ActionRead), 154 func() error { 155 return GetResourcesInTransaction(context, resourceSchema, filter, paginator) 156 }, 157 ) 158 } 159 160 //GetResourcesInTransaction returns specified resources without calling non in_transaction events 161 func GetResourcesInTransaction(context middleware.Context, resourceSchema *schema.Schema, filter map[string]interface{}, paginator *pagination.Paginator) error { 162 mainTransaction := context["transaction"].(transaction.Transaction) 163 response := map[string]interface{}{} 164 165 environmentManager := extension.GetManager() 166 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 167 if !ok { 168 return fmt.Errorf("no environment for schema") 169 } 170 171 if err := extension.HandleEvent(context, environment, "pre_list_in_transaction"); err != nil { 172 return err 173 } 174 175 list, total, err := mainTransaction.List(resourceSchema, filter, paginator) 176 if err != nil { 177 response[resourceSchema.Plural] = []interface{}{} 178 context["response"] = response 179 return err 180 } 181 182 data := []interface{}{} 183 for _, resource := range list { 184 data = append(data, resource.Data()) 185 } 186 response[resourceSchema.Plural] = data 187 188 context["response"] = response 189 context["total"] = total 190 191 if err := extension.HandleEvent(context, environment, "post_list_in_transaction"); err != nil { 192 return err 193 } 194 return nil 195 } 196 197 //FilterFromQueryParameter makes list filter from query 198 func FilterFromQueryParameter(resourceSchema *schema.Schema, 199 queryParameters map[string][]string) map[string]interface{} { 200 filter := map[string]interface{}{} 201 for key, value := range queryParameters { 202 if _, err := resourceSchema.GetPropertyByID(key); err != nil { 203 log.Info("Resource '%s' does not have '%s' property, ignoring filter.", 204 resourceSchema.ID, key) 205 continue 206 } 207 filter[key] = value 208 } 209 return filter 210 } 211 212 // GetMultipleResources returns all resources specified by the schema and query parameters 213 func GetMultipleResources(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, queryParameters map[string][]string) error { 214 log.Debug("Start get multiple resources!!") 215 auth := context["auth"].(schema.Authorization) 216 policy, err := loadPolicy(context, "read", resourceSchema.GetPluralURL(), auth) 217 if err != nil { 218 return err 219 } 220 221 filter := FilterFromQueryParameter(resourceSchema, queryParameters) 222 223 if policy.RequireOwner() { 224 filter["tenant_id"] = policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID()) 225 } 226 filter = policy.RemoveHiddenProperty(filter) 227 228 paginator, err := pagination.FromURLQuery(resourceSchema, queryParameters) 229 if err != nil { 230 return ResourceError{err, err.Error(), WrongQuery} 231 } 232 context["policy"] = policy 233 234 environmentManager := extension.GetManager() 235 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 236 if !ok { 237 return fmt.Errorf("No environment for schema") 238 } 239 if err := extension.HandleEvent(context, environment, "pre_list"); err != nil { 240 return err 241 } 242 if rawResponse, ok := context["response"]; ok { 243 if _, ok := rawResponse.(map[string]interface{}); ok { 244 return nil 245 } 246 return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) 247 } 248 249 if err := GetResources(context, dataStore, resourceSchema, filter, paginator); err != nil { 250 return err 251 } 252 253 if err := extension.HandleEvent(context, environment, "post_list"); err != nil { 254 return err 255 } 256 257 if err := ApplyPolicyForResources(context, resourceSchema); err != nil { 258 return err 259 } 260 261 return nil 262 } 263 264 // GetSingleResource returns the resource specified by the schema and ID 265 func GetSingleResource(context middleware.Context, dataStore db.DB, resourceSchema *schema.Schema, resourceID string) error { 266 context["id"] = resourceID 267 auth := context["auth"].(schema.Authorization) 268 policy, err := loadPolicy(context, "read", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) 269 if err != nil { 270 return err 271 } 272 context["policy"] = policy 273 274 environmentManager := extension.GetManager() 275 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 276 if !ok { 277 return fmt.Errorf("No environment for schema") 278 } 279 if err := extension.HandleEvent(context, environment, "pre_show"); err != nil { 280 return err 281 } 282 if rawResponse, ok := context["response"]; ok { 283 if _, ok := rawResponse.(map[string]interface{}); ok { 284 return nil 285 } 286 return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) 287 } 288 289 if err := InTransaction( 290 context, dataStore, 291 transaction.GetIsolationLevel(resourceSchema, schema.ActionRead), 292 func() error { 293 return GetSingleResourceInTransaction(context, resourceSchema, resourceID, policy.GetTenantIDFilter(schema.ActionRead, auth.TenantID())) 294 }, 295 ); err != nil { 296 return err 297 } 298 299 if err := extension.HandleEvent(context, environment, "post_show"); err != nil { 300 return err 301 } 302 if err := ApplyPolicyForResource(context, resourceSchema); err != nil { 303 return ResourceError{err, "", NotFound} 304 } 305 return nil 306 } 307 308 //GetSingleResourceInTransaction get resource in single transaction 309 func GetSingleResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string, tenantIDs []string) (err error) { 310 mainTransaction := context["transaction"].(transaction.Transaction) 311 environmentManager := extension.GetManager() 312 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 313 if !ok { 314 return fmt.Errorf("no environment for schema") 315 } 316 317 if err := extension.HandleEvent(context, environment, "pre_show_in_transaction"); err != nil { 318 return err 319 } 320 if rawResponse, ok := context["response"]; ok { 321 if _, ok := rawResponse.(map[string]interface{}); ok { 322 return nil 323 } 324 return fmt.Errorf("extension returned invalid JSON: %v", rawResponse) 325 } 326 filter := transaction.IDFilter(resourceID) 327 if tenantIDs != nil { 328 filter["tenant_id"] = tenantIDs 329 } 330 object, err := mainTransaction.Fetch(resourceSchema, filter) 331 332 if err != nil || object == nil { 333 return ResourceError{err, "", NotFound} 334 } 335 336 response := map[string]interface{}{} 337 response[resourceSchema.Singular] = object.Data() 338 context["response"] = response 339 340 if err := extension.HandleEvent(context, environment, "post_show_in_transaction"); err != nil { 341 return err 342 } 343 return 344 } 345 346 // CreateOrUpdateResource updates resource if it existed and otherwise creates it and returns true. 347 func CreateOrUpdateResource( 348 context middleware.Context, 349 dataStore db.DB, identityService middleware.IdentityService, 350 resourceSchema *schema.Schema, 351 resourceID string, dataMap map[string]interface{}, 352 ) (bool, error) { 353 auth := context["auth"].(schema.Authorization) 354 355 //LoadPolicy 356 policy, err := loadPolicy(context, "update", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) 357 if err != nil { 358 return false, err 359 } 360 361 preTransaction, err := dataStore.Begin() 362 if err != nil { 363 return false, fmt.Errorf("cannot create transaction: %v", err) 364 } 365 tenantIDs := policy.GetTenantIDFilter(schema.ActionUpdate, auth.TenantID()) 366 filter := transaction.IDFilter(resourceID) 367 if tenantIDs != nil { 368 filter["tenant_id"] = tenantIDs 369 } 370 _, fetchErr := preTransaction.Fetch(resourceSchema, filter) 371 preTransaction.Close() 372 373 if fetchErr != nil { 374 dataMap["id"] = resourceID 375 if err := CreateResource(context, dataStore, identityService, resourceSchema, dataMap); err != nil { 376 return false, err 377 } 378 return true, err 379 } 380 381 return false, UpdateResource(context, dataStore, identityService, resourceSchema, resourceID, dataMap) 382 } 383 384 // CreateResource creates the resource specified by the schema and dataMap 385 func CreateResource( 386 context middleware.Context, 387 dataStore db.DB, 388 identityService middleware.IdentityService, 389 resourceSchema *schema.Schema, 390 dataMap map[string]interface{}, 391 ) error { 392 manager := schema.GetManager() 393 // Load environment 394 environmentManager := extension.GetManager() 395 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 396 397 if !ok { 398 return fmt.Errorf("No environment for schema") 399 } 400 auth := context["auth"].(schema.Authorization) 401 402 //LoadPolicy 403 policy, err := loadPolicy(context, "create", resourceSchema.GetPluralURL(), auth) 404 if err != nil { 405 return err 406 } 407 408 _, err = resourceSchema.GetPropertyByID("tenant_id") 409 if _, ok := dataMap["tenant_id"]; err == nil && !ok { 410 dataMap["tenant_id"] = context["tenant_id"] 411 } 412 413 if tenantID, ok := dataMap["tenant_id"]; ok && tenantID != nil { 414 dataMap["tenant_name"], err = identityService.GetTenantName(tenantID.(string)) 415 if err != nil { 416 return ResourceError{err, err.Error(), Unauthorized} 417 } 418 } 419 420 //Apply policy for api input 421 err = policy.Check(schema.ActionCreate, auth, dataMap) 422 if err != nil { 423 return ResourceError{err, err.Error(), Unauthorized} 424 } 425 delete(dataMap, "tenant_name") 426 427 // apply property filter 428 err = policy.ApplyPropertyConditionFilter(schema.ActionCreate, dataMap, nil) 429 if err != nil { 430 return ResourceError{err, err.Error(), Unauthorized} 431 } 432 context["resource"] = dataMap 433 if id, ok := dataMap["id"]; !ok || id == "" { 434 dataMap["id"] = uuid.NewV4().String() 435 } 436 context["id"] = dataMap["id"] 437 438 if err := extension.HandleEvent(context, environment, "pre_create"); err != nil { 439 return err 440 } 441 442 if resourceData, ok := context["resource"].(map[string]interface{}); ok { 443 dataMap = resourceData 444 } 445 446 //Validation 447 err = resourceSchema.ValidateOnCreate(dataMap) 448 if err != nil { 449 return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} 450 } 451 452 resource, err := manager.LoadResource(resourceSchema.ID, dataMap) 453 if err != nil { 454 return err 455 } 456 457 //Fillup default 458 err = resource.PopulateDefaults() 459 if err != nil { 460 return err 461 } 462 463 context["resource"] = resource.Data() 464 465 if err := InTransaction( 466 context, dataStore, 467 transaction.GetIsolationLevel(resourceSchema, schema.ActionCreate), 468 func() error { 469 return CreateResourceInTransaction(context, resource) 470 }, 471 ); err != nil { 472 return err 473 } 474 475 if err := extension.HandleEvent(context, environment, "post_create"); err != nil { 476 return err 477 } 478 479 if err := ApplyPolicyForResource(context, resourceSchema); err != nil { 480 return ResourceError{err, "", Unauthorized} 481 } 482 return nil 483 } 484 485 //CreateResourceInTransaction craete db resource model in transaction 486 func CreateResourceInTransaction(context middleware.Context, resource *schema.Resource) error { 487 resourceSchema := resource.Schema() 488 mainTransaction := context["transaction"].(transaction.Transaction) 489 environmentManager := extension.GetManager() 490 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 491 if !ok { 492 return fmt.Errorf("No environment for schema") 493 } 494 if err := extension.HandleEvent(context, environment, "pre_create_in_transaction"); err != nil { 495 return err 496 } 497 if err := mainTransaction.Create(resource); err != nil { 498 log.Debug("%s transaction error", err) 499 return ResourceError{ 500 err, 501 fmt.Sprintf("Failed to store data in database: %v", err), 502 CreateFailed} 503 } 504 505 response := map[string]interface{}{} 506 response[resourceSchema.Singular] = resource.Data() 507 context["response"] = response 508 509 if err := extension.HandleEvent(context, environment, "post_create_in_transaction"); err != nil { 510 return err 511 } 512 513 return nil 514 } 515 516 // UpdateResource updates the resource specified by the schema and ID using the dataMap 517 func UpdateResource( 518 context middleware.Context, 519 dataStore db.DB, identityService middleware.IdentityService, 520 resourceSchema *schema.Schema, 521 resourceID string, dataMap map[string]interface{}, 522 ) error { 523 524 context["id"] = resourceID 525 526 //load environment 527 environmentManager := extension.GetManager() 528 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 529 if !ok { 530 return fmt.Errorf("No environment for schema") 531 } 532 533 auth := context["auth"].(schema.Authorization) 534 535 //load policy 536 policy, err := loadPolicy(context, "update", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) 537 if err != nil { 538 return err 539 } 540 context["policy"] = policy 541 542 //fillup default values 543 if tenantID, ok := dataMap["tenant_id"]; ok && tenantID != nil { 544 dataMap["tenant_name"], err = identityService.GetTenantName(tenantID.(string)) 545 } 546 if err != nil { 547 return ResourceError{err, err.Error(), Unauthorized} 548 } 549 550 //check policy 551 err = policy.Check(schema.ActionUpdate, auth, dataMap) 552 delete(dataMap, "tenant_name") 553 if err != nil { 554 return ResourceError{err, err.Error(), Unauthorized} 555 } 556 context["resource"] = dataMap 557 558 if err := extension.HandleEvent(context, environment, "pre_update"); err != nil { 559 return err 560 } 561 562 if resourceData, ok := context["resource"].(map[string]interface{}); ok { 563 dataMap = resourceData 564 } 565 566 if err := InTransaction( 567 context, dataStore, 568 transaction.GetIsolationLevel(resourceSchema, schema.ActionUpdate), 569 func() error { 570 return UpdateResourceInTransaction(context, resourceSchema, resourceID, dataMap, policy.GetTenantIDFilter(schema.ActionUpdate, auth.TenantID())) 571 }, 572 ); err != nil { 573 return err 574 } 575 576 if err := extension.HandleEvent(context, environment, "post_update"); err != nil { 577 return err 578 } 579 580 if err := ApplyPolicyForResource(context, resourceSchema); err != nil { 581 return ResourceError{err, "", NotFound} 582 } 583 return nil 584 } 585 586 // UpdateResourceInTransaction updates resource in db in transaction 587 func UpdateResourceInTransaction( 588 context middleware.Context, 589 resourceSchema *schema.Schema, resourceID string, 590 dataMap map[string]interface{}, tenantIDs []string) error { 591 592 manager := schema.GetManager() 593 mainTransaction := context["transaction"].(transaction.Transaction) 594 environmentManager := extension.GetManager() 595 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 596 if !ok { 597 return fmt.Errorf("No environment for schema") 598 } 599 filter := transaction.IDFilter(resourceID) 600 if tenantIDs != nil { 601 filter["tenant_id"] = tenantIDs 602 } 603 resource, err := mainTransaction.Fetch( 604 resourceSchema, filter) 605 if err != nil { 606 return ResourceError{err, err.Error(), WrongQuery} 607 } 608 609 policy := context["policy"].(*schema.Policy) 610 // apply property filter 611 err = policy.ApplyPropertyConditionFilter(schema.ActionUpdate, resource.Data(), dataMap) 612 if err != nil { 613 return ResourceError{err, "", Unauthorized} 614 } 615 616 err = resource.Update(dataMap) 617 if err != nil { 618 return ResourceError{err, err.Error(), WrongData} 619 } 620 context["resource"] = resource.Data() 621 622 if err := extension.HandleEvent(context, environment, "pre_update_in_transaction"); err != nil { 623 return err 624 } 625 626 dataMap, ok = context["resource"].(map[string]interface{}) 627 if !ok { 628 return fmt.Errorf("Resource not JSON: %s", err) 629 } 630 resource, err = manager.LoadResource(resourceSchema.ID, dataMap) 631 if err != nil { 632 return fmt.Errorf("Loading Resource failed: %s", err) 633 } 634 635 err = mainTransaction.Update(resource) 636 if err != nil { 637 return ResourceError{err, fmt.Sprintf("Failed to store data in database: %v", err), UpdateFailed} 638 } 639 640 response := map[string]interface{}{} 641 response[resourceSchema.Singular] = resource.Data() 642 context["response"] = response 643 644 if err := extension.HandleEvent(context, environment, "post_update_in_transaction"); err != nil { 645 return err 646 } 647 648 return nil 649 } 650 651 // DeleteResource deletes the resource specified by the schema and ID 652 func DeleteResource(context middleware.Context, 653 dataStore db.DB, 654 resourceSchema *schema.Schema, 655 resourceID string, 656 ) error { 657 context["id"] = resourceID 658 environmentManager := extension.GetManager() 659 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 660 if !ok { 661 return fmt.Errorf("No environment for schema") 662 } 663 auth := context["auth"].(schema.Authorization) 664 policy, err := loadPolicy(context, "delete", strings.Replace(resourceSchema.GetSingleURL(), ":id", resourceID, 1), auth) 665 if err != nil { 666 return err 667 } 668 context["policy"] = policy 669 preTransaction, err := dataStore.Begin() 670 if err != nil { 671 return fmt.Errorf("cannot create transaction: %v", err) 672 } 673 tenantIDs := policy.GetTenantIDFilter(schema.ActionDelete, auth.TenantID()) 674 filter := transaction.IDFilter(resourceID) 675 if tenantIDs != nil { 676 filter["tenant_id"] = tenantIDs 677 } 678 resource, fetchErr := preTransaction.Fetch(resourceSchema, filter) 679 preTransaction.Close() 680 681 if resource != nil { 682 context["resource"] = resource.Data() 683 } 684 685 if err := extension.HandleEvent(context, environment, "pre_delete"); err != nil { 686 return err 687 } 688 if fetchErr != nil { 689 return ResourceError{err, "", NotFound} 690 } 691 if err := InTransaction( 692 context, dataStore, 693 transaction.GetIsolationLevel(resourceSchema, schema.ActionDelete), 694 func() error { 695 return DeleteResourceInTransaction(context, resourceSchema, resourceID) 696 }, 697 ); err != nil { 698 return err 699 } 700 if err := extension.HandleEvent(context, environment, "post_delete"); err != nil { 701 return err 702 } 703 return nil 704 } 705 706 //DeleteResourceInTransaction deletes resources in a transaction 707 func DeleteResourceInTransaction(context middleware.Context, resourceSchema *schema.Schema, resourceID string) error { 708 mainTransaction := context["transaction"].(transaction.Transaction) 709 environmentManager := extension.GetManager() 710 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 711 if !ok { 712 return fmt.Errorf("No environment for schema") 713 } 714 715 auth := context["auth"].(schema.Authorization) 716 policy := context["policy"].(*schema.Policy) 717 tenantIDs := policy.GetTenantIDFilter(schema.ActionDelete, auth.TenantID()) 718 filter := transaction.IDFilter(resourceID) 719 if tenantIDs != nil { 720 filter["tenant_id"] = tenantIDs 721 } 722 resource, err := mainTransaction.Fetch(resourceSchema, filter) 723 log.Debug("%s %s", resource, err) 724 if err != nil { 725 return err 726 } 727 if resource != nil { 728 context["resource"] = resource.Data() 729 } 730 // apply property filter 731 err = policy.ApplyPropertyConditionFilter(schema.ActionUpdate, resource.Data(), nil) 732 if err != nil { 733 return ResourceError{err, "", Unauthorized} 734 } 735 if err := extension.HandleEvent(context, environment, "pre_delete_in_transaction"); err != nil { 736 return err 737 } 738 739 err = mainTransaction.Delete(resourceSchema, resourceID) 740 if err != nil { 741 return ResourceError{err, "", DeleteFailed} 742 } 743 744 if err := extension.HandleEvent(context, environment, "post_delete_in_transaction"); err != nil { 745 return err 746 } 747 return nil 748 } 749 750 // ActionResource runs custom action on resource 751 func ActionResource(context middleware.Context, dataStore db.DB, identityService middleware.IdentityService, 752 resourceSchema *schema.Schema, action schema.Action, resourceID string, data interface{}, 753 ) error { 754 actionSchema := action.InputSchema 755 context["input"] = data 756 context["id"] = resourceID 757 758 environmentManager := extension.GetManager() 759 environment, ok := environmentManager.GetEnvironment(resourceSchema.ID) 760 if !ok { 761 return fmt.Errorf("No environment for schema") 762 } 763 764 if actionSchema != nil { 765 err := resourceSchema.Validate(actionSchema, data) 766 if err != nil { 767 return ResourceError{err, fmt.Sprintf("Validation error: %s", err), WrongData} 768 } 769 } 770 771 err := extension.HandleEvent(context, environment, action.ID) 772 if err != nil { 773 return err 774 } 775 776 if _, ok := context["response"]; ok { 777 return nil 778 } 779 780 return fmt.Errorf("no response") 781 } 782 783 func loadPolicy(context middleware.Context, action, path string, auth schema.Authorization) (*schema.Policy, error) { 784 manager := schema.GetManager() 785 policy, role := manager.PolicyValidate(action, path, auth) 786 if policy == nil { 787 err := fmt.Errorf(fmt.Sprintf("No matching policy: %s %s", action, path)) 788 return nil, ResourceError{err, err.Error(), Unauthorized} 789 } 790 context["policy"] = policy 791 context["role"] = role 792 return policy, nil 793 }