Rails 4 custom admin backend - ruby-on-rails

I'm wanting to create an admin backend for a practice app. After reading around I've come to a general idea of what to do but would like some clarification:
Use namespace to route the backend at example.com/admin...
Put any adminifiable resources inside namespace e.g resources :posts
At this point do I duplicate the normal (public facing) controllers and put them into the admin directory? (Along with CRUD views)
In theory the public facing controllers only need index and show actions, right? As new/create/update/destroy will only be accessed in the admin/controller.
Any clarification or advice is greatly appreciated. Just trying to wrap my head around this.

I would recommend against duplicating your controllers, or any part of your application for that matter. That goes entirely against the DRY principle, which stands for "Don't Repeat Yourself." Duplicate code becomes really hard to maintain and test as your application grows.
Instead, I would recommend limiting access to certain actions using before filters. For example, let's say that you want users to be able to create posts, read posts and see listings of posts. In your PostsController, you can have something like this:
before_action :admin_user?, only: [:edit, :destroy]
Note: before_action is just the new name for before_filters.
So then actions like index would execute normally for all users, but if a user calls the destroy action, the controller would first check to see if the user is an admin, by calling an admin_user? method (typically defined in ApplicationController). This method could be a simple conditional, like "if the user is not an admin, flash an error message and redirect them back to where they were before the request" and then use it to protect any action or resource you want. You could also use it in the views to show delete buttons on posts only if the user is an admin, for instance.
That's for resource-specific actions. Often times it's also a good idea to have a section of the site that consolidates resource views and administrative actions. This would be its own controller/view (I call mine AdminController) and you can protect all actions in it with the above method:
before_action :admin_user?
To make your resources available to AdminController using the methods defined inside the individual resource controllers, you can do this in routes.rb:
namespace :admin do
resources :users
end
This will make it so that http://yoursite.com/admin/users/index will still call the index action in the Users controller, but it will happen within the context of an admin user (because of the before_action above).

Related

Do actions that are not defined in the controller need authorization such as before_action nevertheless?

My UsersController does not have the actions destroy, index and show defined, as they won't be needed. I'll delete users out of the database and a users page or a user index page won't be available in the application.
However, is it necessary to secure those actions nonetheless with a filter such as before_action :correct_user to provide maximum security or is there no way a potential attacker could somehow manipulate actions in order to view or destroy users?
Besides that, does a create and new action of a PostsController need to be protected by a filter as well? Read: Is it possible for people with too much time on their hands to create posts with the ID of other users?
Also, what are some ways to make sure all actions are bulletproof? I am currently using TDD for this -- any alternatives?
I am rather new to authorization and security – are there any good resources on this topic? Books, articles, screencasts will do.
You can just restrict your resource routes so that those actions are not available.
Full details are available in the routing guide:
http://guides.rubyonrails.org/routing.html#restricting-the-routes-created
Something like this would do it:
resources :users, except: [:index, :show, :destroy]
If you have not defined those actions, your controller will not do anything, except trying to render the corresponding view templates (e.g. app/views/users/show.html.erb), which should not exist if you do not need them.
While I do not see security concerns, it is better not to create routes for actions that are not defined: if the routes exist, the action is not defined, and the view template does not exist, your users will get an HTTP status 500 (internal server error), instead of the correct status 404 (not found). If you are using resources in your routes.rb file, you should exclude the actions that you do not need:
resources :users, except: [:destroy, :index, :show]
Regarding your PostsController, you should always create objects as the current_user, and ignore whatever user id an attacker might add to the parameters of the request. If you are using strong parameters, there is no reason to even have user_id in the permitted parameters.
You should also check that a user is signed in whenever necessary; if you are managing authentication with devise, that would be before_action :authenticate_user!: this will redirect the user to the sign in page when they try to access a protected action without signing in first.
My favorite book on testing (with RSpec) is Everyday Rails Testing with RSpec.

Creating Questions in Ruby on Rails

I have been following this guide to make a Ruby on Rails web app:
http://guides.rubyonrails.org/getting_started.html
It is supposed to be a quiz where people answer questions.
I have made :questions a resource. However, the guide mentions a page on its website to be able to create and delete its resource articles. Obviously this makes no sense with quiz questions as I just want to create them once and after that no more can be created or deleted. However, there is no mention of this in the guide?
If you don't want to be able to create/update/destroy your questions through controller, you can exclude these restful routes with :except, or specify the routes you need with :only which is more obvious(take a look at the routing guide):
resources :questions, only: [:index, :show]
This will create following routes:
GET /questions questions#index
GET /questions/:id questions#show
To fill your database with questions, use seeds(which are described in AR migrations guide). Put your questions creation related code into db/seeds.rb file and run rake db:seed afterwards.
You should specify in your routes to only :show the questions.
Do something like this in your routes:
resources :questions, only: [:show]
So as much I understand, you want to create resources and data on your own but don't want other users to be able to create or delete a resource, right?
let's start with the basics:
In MVC, the model/resource is the main representation of the entities.
The routes file indicate what routes are available at the first place
for each entity to which HTML requests can be sent. It also matches the route with a respective controller action. The controller
provides a gateway for users to interact with your application, takes
a request from them, take actions needed( issue SQL commands to create, show, delete from the database etc) and give a suitable response at the end.
So, suppose you want users to create a new resource on your server. First, you provide a route on which they can send a request. Then, you give them a create new resource button in your view through which they can interact. That button will use the route, match it with a controller action and send a request to that controller method. Depending on what is inside the controller action, the controller then creates the resource and redirect the user with a 302 with a notification.
Now, suppose you don't want a request to go through. What will you do?
You will firstly not create a route on which a request can be sent. You will also not create a controller action on which request can be received.
That's the use case in your scenario.
Since you don't want users to be able to send a request to the server to create the resource:
Don't provide the routes for them to send any such HTML request
Don't provide any controller which will receive such request
And obviously, don't provide any button in the view to take that action.
Now, the question would be: Then how will I create the resource?
You have three options:
Use a seed file: You can use a seed file where you add all the data you want. This tutorial and many other resources on the web can help you out. http://www.xyzpub.com/en/ruby-on-rails/3.2/seed_rb.html
Use rails console: Give create commands: Question.create(:name => "hello", :description => "How you doing!"). See the link here: https://apidock.com/rails/ActiveRecord/Base/create/class
If you are a beginner, you need to work a little more before trying this: Create authorized users and what actions they can take. You can also create an admin dashboard and provide all actions to admin users using activeadmin gem.
This blog explains the process: http://www.onceaday.today/subjects/1/posts/9 3
Finally, as you do need to show all the questions: create only show route, match it with a controller action, write the method in the controller action which issues a SQL command to get all the resource from the database and give the respective show view the data it can render.
Hope it helps you out!

A good approach to different indexes of same resource in Rails?

Something I've been wondering about is the best approach to creating a different index view of the same resource in Rails.
For example, let's say you have a group of Users, and the current Index action of the Users Controller that generates a tabular view of the users. You then want to create a second Index view that plotted all the users on a map. Would the best approach be to create a new action in the Users controller, or to create a new controller (like UsersMap) with a new index?
Thoughts appreciated...
My route file looks like this:
resources :users, :only => [:index, :index_with_map, :show, :edit, :update] do
match '/index_with_map' => 'users#index_with_map'
end
But hitting "users/index_with_map" results in:
ActiveRecord::RecordNotFound at /admin/users/index_with_map Couldn't find User with id=index_with_map
You could add that action to the UsersController, but if you are going to have more actions including Users and Maps, you might wanna think of making a new controller (to conform to the Single Responsibility Principle).
From the guides:
If you find yourself adding many extra actions to a resourceful route,
it’s time to stop and ask yourself whether you’re disguising the
presence of another resource.
You could do this:
resources :users do
collection do
get 'index_with_map'
end
end
This will enable Rails to recognize a path such as /users/index_with_map with GET.
The best approach would be to add new action that will deal with the users map. Then you will only need to create the corresponding view file for that action.
The users are for the same table, the functionality is only different. So why the need of different controller?
Also if you will create the another controller just for map view then there is no use of that as it's just a wastage of resources.
Another point is -
Controllers provides the user to interact with the model. So two controllers means you typically want two models. So different controllers are used when you want to do different (categories of) things.

How to organize controller in moderately large Rails application?

I'm working on an application with quite a few related models and would like to hear some opinions on how best to organize the controllers.
Here are some options I have been considering:
1) Namespace the controllers. So, for example, have a controllers/admin directory and a controllers/public directory. This seems appealing for organization but also sorta contrived since a single resource could often have actions which might sensibly belong in different directories (e.g. the show action is public whereas the create action is admin). So this would mean breaking up some of my resources into two separate controllers - one public, one admin. Seems bad.
2) Create nested resources. I've only occasionally used nested resources so it's not always clear to me when it's best to nest resources versus simply passing the data you need through the params explicitly. Does anyone has some suggestions/examples of how best to use nested resources? When is it a good idea? When is it an overkill?
3) Just leave the default scaffolded controllers alone. Create new collection/member actions where needed and use before filters to set permissions within each controller. This seems most appealing since it keeps things simple upfront. But I'm sorta nervous about things getting messy down the line as some of the controllers might start to bloat with several new actions.
If anyone with experience designing large applications could offer some guidance here, it'd greatly appreciated.
For organizing within our applications, I have done a little bit of everything depending on the situation.
First, regarding the separate controllers for admin/user functions, I will say that you probably don't want to go that route. We used authorization and before_filter to manage rights within the application. We rolled our own, but 20/20 hind-sight, we should have use CanCan. From there you can setup something like this (this is pseudo-code, actual language would depend on how you implemented authorization):
before_filter :can_edit_user, :only => [:new, :create, :edit, :update] #or :except => [:index, :show]
protected
def can_edit_user
redirect_to never_never_land_path unless current_user.has_rights?('edit_user')
end
Or at a higher level
before_filter :require_admin, :only [:new, :create]
and in your application controller
def require_admin
redirect_to never_never_land_path unless current_user.administrator?
end
It would depend which route, but I would use that for authorization instead of splitting up controllers.
As far as name spaces vs. Nested Resources, it depends on the situation. In several of our apps, we have both. We use name spaces when there is a cause for a logical separation or there will be shared functions between a group of controllers. Case and point for us is we put administrative functions within a namespace, and within we have users, roles and other proprietary admin functions.
map.namespace :admin do |admin|
admin.resources :users
admin.resources :roles
end
and then within those controllers we have a base controller, that stores our shared functions.
class Admin::Base < ApplicationController
before_filter :require_admin
end
class Admin::UsersController < Admin::Base
def new
....
end
This provides us logical separation of data and the ability to dry up our code a bit by sharing things like the before_filter.
We use nested controllers if there is going to be a section of code where you want some things to persist between controllers. The case from our application is our customers. We search for and load a customer and then within that customer, they have orders, tickets, locations. Within that area we have the customer loaded while we look at the different tabs.
map.resources :customers do |customer|
customer.resources :tickets
customer.resources :orders
customer.resources :locations
end
and that gives us urls:
customers/:id
customers/:customer_id/orders/:id
customers/:customer_id/tickets/:id
Other advantages we have experienced from this is ease of setting up menu systems and tabs. These structures lend themselves well to an organized site.
I hope this helps!
Also, looks like nesting resources more than one level deep is almost certainly a bad idea:
http://weblog.jamisbuck.org/2007/2/5/nesting-resources
Yeah, it's a old article, but it makes a lot of sense to me.
If anyone disagrees, I'd like to hear why.

Rails-way of structuring admin/user/public controllers

A fictitious Rails app has the following resources:
Photographers
Images
Comments
A Photographer has many Images, that have many Comments
Each photographer has a login and is able to view, upload, edit and delete their images, comments as well as their own profile.
An administration interface is available and can edit both images, photographers and comments.
Furthermore, the photographer, their images and their comments are available from a public interface without login where visitors can add comments.
My question is: What is the Rails-way of structuring the controllers? I was thinking of going with namespaces for each 'role' (public, account, admin) like this:
# For administrator
Admin::PhotographersController
Admin::ImagesController
Admin::CommentsController
# For a logged in photographer
AccountController (?)
Account::ImagesController
Account::CommentsController
# For public
PhotographersController
ImagesController
CommentsController
However - some of the methods of these controllers are overlapping. Is this the best way, even though it's not that DRY?
Thanks!
If they are overlapping, you could extend the base controllers into the account/admin namespaces. eg you do your ImagesController which is for the actions everyone can see. This extends ApplicationController as normal. Then you do your admin version of ImageController, and that extends ImagesController. Then you add/override methods in the admin version for the required different behaviours, or it may just be as simple as adding a couple of before filters such as require_admin for example, which checks that current_user is an admin user, and redirects them away if not.
as you said, this is not really DRY. at least, you could structure routes and controllers to act for all the requirements, for example:
resources :photos, :only => [:index, :show] # offer only index and show actions to public
scope "/admin" do
resources :photos # full access for logged in users
end
if you also need index and show actions, you can add some checks inside them to load a different view (say you have a public and admin layouts).
another way could be to have a unique layout, no /admin/ sections and offer editing features to logged in users. so if logged and owner of some photo, allow editing and show context links. it's a matter of tastes :P

Resources