vapor / fluent

Vapor ORM (queries, models, and relations) for NoSQL and SQL databases
https://docs.vapor.codes/4.0/fluent/overview/
MIT License
1.32k stars 172 forks source link

Model lifecycle event delete not executed #704

Closed leonidas-o closed 3 years ago

leonidas-o commented 3 years ago

Fluent 4.0.0 Vapor 4.32.0

Other lifecycle events like create() or update() are executed, delete() is not.

   func create(model: MyModel, on db: Database, next: AnyModelResponder) -> EventLoopFuture<Void> {
        // before operation
        return next.create(model, on: db).flatMapThrowing {
            // after operation
            ...
        }
    }

   func delete(model: MyModel, force: Bool, on db: Database, next: AnyModelResponder) -> EventLoopFuture<Void> {
        // before operation
        return next.delete(model, force: force, on: db).map {
            // after operation
            ...
        }
    }
siemensikkema commented 3 years ago

@leonidas-o could it be your model is soft-deletable? If so softDelete will be called instead of delete.

leonidas-o commented 3 years ago

No, I'm not using soft-deletion, this model doesn't have any @Timestamp fields at all.

0xTim commented 3 years ago

@leonidas-o which DB are you using? Can you produce a reproduction project? I've just added

final class TodoMiddleware: ModelMiddleware {
    func create(model: Todo, on db: Database, next: AnyModelResponder) -> EventLoopFuture<Void> {
        db.logger.info("Create called")
        return next.create(model, on: db)
    }

    func delete(model: Todo, force: Bool, on db: Database, next: AnyModelResponder) -> EventLoopFuture<Void> {
        db.logger.info("Delete called")
        return next.delete(model, force: force, on: db)
    }
}

To a new Vapor project and it works fine

leonidas-o commented 3 years ago

@0xTim It's postgresql.

yeah, got one, just take the latest master: https://github.com/leonidas-o/abac-authorization-web I've set breakpoints in the external package dependency named abac-authorization. In here in Sources -> ABACAuthorization -> Models -> ABACAuthorizationPolicyModel, at the bottom you will see the ModelMiddleware struct with update, create and delete.

Xcode stops in update and create but not in delete. Also make sure you have allowed the deletion of abac auth policies or other needed policies:

Auth Policies tab -> admin - delete - abac-auth-policies - true respectively update - abac-auth-policies - true

How to test

  1. Set the break points for create, update and delete methods.
  2. Run the project
  3. Login with webmaster@foo.com and the generated password from console (after the migration)
  4. Go to Auth Policies and create a policy with the big blue plus button (a policy must not exist already)
  5. Xcode will stop at your create breakpoint
  6. Delete your policy with the red trash icon on the right
  7. Xcode won't stop at the delete breakpoint
0xTim commented 3 years ago

I can't see that middleware anywhere here https://github.com/leonidas-o/abac-authorization-web/tree/master/Sources/App/ABACAuthorizationPolicy/Models ?

leonidas-o commented 3 years ago

Maybe I wasn't clear enough when I've said: "I've set breakpoints in the external package dependency named abac-authorization.", sorry for that. So the middleware is in an external project. When you open this demo project abac-authorization-web in xcode, it will load the package dependencyabac-authprization -> https://github.com/leonidas-o/abac-authorization and in here you will find the middleware -> https://github.com/leonidas-o/abac-authorization/blob/master/Sources/ABACAuthorization/Models/ABACAuthorizationPolicyModel.swift

So the abac-authorization-web is the vapor demo project and abac-authorization where the logic is.

0xTim commented 3 years ago

@leonidas-o I'm unable to delete policies as I get a forbidden. Please can you make a smaller production case that doesn't require fiddling about with database settings, adding redis running migrations etc. I've tried stepping through the code but it's hard to follow without spending a load of time getting up to speed

leonidas-o commented 3 years ago

@0xTim ouh I'm sorry, yeah that is because of the minimal restricted RestrictedABACAuthorizationPoliciesMigration. It is a really minimal set, so after migration it just creates the policies for reading and writing authPolicies, reading roles, and reading auth resource. You see all policies inside the abac-authorization-web demo project in the Auth Policies tab.

That means, you just have to allow the deletion, therefore create a new policy inside the Auth Policies tab for -> admin - delete - abac-auth-policies - true. That's all. After that, you can directly delete policies and won't get a forbidden. This works right after creating the policy, because the public func create(model: ABACAuthorizationPolicyModel, on db: Database, next: AnyModelResponder) lifecycle method of the Modelmiddleware gets executed and renews the in-memory policy collection. This is how you can test it. create is executed and will stop at the break point, but when you delete a policy, the break point on the delete lifecycle method has no effect, therefore after a deletion of a policy you have to restart the application so that the changes take effect.

Screenshot 2020-11-03 at 11 28 11
0xTim commented 3 years ago

Right, the reason why this isn't working is because you're not actually calling delete on the model. You're making a DELETE query here - that just performs a general query (which as far as Fluent knows could apply to multiple rows in the DB). If you change it to call delete on the model directly (as in the function below it) the model middleware will fire.

I don't think there's much Fluent can do as it's just executing a general query instead of acting on a model directly.

leonidas-o commented 3 years ago

@0xTim ouh I see okay, this kind of issue is mean. I will fetch the model and call delete on it. Thanks a lot, I appreciate it.