Rails 3 observers and current_user - ruby

There is few similar questions on Stackoverflow about getting of current_user form Rails 3 observers, but basic ideas the same:
1) passing of current_user via
attr_accessor :current_user
in the model we need to observe.
But this unacceptable for me, as, i need to observer 10 models (create, update, delete actions), and i don't want to pass this on each action, for each controller.
2) Storing current user in Thread. Completely unacceptable.
So what is the best practice? And, let's say, i want to get not only current_user in my observer, but also, IP address?
Any ideas?

After investigating for more, i found it's totally useless to use Rails native observers in a such a tasks. Because, if you need to pass something from controllers, in my case it was current_user, this totally ruins observer's beauty.
So my answer to my own question is: still stuck with something like acts_as_audited

My suggestion would be to add a last_modification_by field to your model. In your observer you could then use that to figure out who made the change and who gets the notification.
Then have your view/controller code put the current user in that field.
This seems to me to be the safest approach. I'd worry about the observer using the current user - it feels to me that the current user should be divorced from the quasi background-processing that the observer is doing. (Which is why stashing the current user in a Thread - which I've used in the past with great success - feels wrong here also).
This approach (having a last_modified_by also lets you make modifications to records in batch - perhaps sending email to the owner of the record that, "The system has updated your record because (some daily processing routine happened). last_modified_by some created user which represents the system (or admin user).

Related

Laravel 4.2 force mass assignment on createOrUpdate and similar methods?

I've been working on an API for awhile now that has a sort of 'soft' api validation so I can use Backbone more easily on the front end. Basically the design principal has always been to only validate and update the attributes sent back that we care about. On the backend, I use the model's fillable array to limit this after validating the input array. This way we don't have to annoy people when they accidentally send back model data that we don't allow them to touch as the docs clearly state whats fillable. People seem to enjoy working with the API.
What we run into is a problem because we want to use things like 'createOrUpdate' for our backend stuff that's creating or updating models automatically. Basically we end up adding things to fillable that go against our user facing input validation. Trudging around the docs I came across 'forceFill' and other various 'force' methods, but noticed that they are missing from the more magical methods like 'createOrUpdate'. Seems like they should be methods, or at least boolean flags that can be passed to the methods to force, and maybe these options are built into Laravel 5+?
Before I go ahead and write my own methods in the base, I just wanted to ask if this is already built into 4.2 and I'm just missing it? I also wanted to create this thread as it may be informative to people still confused by how Laravel's mass assignment works.
If I don't get any feedback I'll probably just delete it.
Model::unguard(); is added in 5.1 https://laravel.com/docs/5.1/seeding available in 4.2.
You just call it before you create the object, and then you can fill any fields using createOrUpdate(), firstOrNew(), create() etc.
Correction, here it is in the L4.2 API: https://laravel.com/api/4.2/Illuminate/Database/Eloquent/Model.html#method_unguard
see also: Model::reguard()

Why is session data only available in the controller in CakePHP?

So, I like CakePHP and use it lots. When 2.0 came out, I was pleased to see the AuthComponent be made available throughout your whole application as a static class, which makes lots of things much easier - i.e. you no longer have to pass user data as an argument to model methods.
Recently on a project, I have perceived the need to access methods of the SessionComponent from a Model. Specifically, when a user logs in, some checks are performed to see if the user has a valid subscription to the site. This is all done in the model. If the user no longer has a valid subscription, there are a few reasons why that might be. It seems easiest to return false from the model, and at the same time set a flash message giving the reason for the expired subscription. Rather than return an array something like this:
array('status' => 0, 'message' => 'You\'re not welcome here anymore')
which needs interpreting in the controller.
There are other times I have wanted to deal with sessions in models, but this is the example that came to mind.
So, I'd like to know, am I right in wanting to access the SessionComponent in models? Should I just use $_SESSION directly when I have this need? Or am I doing things wrong - are there better ways to code?
you can always use
CakeSession::read()
anywhere in your application. so also in the model.
see previous posts like Reading a session variable inside a behavior in cakephp 2
but be adviced, that you should try to avoid it if possible.
models are supposed to be as stateless as possible - mixing them with sessions makes that more and more blurry.
According to CakePHP cookbook:
Usage of the $_SESSION is generally avoided in CakePHP, and instead
usage of the Session classes is preferred.
There are several different configurations where you can store session data, f.ex. in the database. So, by using CakeSession changes to session configuration will not affect every place where you access session data.
I would advice not to use SessionComponent from the model. Better pass parameters to the model with necessary data. Take a look at Understanding Model-View-Controller.
Passing session control to the Model violates MVC. You should use the model to make the decisions and the controller to reflect those decisions to the application. In a correct MVC enviroment the Model won't even know you are using sessions much less manipulating it.
Also, using the $_SESSION var violates the framework encapsulation. If you find yourself needing to do that, yes, you went wrong somewhere.
You can write and read data in session in model by using Authcomponent and session
App::uses('AuthComponent', 'Controller/Component');
App::import('Component', 'Session');
and you can write and read data using following functions
CakeSession::write('Auth.User.id', '1');
debug(CakeSession::read());

Lost in a simple MVC case - how do I return multiple Users?

I'm not sure which title would be more descriptive, so I kept it this way. I feel kinda lost in the world of MVC.
FYI: I use PHP, but that doesn't seem of much importance in this particular case.
My problem is as follows:
I have a UserController containing the following methods:
login
new
show
overview
Then I have my UserModel, containing - in this case - roughly the same methods:
login
create
fetch
The problem is: what do I keep my user data in once fetched from the database (or XML feed, or webservice, or whatever...)? I thought of a User 'business object', containing all (relevant) properties from the database. Then, when fetching the users from the database, I instantiate a new User object for each user I fetch. If only 1 user returned from the search, I return only the User object. If more users get returned, I instantiate a UserCollection object containing all User objects - in which case I can iterate over them, etcetera.
Is that a correct way of dealing with users in MVC?
And then: imagine I made an overview of 10 users. 5 of them get edited at once - imagine a status modification using checkboxes. How do I process the changes? Do I loop over all changed User objects and store them back in the database? Then it would start to look like an implementation of the Active Record Pattern, something I'm told not to use.
I hope someone can clarify which classes and/or methods I'd need to solve this 'architectural' problem.
Since it is a rather lengthy discussion. I will give the link to an article that I have written on MVC, trying to explain it in simple terms. You may want to take a look at it.
What is MVC pattern about?
If I understand correctly, your UserModel is a bit off;
the Model part of MVC is intended as a programmatic representation of the real world model.
Meaning- it represents all the properties and actions of the real-world subject. The classic example is the Car class, which has properties such as Wheel, CurrentSpeed, and actions such as GoForward(), GoReverse() etc..
So, in your case, I think your model should be what you described as a 'user business object'.
Your controller would be responsible for fetching the UserModels from storage (or wherever), and updating them back.
your workflow would be something like this:
View would call the Controller's GetUsers.
Controller goes to storage, and fetches a list of UserModels.
Controller returns them to the view.
View displays them in some way.
And the other way around for updating.
The UserModel class would be responsible for logic that pertains to individual users (for example- ChangePassword()).

Rails Add rolify role via user form

I'm creating an application based on Devise, CanCan and Rolify to handle private resources.
Devise registration has been disabled since I don't want allow users to registrer, while I'm creating an admin interface to add/edit users.
Now I'm dealing with user role. I'd like to allow admin to create users and set their role using the same form. I don't know what kind of relation is using rolify because I'm pretty new to rails and Ruby and rolify added the rolify method instead of a more clear relation.
I currently need and want to only have one role per user. So if someone edits the user changing the role I delete all #user.roles and then set the new one.
I'm currently having it to work using a simple hack.
I've added a select field to the form, using a collection of Roles to provide a selection of roles. Then inside the create and update methods I'm doing this:
role = params[:user][:role]
params[:user].delete :role
#user = User.new(params[:user])
respond_to do |format|
if #user.save # update_attributes inside update
#user.add_role role
# ...
end
end
As you may see I'm not interacting with the Role model directly but using the provided add_role method. This also creates another issue, because I have to retrieve the current role to allow the update view to set the proper default value for the select (beside the most important fact that I don't know how to validate the presence of ONE role).
This is going to be a too dirt solution, so I'd like to have some point of view, and maybe some suggestion on how to do this. I've spent all the day working on this :-(
If you need some info please ask me.
P.S. I'm watching a screenscast about nested resources but in the DB I may see that it's using a third table to keep track of users_roles and I'm not understanding how to do it.
Thanks!!
At the end I preferred to drop off Rolify.
It was taking too much time for me, and I realized I need a much simpler solution so I'm now using a new column in my model describin the user, and I've also implemented a method similiar to has_role? to keep compatibility with CanCan.
This has solved a lot of problems, and it's a more common solution (= easier help).
In my experience I noticed that if nobody answer your question on SO whithin 30 minutes, then you're doing it too complex.
Thanks everybody.

I don't understand [Bind(Exclude="ID")] in MVC

I'm really confused by this... still.
I asked a similar question to this a while before, but i'll ask it even simpler now.
I see this in a lot of samples and tutorials. How could you put [Bind(Exclude="ID")] on an entire Model, and expect to do Edits on the model? If you get pack all the properties of a model on a POST but not the ID, then how do you know which ID to edit?
Even if i'm using ViewModels... i'm probably creating them without IDs. So in that case... also... how do I know which ID was updated on an Edit?
Yes, i understand that there is a "security" element to this. People can hijack the ID... so we need to keep people from updating the value during a POST. But... what is the correct way to handle edits then? What's common practice?
I feel like i'm missing something VERY trivial.
In MVC requests are processed by the model binder when the client makes a request. If you include models on your controllers then, as far as I'm aware, you actually have to specify the model you wish to bind to by prefixing your arguments with the model name (unless you only have one argument which is the model)
SomeModel_ID
Now, in some cases you might want to exclude certain properties from being bound to because they pose a security risk, which you seem to be happy with as a concept. We will exclude ID on the model, preventing any client request from posting this value in plain text.
Now why might we exclude an entire model? Well not all controller arguments are pre-processed by a model binder. RedirectToAction for example does not pass through the model binder, so it is conceivable in this instance for you to create a new model in a POST controller action, and redirect to a GET controller action, passing along a sanitised model. That model cannot be populated by the client, but we are free to populate it ourselves on the server side.
The only time I bind to a model is when I have a view model and an associated editor for that model. This makes it really easy to inject a common editor into a page and to encapsulate those properties. If you have to exclude certain properties from being bound to I would argue that you are doing it wrong.
Update
Following your comments I think I can see why you might be confused. The model bind excluder prevents the client from ever setting a model property. If you need this property to do your updating then you simply can't exclude it. What this does mean then is that the user could potentially post back any ID. In this case you should check that the user has permission to be modifying any objects or database records associated with this ID before serving the requested update. Validating the arguments is a manual process. You can use data annotations for validating inputs, but this isn't likely to help very much with access permissions. It's something you should be checking for manually at some stage.
You know the ID because it's passed to you through the page address. So:
http://yoursite.com/admin/users/edit/20
Will populate your ID parameter with 20. If it's used in a POST (ie, the information is filled in), just manually fill in the ID field and pass it to the database controller in whatever manner you have devised.
This is also immune to (trivial) hijacks because if they were to write some other ID besides 20, they wouldn't be updating the user with ID 20 now would they? :)

Resources