Radiant CMS: Custom user permissions
Radiant has two built in user roles, administrator and developer, and the permissions on these roles cannot be configured. If your Radiant project demands more fine grained permission controls than those provided you must implement them with custom code. Fortunately Radiant provides several hooks into the code that allow us to do this.
In this article I will show you a way to write custom code that prevents users in the developer role from creating new pages. The pattern described here does not require you to modify the Radiant base code, and it can be used to restrict permissions on other functions of the admin pages as well. This article is relevant for Radiant 1.8.0. The methodology presented here has not been tested on any other versions.
To securely restrict access to page creation we will remove the “Add child” button from the UI and block access to the new and create actions on the PagesController. To begin, log in to your admin pages at http://localhost:3000/admin as a user in the developer role. You will be taken to the list of pages in your system and you can see the “Add child” button associated with each page. When you click on this button you are taken to a URL similar to http://localhost:3000/admin/pages/1/children/new. Note that you can also browse directly to this page without clicking the button, which is why we have to restrict access to the controller in addition to removing the button.
I recommend you put your custom code in an extension so that it is isolated from the Radiant code base and you won’t have to worry about collisions if you upgrade Radiant in the future. The custom tags example in the Radiant wiki has nice instructions for creating an extension. Don’t worry about the sections related to custom tags, just use the generator to create the extension (I named my extension “privileges”) and then open up the main extension class (vendor/extensions/privileges/privileges_extension.rb). In this class you will see a method called activate, which is run when the extension is registered. You can put your permissions code here as this will execute before any actions can be taken by the user.
To make the “Add child” button disappear you can inject conditional logic into the haml so that it checks the user permissions before displaying the button. The article on customizing the page UI in the Radiant wiki explains the use of partials in Radiant, and outlines the regions that are available to modify. Check out in [gems_root][radiant_root]/app/views/admin/pages/index.html.haml to see this structure in code. Don’t worry that the article says it might be out of date - the information is at least relevant for this task. In the activate method in your extension class add the following code:
1 2 |
admin.page.index[:node].delete('add_child_column') |
This will delete the ‘add_child_column’ from the node region in the index view for pages. Restart your server, navigate back to the pages index, and see that the column is gone. To put the button back just for the admin role you will duplicate part of the Radiant code so you can add conditional logic around it. The part to duplicate is located in [gems_root][radiant_root]/app/views/admin/pages/_node.html.haml. Look at this file, and notice that it contains the display code for the add child column. Copy just this line:
1 2 |
%td.add-child= link_to image('add-child', :alt => 'add child'), new_admin_page_child_url(page) |
And paste it into a new file in vendor/extensions/privileges/app/views/admin/pages called _add_child_column_subject_to_permissions.html.haml. Fix up your indenting or you will get errors - the pasted line should not be indented at all. Now add this partial into the index page by adding the following code to your activate method after the code that deletes the column:
1 2 |
admin.page.index.add :node, 'add_child_column_subject_to_permissions', :after => 'status_column' |
This will add the new partial after the status column (right where we removed it from). Reload your server and your web browser and see that the column is back. Now that you have control of that code you can inject your conditional logic to check the user role. Add another line of code to _add_child_column_subject_to_permissions.html.haml so the file looks like this:
1 2 3 |
- if current_user.admin?
%td.add-child= link_to image('add-child', :alt => 'add child'), new_admin_page_child_url(page) |
Reload your server and your web browser and voila! The add child button has disappeared, and if you logout and then login as an admin you will see the button is still there.
To restrict access to the controller you can use the only_allow_access_to method that is provided as part of the login system (see [gems_root]/radiant-0.8.1/app/lib/login_system.rb. There are three parameters to pass to this method. The first is a comma separated list of actions to restrict. The second is a hash that includes the list of users who may access the action, and the URL to send the user to if they don’t have permission. The third is a message to show the user if they don’t have permission. We will inject this method call into the PagesController so it is called when the controller is instantiated like so:
1 2 3 4 5 6 |
Admin::PagesController.class_eval do
only_allow_access_to :create, :new,
{:when => ["admin"], :denied_url => {:controller => 'pages', :action => 'index'},
:denied_message => "You don't have permission to perform this action."}
end |
This restricts access to the create and new actions to ensure that the user cannot access the new page form or use some other means to submit a post request with the new page data.
Reload your server, log in as a developer and travel back to http://localhost:3000/admin/pages/1/children/new. You will see that you cannot access this page. So that’s it, developers can no longer create new pages!
3 Comments
Hi Lorien,
thank you very much for this article! It was exactly what I was looking for, thank you!
- Johannes
Hi lorien,
This article is just what I was looking for! I was wondering, though, if you would mind giving me a quick bit of help on the line “admin.page.index[:node].delete(‘add_child_column’)”. I’m trying to remove the ability to update status from the new/edit pages, but I’m not sure how to use this syntax to remove parts of a partial. If you could help I’d really appreciate it. Thanks again!
Actually, I finally figured it out! Only problem is now that i try to conditionally add it back, I get an error on @page.fields. :\