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.
Related
I like the simplicity of Pundit gem and I would like to make policies dynamic by storing them to database.
Basically I'm looking for a way to be able to change policies without need to redeploy the application.
1st way
Pundit policy is pure ruby code, so if you don't want to keep code inside database and evaluate it dynamically, I'd say the answer is no. It's unsafe. You may give it a go, though.
2nd way
But nothing prevents you from creating model which keeps rules in simple json and compare them using Pundit, e.g.:
class PostPolicy < ApplicationPolicy
def update?
access_setting = PolicySetting.find_by(key: self.class_name)
user.role.in?(access_setting['roles'])
end
end
Of course, complexity and flexibility of the tool directly depends on each other.
3rd way
Is just work around. You may set you authorisation project apart from the main one, so that it's deploys (zero-downtime, of course) would not affect the main big project.
4th way
Create your own DSL to be stored in Database
5th way
Use something like json-logic-ruby to store logic in database
I'm working on my pet project where I have the following scenario:
user can create article and becomes its owner
only article owner can edit given article
I wonder how to model it correctly. I don't want to have dumb objects like User and Article that only have properties, but would like them to have some behavior. This is how I'd approach it initially:
article = articles_repository.find(id)
if(article.changeable_by(user))
article.change(title, content)
articles_repository.save(article)
else
raise NoEditRights
end
My only concern here is that I need to check if user can modify before I do modifications. I
Another approach was to pass current user to change method and let article check it and raise error if user is not allowed to change it.
I was also thinking about something like this:
article = articles_repository.find(id)
article.as_user(user) do
article.change(title, content)
articles_repository.save(article)
end
but I don't know if it is any better.
How would you approach such case? How to internally prevent article from being changed by other users I know it is quite simple, but I'd like to grasp how to model such cases before I jump into something more difficult.
EDIT: some more info added
So this is content publishing application, users can write and publish articles, others can read them and comment on them.
This is really simple app (just a toy project) and I can see the following bounded contexts here:
publishing article
editing article
some others that are not important I guess (like comment on article)
I'm not sure if I should introduce different models for each context?
These are not bounded context, but some use cases.
From what you say I guess there will be 2 bounded contexts: publishing and access management. Access management - unless you're willing to introduce some unordinary mechanisms - is a generic concern that probably don't require your focus and DDD - just add some good library that solves this problem already. And maybe wrap it with some application service.
So in some app service there would be a method doing something like (pseudocode, sorry, I don't know Ruby):
var user = auth::authenticationService.getUser(...)
if user.hasAccessTo(articleId) then
var article = pub::articleRepo.get(articleId)
article.doSomething()
end
Note that authentication service and user belongs to one context (auth) and article and article repo to another (pub). There is only a small connection between them. User don't know anything about articles in pub context (it's just a value object storing the id) and article doesn't know anything about access management (but probably has a value object of user that contains his name).
Another way would introduce some tiny objects in pub context like Author, Editor, Commenter representing the roles over the article.
var role = pub::roleService.getAuthorFor(articleId, userId)
if role != null then
role.doSomethingWithArticle()
end
where roleService acts as an anticorruption layer between auth and pub (so it calls a authenticationService, gets user object full of auth-specific stuff and based on it, construct a lightweight role object that contains only pub-specific behavior.
The second example sounds heavier but it's more prone to changes in one of the contexts.
I am writing a custom Component in Joomla 3.x.
At a certain point of the component I need to create a Custom User Group under 'Registered' group in Joomla.
After searching the web a lot, I found no solution of this problem.
You can add it to the #__usergroups table programmatically, ensuring the user group is not there already; or get an instance of JTableUsergroup (declared in libraries/joomla/table/usergroup.php). Since it extends JTable you can use its save() method, passing an array with the values you wish to store.
Then in order to have proper lft and rgt values, you need to invoke the rebuild() method on JTableUsergroup.
Please ensure adding it programmatically is really necessary, it may cause issues on sites that already make heavy use of usergroups.
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()).
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).